@@ -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
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4