UNPKG

@1771technologies/lytenyte-pro

Version:

Blazingly fast headless React data grid with 100s of features.

168 lines (167 loc) 7.07 kB
import { useCallback, useMemo } from "react"; import { useTwoFlowState } from "@1771technologies/lytenyte-core/yinternal"; import { isCompleteFilter } from "./is-complete-filter.js"; import { toFilterItem } from "./to-filter-item.js"; export const useFilterSelect = ({ grid, column, maxCount = 2 }) => { const model = grid.state.filterModel.useValue(); const filterOnModel = model[column.id]; const defaultFilter = useMemo(() => { if (column.type === "date" || column.type === "datetime") return { kind: "date", operator: "equals" }; if (column.type === "number") return { kind: "number", operator: "equals" }; return { kind: "string", operator: "equals" }; }, [column.type]); const filterValue = useMemo(() => { if (filterOnModel) { if (filterOnModel.kind === "func") return [{ kind: "function", func: filterOnModel.func }]; if (filterOnModel.kind === "number") return [{ kind: "number", value: filterOnModel.value, operator: filterOnModel.operator }]; if (filterOnModel.kind === "string") return [{ kind: "string", value: filterOnModel.value, operator: filterOnModel.operator }]; if (filterOnModel.kind === "date") return [{ kind: "date", value: filterOnModel.value, operator: filterOnModel.operator }]; if (filterOnModel.kind === "combination") { const filters = []; const stack = [...filterOnModel.filters.map((c) => [filterOnModel.operator, c])]; while (stack.length) { const [nextExtender, f] = stack.shift(); if (f.kind === "combination") { stack.push(...f.filters.map((c) => [f.operator, c])); } else if (f.kind === "string") { filters.push({ kind: "string", operator: f.operator, value: f.value, nextExtender }); } else if (f.kind === "number") { filters.push({ kind: "number", operator: f.operator, value: f.value, nextExtender }); } else if (f.kind === "date") { filters.push({ kind: "date", operator: f.operator, value: f.value, nextExtender }); } } return filters; } return []; } return [defaultFilter]; }, [filterOnModel, defaultFilter]); const [filters, setFilters] = useTwoFlowState(filterValue); const finalFilters = useMemo(() => { if (!filters.length) return [defaultFilter]; const last = filters.at(-1); if (last.kind === "function" || filters.length >= maxCount) return filters; if (last.operator == undefined || last.value == null) return filters; return [...filters, defaultFilter]; }, [defaultFilter, filters, maxCount]); const reset = useCallback(() => { return setFilters([defaultFilter]); }, [defaultFilter, setFilters]); const apply = useCallback(() => { const fn = finalFilters.find((c) => c.kind === "function"); if (fn) { grid.state.filterModel.set((prev) => { const next = { ...prev }; next[column.id] = { kind: "func", func: fn.func }; return prev; }); return; } const filters = finalFilters.filter((c) => isCompleteFilter(c)); if (filters.length === 1) { const filter = filters[0]; if (!isCompleteFilter(filter)) { grid.state.filterModel.set((prev) => { const filters = { ...prev }; delete filters[column.id]; return filters; }); return; } let next = null; if (filter.kind === "date") next = { kind: "date", value: filter.value, operator: filter.operator }; else if (filter.kind === "number") next = { kind: "number", value: filter.value, operator: filter.operator }; else if (filter.kind === "string") next = { kind: "string", value: filter.value, operator: filter.operator }; if (next) { grid.state.filterModel.set((prev) => { const filters = { ...prev }; filters[column.id] = next; return filters; }); } return; } const combinedFilters = []; let i = 0; while (i < filters.length) { const filter = filters[i]; if ((filter.nextExtender ?? "AND") === "AND") { const ors = [filter]; while (i + 1 < filters.length) { const next = filters[i + 1]; ors.push(next); i++; if ((next.nextExtender ?? "AND") !== "AND" || i + 1 >= filters.length) break; } if (ors.length > 1) { combinedFilters.push({ kind: "combination", filters: ors.map(toFilterItem), operator: "AND", }); } else { combinedFilters.push(toFilterItem(ors[0])); } } else { combinedFilters.push(toFilterItem(filter)); } i++; } grid.state.filterModel.set((prev) => { if (combinedFilters.length === 0) { const filters = { ...prev }; delete filters[column.id]; return filters; } if (combinedFilters.length === 1) { const filters = { ...prev }; filters[column.id] = combinedFilters[0]; return filters; } const filters = { ...prev }; filters[column.id] = { kind: "combination", filters: combinedFilters, operator: "OR" }; return filters; }); return; }, [column.id, finalFilters, grid.state.filterModel]); const setF = useCallback((v) => { const next = typeof v === "function" ? v(finalFilters) : v; setFilters(next); }, [finalFilters, setFilters]); const clear = useCallback(() => { setF([]); grid.state.filterModel.set((prev) => { const filters = { ...prev }; delete filters[column.id]; return filters; }); }, [column.id, grid.state.filterModel, setF]); return useMemo(() => ({ reset, apply, clear, defaultFilter, filters: finalFilters, setFilters: setF, maxCount, }), [apply, clear, defaultFilter, finalFilters, maxCount, reset, setF]); };