Skip to content

Commit 693e5c4

Browse files
committed
feat: reset active item when query has changed
Also: do not reset query when selecting custom level
1 parent 840592c commit 693e5c4

File tree

2 files changed

+96
-8
lines changed

2 files changed

+96
-8
lines changed

src/components/LevelSelect.tsx

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { Button, Classes, MenuDivider, MenuItem } from '@blueprintjs/core'
2+
import { getCreateNewItem } from '@blueprintjs/select'
23

34
import clsx from 'clsx'
45
import Fuse from 'fuse.js'
5-
import { FC, useMemo } from 'react'
6+
import { FC, useEffect, useMemo, useState } from 'react'
67

78
import { useLevels } from '../apis/level'
8-
import { createCustomLevel, isHardMode } from '../models/level'
9+
import { createCustomLevel, isCustomLevel, isHardMode } from '../models/level'
910
import { Level } from '../models/operation'
1011
import { useDebouncedQuery } from '../utils/useDebouncedQuery'
1112
import { Select } from './Select'
@@ -40,7 +41,17 @@ export const LevelSelect: FC<LevelSelectProps> = ({
4041
)
4142

4243
const { query, debouncedQuery, updateQuery, onOptionMouseDown } =
43-
useDebouncedQuery()
44+
useDebouncedQuery({
45+
onDebouncedQueryChange: (value) => {
46+
if (value !== debouncedQuery) {
47+
// 清空 activeItem,之后会自动设置为第一项
48+
setActiveItem(null)
49+
}
50+
},
51+
})
52+
const [activeItem, setActiveItem] = useState<Level | 'createNewItem' | null>(
53+
null,
54+
)
4455

4556
const selectedLevel = useMemo(() => {
4657
const level = levels.find((el) => el.stageId === value)
@@ -103,10 +114,26 @@ export const LevelSelect: FC<LevelSelectProps> = ({
103114
: levels
104115
}, [debouncedQuery, selectedLevel, levels, fuse])
105116

117+
useEffect(() => {
118+
if (!selectedLevel) {
119+
setActiveItem(null)
120+
} else if (isCustomLevel(selectedLevel)) {
121+
setActiveItem('createNewItem')
122+
} else {
123+
setActiveItem(selectedLevel)
124+
}
125+
}, [selectedLevel])
126+
106127
return (
107128
<Select<Level>
108129
items={levels}
109130
itemListPredicate={() => filteredLevels}
131+
activeItem={
132+
activeItem === 'createNewItem' ? getCreateNewItem() : activeItem
133+
}
134+
onActiveItemChange={(item, isCreateNewItem) => {
135+
setActiveItem(isCreateNewItem ? 'createNewItem' : item)
136+
}}
110137
query={query}
111138
onQueryChange={(query) => updateQuery(query, false)}
112139
onReset={() => onChange('')}
@@ -132,19 +159,22 @@ export const LevelSelect: FC<LevelSelectProps> = ({
132159
}
133160
selectedItem={selectedLevel}
134161
onItemSelect={(level) => {
135-
// 重置 query 以显示同类关卡
136-
updateQuery('', true)
162+
if (!isCustomLevel(level)) {
163+
// 重置 query 以显示同类关卡
164+
updateQuery('', true)
165+
}
137166
onChange(level.stageId)
138167
}}
139168
createNewItemFromQuery={(query) => createCustomLevel(query)}
140169
createNewItemRenderer={(query, active, handleClick) => (
141170
<MenuItem
142171
key="create-new-item"
143172
roleStructure="listoption"
144-
text={`使用自定义关卡名 "${query}"`}
173+
className={clsx(active && Classes.ACTIVE)}
174+
text={`直接搜索关卡 "${query}"`}
145175
icon="text-highlight"
146176
onClick={handleClick}
147-
active={active}
177+
selected={selectedLevel && isCustomLevel(selectedLevel)}
148178
/>
149179
)}
150180
inputProps={{

src/components/Select.tsx

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { Button, ButtonProps, Label } from '@blueprintjs/core'
2-
import { Select2, Select2Props } from '@blueprintjs/select'
2+
import {
3+
QueryList,
4+
Select2,
5+
Select2Props,
6+
isCreateNewItem,
7+
} from '@blueprintjs/select'
38

49
import clsx from 'clsx'
510

@@ -32,6 +37,8 @@ export const Select = <T,>({
3237
),
3338
}}
3439
{...props}
40+
// 传给 QueryList,给下面的补丁用
41+
{...{ _parentType: 'Select' }}
3542
/>
3643
{canReset && (
3744
<Button
@@ -50,3 +57,54 @@ export const Select = <T,>({
5057
</Label>
5158
)
5259
}
60+
61+
// 修复 BP 的远古 bug:https://github.com/palantir/blueprint/issues/3751
62+
// 补丁只对 Select 组件启用,因为不知道对 Suggest 和 MultiSelect 是否有效,先不管了
63+
64+
const originalSetQuery =
65+
(QueryList.prototype.setQuery as any)._original ??
66+
QueryList.prototype.setQuery
67+
QueryList.prototype.setQuery = function (...args) {
68+
;(this as any)._isCallingSetQuery = true
69+
originalSetQuery.apply(this, args)
70+
;(this as any)._isCallingSetQuery = false
71+
}
72+
;(QueryList.prototype.setQuery as any)._original = originalSetQuery
73+
74+
const originalGetActiveIndex =
75+
(QueryList.prototype['getActiveIndex'] as any)._original ??
76+
QueryList.prototype['getActiveIndex']
77+
QueryList.prototype['getActiveIndex'] = function (items) {
78+
if (
79+
this.props['_parentType'] === 'Select' &&
80+
(this as any)._isCallingSetQuery
81+
) {
82+
const activeItem =
83+
this.props.activeItem === undefined
84+
? this.state.activeItem
85+
: this.props.activeItem
86+
87+
if (isCreateNewItem(activeItem)) {
88+
// bug 1:如果 activeItem 是 createNewItem,QueryList 会直接将 activeItem 刷新为第一个 item,
89+
// 为了阻止这种行为,这里返回 0 以绕过 activeIndex < 0 的判断:https://github.com/palantir/blueprint/blob/e365c08b2f133ad102de8cdf687b9609e824d96c/packages/select/src/components/query-list/queryList.tsx#L328
90+
return 0
91+
}
92+
93+
return originalGetActiveIndex.call(
94+
{
95+
props: this.props,
96+
state: {
97+
...this.state,
98+
99+
// bug 2:QueryList 在 setState({ activeItem: props.activeItem }) 之后并未等待 state 更新就直接读取 state.activeItem,
100+
// 导致 activeItem 仍然是旧值,所以这里直接使用 props.activeItem 来覆盖
101+
activeItem,
102+
},
103+
},
104+
items,
105+
)
106+
}
107+
return originalGetActiveIndex.call(this, items)
108+
}
109+
;(QueryList.prototype['getActiveIndex'] as any)._original =
110+
originalGetActiveIndex

0 commit comments

Comments
 (0)