import { SelectOption } from '../atoms/Select'; export type FilterFieldType = "text" | "select" | "date" | "daterange" | "number" | "boolean" | "combobox"; export interface FilterField { key: string; label: string; type: FilterFieldType; /** Options for `type: "select"`. Also honored for `type: "boolean"` to override * the default Yes/No labels — pass exactly two options with values * `"true"` / `"false"` (e.g. Enabled/Disabled). Omit for the i18n Yes/No default. */ options?: SelectOption[]; /** For `type: "select"` — render a searchable (type-to-filter) dropdown. * Forwarded to the underlying `Select`; ignored for other field types. */ searchable?: boolean; /** * For `type: "combobox"` (async server-lookup filter field) — fired (debounced * ~300ms by the underlying `Combobox`) with the typed query. Fetch matching * options externally and re-pass `fields` with the updated `options`; this is * how a large/remote set (e.g. a user/creator directory) is filtered without * preloading everything into a static `select`. Ignored for other field types. */ onSearch?: (query: string) => void; /** For `type: "combobox"` — show the loading spinner while `options` load. */ loading?: boolean; /** * Key of the field this one depends on. When the parent field's value * changes, this field's staged value is auto-cleared (e.g. city → district). * The panel does NOT own the dependent options — recompute them externally * (via {@link FilterPanelProps.onDraftChange}) and re-pass `fields`. */ dependsOn?: string; /** * For `type: "daterange"` — the `FilterValue.field` key carrying the range * START bound. @default `` `${key}From` `` * * The emitted `FilterValue.field` values for a `daterange` field are ALWAYS its * two bound keys (`fromKey` / `toKey`) — the field's own `key` drives only the * label, the React key, the active chip and `dependsOn` targeting. That is what * lets `FilterValue.value` stay a `string`: a range is two entries, not one * `{from,to}` object, so no consumer has to narrow a union. Defaults follow the * suffix pattern, so `{ key: "date", type: "daterange" }` emits `dateFrom` + * `dateTo`; a consumer whose wire keys differ (e.g. `startDate`/`endDate`) * passes `fromKey`/`toKey` explicitly. * * The operator is ALWAYS the documented one (`">="` for the start bound, `"<="` * for the end bound) and a seeded operator on a bound key is deliberately * IGNORED — see {@link FilterPanelProps.showOperators}, whose "a seeded operator * survives even when the picker is hidden" rule this field type is the one * documented exception to. A `daterange` has no operator picker and never will * (the two bounds *are* its semantics), so there is no user intent to preserve. * * REQUIRED of the caller: `fromKey` and `toKey` must differ from each other, and * neither may collide with another field's `key` or bound key. The panel keys its * internal draft by these names, so a collision makes two controls share one * slot — the same pre-existing constraint that already applies to two plain * fields sharing a `key`. Setting this field's own `key` equal to one of its * bound keys is harmless, but then that `key` does appear in the output. */ fromKey?: string; /** * For `type: "daterange"` — the `FilterValue.field` key carrying the range * END bound, always emitted with operator `"<="`. @default `` `${key}To` `` * See {@link FilterField.fromKey} for the full contract (operator * normalization + the key-uniqueness requirement). */ toKey?: string; /** * For `type: "daterange"` — offer the underlying `DateRangePicker`'s * quick-range shortcuts (Today / Yesterday / Last 7 days / … / Last year). * They render as a column INSIDE the portaled calendar popover, so the 280px * rail's width no longer constrains them; the default stays off so an * existing rail's popover keeps its current footprint unless opted in. * @default false */ presets?: boolean; /** * For `type: "daterange"` and `type: "date"` — forwarded to the hosted * `DateRangePicker` / `DatePicker`, opening hour:minute selection inside the * calendar popover. The trigger field's own footprint is unchanged, so a 280px * rail is unaffected. * * The WIRE CONTRACT DOES NOT CHANGE: a bound has always been emitted as full * ISO 8601 (`toISOString()`), so it could always carry a time — there was just * no control to set one, which pinned every bound to 00:00 and made a whole day * the narrowest expressible range. (NB-DATETIME-01) @default false */ showTime?: boolean; /** Time step while `showTime` is on, in MINUTES. @default 1 */ minuteStep?: number; /** * For `type: "daterange"` and `type: "date"` — forwarded to the hosted * picker's `timeLayout` (NB-DATETIME-02). The picker default is `"beside"` * (time column right of the day grid); pass `"below"` to keep the pre-1.22 * full-width row under the grid — the compact choice for a narrow rail. */ timeLayout?: "beside" | "below"; } export interface FilterValue { field: string; operator: string; value: string; } export interface FilterPanelProps { fields: FilterField[]; values?: FilterValue[]; onApply: (filters: FilterValue[]) => void; onClear: () => void; onClose: () => void; /** * Fires on every in-panel draft mutation (edit / operator change / clear / * initial seed), BEFORE Apply. The payload is the current draft — every * seeded or edited field, INCLUDING ones cleared to empty (so an explicit * clear is visible); a field never seeded and never touched is absent. Use it * to react to a staged field — e.g. recompute a dependent field's options off * the staged parent value, then re-pass `fields` (that re-pass does NOT * re-fire this — the fire effect keys only on the internal draft). May fire * more than once for the same draft (e.g. React StrictMode double-invoke), so * treat the handler as idempotent. Does NOT replace `onApply` (which still * only fires from Apply, filtering out empties). */ onDraftChange?: (draft: FilterValue[]) => void; /** Render the per-field operator picker. Default `true`. Set `false` for a * plain value-only filter: the picker is hidden, but each field still carries * an operator at Apply — the type default for freshly-edited fields, or a * seeded operator (from `values`) which is preserved as-is. */ showOperators?: boolean; } export declare function FilterPanel({ fields, values: externalValues, onApply, onClear, onClose, onDraftChange, showOperators, }: FilterPanelProps): import("react").JSX.Element; //# sourceMappingURL=FilterPanel.d.ts.map