@veebisepad/vue-query-filters
Version:
Reactive utility for managing URL query parameters in Vue 3.
225 lines (218 loc) • 8.13 kB
TypeScript
import { Reactive } from 'vue';
/**
* Base interface for all filter types
* @template T The type of value this filter handles
*/
interface AllowedFilter<T> {
/** Default value when filter is not specified */
defaultValue: T;
/**
* Converts a URL parameter string to filter value
* @param value The string value from URL query parameter
* @param delimiter Character used to split multiple values
* @returns Properly typed filter value
*/
parseQueryParam(value: string | null, delimiter: string): T;
/**
* Converts filter value to URL parameter string
* @param value The filter value to convert
* @param delimiter Character used to join multiple values
* @returns String representation for URL or null if empty/default
*/
serializeQueryParam(value: T, delimiter: string): string | null;
/**
* Checks if a specific value exists in the filter
* @param filterValue Current filter value
* @param value Value to check for
* @returns True if value exists in filter
*/
hasValue(filterValue: T, value: string | number): boolean;
/**
* Transforms filter keys for query parameter names
* @param key Original filter key from the application
* @returns Transformed key suitable for URL parameters
*/
transformKey: (key: string) => string;
}
/** Map of filter keys to their filter implementation */
type Filters = Record<string, AllowedFilter<any>>;
/** Map of filter keys to their string representation for URL */
type QueryObject = Record<string, string>;
/**
* Maps filter types to their value types using the defaultValue property
* @template T Input type with defaultValue property
*/
type FilterValueMap<T> = T extends {
defaultValue: infer V;
} ? V : never;
/**
* Complete filter object with values and methods
* @template T Filter configuration type
*/
type QueryFilters<T extends Filters> = Reactive<{
[K in keyof T]: FilterValueMap<T[K]>;
} & FilterMethods<T>>;
type FilterState<T extends Filters> = {
[K in keyof T]: FilterValueMap<T[K]>;
} & FilterMethods<T>;
/**
* Configuration options for filters
*/
interface Options {
/** Character used to separate multiple values in URL parameters */
delimiter: string;
/**
* Callback triggered when filters are applied
* @param filters The query object with current filter values
*/
onApply?(filters: QueryObject): void;
/**
* When true, preserves the order of query parameters as they appear in the URL
* @default true
*/
preserveQueryOrder?: boolean;
/**
* Optional Location object to use instead of window.location (for SSR)
*/
location?: LocationLike;
}
/**
* Methods available on the filter object
* @template T Type of the filters configuration
*/
interface FilterMethods<T extends Filters> {
/**
* Converts current filter values to URLSearchParams
* @returns URLSearchParams object for use in fetch or URL construction
*/
toSearchParams(): URLSearchParams;
/**
* Triggers the onApply callback with current filter values
*/
get(): void;
/**
* Creates an object with filter keys and their string representations
* @param transformKeys Whether to transform keys using the keyTransformer function
* @returns Object mapping filter keys to query string values
*/
toQueryObject(transformKeys?: boolean): Record<string, string>;
/**
* Creates an object with filter keys and their string representations,
* preserving the order of parameters from the current URL
* @param transformKeys Whether to transform keys using the keyTransformer function
* @returns Object mapping filter keys to query string values in URL order
*/
toOrderedQueryObject(transformKeys?: boolean): Record<string, string>;
/**
* Checks if a value exists in the specified filter
* @param filter The filter key to check
* @param value The value to look for
* @returns True if the value exists in the filter
*/
has<K extends keyof T>(filter: K, value: string | number): boolean;
/**
* Resets one or more filters to their default values
* @param filterKey Single key or array of keys to reset
* @param shouldGet Whether to trigger the onApply callback after clearing
*/
clear<K extends keyof T>(filterKey: K | K[], shouldGet?: boolean): void;
/**
* Resets all filters to their default values
*/
clearAll(): void;
/**
* Updates filter options
* @param newOptions New options to merge with existing ones
*/
setOptions(newOptions: Partial<Options>): void;
/**
* Filters data object
* @returns Object with filter keys and their values
*/
data(): {
[K in keyof T]: FilterValueMap<T[K]>;
};
}
interface SingleFilter<T> extends AllowedFilter<T> {
defaultValue: T;
}
interface MultipleFilter<T> extends AllowedFilter<T[]> {
defaultValue: T[];
}
interface RangeFilter<T> extends AllowedFilter<{
from: T;
to: T;
}> {
defaultValue: {
from: T;
to: T;
};
}
type FilterFactoryOptions = {
keyTransformer?: (key: string) => string;
};
type LocationLike = Pick<Location, 'search'> | URL;
/**
* Creates a collection of filter factory functions
* @returns Object containing filter factory methods
*/
declare const createFilterFactory: (options?: FilterFactoryOptions) => {
/**
* Creates a filter for a single value
* @template SingleType Type of the filter value
* @param defaultValue Default value when filter key is not present
* @returns A filter object handling single values
*/
single: <SingleType>(defaultValue?: SingleType) => SingleFilter<SingleType>;
/**
* Creates a filter for multiple values
* @template MultipleType Type of each item in the array
* @param defaultValue Default array when filter key is not present
* @returns A filter object handling arrays
*/
multiple: <MultipleType>(defaultValue?: MultipleType[]) => MultipleFilter<MultipleType>;
/**
* Creates a range filter
* @template RangeType Type of the range endpoints
* @param defaultValue Default range when filter key is not present
* @returns A filter object handling { from: RangeType; to: RangeType }
*/
range: <RangeType>(defaultValue?: {
from: RangeType;
to: RangeType;
}) => RangeFilter<RangeType>;
/**
* Allows creating a custom filter
* @template CustomType Type of the filter value
* @param filter A fully-defined filter object
* @returns The same filter, for custom scenarios
*/
custom: <CustomType>(filter: AllowedFilter<CustomType>) => AllowedFilter<CustomType>;
};
/**
* Creates a reactive filter object from filter configurations
* @param filters Map of filter keys to their configurations
* @param initialOptions Initial options for the filters
* @returns Reactive filter object with values and methods
*/
declare function useFilters<T extends Filters>(filters: T, initialOptions?: Partial<Options>): QueryFilters<T>;
/**
* Pre-configured filter factory for convenience
* @example
* import { factory } from 'vue-query-filters';
*
* const filters = useFilters({
* search: factory.single<string>(),
* categories: factory.multiple<string>()
* });
*/
declare const factory: {
single: <SingleType>(defaultValue?: SingleType) => SingleFilter<SingleType>;
multiple: <MultipleType>(defaultValue?: MultipleType[]) => MultipleFilter<MultipleType>;
range: <RangeType>(defaultValue?: {
from: RangeType;
to: RangeType;
}) => RangeFilter<RangeType>;
custom: <CustomType>(filter: AllowedFilter<CustomType>) => AllowedFilter<CustomType>;
};
export { type AllowedFilter, type FilterFactoryOptions, type FilterMethods, type FilterState, type FilterValueMap, type Filters, type LocationLike, type MultipleFilter, type Options, type QueryFilters, type QueryObject, type RangeFilter, type SingleFilter, createFilterFactory, factory, useFilters };