import { C as TextFilterOptions, D as ValueFilter, E as UnaryNumberOp, S as SetMatchMode, T as TextMatchMode, _ as NumberOp, a as DateFilterProps, b as SetFilterState, c as DateUnit, d as FilterOp, f as IntervalNumberOp, g as NumberFilterState, h as NumberFilterProps, i as DateFilterOptions, l as Facet, m as NumberFilterOptions, n as BucketFilterOptions, o as DateFilterState, p as NumberBounds, r as BucketFilterProps, s as DateLike, t as Bucket, u as FilterCondition, v as SetFilterOptions, w as TextFilterProps, x as SetFilterValue, y as SetFilterProps } from "./filter.types-DnF8td18.mjs"; //#region src/filter/set-filter.model.d.ts /** * A filter over a discrete set of values — the checkbox-list filter. * * Nothing here knows what a row is. It is handed one already-extracted value and answers whether * that value passes, which is what lets the same instance sit on a table column, a sidebar rail, or * a plain `array.filter`. * * Blanks need no separate state: {@link BLANK} sits inside `selected` like any other value, so * `matches`, `has`, `toggle`, `select`, `value`, `active`, `selectedCount` and `clear` are all * unchanged by it, and "select all" needs no special case. */ declare class SetFilter implements ValueFilter { /** The chosen values. Empty = inactive = everything passes. */ selected: Set; /** * How selections combine — any of, all of, or none of. Observable because it is *state*, not * configuration: a UI can offer the toggle. See {@link SetMatchMode}; only `"all"` is restricted * to array-valued data. */ matchMode: SetMatchMode; /** * Whatever your components need to render this filter. Empty until you augment * {@link SetFilterProps} — the library never reads it. */ readonly props: SetFilterProps; /** Declared value domain, in declaration order. See {@link SetFilterOptions.options}. */ readonly options: readonly SetFilterValue[] | undefined; /** * Groups raw values before they are compared. See {@link SetFilterOptions.project} — and note * `matches` applies it itself, so callers pass raw values in. */ readonly project: ((value: unknown) => unknown) | undefined; /** Whether facet counts were asked for. See {@link SetFilterOptions.counts}. */ readonly counts: boolean; /** * Whether the values are arrays, and so whether a UI should offer `"all"` as a match mode. * `"none"` is not gated by it. Advisory only — see {@link SetFilterOptions.multiValue}. */ readonly multiValue: boolean; get active(): boolean; /** How many values are selected — the number a filter chip shows. */ get selectedCount(): number; /** JSON-serializable state; round-trips through {@link setValue}. */ get value(): SetFilterState; /** * True in every mode but `"any"` — under `"all"` and `"none"` alike each additional pick narrows * the result instead of widening it, so facet counts have to be taken against the current * selection rather than ignoring it. See {@link ValueFilter.intersecting}. * * The count that produces reads differently in the two modes, and both readings are the useful * one. Under `"all"` it is "tick this too and you get that many rows". Under `"none"` the walk * counts rows this filter currently *admits* that carry the value — which is exactly the rows * ticking it would remove, so it reads as "excluding this drops that many". An already-excluded * value therefore tallies zero, which is true: excluding it again removes nothing. Zero-count * entries are kept in the facet list, so it can still be unticked. */ get intersecting(): boolean; /** * The selection as a server condition — `"in"`, `"all"` or `"notIn"`, following the match mode. * `undefined` while inactive. */ get condition(): FilterCondition | undefined; constructor(options?: SetFilterOptions); matches(value: unknown): boolean; has(value: SetFilterValue): boolean; toggle(value: SetFilterValue): void; /** Replace the whole selection. Passing nothing clears it. */ select(values?: Iterable): void; setMatchMode(matchMode: SetMatchMode): void; /** * Restore state from {@link value}. Anything that is not a recognisable set-filter snapshot — * a range filter's state left in storage under this key, a hand-edited URL — resets rather than * being trusted, and unusable entries within a valid one are dropped. */ setValue(value?: unknown): void; /** * Clear the selection. `matchMode` is left alone — it is a mode the user chose, like a sort * direction, not part of what is being filtered. */ clear(): void; } //#endregion //#region src/filter/bucket-filter.model.d.ts /** * Build the projection a set of buckets describes: a value in, its bucket's label out. * * Ranges are `[min, max)` — inclusive lower, exclusive upper — so two adjacent buckets sharing a * number don't both claim it. The first matching bucket wins, which is what makes overlapping * definitions resolve by declaration order instead of being an error nobody can act on. * * Exported because the projection is useful without the filter: the same function labels a value * for a cell renderer or a chart legend, and reusing it is what keeps the table and the filter * agreeing on which bucket a score is in. */ declare const bucketProjection: (buckets: readonly Bucket[]) => (value: unknown) => unknown; /** * A set filter over named ranges — pick "B" rather than typing 80 to 90. * * A `SetFilter` whose domain is derived, and deliberately a subclass rather than a parallel type: a * bucket filter *is* a checkbox list, so everything already built for one applies — facets, counts, * blanks, match modes, serialization — and a popover narrowing by `instanceof SetFilter` renders it * with no changes. * * The column keeps showing and sorting the **raw** value; only the filter sees the buckets. That is * the point: a score column still sorts 84 above 81 inside the "B" bucket. * * ```ts * { * key: "score", * filter: () => new BucketFilter({ * buckets: [ * { label: "A", min: 90 }, * { label: "B", min: 80, max: 90 }, * { label: "C", min: 70, max: 80 }, * { label: "D", min: 60, max: 70 }, * { label: "F", max: 60 }, * ], * }), * } * ``` * * Note for server mode: the condition carries the selected *labels*, which a server can only act on * if it knows the same bucket definitions. Map them to ranges yourself when building the request, or * keep bucket filters client-side. */ declare class BucketFilter extends SetFilter { readonly buckets: readonly Bucket[]; /** Narrowed to {@link BucketFilterProps}; see {@link SetFilterProps}. */ readonly props: BucketFilterProps; /** The bucket a value falls in, or `undefined` when it falls outside every one. */ bucketOf(value: unknown): Bucket | undefined; constructor(options: BucketFilterOptions); /** The bucket labels, in declaration order — the same list `options` holds. */ get labels(): SetFilterValue[]; } //#endregion //#region src/filter/date-filter.model.d.ts /** * An inclusive date range, either bound optional. * * Absorbs the three shapes a date column actually arrives in — `Date`, epoch number, ISO string — * on both sides: the cell values it compares and the bounds you hand it. So * `setRange("2020-01-01", new Date())` works over a column of unix timestamps. * * Bounds are stored as epoch **milliseconds**, which is what keeps `value` a pair of plain numbers * and the JSON round-trip free of date-string parsing. */ declare class DateFilter implements ValueFilter { min: number | undefined; max: number | undefined; /** * Whatever your components need to render this filter. Empty until you augment * {@link DateFilterProps} — the library never reads it. */ readonly props: DateFilterProps; /** How a bare number is read. See {@link DateFilterOptions.unit}. */ readonly unit: DateUnit; get active(): boolean; /** JSON-serializable state; round-trips through {@link setValue}. Unset bounds are omitted. */ get value(): DateFilterState; /** The bounds as a server condition, in epoch milliseconds. `undefined` while inactive. */ get condition(): FilterCondition | undefined; /** The bounds as `Date` objects, for handing to a date picker. */ get range(): { min?: Date; max?: Date; }; constructor(options?: DateFilterOptions); /** * Inclusive on both ends. A value that isn't date-shaped fails while the filter is active — a * blank cell is outside every range, which is what a date picker implies. */ matches(value: unknown): boolean; setMin(min: DateLike | undefined): void; setMax(max: DateLike | undefined): void; setRange(min: DateLike | undefined, max: DateLike | undefined): void; /** * Restore state from {@link value}. Bounds are expected as epoch milliseconds, since that is what * `value` emits; anything non-numeric is dropped rather than trusted. */ setValue(value?: unknown): void; clear(): void; } //#endregion //#region src/filter/number-filter.model.d.ts /** Whether an op takes a pair of bounds rather than a single number. */ declare const isIntervalOp: (op: NumberOp) => op is IntervalNumberOp; /** * A numeric comparison: an operator plus its operand. * * The operand's shape follows the operator — a single number for `eq`/`neq`/`gt`/`lt`/`gte`/`lte`, * `{ min, max }` for the two `between` variants — and the types tie the two together, so a * mismatched pair is a compile error at the call site rather than something `active` has to reject. * * ```ts * new NumberFilter({ op: "gte", operand: 60 }); * new NumberFilter({ op: "between", operand: { min: 60, max: 80 } }); * new NumberFilter({ op: "between", operand: { min: 60 } }); // 60 and up * ``` * * Interval bounds are **independently optional**, and `min` / `max` / `setMin` / `setMax` let a * two-input range control read and write them one at a time — the same shape `DateFilter` uses. That * is what keeps such a control stateless: clearing the upper box leaves the lower one alone, so there * is no draft copy in component state and nothing to go stale when something else calls * `clearColumnFilters()`. * * ```tsx * filter.setMin(parse(e.target.value))} /> * filter.setMax(parse(e.target.value))} /> * ``` * * For dates use `DateFilter`, which speaks `Date`s and ISO strings; for grouping numbers into named * ranges use `BucketFilter`. */ declare class NumberFilter implements ValueFilter { op: NumberOp; /** * Whatever your components need to render this filter. Empty until you augment * {@link NumberFilterProps} — the library never reads it. */ readonly props: NumberFilterProps; /** * A single number, or `{ min, max }` for the interval ops — each bound independently optional, so * a range control can hold one while the other is still empty. */ operand: number | NumberBounds | undefined; /** * Whether the operand actually fits the operator. Switching operator without switching operand * leaves the filter inactive rather than guessing — a `[60, 80]` pair means nothing to `gte`, and * silently taking the first element would filter by something the user never asked for. Use * {@link set} to change both at once. */ get active(): boolean; /** * The lower bound, for an interval operator. `undefined` for the unary ones, where a UI renders * one input rather than two. * * Named to match `DateFilter`, and present so a range control can drive its inputs straight off * the filter — read `min`, write `setMin` — with no draft copy in component state, and therefore * nothing to go stale when something else calls `clearColumnFilters()`. */ get min(): number | undefined; /** The upper bound, for an interval operator. `undefined` for the unary ones. */ get max(): number | undefined; /** JSON-serializable state; round-trips through {@link setValue}. */ get value(): NumberFilterState; get condition(): FilterCondition | undefined; constructor(options?: NumberFilterOptions); /** * Numeric strings are accepted, because a column of `"42"` is a data shape rather than a mistake. * Anything that isn't a number fails while the filter is active — a blank cell satisfies no * comparison, not even `neq`, which would otherwise quietly include every empty row. */ matches(value: unknown): boolean; /** Change the operator alone. May leave the filter inactive — see {@link active}. */ setOp(op: NumberOp): void; setOperand(operand: number | NumberBounds | undefined): void; /** * Set the lower bound alone, leaving the upper one where it is. A no-op under a unary operator, * where a UI renders one input rather than two. */ setMin(min: number | undefined): void; /** Set the upper bound alone, leaving the lower one where it is. */ setMax(max: number | undefined): void; /** Set both bounds at once. A no-op under a unary operator. Mirrors `DateFilter.setRange`. */ setRange(min: number | undefined, max: number | undefined): void; /** Change operator and operand together, which is what an operator dropdown should do. */ set(op: UnaryNumberOp, operand?: number): void; set(op: IntervalNumberOp, operand?: NumberBounds): void; /** * Restore state from {@link value}. An operand that doesn't fit the operator is dropped rather * than trusted, so a snapshot written when the operator was `between` leaves a `gte` inactive * instead of comparing against a pair. */ setValue(value?: unknown): void; /** Clear the operand. The operator is left alone — it is a choice the user made. */ clear(): void; } //#endregion //#region src/filter/text-filter.model.d.ts /** * A single-value text filter — the "contains" box that lives on one column. * * Distinct from a table's cross-column search, which needs every column's accessor at once and so * cannot be a value predicate. Both go through `textMatches`, so they compare identically. * * `match` and `caseSensitive` are configuration rather than state: unlike a set filter's * `matchMode`, they are not things a UI typically hands to the user. */ declare class TextFilter implements ValueFilter { text: string; /** * Whatever your components need to render this filter. Empty until you augment * {@link TextFilterProps} — the library never reads it. */ readonly props: TextFilterProps; readonly match: TextMatchMode; readonly caseSensitive: boolean; get active(): boolean; /** JSON-serializable state; round-trips through {@link setValue}. */ get value(): string; /** * The query as a server condition. The op is the configured `match`, so `"contains"` / * `"startsWith"` / `"equals"` carry across unchanged. `undefined` while inactive. * * `caseSensitive` is deliberately not serialized: it describes how *this* process compares, and * a server's collation is its own business. */ get condition(): FilterCondition | undefined; constructor(options?: TextFilterOptions); matches(value: unknown): boolean; setText(text: string): void; /** Restore state from {@link value}. Anything that is not a string clears the query. */ setValue(value?: unknown): void; clear(): void; } //#endregion //#region src/filter/util.d.ts /** * The sentinel a missing or empty value normalises to. * * `""` is the right choice *because* it conflates empty-string with missing — that is the behaviour * a "(Blank)" checkbox is expected to have. A more distinctive sentinel would preserve a * distinction no filter UI has a way to express, and would stop the domain being JSON-safe. */ declare const BLANK: ""; /** * Whether a value counts as missing/empty: `null`, `undefined`, `""`, or an array that contributes * nothing once flattened (so a `tags: []` row is reachable through the "(Blank)" facet rather than * being unreachable). */ declare const isBlank: (value: unknown) => boolean; /** * The set of facet values one raw cell value contributes: arrays flattened, blanks dropped, and * `{ BLANK }` when nothing non-blank survives. * * This is the single definition of the blank rule, shared verbatim by `SetFilter.matches` and the * table's facet tally. If those two ever disagreed you would get a facet in the list that selects * no rows — which reads as a broken filter rather than as a normalisation bug, and is why this is * one exported function rather than a rule written twice. * * Non-primitives are stringified so the domain stays comparable by value and JSON-safe; see * {@link SetFilterValue}. */ declare const facetValues: (value: unknown) => Set; /** * Compare a query against one value as text. An empty query matches everything, which is what makes * "no query typed" a pass-through rather than a special case at every call site. * * Nothing is trimmed: a trailing space is a legitimate part of a "contains" query, and trimming here * but not there is how the two drift apart. */ declare const textMatches: (query: string, value: unknown, match?: TextMatchMode, caseSensitive?: boolean) => boolean; //#endregion export { BLANK, Bucket, BucketFilter, BucketFilterOptions, BucketFilterProps, DateFilter, DateFilterOptions, DateFilterProps, DateFilterState, DateLike, DateUnit, Facet, FilterCondition, FilterOp, IntervalNumberOp, NumberBounds, NumberFilter, NumberFilterOptions, NumberFilterProps, NumberFilterState, NumberOp, SetFilter, SetFilterOptions, SetFilterProps, SetFilterState, SetFilterValue, SetMatchMode, TextFilter, TextFilterOptions, TextFilterProps, TextMatchMode, UnaryNumberOp, ValueFilter, bucketProjection, facetValues, isBlank, isIntervalOp, textMatches }; //# sourceMappingURL=filter.d.mts.map