@@ -21,12 +21,19 @@ export default {
21
21
filterIncludedFields : {
22
22
type : Array
23
23
// default: undefined
24
+ } ,
25
+ filterDebounce : {
26
+ type : [ Number , String ] ,
27
+ default : 0 ,
28
+ validator : val => / ^ \d + / . test ( String ( val ) )
24
29
}
25
30
} ,
26
31
data ( ) {
27
32
return {
28
33
// Flag for displaying which empty slot to show and some event triggering
29
- isFiltered : false
34
+ isFiltered : false ,
35
+ // Where we store the copy of the filter citeria after debouncing
36
+ localFilter : null
30
37
}
31
38
} ,
32
39
computed : {
@@ -36,6 +43,9 @@ export default {
36
43
computedFilterIncluded ( ) {
37
44
return this . filterIncludedFields ? concat ( this . filterIncludedFields ) . filter ( Boolean ) : null
38
45
} ,
46
+ computedFilterDebounce ( ) {
47
+ return parseInt ( this . filterDebounce , 10 ) || 0
48
+ } ,
39
49
localFiltering ( ) {
40
50
return this . hasProvider ? ! ! this . noProviderFiltering : true
41
51
} ,
@@ -47,22 +57,6 @@ export default {
47
57
localFilter : this . localFilter
48
58
}
49
59
} ,
50
- // Sanitized/normalized version of filter prop
51
- localFilter ( ) {
52
- // Using internal filter function, which only accepts string or RegExp
53
- if (
54
- this . localFiltering &&
55
- ! isFunction ( this . filterFunction ) &&
56
- ! ( isString ( this . filter ) || isRegExp ( this . filter ) )
57
- ) {
58
- return ''
59
- }
60
-
61
- // Could be a string, object or array, as needed by external filter function
62
- // We use `cloneDeep` to ensure we have a new copy of an object or array
63
- // without Vue reactive observers
64
- return cloneDeep ( this . filter )
65
- } ,
66
60
// Sanitized/normalize filter-function prop
67
61
localFilterFn ( ) {
68
62
// Return `null` to signal to use internal filter function
@@ -72,13 +66,14 @@ export default {
72
66
// Returns the original `localItems` array if not sorting
73
67
filteredItems ( ) {
74
68
const items = this . localItems || [ ]
69
+ // Note the criteria is debounced
70
+ const criteria = this . filterSanitize ( this . localFilter )
75
71
76
72
// Resolve the filtering function, when requested
77
73
// We prefer the provided filtering function and fallback to the internal one
78
74
// When no filtering criteria is specified the filtering factories will return `null`
79
75
let filterFn = null
80
76
if ( this . localFiltering ) {
81
- const criteria = this . localFilter
82
77
filterFn =
83
78
this . filterFnFactory ( this . localFilterFn , criteria ) ||
84
79
this . defaultFilterFnFactory ( criteria )
@@ -94,6 +89,32 @@ export default {
94
89
}
95
90
} ,
96
91
watch : {
92
+ // Watch for debounce being set to 0
93
+ computedFilterDebounce ( newVal , oldVal ) {
94
+ if ( ! newVal && this . filterTimer ) {
95
+ clearTimeout ( this . filterTimer )
96
+ this . filterTimer = null
97
+ this . localFilter = this . filter
98
+ }
99
+ } ,
100
+ // Watch for changes to the filter criteria, and debounce if necessary
101
+ filter ( newFilter , oldFilter ) {
102
+ const timeout = this . computedFilterDebounce
103
+ if ( this . filterTimer ) {
104
+ clearTimeout ( this . filterTimer )
105
+ this . filterTimer = null
106
+ }
107
+ if ( timeout ) {
108
+ // If we have a debounce time, delay the update of this.localFilter
109
+ this . filterTimer = setTimeout ( ( ) => {
110
+ this . filterTimer = null
111
+ this . localFilter = this . filterSanitize ( this . filter )
112
+ } , timeout )
113
+ } else {
114
+ // Otherwise, immediately update this.localFilter
115
+ this . localFilter = this . filterSanitize ( this . filter )
116
+ }
117
+ } ,
97
118
// Watch for changes to the filter criteria and filtered items vs localItems).
98
119
// And set visual state and emit events as required
99
120
filteredCheck ( { filteredItems, localItems, localFilter } ) {
@@ -123,13 +144,42 @@ export default {
123
144
}
124
145
} ,
125
146
created ( ) {
147
+ // Create non-reactive prop where we store the debounce timer id
148
+ this . filterTimer = null
149
+ // If filter is "pre-set", set the criteria
150
+ // This will trigger any watchers/dependants
151
+ this . localFilter = this . filterSanitize ( this . filter )
126
152
// Set the initial filtered state.
127
153
// In a nextTick so that we trigger a filtered event if needed
128
154
this . $nextTick ( ( ) => {
129
155
this . isFiltered = Boolean ( this . localFilter )
130
156
} )
131
157
} ,
158
+ beforeDestroy ( ) {
159
+ /* istanbul ignore next */
160
+ if ( this . filterTimer ) {
161
+ clearTimeout ( this . filterTimer )
162
+ this . filterTimer = null
163
+ }
164
+ } ,
132
165
methods : {
166
+ filterSanitize ( criteria ) {
167
+ // Sanitizes filter criteria based on internal or external filtering
168
+ if (
169
+ this . localFiltering &&
170
+ ! isFunction ( this . filterFunction ) &&
171
+ ! ( isString ( criteria ) || isRegExp ( criteria ) )
172
+ ) {
173
+ // If using internal filter function, which only accepts string or RegExp
174
+ // return null to signify no filter
175
+ return null
176
+ }
177
+
178
+ // Could be a string, object or array, as needed by external filter function
179
+ // We use `cloneDeep` to ensure we have a new copy of an object or array
180
+ // without Vue's reactive observers
181
+ return cloneDeep ( criteria )
182
+ } ,
133
183
// Filter Function factories
134
184
filterFnFactory ( filterFn , criteria ) {
135
185
// Wrapper factory for external filter functions
0 commit comments