@1771technologies/lytenyte-pro
Version:
Blazingly fast headless React data grid with 100s of features.
168 lines (167 loc) • 7.07 kB
JavaScript
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]);
};