@@ -10,6 +10,7 @@ import Icon from '../icon'
10
10
import { hasProp , filterEmpty , getOptionProps , getStyle , getClass , getAttrs , getComponentFromProp , isValidElement } from '../_util/props-util'
11
11
import BaseMixin from '../_util/BaseMixin'
12
12
import { cloneElement } from '../_util/vnode'
13
+ import warning from '../_util/warning'
13
14
14
15
const CascaderOptionType = PropTypes . shape ( {
15
16
value : PropTypes . string ,
@@ -32,6 +33,7 @@ const ShowSearchType = PropTypes.shape({
32
33
render : PropTypes . func ,
33
34
sort : PropTypes . func ,
34
35
matchInputWidth : PropTypes . bool ,
36
+ limit : PropTypes . oneOfType ( [ Boolean , Number ] ) ,
35
37
} ) . loose
36
38
function noop ( ) { }
37
39
@@ -78,6 +80,9 @@ const CascaderProps = {
78
80
suffixIcon : PropTypes . any ,
79
81
}
80
82
83
+ // We limit the filtered item count by default
84
+ const defaultLimit = 50
85
+
81
86
function defaultFilterOption ( inputValue , path , names ) {
82
87
return path . some ( option => option [ names . label ] . indexOf ( inputValue ) > - 1 )
83
88
}
@@ -99,6 +104,26 @@ function getFilledFieldNames ({ fieldNames = {}}) {
99
104
return names
100
105
}
101
106
107
+ function flattenTree (
108
+ options = [ ] ,
109
+ props ,
110
+ ancestor = [ ] ,
111
+ ) {
112
+ const names = getFilledFieldNames ( props )
113
+ let flattenOptions = [ ]
114
+ const childrenName = names . children
115
+ options . forEach ( option => {
116
+ const path = ancestor . concat ( option )
117
+ if ( props . changeOnSelect || ! option [ childrenName ] || ! option [ childrenName ] . length ) {
118
+ flattenOptions . push ( path )
119
+ }
120
+ if ( option [ childrenName ] ) {
121
+ flattenOptions = flattenOptions . concat ( flattenTree ( option [ childrenName ] , props , path ) )
122
+ }
123
+ } )
124
+ return flattenOptions
125
+ }
126
+
102
127
const defaultDisplayRender = ( { labels } ) => labels . join ( ' / ' )
103
128
104
129
const Cascader = {
@@ -110,9 +135,13 @@ const Cascader = {
110
135
prop : 'value' ,
111
136
event : 'change' ,
112
137
} ,
138
+ inject : {
139
+ configProvider : { default : { } } ,
140
+ localeData : { default : { } } ,
141
+ } ,
113
142
data ( ) {
114
143
this . cachedOptions = [ ]
115
- const { value, defaultValue, popupVisible, showSearch, options, flattenTree } = this
144
+ const { value, defaultValue, popupVisible, showSearch, options } = this
116
145
return {
117
146
sValue : value || defaultValue || [ ] ,
118
147
inputValue : '' ,
@@ -137,7 +166,7 @@ const Cascader = {
137
166
} ,
138
167
options ( val ) {
139
168
if ( this . showSearch ) {
140
- this . setState ( { flattenOptions : this . flattenTree ( this . options , this . $props ) } )
169
+ this . setState ( { flattenOptions : flattenTree ( val , this . $props ) } )
141
170
}
142
171
} ,
143
172
} ,
@@ -171,11 +200,11 @@ const Cascader = {
171
200
172
201
handlePopupVisibleChange ( popupVisible ) {
173
202
if ( ! hasProp ( this , 'popupVisible' ) ) {
174
- this . setState ( {
203
+ this . setState ( state => ( {
175
204
sPopupVisible : popupVisible ,
176
205
inputFocused : popupVisible ,
177
- inputValue : popupVisible ? this . inputValue : '' ,
178
- } )
206
+ inputValue : popupVisible ? state . inputValue : '' ,
207
+ } ) )
179
208
}
180
209
this . $emit ( 'popupVisibleChange' , popupVisible )
181
210
} ,
@@ -244,36 +273,42 @@ const Cascader = {
244
273
}
245
274
} ,
246
275
247
- flattenTree ( options , props , ancestor = [ ] ) {
248
- const names = getFilledFieldNames ( props )
249
- let flattenOptions = [ ]
250
- const childrenName = names . children
251
- options . forEach ( ( option ) => {
252
- const path = ancestor . concat ( option )
253
- if ( props . changeOnSelect || ! option [ childrenName ] || ! option [ childrenName ] . length ) {
254
- flattenOptions . push ( path )
255
- }
256
- if ( option [ childrenName ] ) {
257
- flattenOptions = flattenOptions . concat (
258
- this . flattenTree ( option [ childrenName ] , props , path )
259
- )
260
- }
261
- } )
262
- return flattenOptions
263
- } ,
264
-
265
276
generateFilteredOptions ( prefixCls ) {
266
277
const { showSearch, notFoundContent, $scopedSlots } = this
267
278
const names = getFilledFieldNames ( this . $props )
268
279
const {
269
280
filter = defaultFilterOption ,
270
281
// render = this.defaultRenderFilteredOption,
271
282
sort = defaultSortFilteredOption ,
283
+ limit = defaultLimit ,
272
284
} = showSearch
273
- const { flattenOptions = [ ] , inputValue } = this . $data
274
285
const render = showSearch . render || $scopedSlots . showSearchRender || this . defaultRenderFilteredOption
275
- const filtered = flattenOptions . filter ( ( path ) => filter ( inputValue , path , names ) )
276
- . sort ( ( a , b ) => sort ( a , b , inputValue , names ) )
286
+ const { flattenOptions = [ ] , inputValue } = this . $data
287
+
288
+ // Limit the filter if needed
289
+ let filtered
290
+ if ( limit > 0 ) {
291
+ filtered = [ ]
292
+ let matchCount = 0
293
+
294
+ // Perf optimization to filter items only below the limit
295
+ flattenOptions . some ( path => {
296
+ const match = filter ( inputValue , path , names )
297
+ if ( match ) {
298
+ filtered . push ( path )
299
+ matchCount += 1
300
+ }
301
+ return matchCount >= limit
302
+ } )
303
+ } else {
304
+ warning (
305
+ typeof limit !== 'number' ,
306
+ "'limit' of showSearch in Cascader should be positive number or false." ,
307
+ )
308
+ filtered = flattenOptions . filter ( path => filter ( inputValue , path , names ) )
309
+ }
310
+
311
+ filtered . sort ( ( a , b ) => sort ( a , b , inputValue , names ) )
277
312
278
313
if ( filtered . length > 0 ) {
279
314
return filtered . map ( ( path ) => {
@@ -307,14 +342,22 @@ const Cascader = {
307
342
} ,
308
343
309
344
render ( ) {
310
- const { $slots, sPopupVisible, inputValue, $listeners } = this
345
+ const { $slots, sPopupVisible, inputValue, $listeners, configProvider , localeData } = this
311
346
const { sValue : value , inputFocused } = this . $data
312
347
const props = getOptionProps ( this )
313
348
let suffixIcon = getComponentFromProp ( this , 'suffixIcon' )
314
349
suffixIcon = Array . isArray ( suffixIcon ) ? suffixIcon [ 0 ] : suffixIcon
350
+ const { getPopupContainer : getContextPopupContainer } = configProvider
315
351
const {
316
- prefixCls, inputPrefixCls, placeholder, size, disabled,
317
- allowClear, showSearch = false , ...otherProps } = props
352
+ prefixCls,
353
+ inputPrefixCls,
354
+ placeholder = localeData . placeholder ,
355
+ size,
356
+ disabled,
357
+ allowClear,
358
+ showSearch = false ,
359
+ ...otherProps
360
+ } = props
318
361
319
362
const sizeCls = classNames ( {
320
363
[ `${ inputPrefixCls } -lg` ] : size === 'large' ,
@@ -448,9 +491,11 @@ const Cascader = {
448
491
< Icon type = 'redo' spin />
449
492
</ span >
450
493
)
494
+ const getPopupContainer = props . getPopupContainer || getContextPopupContainer
451
495
const cascaderProps = {
452
496
props : {
453
497
...props ,
498
+ getPopupContainer,
454
499
options : options ,
455
500
value : value ,
456
501
popupVisible : sPopupVisible ,
0 commit comments