@@ -95,17 +95,6 @@ const OptionList: React.ForwardRefRenderFunction<ReviseRefOptionListProps> = (_,
95
95
[ memoTreeData , isOverMaxCount , checkedKeys ] ,
96
96
) ;
97
97
98
- // ========================== Active ==========================
99
- const [ activeKey , setActiveKey ] = React . useState < Key > ( null ) ;
100
- const activeEntity = keyEntities [ activeKey as SafeKey ] ;
101
-
102
- const onActiveChange = ( key : Key ) => {
103
- if ( isOverMaxCount && ! checkedKeys . includes ( key ) ) {
104
- return ;
105
- }
106
- setActiveKey ( key ) ;
107
- } ;
108
-
109
98
// ========================== Values ==========================
110
99
const mergedCheckedKeys = React . useMemo ( ( ) => {
111
100
if ( ! checkable ) {
@@ -123,18 +112,31 @@ const OptionList: React.ForwardRefRenderFunction<ReviseRefOptionListProps> = (_,
123
112
// Single mode should scroll to current key
124
113
if ( open && ! multiple && checkedKeys . length ) {
125
114
treeRef . current ?. scrollTo ( { key : checkedKeys [ 0 ] } ) ;
126
- setActiveKey ( checkedKeys [ 0 ] ) ;
127
115
}
128
116
// eslint-disable-next-line react-hooks/exhaustive-deps
129
117
} , [ open ] ) ;
130
118
131
- // ========================== Search ==========================
132
- const lowerSearchValue = String ( searchValue ) . toLowerCase ( ) ;
133
- const filterTreeNode = ( treeNode : EventDataNode < any > ) => {
134
- if ( ! lowerSearchValue ) {
135
- return false ;
119
+ // ========================== Events ==========================
120
+ const onListMouseDown : React . MouseEventHandler < HTMLDivElement > = event => {
121
+ event . preventDefault ( ) ;
122
+ } ;
123
+
124
+ const onInternalSelect = ( __ : Key [ ] , info : TreeEventInfo ) => {
125
+ const { node } = info ;
126
+
127
+ if ( checkable && isCheckDisabled ( node ) ) {
128
+ return ;
129
+ }
130
+
131
+ const isSelected = ! checkedKeys . includes ( node . key ) ;
132
+
133
+ onSelect ( node . key , {
134
+ selected : isSelected ,
135
+ } ) ;
136
+
137
+ if ( ! multiple ) {
138
+ toggleOpen ( false ) ;
136
139
}
137
- return String ( treeNode [ treeNodeFilterProp ] ) . toLowerCase ( ) . includes ( lowerSearchValue ) ;
138
140
} ;
139
141
140
142
// =========================== Keys ===========================
@@ -148,13 +150,6 @@ const OptionList: React.ForwardRefRenderFunction<ReviseRefOptionListProps> = (_,
148
150
return searchValue ? searchExpandedKeys : expandedKeys ;
149
151
} , [ expandedKeys , searchExpandedKeys , treeExpandedKeys , searchValue ] ) ;
150
152
151
- React . useEffect ( ( ) => {
152
- if ( searchValue ) {
153
- setSearchExpandedKeys ( getAllKeys ( treeData , fieldNames ) ) ;
154
- }
155
- // eslint-disable-next-line react-hooks/exhaustive-deps
156
- } , [ searchValue ] ) ;
157
-
158
153
const onInternalExpand = ( keys : Key [ ] ) => {
159
154
setExpandedKeys ( keys ) ;
160
155
setSearchExpandedKeys ( keys ) ;
@@ -164,28 +159,78 @@ const OptionList: React.ForwardRefRenderFunction<ReviseRefOptionListProps> = (_,
164
159
}
165
160
} ;
166
161
167
- // ========================== Events ==========================
168
- const onListMouseDown : React . MouseEventHandler < HTMLDivElement > = event => {
169
- event . preventDefault ( ) ;
162
+ // ========================== Search ==========================
163
+ const lowerSearchValue = String ( searchValue ) . toLowerCase ( ) ;
164
+ const filterTreeNode = ( treeNode : EventDataNode < any > ) => {
165
+ if ( ! lowerSearchValue ) {
166
+ return false ;
167
+ }
168
+ return String ( treeNode [ treeNodeFilterProp ] ) . toLowerCase ( ) . includes ( lowerSearchValue ) ;
170
169
} ;
171
170
172
- const onInternalSelect = ( __ : Key [ ] , info : TreeEventInfo ) => {
173
- const { node } = info ;
171
+ React . useEffect ( ( ) => {
172
+ if ( searchValue ) {
173
+ setSearchExpandedKeys ( getAllKeys ( treeData , fieldNames ) ) ;
174
+ }
175
+ // eslint-disable-next-line react-hooks/exhaustive-deps
176
+ } , [ searchValue ] ) ;
174
177
175
- if ( checkable && isCheckDisabled ( node ) ) {
178
+ // ========================== Get First Selectable Node ==========================
179
+ const getFirstMatchingNode = ( nodes : EventDataNode < any > [ ] ) : EventDataNode < any > | null => {
180
+ for ( const node of nodes ) {
181
+ if ( node . disabled || node . selectable === false ) {
182
+ continue ;
183
+ }
184
+
185
+ if ( searchValue ) {
186
+ if ( filterTreeNode ( node ) ) {
187
+ return node ;
188
+ }
189
+ } else {
190
+ return node ;
191
+ }
192
+
193
+ if ( node [ fieldNames . children ] ) {
194
+ const matchInChildren = getFirstMatchingNode ( node [ fieldNames . children ] ) ;
195
+ if ( matchInChildren ) {
196
+ return matchInChildren ;
197
+ }
198
+ }
199
+ }
200
+ return null ;
201
+ } ;
202
+
203
+ // ========================== Active ==========================
204
+ const [ activeKey , setActiveKey ] = React . useState < Key > ( null ) ;
205
+ const activeEntity = keyEntities [ activeKey as SafeKey ] ;
206
+
207
+ const onActiveChange = ( key : Key ) => {
208
+ if ( isOverMaxCount && ! checkedKeys . includes ( key ) ) {
176
209
return ;
177
210
}
211
+ setActiveKey ( key ) ;
212
+ } ;
178
213
179
- const isSelected = ! checkedKeys . includes ( node . key ) ;
214
+ React . useEffect ( ( ) => {
215
+ if ( ! open ) {
216
+ return ;
217
+ }
218
+ let nextActiveKey = null ;
180
219
181
- onSelect ( node . key , {
182
- selected : isSelected ,
183
- } ) ;
220
+ const getFirstNode = ( ) => {
221
+ const firstNode = getFirstMatchingNode ( memoTreeData ) ;
222
+ return firstNode ? firstNode [ fieldNames . value ] : null ;
223
+ } ;
184
224
185
- if ( ! multiple ) {
186
- toggleOpen ( false ) ;
225
+ // single mode active first checked node
226
+ if ( ! multiple && checkedKeys . length && ! searchValue ) {
227
+ nextActiveKey = checkedKeys [ 0 ] ;
228
+ } else {
229
+ nextActiveKey = getFirstNode ( ) ;
187
230
}
188
- } ;
231
+
232
+ setActiveKey ( nextActiveKey ) ;
233
+ } , [ open , searchValue ] ) ;
189
234
190
235
// ========================= Keyboard =========================
191
236
React . useImperativeHandle ( ref , ( ) => ( {
@@ -204,8 +249,8 @@ const OptionList: React.ForwardRefRenderFunction<ReviseRefOptionListProps> = (_,
204
249
// >>> Select item
205
250
case KeyCode . ENTER : {
206
251
if ( activeEntity ) {
207
- const { selectable, value } = activeEntity ?. node || { } ;
208
- if ( selectable !== false ) {
252
+ const { selectable, value, disabled } = activeEntity ?. node || { } ;
253
+ if ( selectable !== false && ! disabled ) {
209
254
onInternalSelect ( null , {
210
255
node : { key : activeKey } ,
211
256
selected : ! checkedKeys . includes ( value ) ,
0 commit comments