Skip to content

Commit 7e681a8

Browse files
committed
feat: add
eoLayout option
1 parent 81ba9a9 commit 7e681a8

File tree

5 files changed

+220
-26
lines changed

5 files changed

+220
-26
lines changed

src/components/OperationCard.tsx

Lines changed: 151 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
DrawerSize,
66
Elevation,
77
H4,
8+
H5,
89
Icon,
910
Tag
1011
} from '@blueprintjs/core'
@@ -15,16 +16,17 @@ import { handleCopyShortCode, handleDownloadJSON } from 'services/operation'
1516

1617
import { RelativeTime } from 'components/RelativeTime'
1718
import { OperationRating } from 'components/viewer/OperationRating'
18-
import { OperationListItem } from 'models/operation'
19+
import { OpDifficulty, OperationListItem } from 'models/operation'
1920

2021
import { useLevels } from '../apis/arknights'
2122
import { CopilotDocV1 } from '../models/copilot.schema'
2223
import { createCustomLevel, findLevelByStageName } from '../models/level'
2324
import { Paragraphs } from './Paragraphs'
24-
import { EDifficultyLevel } from './entity/ELevel'
25+
import { EDifficulty } from './entity/EDifficulty'
26+
import { EDifficultyLevel, NeoELevel } from './entity/ELevel'
2527
import { OperationViewer } from './viewer/OperationViewer'
2628

