sprintify-ui 0.10.62 → 0.10.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface SearchOpts {
|
|
2
|
+
maxDepth?: number;
|
|
3
|
+
maxCharsPerItem?: number;
|
|
4
|
+
maxArray?: number;
|
|
5
|
+
cache?: WeakMap<object, string>;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Recursively searches an item (object, array, primitive) for a case-insensitive match of the query string.
|
|
9
|
+
* Limits can be set to avoid excessive CPU usage on large/deep items.
|
|
10
|
+
* @param item The item to search within.
|
|
11
|
+
* @param queryRaw The query string to search for.
|
|
12
|
+
* @param opts Optional search options.
|
|
13
|
+
*/
|
|
14
|
+
export declare function deepIncludes(item: any, queryRaw: string, opts?: SearchOpts): boolean;
|
package/package.json
CHANGED
|
@@ -188,6 +188,7 @@ type Direction = 'asc' | 'desc';
|
|
|
188
188
|
import { Size } from '@/utils/sizes';
|
|
189
189
|
import BaseInput from './BaseInput.vue';
|
|
190
190
|
import { useInputSize } from '@/composables/inputSize';
|
|
191
|
+
import { deepIncludes } from '@/utils/deepIncludes';
|
|
191
192
|
|
|
192
193
|
const DEFAULT_QUERY = {
|
|
193
194
|
page: 1,
|
|
@@ -785,22 +786,11 @@ function searchItems(items: Collection | undefined) {
|
|
|
785
786
|
return [];
|
|
786
787
|
}
|
|
787
788
|
|
|
788
|
-
const searchWords = searchKeywords.value.toLowerCase();
|
|
789
|
-
|
|
790
789
|
if (props.search) {
|
|
791
790
|
return props.search(items, searchKeywords.value);
|
|
792
791
|
}
|
|
793
792
|
|
|
794
|
-
return items
|
|
795
|
-
return Object.values(item).some((value) => {
|
|
796
|
-
if (typeof value === 'string') {
|
|
797
|
-
return value.toLowerCase().includes(searchWords);
|
|
798
|
-
}
|
|
799
|
-
if (typeof value === 'object') {
|
|
800
|
-
return JSON.stringify(value).toLowerCase().includes(searchWords);
|
|
801
|
-
}
|
|
802
|
-
});
|
|
803
|
-
});
|
|
793
|
+
return items.filter((it) => deepIncludes(it, searchKeywords.value));
|
|
804
794
|
}
|
|
805
795
|
|
|
806
796
|
function sortItems(items: Collection | undefined) {
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export interface SearchOpts {
|
|
2
|
+
maxDepth?: number; // stop descending after this depth
|
|
3
|
+
maxCharsPerItem?: number; // stop scanning item after reading this many chars
|
|
4
|
+
maxArray?: number; // cap how many array items to inspect per array
|
|
5
|
+
cache?: WeakMap<object, string>; // optional: lazy per-item cache of a “search blob”
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Recursively searches an item (object, array, primitive) for a case-insensitive match of the query string.
|
|
10
|
+
* Limits can be set to avoid excessive CPU usage on large/deep items.
|
|
11
|
+
* @param item The item to search within.
|
|
12
|
+
* @param queryRaw The query string to search for.
|
|
13
|
+
* @param opts Optional search options.
|
|
14
|
+
*/
|
|
15
|
+
export function deepIncludes(item: any, queryRaw: string, opts: SearchOpts = {}): boolean {
|
|
16
|
+
const q = queryRaw.toLowerCase();
|
|
17
|
+
const {
|
|
18
|
+
maxDepth = 3,
|
|
19
|
+
maxCharsPerItem = 3000,
|
|
20
|
+
maxArray = 64,
|
|
21
|
+
cache,
|
|
22
|
+
} = opts;
|
|
23
|
+
|
|
24
|
+
let consumed = 0;
|
|
25
|
+
const seen = new WeakSet<object>();
|
|
26
|
+
|
|
27
|
+
const feed = (s: string): boolean => {
|
|
28
|
+
// consume without allocating new strings repeatedly
|
|
29
|
+
consumed += s.length;
|
|
30
|
+
return s.toLowerCase().includes(q) || consumed >= maxCharsPerItem;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const visit = (val: any, depth: number): boolean => {
|
|
34
|
+
if (val == null) return false;
|
|
35
|
+
if (consumed >= maxCharsPerItem) return false;
|
|
36
|
+
|
|
37
|
+
const t = typeof val;
|
|
38
|
+
|
|
39
|
+
if (t === 'string') return feed(val);
|
|
40
|
+
if (t === 'number' || t === 'boolean') return feed(String(val));
|
|
41
|
+
if (t === 'bigint') return feed(val.toString());
|
|
42
|
+
if (t === 'symbol' || t === 'function') return false;
|
|
43
|
+
|
|
44
|
+
// objects & arrays
|
|
45
|
+
if (t === 'object') {
|
|
46
|
+
if (seen.has(val)) return false;
|
|
47
|
+
seen.add(val);
|
|
48
|
+
|
|
49
|
+
// Optional lazy cache: build once per object identity
|
|
50
|
+
if (cache && cache.has(val)) {
|
|
51
|
+
return feed(cache.get(val)!); // already lowercased when stored? not necessary since feed lowercases
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (depth >= maxDepth) return false;
|
|
55
|
+
|
|
56
|
+
if (Array.isArray(val)) {
|
|
57
|
+
const len = Math.min(val.length, maxArray);
|
|
58
|
+
for (let i = 0; i < len; i++) {
|
|
59
|
+
if (visit(val[i], depth + 1)) return true;
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// plain object: iterate own enumerable keys only
|
|
65
|
+
for (const k in val) {
|
|
66
|
+
// match on key names too (often helpful if values are IDs)
|
|
67
|
+
if (feed(k)) return true;
|
|
68
|
+
if (visit(val[k], depth + 1)) return true;
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return false;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
return visit(item, 0);
|
|
77
|
+
}
|