sprintify-ui 0.10.61 → 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.
@@ -1778,6 +1778,13 @@ declare const __VLS_self: import("vue").DefineComponent<import("vue").ExtractPro
1778
1778
  type: StringConstructor;
1779
1779
  default(): string;
1780
1780
  };
1781
+ /**
1782
+ * Function to search local data
1783
+ */
1784
+ search: {
1785
+ default: undefined;
1786
+ type: PropType<(items: Collection, search: string | null) => Collection>;
1787
+ };
1781
1788
  }>, {
1782
1789
  t: typeof t;
1783
1790
  BaseDataIterator: typeof BaseDataIterator;
@@ -2066,11 +2073,19 @@ declare const __VLS_self: import("vue").DefineComponent<import("vue").ExtractPro
2066
2073
  type: StringConstructor;
2067
2074
  default(): string;
2068
2075
  };
2076
+ /**
2077
+ * Function to search local data
2078
+ */
2079
+ search: {
2080
+ default: undefined;
2081
+ type: PropType<(items: Collection, search: string | null) => Collection>;
2082
+ };
2069
2083
  }>> & Readonly<{
2070
2084
  onDelete?: ((...args: any[]) => any) | undefined;
2071
2085
  "onUpdate:checked-rows"?: ((...args: any[]) => any) | undefined;
2072
2086
  onFetch?: ((...args: any[]) => any) | undefined;
2073
2087
  }>, {
2088
+ search: (items: Collection, search: string | null) => Collection;
2074
2089
  size: "xs" | "sm" | "md" | "lg" | "xl";
2075
2090
  items: CollectionItem[] | undefined;
2076
2091
  actions: ActionItem[];
@@ -2360,6 +2375,13 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
2360
2375
  type: StringConstructor;
2361
2376
  default(): string;
2362
2377
  };
2378
+ /**
2379
+ * Function to search local data
2380
+ */
2381
+ search: {
2382
+ default: undefined;
2383
+ type: PropType<(items: Collection, search: string | null) => Collection>;
2384
+ };
2363
2385
  }>, {
2364
2386
  fetch: typeof fetch;
2365
2387
  fetchWithoutLoading: typeof fetchWithoutLoading;
@@ -2620,11 +2642,19 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
2620
2642
  type: StringConstructor;
2621
2643
  default(): string;
2622
2644
  };
2645
+ /**
2646
+ * Function to search local data
2647
+ */
2648
+ search: {
2649
+ default: undefined;
2650
+ type: PropType<(items: Collection, search: string | null) => Collection>;
2651
+ };
2623
2652
  }>> & Readonly<{
2624
2653
  onDelete?: ((...args: any[]) => any) | undefined;
2625
2654
  "onUpdate:checked-rows"?: ((...args: any[]) => any) | undefined;
2626
2655
  onFetch?: ((...args: any[]) => any) | undefined;
2627
2656
  }>, {
2657
+ search: (items: Collection, search: string | null) => Collection;
2628
2658
  size: "xs" | "sm" | "md" | "lg" | "xl";
2629
2659
  items: CollectionItem[] | undefined;
2630
2660
  actions: ActionItem[];
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sprintify-ui",
3
- "version": "0.10.61",
3
+ "version": "0.10.63",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "build": "rimraf dist && vue-tsc && vite build",
@@ -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,20 +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?.filter((item) => {
795
- return Object.values(item).some((value) => {
796
- if (typeof value === 'string') {
797
- return value.toLowerCase().includes(searchWords);
798
- }
799
- return false;
800
- });
801
- });
793
+ return items.filter((it) => deepIncludes(it, searchKeywords.value));
802
794
  }
803
795
 
804
796
  function sortItems(items: Collection | undefined) {
@@ -14,6 +14,7 @@
14
14
  :sections="sectionsInternal"
15
15
  :scroll-top-on-fetch="maxHeight ? false : scrollTopOnFetch"
16
16
  :filters-position="filtersPosition"
17
+ :search="search"
17
18
  @fetch="onFetch"
18
19
  @will-scroll-top="onWillScrollTop"
19
20
  >
@@ -562,6 +563,14 @@ const props = defineProps({
562
563
  return window.location.pathname;
563
564
  },
564
565
  },
566
+
567
+ /**
568
+ * Function to search local data
569
+ */
570
+ search: {
571
+ default: undefined,
572
+ type: Function as PropType<(items: Collection, search: string | null) => Collection>,
573
+ }
565
574
  });
566
575
 
567
576
  const sizeInternal = useInputSize(props.size);
@@ -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
+ }