27-
export const OperationCard = ({
29+
export const NeoOperationCard = ({
2830
operation,
2931
operationDoc,
3032
}: {
@@ -97,13 +99,11 @@ export const OperationCard = ({
9799
</Tooltip2>
98100
</div>
99101
<div className="flex items-center text-slate-900">
100-
<EDifficultyLevel
101-
level={
102-
findLevelByStageName(levels, operationDoc.stageName) ||
103-
createCustomLevel(operationDoc.stageName)
104-
}
105-
difficulty={operationDoc.difficulty}
106-
/>
102+
<div className="flex flex-wrap">
103+
<NeoELevel level={findLevelByStageName(levels, operationDoc.stageName) ||
104+
createCustomLevel(operationDoc.stageName)} />
105+
<EDifficulty difficulty={operationDoc.difficulty ?? OpDifficulty.UNKNOWN} />
106+
</div>
107107
</div>
108108
<div className='flex-1 flex flex-col gap-2 justify-center'>
109109
<div className="text-gray-700 leading-normal">
@@ -162,6 +162,147 @@ export const OperationCard = ({
162162
)
163163
}
164164

165+
export const OperationCard = ({
166+
operation,
167+
operationDoc,
168+
}: {
169+
operation: OperationListItem
170+
operationDoc: CopilotDocV1.Operation
171+
}) => {
172+
const levels = useLevels()?.data?.data || []
173+
const [drawerOpen, setDrawerOpen] = useState(false)
174+
175+
return (
176+
<>
177+
<Drawer
178+
size={DrawerSize.LARGE}
179+
isOpen={drawerOpen}
180+
onClose={() => setDrawerOpen(false)}
181+
>
182+
<OperationViewer
183+
operationId={operation.id}
184+
onCloseDrawer={() => setDrawerOpen(false)}
185+
/>
186+
</Drawer>
187+
188+
<Card
189+
interactive={true}
190+
elevation={Elevation.TWO}
191+
className="mb-4 sm:mb-2 last:mb-0"
192+
onClick={() => setDrawerOpen(true)}
193+
>
194+
<div className="flex flex-wrap mb-4 sm:mb-2">
195+
{/* title */}
196+
<div className="flex flex-col gap-3">
197+
<div className="flex gap-2">
198+
<H4 className="inline-block pb-1 border-b-2 border-zinc-200 border-solid mb-2">
199+
{operationDoc.doc.title}
200+
</H4>
201+
<Tooltip2
202+
placement="bottom"
203+
content={
204+
<div className="max-w-sm dark:text-slate-900">
205+
下载原 JSON
206+
</div>
207+
}
208+
>
209+
<Button
210+
small
211+
icon="download"
212+
onClick={(e) => {
213+
e.stopPropagation()
214+
handleDownloadJSON(operationDoc)
215+
}}
216+
/>
217+
</Tooltip2>
218+
<Tooltip2
219+
placement="bottom"
220+
content={
221+
<div className="max-w-sm dark:text-slate-900">
222+
复制神秘代码
223+
</div>
224+
}
225+
>
226+
<Button
227+
small
228+
icon="clipboard"
229+
onClick={(e) => {
230+
e.stopPropagation()
231+
handleCopyShortCode(operation)
232+
}}
233+
/>
234+
</Tooltip2>
235+
</div>
236+
<H5 className="flex items-center text-slate-900 -mt-3">
237+
<EDifficultyLevel
238+
level={
239+
findLevelByStageName(levels, operationDoc.stageName) ||
240+
createCustomLevel(operationDoc.stageName)
241+
}
242+
difficulty={operationDoc.difficulty}
243+
/>
244+
</H5>
245+
</div>
246+
247+
<div className="lg:flex-1 hidden" />
248+
249+
{/* meta */}
250+
<div className="flex flex-col flex-1 gap-y-1.5 gap-x-4">
251+
<div className="flex flex-wrap sm:justify-end items-center gap-x-4 gap-y-1 text-zinc-500">
252+
<div className="flex items-center gap-1.5">
253+
<Icon icon="star" />
254+
<OperationRating
255+
className="text-sm"
256+
operation={operation}
257+
layout="horizontal"
258+
/>
259+
</div>
260+
261+
<Tooltip2 placement="top" content={`访问量:${operation.views}`}>
262+
<div>
263+
<Icon icon="eye-open" className="mr-1.5" />
264+
<span>{operation.views}</span>
265+
</div>
266+
</Tooltip2>
267+
268+
<div>
269+
<Icon icon="time" className="mr-1.5" />
270+
<RelativeTime
271+
Tooltip2Props={{ placement: 'top' }}
272+
moment={operation.uploadTime}
273+
/>
274+
</div>
275+
</div>
276+
<div className="text-zinc-500 self-end">
277+
<Tooltip2 placement="top" content={`作者:${operation.uploader}`}>
278+
<div>
279+
<Icon icon="user" className="mr-1.5" />
280+
<span>{operation.uploader}</span>
281+
</div>
282+
</Tooltip2>
283+
</div>
284+
</div>
285+
</div>
286+
<div className="flex md:flex-row flex-col gap-4">
287+
<div className="text-gray-700 leading-normal md:w-1/2">
288+
{/* <div className="text-sm text-zinc-600 mb-2 font-bold">作业描述</div> */}
289+
<Paragraphs
290+
content={operationDoc.doc.details}
291+
limitHeight={21 * 13.5} // 13 lines, 21px per line; the extra 0.5 line is intentional so the `mask` effect is obvious
292+
/>
293+
</div>
294+
<div className="md:w-1/2">
295+
<div className="text-sm text-zinc-600 mb-2 font-bold">
296+
干员/干员组
297+
</div>
298+
<OperatorTags operationDoc={operationDoc} />
299+
</div>
300+
</div>
301+
</Card>
302+
</>
303+
)
304+
}
305+
165306
const OperatorTags = ({
166307
operationDoc: { opers, groups },
167308
}: {

src/components/OperationList.tsx

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import { Button, NonIdealState } from '@blueprintjs/core'
22

33
import { UseOperationsParams, useOperations } from 'apis/query'
4-
import { ComponentType, useMemo } from 'react'
4+
import { ComponentType, ReactNode, useMemo } from 'react'
55

66
import { toCopilotOperation } from '../models/converter'
77
import { CopilotDocV1 } from '../models/copilot.schema'
88
import { OperationListItem } from '../models/operation'
9-
import { OperationCard } from './OperationCard'
9+
import { NeoOperationCard, OperationCard } from './OperationCard'
1010
import { withSuspensable } from './Suspensable'
1111

12-
export const OperationList: ComponentType<UseOperationsParams> =
12+
export const OperationList: ComponentType<UseOperationsParams & { neoLayout?: boolean }> =
1313
withSuspensable(
1414
(props) => {
1515
const { operations, size, setSize, isValidating, isReachingEnd } =
@@ -28,17 +28,30 @@ export const OperationList: ComponentType<UseOperationsParams> =
2828
return { operation, doc }
2929
})
3030

31+
const { neoLayout = true } = props
32+
33+
let items: ReactNode = operationsWithDoc.map(({ operation, doc }) => (
34+
<OperationCard
35+
operation={operation}
36+
operationDoc={doc}
37+
key={operation.id}
38+
/>
39+
))
40+
if (neoLayout) {
41+
items = <div className='grid gap-4' style={{ gridTemplateColumns: "repeat(auto-fill, minmax(20rem, 1fr)" }}>
42+
{operationsWithDoc.map(({ operation, doc }) => (
43+
<NeoOperationCard
44+
operation={operation}
45+
operationDoc={doc}
46+
key={operation.id}
47+
/>
48+
))}
49+
</div>
50+
}
51+
3152
return (
3253
<>
33-
<div className='grid gap-4' style={{ gridTemplateColumns: "repeat(auto-fill, minmax(20rem, 1fr)" }}>
34-
{operationsWithDoc.map(({ operation, doc }) => (
35-
<OperationCard
36-
operation={operation}
37-
operationDoc={doc}
38-
key={operation.id}
39-
/>
40-
))}
41-
</div>
54+
{items}
4255

4356
{isReachingEnd && operations.length === 0 && (
4457
<NonIdealState

src/components/Operations.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
Card,
55
FormGroup,
66
InputGroup,
7+
Switch
78
} from '@blueprintjs/core'
89

910
import { UseOperationsParams } from 'apis/query'
@@ -14,6 +15,7 @@ import { ComponentType, useMemo, useState } from 'react'
1415
import { CardTitle } from 'components/CardTitle'
1516
import { OperationList } from 'components/OperationList'
1617

18+
import { neoLayoutAtom } from 'store/pref'
1719
import { authAtom } from '../store/auth'
1820
import { OperatorSelect } from './OperatorSelect'
1921
import { withSuspensable } from './Suspensable'
@@ -27,6 +29,7 @@ export const Operations: ComponentType = withSuspensable(() => {
2729
[],
2830
)
2931
const [authState] = useAtom(authAtom)
32+
const [neoLayout, setNeoLayout] = useAtom(neoLayoutAtom)
3033

3134
return (
3235
<>
@@ -86,7 +89,7 @@ export const Operations: ComponentType = withSuspensable(() => {
8689
}
8790
/>
8891
</FormGroup>
89-
<FormGroup label="排序" contentClassName="flex flex-wrap gap-y-2">
92+
<FormGroup label="排序" contentClassName="flex flex-wrap gap-y-2 gap-x-2">
9093
<ButtonGroup>
9194
<Button
9295
icon="flame"
@@ -130,11 +133,17 @@ export const Operations: ComponentType = withSuspensable(() => {
130133
看看我的
131134
</Button>
132135
)}
136+
137+
<Switch checked={neoLayout} onChange={(e) => {
138+
setNeoLayout((e.target as HTMLInputElement).checked)
139+
}}>
140+
启用新版布局
141+
</Switch>
133142
</FormGroup>
134143
</Card>
135144

136145
<div className="tabular-nums">
137-
<OperationList {...queryParams} />
146+
<OperationList {...queryParams} neoLayout={neoLayout} />
138147
</div>
139148
</>
140149
)

src/components/entity/ELevel.tsx

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Tag } from '@blueprintjs/core'
1+
import { H4, Tag } from '@blueprintjs/core'
22

33
import { FC } from 'react'
44

@@ -7,7 +7,7 @@ import { Level, OpDifficulty } from 'models/operation'
77

88
import { isCustomLevel } from '../../models/level'
99

10-
export const ELevel: FC<{
10+
export const NeoELevel: FC<{
1111
className?: string
1212
level: Level
1313
}> = ({ level }) => {
@@ -34,6 +34,34 @@ export const ELevel: FC<{
3434
)
3535
}
3636

37+
export const ELevel: FC<{
38+
className?: string
39+
level: Level
40+
}> = ({ level }) => {
41+
let { catOne, catTwo, catThree } = level
42+
43+
if (isCustomLevel(level)) {
44+
catOne = '自定义关卡'
45+
catTwo = ''
46+
catThree = level.name
47+
}
48+
49+
return (
50+
<Tag className="transition border border-solid !text-xs tracking-tight !p-1 leading-none !min-h-0 bg-slate-200 border-slate-300 text-slate-700">
51+
<div className="flex items-center mx-1">
52+
<div className="flex flex-col mr-2">
53+
<H4 className="inline-block font-bold my-auto">{catThree}</H4>
54+
</div>
55+
<div className="flex flex-col">
56+
<span className="text-xs font-light">{catOne}</span>
57+
<span className="text-xs">{catTwo}</span>
58+
</div>
59+
</div>
60+
</Tag>
61+
)
62+
}
63+
64+
3765
export const EDifficultyLevel: FC<{
3866
level: Level
3967
difficulty?: OpDifficulty

src/store/pref.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { atomWithStorage } from "jotai/utils"
2+
3+
export const neoLayoutAtom = atomWithStorage("neo-layout", true)

0 commit comments

Comments
 (0)