mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-25 15:55:48 +00:00
fix: create TableFilterStateValue type
This commit is contained in:
@@ -1,13 +1,29 @@
|
|||||||
import { useCallback, useEffect, useMemo, useReducer } from 'react';
|
import { useCallback, useEffect, useMemo, useReducer } from 'react';
|
||||||
import { useTableFilterStore } from '@/stores/table/table-filter.store';
|
import { useTableFilterStore } from '@/stores/table/table-filter.store';
|
||||||
|
import { OptionType } from '@/components/input/SelectInput';
|
||||||
|
|
||||||
|
type TableFilterStateValue =
|
||||||
|
| undefined
|
||||||
|
| null
|
||||||
|
| boolean
|
||||||
|
| string
|
||||||
|
| string[]
|
||||||
|
| number
|
||||||
|
| number[]
|
||||||
|
| OptionType<number>
|
||||||
|
| OptionType<number>[]
|
||||||
|
| OptionType<string>
|
||||||
|
| OptionType<string>[];
|
||||||
|
|
||||||
/** Core filter shape (page + pageSize) extended by your custom fields */
|
/** Core filter shape (page + pageSize) extended by your custom fields */
|
||||||
export type TableFilterState<TExtra extends Record<string, unknown>> = {
|
export type TableFilterState<
|
||||||
|
TExtra extends Record<string, TableFilterStateValue>,
|
||||||
|
> = {
|
||||||
page: number;
|
page: number;
|
||||||
pageSize: number;
|
pageSize: number;
|
||||||
} & TExtra;
|
} & TExtra;
|
||||||
|
|
||||||
type Action<TExtra extends Record<string, unknown>> =
|
type Action<TExtra extends Record<string, TableFilterStateValue>> =
|
||||||
| { type: 'SET_PAGE'; page: number }
|
| { type: 'SET_PAGE'; page: number }
|
||||||
| { type: 'SET_PAGE_SIZE'; pageSize: number; resetPage?: boolean }
|
| { type: 'SET_PAGE_SIZE'; pageSize: number; resetPage?: boolean }
|
||||||
| { type: 'SET_FILTERS'; filters: Partial<TExtra>; resetPage?: boolean }
|
| { type: 'SET_FILTERS'; filters: Partial<TExtra>; resetPage?: boolean }
|
||||||
@@ -20,7 +36,9 @@ type Action<TExtra extends Record<string, unknown>> =
|
|||||||
| { type: 'REPLACE_ALL'; next: TableFilterState<TExtra> }
|
| { type: 'REPLACE_ALL'; next: TableFilterState<TExtra> }
|
||||||
| { type: 'RESET' };
|
| { type: 'RESET' };
|
||||||
|
|
||||||
export type UseTableFilterOptions<TExtra extends Record<string, unknown>> = {
|
export type UseTableFilterOptions<
|
||||||
|
TExtra extends Record<string, TableFilterStateValue>,
|
||||||
|
> = {
|
||||||
/** Initial state; anything you omit falls back to defaults */
|
/** Initial state; anything you omit falls back to defaults */
|
||||||
initial?: Partial<TableFilterState<TExtra>>;
|
initial?: Partial<TableFilterState<TExtra>>;
|
||||||
/** Called after any state change */
|
/** Called after any state change */
|
||||||
@@ -43,9 +61,9 @@ function clampToInt(n: number, min = 1) {
|
|||||||
return v < min ? min : v;
|
return v < min ? min : v;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createInitialState<TExtra extends Record<string, unknown>>(
|
function createInitialState<
|
||||||
opts: UseTableFilterOptions<TExtra> | undefined
|
TExtra extends Record<string, TableFilterStateValue>,
|
||||||
): TableFilterState<TExtra> {
|
>(opts: UseTableFilterOptions<TExtra> | undefined): TableFilterState<TExtra> {
|
||||||
const defaults = {
|
const defaults = {
|
||||||
page: 1,
|
page: 1,
|
||||||
pageSize: opts?.defaultPageSize ?? 10,
|
pageSize: opts?.defaultPageSize ?? 10,
|
||||||
@@ -59,10 +77,22 @@ function createInitialState<TExtra extends Record<string, unknown>>(
|
|||||||
|
|
||||||
function serializeValue(v: unknown): string | null {
|
function serializeValue(v: unknown): string | null {
|
||||||
if (v === undefined || v === null) return null;
|
if (v === undefined || v === null) return null;
|
||||||
|
|
||||||
if (v instanceof Date) return v.toISOString();
|
if (v instanceof Date) return v.toISOString();
|
||||||
if (Array.isArray(v)) return v.map((x) => x ?? '').join(','); // e.g., ids=1,2,3
|
|
||||||
|
if (v instanceof Object && (v as OptionType).value)
|
||||||
|
return String((v as OptionType).value);
|
||||||
|
|
||||||
|
if (Array.isArray(v))
|
||||||
|
return v
|
||||||
|
.map((x) => serializeValue(x))
|
||||||
|
.filter((x) => x !== null)
|
||||||
|
.join(',');
|
||||||
|
|
||||||
const t = typeof v;
|
const t = typeof v;
|
||||||
|
|
||||||
if (t === 'string' || t === 'number' || t === 'boolean') return String(v);
|
if (t === 'string' || t === 'number' || t === 'boolean') return String(v);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return JSON.stringify(v);
|
return JSON.stringify(v);
|
||||||
} catch {
|
} catch {
|
||||||
@@ -70,32 +100,16 @@ function serializeValue(v: unknown): string | null {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// function shallowEqual(a: unknown, b: unknown): boolean {
|
function shallowEqual<T extends TableFilterStateValue>(
|
||||||
// if (a === b) return true;
|
|
||||||
// if (!a || !b) return false;
|
|
||||||
// const ka = Object.keys(a);
|
|
||||||
// const kb = Object.keys(b);
|
|
||||||
// if (ka.length !== kb.length) return false;
|
|
||||||
// for (const k of ka) if (a[k] !== b[k]) return false;
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
function shallowEqual<T extends Record<string, unknown>>(
|
|
||||||
a: T | undefined | null,
|
a: T | undefined | null,
|
||||||
b: T | undefined | null
|
b: T | undefined | null
|
||||||
): boolean {
|
): boolean {
|
||||||
if (a === b) return true;
|
return JSON.stringify(a) === JSON.stringify(b);
|
||||||
if (!a || !b) return false;
|
|
||||||
const ka = Object.keys(a) as (keyof T)[];
|
|
||||||
const kb = Object.keys(b) as (keyof T)[];
|
|
||||||
if (ka.length !== kb.length) return false;
|
|
||||||
for (const k of ka) if (a[k] !== b[k]) return false;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useTableFilter<TExtra extends Record<string, unknown>>(
|
export function useTableFilter<
|
||||||
options?: UseTableFilterOptions<TExtra>
|
TExtra extends Record<string, TableFilterStateValue>,
|
||||||
) {
|
>(options?: UseTableFilterOptions<TExtra>) {
|
||||||
if (options?.persist && !options?.storeName) {
|
if (options?.persist && !options?.storeName) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'storeName is required if persist is true in useTableFilter!'
|
'storeName is required if persist is true in useTableFilter!'
|
||||||
@@ -220,7 +234,9 @@ export function useTableFilter<TExtra extends Record<string, unknown>>(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const extras = useMemo(() => {
|
const extras = useMemo(() => {
|
||||||
const stateWithExtras = state as TableFilterState<Record<string, unknown>>;
|
const stateWithExtras = state as TableFilterState<
|
||||||
|
Record<string, TableFilterStateValue>
|
||||||
|
>;
|
||||||
const rest = Object.fromEntries(
|
const rest = Object.fromEntries(
|
||||||
Object.entries(stateWithExtras).filter(
|
Object.entries(stateWithExtras).filter(
|
||||||
([key]) => key !== 'page' && key !== 'pageSize'
|
([key]) => key !== 'page' && key !== 'pageSize'
|
||||||
@@ -241,10 +257,8 @@ export function useTableFilter<TExtra extends Record<string, unknown>>(
|
|||||||
/** Build URLSearchParams from current state */
|
/** Build URLSearchParams from current state */
|
||||||
const toSearchParams = useCallback(() => {
|
const toSearchParams = useCallback(() => {
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
const source = state as Record<string, unknown>;
|
const source = state as Record<string, TableFilterStateValue>;
|
||||||
const baseline = options?.omitDefaultsInUrl
|
const baseline = options?.omitDefaultsInUrl ? defaults : null;
|
||||||
? (defaults as Record<string, unknown>)
|
|
||||||
: null;
|
|
||||||
const excludedKeys = new Set<string>(
|
const excludedKeys = new Set<string>(
|
||||||
(options?.excludeKeysFromUrl as string[] | undefined) ?? []
|
(options?.excludeKeysFromUrl as string[] | undefined) ?? []
|
||||||
);
|
);
|
||||||
@@ -255,13 +269,7 @@ export function useTableFilter<TExtra extends Record<string, unknown>>(
|
|||||||
const value = source[key];
|
const value = source[key];
|
||||||
if (value === undefined || value === null) continue;
|
if (value === undefined || value === null) continue;
|
||||||
|
|
||||||
if (
|
if (baseline && shallowEqual(value, baseline[key])) {
|
||||||
baseline &&
|
|
||||||
shallowEqual(
|
|
||||||
value as Record<string, unknown>,
|
|
||||||
baseline[key] as Record<string, unknown>
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user