import "./FilterEditor.css"; import { type FilterTree, getLocalizedString, toFilterTree } from "@olenbetong/appframe-core"; import clsx from "clsx"; import { useMemo, useState } from "react"; import { FilterGroupEditor } from "./FilterGroupEditor.js"; import { FilterStringField } from "./FilterStringField.js"; import type { FilterEditorProps } from "./types.js"; /** * The criteria tree of the filter builder. Groups can be nested and each group * combines its items with either `and` or `or`, matching the advanced filter * editor in the Windows client. * * Works both controlled (`value` + `onChange`) and uncontrolled * (`defaultValue`). Saving and sharing filters is handled by `FilterBuilder`, * which composes this component. * * @example * ```jsx * * ``` */ export function FilterEditor({ fields, value, defaultValue, onChange, readOnly, showFilterString, viewName, getFieldOptions, className, "data-size": size = "sm", }: FilterEditorProps) { let [uncontrolled, setUncontrolled] = useState(() => toFilterTree(defaultValue)); let isControlled = value !== undefined; // Normalizing on every render would create a new tree each time, which // retriggers the filter string conversion. let controlled = useMemo(() => toFilterTree(value), [value]); let filter = isControlled ? controlled : uncontrolled; let visibleFields = useMemo(() => fields.filter((field) => !field.excludeFromFilter), [fields]); function update(next: FilterTree) { if (!isControlled) setUncontrolled(next); onChange?.(next); } return (

{filter.mode === "and" ? getLocalizedString("Find rows that match all of these criteria:") : getLocalizedString("Find rows that match one or more of these criteria:")}

{showFilterString && ( )}
); }