Skip to content

Commit 749aad2

Browse files
committed
feat(useFilter): add dependency injection support
Adds createFilter, createFilterContext, and useFilterContext exports for sharing filter configuration across component trees via Vue's provide/inject system.
1 parent 1ff1447 commit 749aad2

File tree

1 file changed

+167
-25
lines changed
  • packages/0/src/composables/useFilter

1 file changed

+167
-25
lines changed

packages/0/src/composables/useFilter/index.ts

Lines changed: 167 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,34 +9,63 @@
99
* - Case-insensitive filtering
1010
* - Custom filter functions
1111
* - Reactive updates
12+
* - Context-based DI support
1213
* - Perfect for search, multi-criteria filtering
1314
*
1415
* Filters arrays based on query strings with configurable matching strategies.
1516
*/
1617

18+
// Factories
19+
import { createContext, useContext } from '#v0/composables/createContext'
20+
import { createTrinity } from '#v0/composables/createTrinity'
21+
1722
// Utilities
1823
import { computed, isRef, toRef, toValue } from 'vue'
1924
import { isObject } from '#v0/utilities'
2025

2126
// Types
22-
import type { ComputedRef, MaybeRefOrGetter, MaybeRef } from 'vue'
27+
import type { App, ComputedRef, MaybeRef, MaybeRefOrGetter, ShallowRef } from 'vue'
28+
import type { ContextTrinity } from '#v0/composables/createTrinity'
2329

2430
export type Primitive = string | number | boolean
2531
export type FilterQuery = MaybeRefOrGetter<Primitive | Primitive[]>
2632
export type FilterItem = Primitive | Record<string, any>
2733
export type FilterMode = 'some' | 'every' | 'union' | 'intersection'
2834
export type FilterFunction = (query: Primitive | Primitive[], item: FilterItem) => boolean
2935

