Skip to content

Commit 2ec48a1

Browse files
committed
feat: rework search panel
1 parent 5252475 commit 2ec48a1

File tree

13 files changed

+737
-382
lines changed

13 files changed

+737
-382
lines changed

src/apis/operation.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,18 @@ import { useSWRRefresh } from 'utils/swr'
1515

1616
export type OrderBy = 'views' | 'hot' | 'id'
1717

18+
export interface OperatorFilterParams {
19+
included: string[]
20+
excluded: string[]
21+
}
22+
1823
export interface UseOperationsParams {
1924
limit?: number
2025
orderBy?: OrderBy
2126
descending?: boolean
2227
keyword?: string
2328
levelKeyword?: string
24-
operator?: string
29+
operator?: OperatorFilterParams
2530
operationIds?: number[]
2631
uploaderId?: string
2732

@@ -84,7 +89,12 @@ export function useOperations({
8489
page: pageIndex + 1,
8590
document: keyword,
8691
levelKeyword,
87-
operator,
92+
operator: operator
93+
? [
94+
...operator.included,
95+
...operator.excluded.map((o) => `~${o}`),
96+
].join(',') || undefined
97+
: undefined,
8898
orderBy,
8999
desc: descending,
90100
copilotIds: operationIds,

src/components/LevelSelect.tsx

Lines changed: 71 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
import { MenuDivider, MenuItem } from '@blueprintjs/core'
1+
import { Button, Classes, MenuDivider, MenuItem } from '@blueprintjs/core'
22

33
import clsx from 'clsx'
44
import Fuse from 'fuse.js'
5-
import { FC, Fragment, useMemo } from 'react'
5+
import { FC, useMemo } from 'react'
66

77
import { useLevels } from '../apis/level'
88
import { createCustomLevel, isHardMode } from '../models/level'
99
import { Level } from '../models/operation'
10-
import { Suggest } from './Suggest'
10+
import { useDebouncedQuery } from '../utils/useDebouncedQuery'
11+
import { Select } from './Select'
1112

1213
interface LevelSelectProps {
1314
className?: string
@@ -29,7 +30,6 @@ export const LevelSelect: FC<LevelSelectProps> = ({
2930
.sort((a, b) => a.levelId.localeCompare(b.levelId)),
3031
[data],
3132
)
32-
3333
const fuse = useMemo(
3434
() =>
3535
new Fuse(levels, {
@@ -39,15 +39,24 @@ export const LevelSelect: FC<LevelSelectProps> = ({
3939
[levels],
4040
)
4141

42-
// value 可以由用户输入,所以可以是任何值,只有用 stageId 才能匹配到唯一的关卡
43-
const selectedLevel = useMemo(
44-
() => levels.find((el) => el.stageId === value) ?? null,
45-
[levels, value],
46-
)
42+
const { query, debouncedQuery, updateQuery, onOptionMouseDown } =
43+
useDebouncedQuery()
44+
45+
const selectedLevel = useMemo(() => {
46+
const level = levels.find((el) => el.stageId === value)
47+
if (level) {
48+
return level
49+
}
50+
// 如果有 value 但匹配不到,就创建一个自定义关卡来显示
51+
if (value) {
52+
return createCustomLevel(value)
53+
}
54+
return undefined
55+
}, [levels, value])
4756

48-
const search = (query: string) => {
49-
// 如果 query 和当前关卡完全匹配(也就是唯一对应),就显示同类关卡
50-
if (selectedLevel && selectedLevel.stageId === query) {
57+
const filteredLevels = useMemo(() => {
58+
// 未输入 query 时显示同类关卡
59+
if (selectedLevel && !debouncedQuery) {
5160
let similarLevels: Level[]
5261
let headerName: string
5362

@@ -83,55 +92,78 @@ export const LevelSelect: FC<LevelSelectProps> = ({
8392
}
8493

8594
if (similarLevels.length > 1) {
86-
const header = createCustomLevel(headerName)
87-
header.stageId = 'header'
95+
const header = createCustomLevel('header')
96+
header.name = headerName
8897
return [header, ...similarLevels]
8998
}
9099
}
91100

92-
return query ? fuse.search(query).map((el) => el.item) : levels
93-
}
101+
return debouncedQuery.trim()
102+
? fuse.search(debouncedQuery).map((el) => el.item)
103+
: levels
104+
}, [debouncedQuery, selectedLevel, levels, fuse])
94105

95106
return (
96-
<Suggest<Level>
97-
updateQueryOnSelect
107+
<Select<Level>
98108
items={levels}
99-
itemListPredicate={search}
109+
itemListPredicate={() => filteredLevels}
110+
query={query}
111+
onQueryChange={(query) => updateQuery(query, false)}
100112
onReset={() => onChange('')}
101-
className={clsx(className, selectedLevel && '[&_input]:italic')}
113+
className={clsx('items-stretch', className)}
114+
itemsEqual={(a, b) => a.stageId === b.stageId}
115+
itemDisabled={(item) => item.stageId === 'header'} // 避免 header 被选中为 active
102116
itemRenderer={(item, { handleClick, handleFocus, modifiers }) =>
103117
item.stageId === 'header' ? (
104-
<Fragment key="header">
105-
<div className="ml-2 text-zinc-500 text-xs">{item.name}</div>
106-
<MenuDivider />
107-
</Fragment>
118+
<MenuDivider key="header" title={item.name} />
108119
) : (
109120
<MenuItem
121+
roleStructure="listoption"
110122
key={item.stageId}
123+
className={clsx(modifiers.active && Classes.ACTIVE)}
111124
text={`${item.catThree} ${item.name}`}
112125
onClick={handleClick}
113126
onFocus={handleFocus}
114-
selected={modifiers.active}
127+
onMouseDown={onOptionMouseDown}
128+
selected={item === selectedLevel}
115129
disabled={modifiers.disabled}
116130
/>
117131
)
118132
}
119133
selectedItem={selectedLevel}
120-
onItemSelect={(level) => onChange(level.stageId)}
121-
inputValueRenderer={(item) => item.stageId}
122-
noResults={<MenuItem disabled text="没有可选的关卡" />}
134+
onItemSelect={(level) => {
135+
// 重置 query 以显示同类关卡
136+
updateQuery('', true)
137+
onChange(level.stageId)
138+
}}
139+
createNewItemFromQuery={(query) => createCustomLevel(query)}
140+
createNewItemRenderer={(query, active, handleClick) => (
141+
<MenuItem
142+
key="create-new-item"
143+
roleStructure="listoption"
144+
text={`使用自定义关卡名 "${query}"`}
145+
icon="text-highlight"
146+
onClick={handleClick}
147+
active={active}
148+
/>
149+
)}
123150
inputProps={{
124-
placeholder: '关卡名、关卡类型、关卡编号',
125-
leftIcon: 'area-of-interest',
126-
large: true,
127-
size: 64,
128-
onBlur: (e) => {
129-
// 失焦时直接把 query 提交上去,用于处理关卡未匹配的情况
130-
if (value !== e.target.value) {
131-
onChange(e.target.value)
132-
}
133-
},
151+
placeholder: '关卡名、类型、编号',
134152
}}
135-
/>
153+
popoverProps={{
154+
minimal: true,
155+
}}
156+
>
157+
{
158+
<Button
159+
minimal
160+
className="!pl-3 !pr-2"
161+
icon="area-of-interest"
162+
rightIcon="chevron-down"
163+
>
164+
{selectedLevel ? selectedLevel.catThree : '关卡'}
165+
</Button>
166+
}
167+
</Select>
136168
)
137169
}

0 commit comments

Comments
 (0)