@@ -10,6 +10,7 @@ import Icon from '../icon'
1010import { hasProp , filterEmpty , getOptionProps , getStyle , getClass , getAttrs , getComponentFromProp , isValidElement } from '../_util/props-util'
1111import BaseMixin from '../_util/BaseMixin'
1212import { cloneElement } from '../_util/vnode'
13+ import warning from '../_util/warning'
1314
1415const CascaderOptionType = PropTypes . shape ( {
1516 value : PropTypes . string ,
@@ -32,6 +33,7 @@ const ShowSearchType = PropTypes.shape({
3233 render : PropTypes . func ,
3334 sort : PropTypes . func ,
3435 matchInputWidth : PropTypes . bool ,
36+ limit : PropTypes . oneOfType ( [ Boolean , Number ] ) ,
3537} ) . loose
3638function noop ( ) { }
3739
@@ -78,6 +80,9 @@ const CascaderProps = {
7880 suffixIcon : PropTypes . any ,
7981}
8082
83+ // We limit the filtered item count by default
84+ const defaultLimit = 50
85+
8186function defaultFilterOption ( inputValue , path , names ) {
8287 return path . some ( option => option [ names . label ] . indexOf ( inputValue ) > - 1 )
8388}
@@ -99,6 +104,26 @@ function getFilledFieldNames ({ fieldNames = {}}) {
99104 return names
100105}
101106
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+
102127const defaultDisplayRender = ( { labels } ) => labels . join ( ' / ' )
103128
104129const Cascader = {
@@ -110,9 +135,13 @@ const Cascader = {
110135 prop : 'value' ,
111136 event : 'change' ,
112137 } ,
138+ inject : {
139+ configProvider : { default : { } } ,
140+ localeData : { default : { } } ,
141+ } ,
113142 data ( ) {
114143 this . cachedOptions = [ ]
115- const { value, defaultValue, popupVisible, showSearch, options, flattenTree } = this
144+ const { value, defaultValue, popupVisible, showSearch, options } = this
116145 return {
117146 sValue : value || defaultValue || [ ] ,
118147 inputValue : '' ,
@@ -137,7 +166,7 @@ const Cascader = {
137166 } ,
138167 options ( val ) {
139168 if ( this . showSearch ) {
140- this . setState ( { flattenOptions : this . flattenTree ( this . options , this . $props ) } )
169+ this . setState ( { flattenOptions : flattenTree ( val , this . $props ) } )
141170 }
142171 } ,
143172 } ,
@@ -171,11 +200,11 @@ const Cascader = {
171200
172201 handlePopupVisibleChange ( popupVisible ) {
173202 if ( ! hasProp ( this , 'popupVisible' ) ) {
174- this . setState ( {
203+ this . setState ( state => ( {
175204 sPopupVisible : popupVisible ,
176205 inputFocused : popupVisible ,
177- inputValue : popupVisible ? this . inputValue : '' ,
178- } )
206+ inputValue : popupVisible ? state . inputValue : '' ,
207+ } ) )
179208 }
180209 this . $emit ( 'popupVisibleChange' , popupVisible )
181210 } ,
@@ -244,36 +273,42 @@ const Cascader = {
244273 }
245274 } ,
246275
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-
265276 generateFilteredOptions ( prefixCls ) {
266277 const { showSearch, notFoundContent, $scopedSlots } = this
267278 const names = getFilledFieldNames ( this . $props )
268279 const {
269280 filter = defaultFilterOption ,
270281 // render = this.defaultRenderFilteredOption,
271282 sort = defaultSortFilteredOption ,
283+ limit = defaultLimit ,
272284 } = showSearch
273- const { flattenOptions = [ ] , inputValue } = this . $data
274285 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 ) )
277312
278313 if ( filtered . length > 0 ) {
279314 return filtered . map ( ( path ) => {
@@ -307,14 +342,22 @@ const Cascader = {
307342 } ,
308343
309344 render ( ) {
310- const { $slots, sPopupVisible, inputValue, $listeners } = this
345+ const { $slots, sPopupVisible, inputValue, $listeners, configProvider , localeData } = this
311346 const { sValue : value , inputFocused } = this . $data
312347 const props = getOptionProps ( this )
313348 let suffixIcon = getComponentFromProp ( this , 'suffixIcon' )
314349 suffixIcon = Array . isArray ( suffixIcon ) ? suffixIcon [ 0 ] : suffixIcon
350+ const { getPopupContainer : getContextPopupContainer } = configProvider
315351 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
318361
319362 const sizeCls = classNames ( {
320363 [ `${ inputPrefixCls } -lg` ] : size === 'large' ,
@@ -448,9 +491,11 @@ const Cascader = {
448491 < Icon type = 'redo' spin />
449492 </ span >
450493 )
494+ const getPopupContainer = props . getPopupContainer || getContextPopupContainer
451495 const cascaderProps = {
452496 props : {
453497 ...props ,
498+ getPopupContainer,
454499 options : options ,
455500 value : value ,
456501 popupVisible : sPopupVisible ,
0 commit comments