30-
export interface UseFilterOptions {
36+
export interface FilterOptions {
3137
customFilter?: FilterFunction
3238
keys?: string[]
3339
mode?: FilterMode
3440
}
3541

36-
export interface UseFilterResult<Z extends FilterItem = FilterItem> {
42+
export interface FilterResult<Z extends FilterItem = FilterItem> {
3743
items: ComputedRef<Z[]>
3844
}
3945

46+
export interface FilterContext<Z extends FilterItem = FilterItem> {
47+
/** The filter mode */
48+
mode: FilterMode
49+
/** Keys to filter on for object items */
50+
keys: string[] | undefined
51+
/** Custom filter function */
52+
customFilter: FilterFunction | undefined
53+
/** Current query ref */
54+
query: ShallowRef<Primitive | Primitive[]>
55+
/**
56+
* Apply filter to an array of items
57+
*
58+
* @param query The query to filter by
59+
* @param items The items to filter
60+
* @returns The filtered items as a computed ref
61+
*/
62+
apply: <T extends Z>(query: FilterQuery, items: MaybeRef<T[]>) => FilterResult<T>
63+
}
64+
65+
export interface FilterContextOptions extends FilterOptions {
66+
namespace?: string
67+
}
68+
4069
function defaultFilter (
4170
query: Primitive | Primitive[],
4271
item: FilterItem,
@@ -76,6 +105,111 @@ function defaultFilter (
76105
return false
77106
}
78107

108+
/**
109+
* Creates a filter context with pre-configured options.
110+
*
111+
* @param options The filter options
112+
* @template Z The type of the items
113+
* @template E The type of the filter context
114+
* @returns A filter context
115+
*
116+
* @see https://0.vuetifyjs.com/composables/utilities/use-filter
117+
*
118+
* @example
119+
* ```ts
120+
* import { createFilter } from '@vuetify/v0'
121+
*
122+
* const filter = createFilter({
123+
* mode: 'intersection',
124+
* keys: ['name', 'email'],
125+
* })
126+
*
127+
* const { items } = filter.apply(query, users)
128+
* ```
129+
*/
130+
export function createFilter<
131+
Z extends FilterItem = FilterItem,
132+
E extends FilterContext<Z> = FilterContext<Z>,
133+
> (options: FilterOptions = {}): E {
134+
const { customFilter, keys, mode = 'some' } = options
135+
const filterFunction = customFilter ?? ((q, i) => defaultFilter(q, i, keys, mode))
136+
const query = toRef<Primitive | Primitive[]>('')
137+
138+
function apply<T extends Z> (
139+
_query: FilterQuery,
140+
items: MaybeRef<T[]>,
141+
): FilterResult<T> {
142+
const itemsRef = isRef(items) ? items : toRef(() => items)
143+
const queryRef = toRef(_query)
144+
145+
const filteredItems = computed(() => {
146+
const q = toValue(queryRef)
147+
query.value = q
148+
const queries = (Array.isArray(q) ? q : [q]).filter(q => String(q).trim())
149+
150+
if (queries.length === 0) return itemsRef.value
151+
152+
const queryParam = queries.length === 1 ? queries[0]! : queries
153+
return itemsRef.value.filter(item =>
154+
filterFunction(queryParam, item),
155+
)
156+
})
157+
158+
return { items: filteredItems }
159+
}
160+
161+
return {
162+
mode,
163+
keys,
164+
customFilter,
165+
query,
166+
apply,
167+
} as E
168+
}
169+
170+
/**
171+
* Creates a filter context with dependency injection support.
172+
*
173+
* @param options The filter context options
174+
* @template Z The type of the items
175+
* @template E The type of the filter context
176+
* @returns A trinity tuple: [useContext, provideContext, defaultContext]
177+
*
178+
* @see https://0.vuetifyjs.com/composables/utilities/use-filter
179+
*
180+
* @example
181+
* ```ts
182+
* import { createFilterContext } from '@vuetify/v0'
183+
*
184+
* export const [useSearchFilter, provideSearchFilter, searchFilter] = createFilterContext({
185+
* namespace: 'app:search',
186+
* mode: 'union',
187+
* keys: ['title', 'description'],
188+
* })
189+
*
190+
* // In parent component
191+
* provideSearchFilter()
192+
*
193+
* // In child component
194+
* const filter = useSearchFilter()
195+
* const { items } = filter.apply(query, products)
196+
* ```
197+
*/
198+
export function createFilterContext<
199+
Z extends FilterItem = FilterItem,
200+
E extends FilterContext<Z> = FilterContext<Z>,
201+
> (_options: FilterContextOptions = {}): ContextTrinity<E> {
202+
const { namespace = 'v0:filter', ...options } = _options
203+
const [useFilterContext, _provideFilterContext] = createContext<E>(namespace)
204+
const context = createFilter<Z, E>(options)
205+
206+
function provideFilterContext (_context: E = context, app?: App): E {
207+
return _provideFilterContext(_context, app)
208+
}
209+
210+
return createTrinity<E>(useFilterContext, provideFilterContext, context)
211+
}
212+
79213
/**
80214
* A reusable function for filtering an array of items.
81215
*
@@ -107,27 +241,35 @@ function defaultFilter (
107241
export function useFilter<Z extends FilterItem> (
108242
query: FilterQuery,
109243
items: MaybeRef<Z[]>,
110-
options: UseFilterOptions = {},
111-
): UseFilterResult<Z> {
112-
const { customFilter, keys, mode = 'some' } = options
113-
const filterFunction = customFilter ?? ((q, i) => defaultFilter(q, i, keys, mode))
114-
115-
const itemsRef = isRef(items) ? items : toRef(() => items)
116-
const queryRef = toRef(query)
117-
118-
const filteredItems = computed(() => {
119-
const q = toValue(queryRef)
120-
const queries = (Array.isArray(q) ? q : [q]).filter(q => String(q).trim())
121-
122-
if (queries.length === 0) return itemsRef.value
123-
124-
const queryParam = queries.length === 1 ? queries[0]! : queries
125-
return itemsRef.value.filter(item =>
126-
filterFunction(queryParam, item),
127-
)
128-
})
244+
options: FilterOptions = {},
245+
): FilterResult<Z> {
246+
const ctx = createFilter<Z>(options)
247+
return ctx.apply(query, items)
248+
}
129249

130-
return {
131-
items: filteredItems,
132-
}
250+
/**
251+
* Returns the current filter context from dependency injection.
252+
*
253+
* @param namespace The namespace for the filter context. Defaults to `'v0:filter'`.
254+
* @template Z The type of the items.
255+
* @template E The type of the filter context.
256+
* @returns The current filter context.
257+
*
258+
* @see https://0.vuetifyjs.com/composables/utilities/use-filter
259+
*
260+
* @example
261+
* ```vue
262+
* <script setup lang="ts">
263+
* import { useFilterContext } from '@vuetify/v0'
264+
*
265+
* const filter = useFilterContext()
266+
* const { items } = filter.apply(query, products)
267+
* </script>
268+
* ```
269+
*/
270+
export function useFilterContext<
271+
Z extends FilterItem = FilterItem,
272+
E extends FilterContext<Z> = FilterContext<Z>,
273+
> (namespace = 'v0:filter'): E {
274+
return useContext<E>(namespace)
133275
}

0 commit comments

Comments
 (0)