@wallarm-org/design-system 0.41.0 → 0.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/FilterInput/FilterInput.d.ts +7 -8
- package/dist/components/FilterInput/FilterInputContext/types.d.ts +12 -9
- package/dist/components/FilterInput/FilterInputContext/useFilterInputContextValue.d.ts +4 -2
- package/dist/components/FilterInput/FilterInputContext/useFilterInputContextValue.js +4 -0
- package/dist/components/FilterInput/FilterInputErrors/parseFilterInputErrors.js +1 -2
- package/dist/components/FilterInput/FilterInputField/ChipsWithGaps.d.ts +1 -1
- package/dist/components/FilterInput/FilterInputField/ChipsWithGaps.js +1 -1
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/FilterInputChip.d.ts +1 -1
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/FilterInputChip.js +2 -2
- package/dist/components/FilterInput/FilterInputField/FilterInputField.js +11 -40
- package/dist/components/FilterInput/FilterInputField/hooks/useSegmentEditKeyboard.d.ts +37 -0
- package/dist/components/FilterInput/FilterInputField/hooks/useSegmentEditKeyboard.js +78 -0
- package/dist/components/FilterInput/FilterInputMenu/FilterInputDateValueMenu/constants.d.ts +2 -2
- package/dist/components/FilterInput/FilterInputMenu/FilterInputFieldMenu/FilterInputFieldMenu.d.ts +1 -1
- package/dist/components/FilterInput/FilterInputMenu/FilterInputMenu.d.ts +1 -1
- package/dist/components/FilterInput/FilterInputMenu/FilterInputValueMenu/FilterInputValueMenu.d.ts +5 -6
- package/dist/components/FilterInput/FilterInputMenu/FilterInputValueMenu/useValueMenuDisplayValues.d.ts +4 -14
- package/dist/components/FilterInput/FilterInputMenu/FilterInputValueMenu/useValueMenuState.d.ts +3 -5
- package/dist/components/FilterInput/FilterInputMenu/hooks/useKeyboardNav.d.ts +1 -1
- package/dist/components/FilterInput/FilterInputMenu/hooks/useKeyboardNav.js +5 -2
- package/dist/components/FilterInput/hooks/useAutoCleanupDetachedElement.d.ts +13 -0
- package/dist/components/FilterInput/hooks/useAutoCleanupDetachedElement.js +22 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/{deriveAutocompleteValues.d.ts → lib/deriveAutocompleteValues.d.ts} +3 -3
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/{deriveAutocompleteValues.js → lib/deriveAutocompleteValues.js} +1 -1
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/lib/getInitialSegmentText.d.ts +9 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/lib/getInitialSegmentText.js +8 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/lib/index.d.ts +3 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/lib/index.js +4 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/{valueCommitHelpers.d.ts → lib/valueResolution.d.ts} +1 -7
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/{valueCommitHelpers.js → lib/valueResolution.js} +3 -25
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useAutocompleteState.d.ts +36 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useAutocompleteState.js +53 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useBlurCommit.d.ts +5 -16
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useBlurCommit.js +12 -5
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useChipActions.d.ts +2 -2
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useChipActions.js +5 -5
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useChipCascade.d.ts +44 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useChipCascade.js +99 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useChipEditing.d.ts +7 -8
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useChipEditing.js +21 -16
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFilterInputAutocomplete.d.ts +9 -10
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFilterInputAutocomplete.js +50 -83
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFocusManagement.d.ts +9 -11
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFocusManagement.js +47 -20
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useInputHandlers.d.ts +6 -4
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useInputHandlers.js +32 -15
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/index.d.ts +1 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/index.js +2 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/{useMenuFlow.d.ts → useMenuFlow/types.d.ts} +15 -18
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/types.js +0 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useFieldFlow.d.ts +11 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useFieldFlow.js +95 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useMenuFlow.d.ts +20 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useMenuFlow.js +47 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useOperatorFlow.d.ts +11 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useOperatorFlow.js +87 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useValueFlow.d.ts +14 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useValueFlow.js +107 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuPositioning.d.ts +25 -8
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuPositioning.js +38 -22
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useResetState.d.ts +10 -19
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useResetState.js +3 -3
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useSegmentEditFlow.d.ts +36 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useSegmentEditFlow.js +69 -0
- package/dist/components/FilterInput/hooks/useFilterInputExpression/buildChips.js +1 -2
- package/dist/components/FilterInput/hooks/useFilterInputPositioning.d.ts +9 -4
- package/dist/components/FilterInput/hooks/useFilterInputPositioning.js +7 -6
- package/dist/components/FilterInput/hooks/useFilterInputSelection/lib/dom.d.ts +4 -4
- package/dist/components/FilterInput/hooks/useFilterInputSelection/useFilterInputSelection.d.ts +2 -2
- package/dist/components/FilterInput/hooks/useFilterInputSelection/useSelectionClipboard.d.ts +2 -5
- package/dist/components/FilterInput/hooks/useFloatingRecomputeOn.d.ts +14 -0
- package/dist/components/FilterInput/hooks/useFloatingRecomputeOn.js +18 -0
- package/dist/components/FilterInput/hooks/useResizeTracker.d.ts +10 -0
- package/dist/components/FilterInput/hooks/useResizeTracker.js +21 -0
- package/dist/components/FilterInput/lib/applyAcceptChar.d.ts +3 -4
- package/dist/components/FilterInput/lib/applyKnownFieldHelpers.d.ts +13 -25
- package/dist/components/FilterInput/lib/constants.d.ts +16 -34
- package/dist/components/FilterInput/lib/constants.js +3 -1
- package/dist/components/FilterInput/lib/dom.d.ts +15 -11
- package/dist/components/FilterInput/lib/dom.js +14 -9
- package/dist/components/FilterInput/lib/fields.d.ts +10 -19
- package/dist/components/FilterInput/lib/index.d.ts +4 -2
- package/dist/components/FilterInput/lib/index.js +5 -3
- package/dist/components/FilterInput/lib/menuFilterText.d.ts +8 -18
- package/dist/components/FilterInput/lib/operators.d.ts +11 -21
- package/dist/components/FilterInput/lib/segmentMenu.d.ts +4 -0
- package/dist/components/FilterInput/lib/segmentMenu.js +7 -0
- package/dist/components/FilterInput/lib/serializeExpression.d.ts +5 -12
- package/dist/components/FilterInput/lib/validation.d.ts +9 -0
- package/dist/components/FilterInput/lib/validation.js +24 -0
- package/dist/metadata/components.json +8 -8
- package/package.json +1 -1
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow.js +0 -260
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import { useMemo } from "react";
|
|
2
|
-
import { QUERY_BAR_SELECTOR,
|
|
3
|
-
const useFilterInputPositioning = ({ anchorRef, containerRef,
|
|
2
|
+
import { QUERY_BAR_SELECTOR, buildAnchoredRect, toAnchorBounds } from "../lib/index.js";
|
|
3
|
+
const useFilterInputPositioning = ({ anchorRef, containerRef, getAnchorBounds, gutter = 12 }, deps = [])=>useMemo(()=>({
|
|
4
4
|
placement: 'bottom-start',
|
|
5
|
-
gutter
|
|
5
|
+
gutter,
|
|
6
6
|
getAnchorRect: ()=>{
|
|
7
7
|
const containerEl = containerRef?.current ?? anchorRef?.current?.closest(QUERY_BAR_SELECTOR);
|
|
8
8
|
const containerRect = containerEl?.getBoundingClientRect();
|
|
9
9
|
if (!containerRect) return null;
|
|
10
|
-
const
|
|
11
|
-
return
|
|
10
|
+
const anchor = getAnchorBounds ? getAnchorBounds(containerRect) : toAnchorBounds(anchorRef?.current?.getBoundingClientRect() ?? containerRect);
|
|
11
|
+
return buildAnchoredRect(anchor, containerRect);
|
|
12
12
|
}
|
|
13
13
|
}), [
|
|
14
14
|
anchorRef,
|
|
15
15
|
containerRef,
|
|
16
|
-
|
|
16
|
+
getAnchorBounds,
|
|
17
|
+
gutter,
|
|
17
18
|
...deps
|
|
18
19
|
]);
|
|
19
20
|
export { useFilterInputPositioning };
|
|
@@ -11,12 +11,12 @@ interface VisualEntry {
|
|
|
11
11
|
}
|
|
12
12
|
/** Snapshot registry entries with cached rects, sorted by visual position (top→bottom, left→right) */
|
|
13
13
|
export declare const getVisualEntries: (registry: Map<string, HTMLElement>) => VisualEntry[];
|
|
14
|
-
/** Resolve the condition chip ID at the mousedown point (
|
|
14
|
+
/** Resolve the condition chip ID at the mousedown point (once per drag). */
|
|
15
15
|
export declare const resolveAnchorChipId: (registry: Map<string, HTMLElement>, target: HTMLElement, x: number, y: number) => string | null;
|
|
16
16
|
/**
|
|
17
|
-
* Mark chips
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* Mark chips drag-selected from anchor to cursor. Both ends resolve in the
|
|
18
|
+
* same visually-sorted array so indices match; intermediate connectors are
|
|
19
|
+
* included automatically.
|
|
20
20
|
*/
|
|
21
21
|
export declare const updateDragSelection: (all: VisualEntry[], anchorChipId: string, currentX: number, currentY: number) => boolean;
|
|
22
22
|
export {};
|
package/dist/components/FilterInput/hooks/useFilterInputSelection/useFilterInputSelection.d.ts
CHANGED
|
@@ -10,9 +10,9 @@ interface UseFilterInputSelectionOptions {
|
|
|
10
10
|
clearAll: () => void;
|
|
11
11
|
setInputText: (text: string) => void;
|
|
12
12
|
closeMenu: () => void;
|
|
13
|
-
/** Replace the entire expression
|
|
13
|
+
/** Replace the entire expression (paste path keeps local state in sync). */
|
|
14
14
|
replaceExpression: (expression: ExprNode | null) => void;
|
|
15
|
-
/** Reset transient autocomplete state after paste
|
|
15
|
+
/** Reset transient autocomplete state after paste. */
|
|
16
16
|
resetAutocompleteState: () => void;
|
|
17
17
|
}
|
|
18
18
|
export declare const useFilterInputSelection: ({ conditions, connectors, fields, containerRef, chipRegistryRef, inputRef, clearAll, setInputText, closeMenu, replaceExpression, resetAutocompleteState, }: UseFilterInputSelectionOptions) => {
|
package/dist/components/FilterInput/hooks/useFilterInputSelection/useSelectionClipboard.d.ts
CHANGED
|
@@ -11,11 +11,8 @@ interface UseSelectionClipboardOptions {
|
|
|
11
11
|
closeMenu: () => void;
|
|
12
12
|
/** Replace the whole expression. Updates local state so paste works in uncontrolled mode too. */
|
|
13
13
|
replaceExpression: (expression: ExprNode | null) => void;
|
|
14
|
-
/**
|
|
15
|
-
*
|
|
16
|
-
* after a successful paste — otherwise a stale `insertIndex` from a prior gap-click or
|
|
17
|
-
* Backspace puts the input cursor between newly-pasted chips instead of at the end.
|
|
18
|
-
*/
|
|
14
|
+
/** Reset transient autocomplete state after paste; otherwise stale
|
|
15
|
+
* insertIndex puts the cursor between newly-pasted chips. */
|
|
19
16
|
resetAutocompleteState: () => void;
|
|
20
17
|
}
|
|
21
18
|
export declare const useSelectionClipboard: ({ conditions, connectors, fields, chipRegistryRef, clearSelection, setPasteError, setInputText, closeMenu, replaceExpression, resetAutocompleteState, }: UseSelectionClipboardOptions) => {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pokes floating-ui (via @zag-js / Ark UI) to recompute when `dep` changes.
|
|
3
|
+
* floating-ui's autoUpdate listens for resize/scroll but not sibling reflow,
|
|
4
|
+
* so a laterally-shifting anchor would lag one paint. Must be useLayoutEffect
|
|
5
|
+
* — useEffect runs after paint.
|
|
6
|
+
*
|
|
7
|
+
* Two scoping rules:
|
|
8
|
+
* - `enabled = false` short-circuits entirely (no event while menu closed).
|
|
9
|
+
* - First mount is skipped (no transition has occurred yet).
|
|
10
|
+
*
|
|
11
|
+
* The dispatch is still a global `resize` (autoUpdate's only listener) — keep
|
|
12
|
+
* dep-change rate low at the call site.
|
|
13
|
+
*/
|
|
14
|
+
export declare const useFloatingRecomputeOn: (dep: unknown, enabled?: boolean) => void;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useLayoutEffect, useRef } from "react";
|
|
2
|
+
const useFloatingRecomputeOn = (dep, enabled = true)=>{
|
|
3
|
+
const prevDepRef = useRef(dep);
|
|
4
|
+
useLayoutEffect(()=>{
|
|
5
|
+
if (!enabled) {
|
|
6
|
+
prevDepRef.current = dep;
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
if ("u" < typeof window) return;
|
|
10
|
+
if (Object.is(prevDepRef.current, dep)) return;
|
|
11
|
+
prevDepRef.current = dep;
|
|
12
|
+
window.dispatchEvent(new Event('resize'));
|
|
13
|
+
}, [
|
|
14
|
+
dep,
|
|
15
|
+
enabled
|
|
16
|
+
]);
|
|
17
|
+
};
|
|
18
|
+
export { useFloatingRecomputeOn };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Observes up to three elements with one ResizeObserver and returns a counter
|
|
3
|
+
* that increments on every resize — use as a memo/effect dep to force
|
|
4
|
+
* recompute when a tracked element changes size. Null entries are skipped.
|
|
5
|
+
*
|
|
6
|
+
* Observing a detached element is safe: ResizeObserver simply doesn't fire
|
|
7
|
+
* until the element re-attaches and lays out, then resumes — no leak. We do
|
|
8
|
+
* NOT filter detached nodes so re-attach is picked up without ref churn.
|
|
9
|
+
*/
|
|
10
|
+
export declare const useResizeTracker: (el1: HTMLElement | null, el2?: HTMLElement | null, el3?: HTMLElement | null) => number;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useEffect, useReducer } from "react";
|
|
2
|
+
const useResizeTracker = (el1, el2, el3)=>{
|
|
3
|
+
const [tick, bump] = useReducer((x)=>x + 1, 0);
|
|
4
|
+
useEffect(()=>{
|
|
5
|
+
const targets = [
|
|
6
|
+
el1,
|
|
7
|
+
el2,
|
|
8
|
+
el3
|
|
9
|
+
].filter((el)=>null != el);
|
|
10
|
+
if (0 === targets.length) return;
|
|
11
|
+
const observer = new ResizeObserver(()=>bump());
|
|
12
|
+
for (const el of targets)observer.observe(el);
|
|
13
|
+
return ()=>observer.disconnect();
|
|
14
|
+
}, [
|
|
15
|
+
el1,
|
|
16
|
+
el2,
|
|
17
|
+
el3
|
|
18
|
+
]);
|
|
19
|
+
return tick;
|
|
20
|
+
};
|
|
21
|
+
export { useResizeTracker };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Strip
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* needed, so callers can cheaply compare identity to detect a no-op.
|
|
2
|
+
* Strip chars failing `acceptChar`. Commas/spaces pass through (multi-select
|
|
3
|
+
* delimiters). Returns the same string when no filtering happens, so callers
|
|
4
|
+
* can identity-compare to detect a no-op.
|
|
6
5
|
*/
|
|
7
6
|
export declare const applyAcceptChar: (text: string, acceptChar: (c: string) => boolean) => string;
|
|
@@ -1,34 +1,22 @@
|
|
|
1
1
|
import type { FieldMetadata } from '../types';
|
|
2
2
|
/**
|
|
3
|
-
* Decorate `fields`
|
|
4
|
-
*
|
|
3
|
+
* Decorate `fields` with design-system helpers for reserved names. DS-supplied
|
|
4
|
+
* callbacks **override** consumer values for the same slot — the field
|
|
5
|
+
* semantics (mask range, accepted chars, backend form) are fixed by DS.
|
|
5
6
|
*
|
|
6
|
-
* Reserved names
|
|
7
|
+
* Reserved names:
|
|
8
|
+
* | `name` | DS owns |
|
|
9
|
+
* | ------------- | -------------------------------------------------------- |
|
|
10
|
+
* | `status_code` | acceptChar, normalize, getSuggestions, validate, serializeValue |
|
|
7
11
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* For reserved names, DS-supplied callbacks **override** any consumer-supplied
|
|
13
|
-
* value for the same slot. This is intentional: the field semantics (mask
|
|
14
|
-
* range `1XX..5XX`, accepted characters, backend form) are fixed by the
|
|
15
|
-
* design system. Consumer-supplied `options`/`getSuggestions` would otherwise
|
|
16
|
-
* fight the canonical mask UX.
|
|
17
|
-
*
|
|
18
|
-
* If the backend uses a different name (e.g. `http_status_code`), the helpers
|
|
19
|
-
* are NOT applied — the consumer must either rename the field to match or
|
|
20
|
-
* wire the factories (`createStatusCode*`) manually.
|
|
21
|
-
*
|
|
22
|
-
* The returned array has **reference-stable identity** when no field matches,
|
|
23
|
-
* so downstream `useMemo` that depends on it does not invalidate unnecessarily.
|
|
12
|
+
* Backend with a different name (e.g. `http_status_code`) must either rename
|
|
13
|
+
* or wire factories (`createStatusCode*`) manually. Returns the input array
|
|
14
|
+
* by reference when no field matches (stable identity for downstream memos).
|
|
24
15
|
*/
|
|
25
16
|
export declare const applyKnownFieldHelpers: (fields: FieldMetadata[]) => FieldMetadata[];
|
|
26
17
|
/**
|
|
27
|
-
* Look up the backend-form serializer for a reserved field name
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* transform that the FilterInput's `serializeValue` slot would produce — so
|
|
31
|
-
* they can drive the lookup off the field name without hard-coding which
|
|
32
|
-
* names are reserved.
|
|
18
|
+
* Look up the backend-form serializer for a reserved field name; returns
|
|
19
|
+
* undefined for unknown names. For consumers that hold their own expression
|
|
20
|
+
* shape but want the same transform as FilterInput's `serializeValue`.
|
|
33
21
|
*/
|
|
34
22
|
export declare const getKnownFieldSerializer: (fieldName: string) => NonNullable<FieldMetadata["serializeValue"]> | undefined;
|
|
@@ -1,45 +1,27 @@
|
|
|
1
1
|
import type { FieldType, FilterInputChipVariant, FilterOperator } from '../types';
|
|
2
|
-
/**
|
|
3
|
-
* Operators that require no value (unary)
|
|
4
|
-
*/
|
|
2
|
+
/** Operators that require no value (unary). */
|
|
5
3
|
export declare const NO_VALUE_OPERATORS: readonly FilterOperator[];
|
|
6
|
-
/**
|
|
7
|
-
* Operators that support multi-select values
|
|
8
|
-
*/
|
|
4
|
+
/** Operators that support multi-select values. */
|
|
9
5
|
export declare const MULTI_SELECT_OPERATORS: readonly FilterOperator[];
|
|
10
|
-
/**
|
|
11
|
-
* Pattern to extract condition index from chip ID (e.g. "chip-2" → 2)
|
|
12
|
-
*/
|
|
6
|
+
/** Extract condition index from chip ID (e.g. "chip-2" → 2). */
|
|
13
7
|
export declare const CHIP_ID_PATTERN: RegExp;
|
|
14
|
-
/**
|
|
15
|
-
* Pattern to extract condition index from connector ID (e.g. "connector-2" → 2)
|
|
16
|
-
*/
|
|
8
|
+
/** Extract condition index from connector ID. */
|
|
17
9
|
export declare const CONNECTOR_ID_PATTERN: RegExp;
|
|
18
|
-
/**
|
|
19
|
-
* DOM selector for the FilterInput root element
|
|
20
|
-
*/
|
|
10
|
+
/** DOM selector for the FilterInput root element. */
|
|
21
11
|
export declare const QUERY_BAR_SELECTOR = "[data-slot=\"filter-input\"]";
|
|
22
|
-
/**
|
|
23
|
-
|
|
24
|
-
|
|
12
|
+
/** Gap between the dropdown and the field border in the empty/initial state. */
|
|
13
|
+
export declare const MENU_BASE_GUTTER = 4;
|
|
14
|
+
/** Extra vertical offset added to the anchor's bottom when the dropdown is
|
|
15
|
+
* anchored to a chip / input — total visual gap becomes
|
|
16
|
+
* MENU_BASE_GUTTER + MENU_CHIP_GUTTER_OFFSET (12). */
|
|
17
|
+
export declare const MENU_CHIP_GUTTER_OFFSET = 8;
|
|
18
|
+
/** Labels for non-chip filter chip variants (connectors, brackets). */
|
|
25
19
|
export declare const VARIANT_LABELS: Partial<Record<FilterInputChipVariant, string>>;
|
|
26
|
-
/**
|
|
27
|
-
* Operator Symbol Mapping
|
|
28
|
-
* Maps operators to their raw symbol displayed on the right side of menus
|
|
29
|
-
*/
|
|
20
|
+
/** Raw operator symbols shown on the right side of menus. */
|
|
30
21
|
export declare const OPERATOR_SYMBOLS: Record<FilterOperator, string>;
|
|
31
|
-
/**
|
|
32
|
-
* Operator Label Mapping
|
|
33
|
-
* Maps operator symbols to human-readable labels
|
|
34
|
-
*/
|
|
22
|
+
/** Human-readable labels for operator symbols. */
|
|
35
23
|
export declare const OPERATOR_LABELS: Record<FilterOperator, string>;
|
|
36
|
-
/**
|
|
37
|
-
* Field type-specific operator labels
|
|
38
|
-
* Some operators have different labels depending on field type
|
|
39
|
-
*/
|
|
24
|
+
/** Field-type-specific operator labels (some override the generic ones). */
|
|
40
25
|
export declare const OPERATOR_LABELS_BY_TYPE: Record<FieldType, Partial<Record<FilterOperator, string>>>;
|
|
41
|
-
/**
|
|
42
|
-
* Operators by Field Type Mapping (with groups for separators)
|
|
43
|
-
* Maps field types to their available operators, grouped logically
|
|
44
|
-
*/
|
|
26
|
+
/** Operators per field type, grouped for menu separators. */
|
|
45
27
|
export declare const OPERATORS_BY_TYPE: Record<FieldType, FilterOperator[][]>;
|
|
@@ -9,6 +9,8 @@ const MULTI_SELECT_OPERATORS = [
|
|
|
9
9
|
const CHIP_ID_PATTERN = /^chip-(\d+)$/;
|
|
10
10
|
const CONNECTOR_ID_PATTERN = /^connector-(\d+)$/;
|
|
11
11
|
const QUERY_BAR_SELECTOR = '[data-slot="filter-input"]';
|
|
12
|
+
const MENU_BASE_GUTTER = 4;
|
|
13
|
+
const MENU_CHIP_GUTTER_OFFSET = 8;
|
|
12
14
|
const VARIANT_LABELS = {
|
|
13
15
|
and: 'AND',
|
|
14
16
|
or: 'OR',
|
|
@@ -163,4 +165,4 @@ const OPERATORS_BY_TYPE = {
|
|
|
163
165
|
]
|
|
164
166
|
]
|
|
165
167
|
};
|
|
166
|
-
export { CHIP_ID_PATTERN, CONNECTOR_ID_PATTERN, MULTI_SELECT_OPERATORS, NO_VALUE_OPERATORS, OPERATORS_BY_TYPE, OPERATOR_LABELS, OPERATOR_LABELS_BY_TYPE, OPERATOR_SYMBOLS, QUERY_BAR_SELECTOR, VARIANT_LABELS };
|
|
168
|
+
export { CHIP_ID_PATTERN, CONNECTOR_ID_PATTERN, MENU_BASE_GUTTER, MENU_CHIP_GUTTER_OFFSET, MULTI_SELECT_OPERATORS, NO_VALUE_OPERATORS, OPERATORS_BY_TYPE, OPERATOR_LABELS, OPERATOR_LABELS_BY_TYPE, OPERATOR_SYMBOLS, QUERY_BAR_SELECTOR, VARIANT_LABELS };
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* `[data-scope="date-picker"]`) intentionally DO NOT match — otherwise blur
|
|
7
|
-
* handlers mistake unrelated page popups (tenant switcher, other dropdowns)
|
|
8
|
-
* for FilterInput's own menu and refuse to close. See AS-882.
|
|
2
|
+
* True if element belongs to a FilterInput-owned menu overlay. Marked by
|
|
3
|
+
* `data-filter-input-menu` on DropdownMenuContent — generic Ark UI selectors
|
|
4
|
+
* are intentionally NOT used so blur handlers don't confuse unrelated page
|
|
5
|
+
* popups (tenant switcher etc.) for FilterInput's own menu. AS-882.
|
|
9
6
|
*/
|
|
10
7
|
export declare const isMenuRelated: (el: HTMLElement | null) => boolean;
|
|
8
|
+
export interface AnchorBounds {
|
|
9
|
+
top: number;
|
|
10
|
+
bottom: number;
|
|
11
|
+
left: number;
|
|
12
|
+
}
|
|
13
|
+
/** Extract anchor bounds from a DOMRect (right edge is computed at build time). */
|
|
14
|
+
export declare const toAnchorBounds: (rect: DOMRect) => AnchorBounds;
|
|
11
15
|
/**
|
|
12
|
-
* Build a DOMRect-compatible object
|
|
13
|
-
*
|
|
14
|
-
*
|
|
16
|
+
* Build a DOMRect-compatible object from the anchor's vertical bounds + left
|
|
17
|
+
* edge; right edge stays at the container's right so the dropdown can expand
|
|
18
|
+
* to full container width below the actual interaction target.
|
|
15
19
|
*/
|
|
16
|
-
export declare const
|
|
20
|
+
export declare const buildAnchoredRect: (anchor: AnchorBounds, containerRect: DOMRect, anchorRight?: number) => {
|
|
17
21
|
x: number;
|
|
18
22
|
y: number;
|
|
19
23
|
width: number;
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
const isMenuRelated = (el)=>!!el?.closest('[data-filter-input-menu]');
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
const toAnchorBounds = (rect)=>({
|
|
3
|
+
top: rect.top,
|
|
4
|
+
bottom: rect.bottom,
|
|
5
|
+
left: rect.left
|
|
6
|
+
});
|
|
7
|
+
const buildAnchoredRect = (anchor, containerRect, anchorRight = containerRect.right)=>({
|
|
8
|
+
x: anchor.left,
|
|
9
|
+
y: anchor.top,
|
|
10
|
+
width: anchorRight - anchor.left,
|
|
11
|
+
height: anchor.bottom - anchor.top,
|
|
12
|
+
top: anchor.top,
|
|
13
|
+
bottom: anchor.bottom,
|
|
14
|
+
left: anchor.left,
|
|
10
15
|
right: anchorRight
|
|
11
16
|
});
|
|
12
|
-
export {
|
|
17
|
+
export { buildAnchoredRect, isMenuRelated, toAnchorBounds };
|
|
@@ -1,34 +1,25 @@
|
|
|
1
1
|
import type { FieldMetadata, FieldValueOption } from '../types';
|
|
2
2
|
/**
|
|
3
|
-
* Find an option in `field.values` matching the
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* `"5"` after `(field = "5")` parses back), and strict `===` would miss the
|
|
7
|
-
* canonical option. Returns undefined when there is no match or the field
|
|
8
|
-
* has no `values` allowlist.
|
|
3
|
+
* Find an option in `field.values` matching the value (stringified compare).
|
|
4
|
+
* Loose-match needed because parser/serializer round-trip stringifies typed
|
|
5
|
+
* primitives (5 → "5") and strict === would miss the canonical option.
|
|
9
6
|
*/
|
|
10
7
|
export declare const findOptionByValue: (field: FieldMetadata | undefined, value: string | number | boolean | null | undefined) => FieldValueOption | undefined;
|
|
11
8
|
/**
|
|
12
|
-
* Get value options for a field.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* `context.selectedValues` is forwarded to `getSuggestions` so helpers can
|
|
16
|
-
* include the currently-committed values in their output (e.g. to preserve a
|
|
17
|
-
* badge style on a concrete code once suggestions have narrowed to masks).
|
|
9
|
+
* Get value options for a field — priority: getSuggestions > values > options.
|
|
10
|
+
* `context.selectedValues` lets helpers preserve a committed value's badge
|
|
11
|
+
* style once suggestions have narrowed.
|
|
18
12
|
*/
|
|
19
13
|
export declare const getFieldValues: (field: FieldMetadata, inputText?: string, context?: {
|
|
20
14
|
selectedValues?: Array<string | number | boolean>;
|
|
21
15
|
}) => FieldValueOption[];
|
|
22
16
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* Fields with `getSuggestions` always get a dropdown (empty list is still a list).
|
|
17
|
+
* True if a field has any value suggestions (dynamic, values, or options).
|
|
18
|
+
* Decides whether to render a value dropdown. getSuggestions always yields one.
|
|
26
19
|
*/
|
|
27
20
|
export declare const hasFieldValues: (field: FieldMetadata) => boolean;
|
|
28
21
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* Fields with `getSuggestions` return false — their suggestion list is a hint,
|
|
32
|
-
* not an allowlist (consumer may accept freeform values that aren't currently suggested).
|
|
22
|
+
* True if the field has an exhaustive static allowlist. getSuggestions
|
|
23
|
+
* fields return false — their list is a hint, not a strict allowlist.
|
|
33
24
|
*/
|
|
34
25
|
export declare const hasStaticAllowlist: (field: FieldMetadata) => boolean;
|
|
@@ -4,12 +4,14 @@ export { applyAcceptChar } from './applyAcceptChar';
|
|
|
4
4
|
export { applyFieldValueTransforms } from './applyFieldValueTransforms';
|
|
5
5
|
export { applyKnownFieldHelpers, getKnownFieldSerializer } from './applyKnownFieldHelpers';
|
|
6
6
|
export { chipIdToConditionIndex, findChipSplitIndex } from './conditions';
|
|
7
|
-
export { CONNECTOR_ID_PATTERN, NO_VALUE_OPERATORS, OPERATOR_LABELS, OPERATOR_LABELS_BY_TYPE, OPERATOR_SYMBOLS, OPERATORS_BY_TYPE, QUERY_BAR_SELECTOR, VARIANT_LABELS, } from './constants';
|
|
8
|
-
export {
|
|
7
|
+
export { CONNECTOR_ID_PATTERN, MENU_BASE_GUTTER, MENU_CHIP_GUTTER_OFFSET, NO_VALUE_OPERATORS, OPERATOR_LABELS, OPERATOR_LABELS_BY_TYPE, OPERATOR_SYMBOLS, OPERATORS_BY_TYPE, QUERY_BAR_SELECTOR, VARIANT_LABELS, } from './constants';
|
|
8
|
+
export { type AnchorBounds, buildAnchoredRect, isMenuRelated, toAnchorBounds } from './dom';
|
|
9
9
|
export { findOptionByValue, getFieldValues, hasFieldValues, hasStaticAllowlist } from './fields';
|
|
10
10
|
export { filterAndSort } from './filterSort';
|
|
11
11
|
export { getCurrentValueTokenText, getValueFilterText } from './menuFilterText';
|
|
12
12
|
export { getFieldOperators, getOperatorFromLabel, getOperatorLabel, isBetweenOperator, isBuildingComplete, isMultiSelectOperator, isNoValueOperator, isOperatorAllowedForField, isValueShapeCompatible, NO_VALUE_PLACEHOLDER, nextBuildingMenu, } from './operators';
|
|
13
13
|
export { type FilterParseError, isFilterParseError, parseExpression } from './parseExpression';
|
|
14
|
+
export { SEGMENT_TO_MENU } from './segmentMenu';
|
|
14
15
|
export { serializeExpression } from './serializeExpression';
|
|
15
16
|
export { createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSerializer, createStatusCodeSuggestions, createStatusCodeValidator, } from './statusCode';
|
|
17
|
+
export { findMatchingFieldValue, getInvalidValueIndices, isValidFieldValue, validateValueForField, } from './validation';
|
|
@@ -3,13 +3,15 @@ import { applyAcceptChar } from "./applyAcceptChar.js";
|
|
|
3
3
|
import { applyFieldValueTransforms } from "./applyFieldValueTransforms.js";
|
|
4
4
|
import { applyKnownFieldHelpers, getKnownFieldSerializer } from "./applyKnownFieldHelpers.js";
|
|
5
5
|
import { chipIdToConditionIndex, findChipSplitIndex } from "./conditions.js";
|
|
6
|
-
import { CONNECTOR_ID_PATTERN, NO_VALUE_OPERATORS, OPERATORS_BY_TYPE, OPERATOR_LABELS, OPERATOR_LABELS_BY_TYPE, OPERATOR_SYMBOLS, QUERY_BAR_SELECTOR, VARIANT_LABELS } from "./constants.js";
|
|
7
|
-
import {
|
|
6
|
+
import { CONNECTOR_ID_PATTERN, MENU_BASE_GUTTER, MENU_CHIP_GUTTER_OFFSET, NO_VALUE_OPERATORS, OPERATORS_BY_TYPE, OPERATOR_LABELS, OPERATOR_LABELS_BY_TYPE, OPERATOR_SYMBOLS, QUERY_BAR_SELECTOR, VARIANT_LABELS } from "./constants.js";
|
|
7
|
+
import { buildAnchoredRect, isMenuRelated, toAnchorBounds } from "./dom.js";
|
|
8
8
|
import { findOptionByValue, getFieldValues, hasFieldValues, hasStaticAllowlist } from "./fields.js";
|
|
9
9
|
import { filterAndSort } from "./filterSort.js";
|
|
10
10
|
import { getCurrentValueTokenText, getValueFilterText } from "./menuFilterText.js";
|
|
11
11
|
import { NO_VALUE_PLACEHOLDER, getFieldOperators, getOperatorFromLabel, getOperatorLabel, isBetweenOperator, isBuildingComplete, isMultiSelectOperator, isNoValueOperator, isOperatorAllowedForField, isValueShapeCompatible, nextBuildingMenu } from "./operators.js";
|
|
12
12
|
import { isFilterParseError, parseExpression } from "./parseExpression/index.js";
|
|
13
|
+
import { SEGMENT_TO_MENU } from "./segmentMenu.js";
|
|
13
14
|
import { serializeExpression } from "./serializeExpression.js";
|
|
14
15
|
import { createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSerializer, createStatusCodeSuggestions, createStatusCodeValidator } from "./statusCode/index.js";
|
|
15
|
-
|
|
16
|
+
import { findMatchingFieldValue, getInvalidValueIndices, isValidFieldValue, validateValueForField } from "./validation.js";
|
|
17
|
+
export { CONNECTOR_ID_PATTERN, DATE_PRESETS, MENU_BASE_GUTTER, MENU_CHIP_GUTTER_OFFSET, NO_VALUE_OPERATORS, NO_VALUE_PLACEHOLDER, OPERATORS_BY_TYPE, OPERATOR_LABELS, OPERATOR_LABELS_BY_TYPE, OPERATOR_SYMBOLS, QUERY_BAR_SELECTOR, SEGMENT_TO_MENU, VARIANT_LABELS, applyAcceptChar, applyFieldValueTransforms, applyKnownFieldHelpers, buildAnchoredRect, chipIdToConditionIndex, createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSerializer, createStatusCodeSuggestions, createStatusCodeValidator, filterAndSort, findChipSplitIndex, findMatchingFieldValue, findOptionByValue, formatDateForChip, getCurrentValueTokenText, getDateDisplayLabel, getFieldOperators, getFieldValues, getInvalidValueIndices, getKnownFieldSerializer, getOperatorFromLabel, getOperatorLabel, getValueFilterText, hasFieldValues, hasStaticAllowlist, isBetweenOperator, isBuildingComplete, isDatePreset, isFilterParseError, isMenuRelated, isMultiSelectOperator, isNoValueOperator, isOperatorAllowedForField, isValidFieldValue, isValueShapeCompatible, nextBuildingMenu, parseExpression, serializeExpression, toAnchorBounds, validateValueForField };
|
|
@@ -1,24 +1,14 @@
|
|
|
1
|
+
import { type ChipSegment } from '../FilterInputField/FilterInputChip';
|
|
1
2
|
import type { FieldValueOption, FilterOperator } from '../types';
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* a comma-separated list such as `"2XX, 3XX, 4"`. Only the last token (`"4"`)
|
|
7
|
-
* represents the value currently being composed; prior tokens are already
|
|
8
|
-
* committed as checked items. Callers use this to feed both `getSuggestions`
|
|
9
|
-
* and the dropdown's filter/sort so the active token drives the menu.
|
|
10
|
-
*
|
|
11
|
-
* For single-value operators the raw input is returned unchanged.
|
|
4
|
+
* The substring the user is actively typing. For multi-select operators with
|
|
5
|
+
* comma-separated input ("2XX, 3XX, 4"), returns only the last token ("4").
|
|
6
|
+
* Single-value operators get the raw input.
|
|
12
7
|
*/
|
|
13
|
-
export declare const getCurrentValueTokenText: (editingSegment:
|
|
8
|
+
export declare const getCurrentValueTokenText: (editingSegment: ChipSegment | null, inputText: string, segmentMenuFilterText: string, selectedOperator: FilterOperator | null) => string;
|
|
14
9
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* is actively typing). If that token already matches a known value, returns
|
|
19
|
-
* `''` so the dropdown shows all options — the user just finished a selection
|
|
20
|
-
* and is about to start the next one.
|
|
21
|
-
*
|
|
22
|
-
* For single-value operators, returns the token as-is.
|
|
10
|
+
* Filter text for the value menu. Multi-select: returns the current token, or
|
|
11
|
+
* '' when it already matches a known value (just-completed selection).
|
|
12
|
+
* Single-value: returns token as-is.
|
|
23
13
|
*/
|
|
24
14
|
export declare const getValueFilterText: (currentTokenText: string, selectedOperator: FilterOperator | null, fieldValues: FieldValueOption[]) => string;
|
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
import type { FieldMetadata, FieldType, FilterOperator, MenuState } from '../types';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* paths can never drift.
|
|
3
|
+
* Placeholder in no-value operator chips so every chip visually has 3
|
|
4
|
+
* segments. Co-located with isNoValueOperator so committed and
|
|
5
|
+
* building-preview paths can't drift.
|
|
7
6
|
*/
|
|
8
7
|
export declare const NO_VALUE_PLACEHOLDER = "\u2014";
|
|
9
|
-
/**
|
|
10
|
-
* Helper to get operator label for specific field type
|
|
11
|
-
*/
|
|
8
|
+
/** Get operator label for a specific field type. */
|
|
12
9
|
export declare const getOperatorLabel: (operator: FilterOperator, fieldType: FieldType) => string;
|
|
13
|
-
/**
|
|
14
|
-
* Reverse lookup: get raw FilterOperator from its display label and field type
|
|
15
|
-
*/
|
|
10
|
+
/** Reverse lookup: FilterOperator from display label and field type. */
|
|
16
11
|
export declare const getOperatorFromLabel: (label: string, fieldType: FieldType) => FilterOperator | null;
|
|
17
12
|
/** Check if operator supports multi-select */
|
|
18
13
|
export declare const isMultiSelectOperator: (op: FilterOperator | null) => op is FilterOperator;
|
|
@@ -25,22 +20,17 @@ export declare const getFieldOperators: (field: FieldMetadata) => FilterOperator
|
|
|
25
20
|
/** Check whether an operator is supported by a field's type/override list */
|
|
26
21
|
export declare const isOperatorAllowedForField: (field: FieldMetadata, operator: FilterOperator) => boolean;
|
|
27
22
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* Reused by the refocus path (useFocusManagement) and the main-input click
|
|
31
|
-
* path (useInputHandlers) so both stay in sync.
|
|
23
|
+
* Next menu to open to continue building a chip; null when fully built.
|
|
24
|
+
* Shared by useFocusManagement (refocus) and useInputHandlers (click).
|
|
32
25
|
*/
|
|
33
26
|
export declare const nextBuildingMenu: (field: FieldMetadata | null, operator: FilterOperator | null) => MenuState | null;
|
|
34
27
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* (is_null / is_not_null) are complete without a value: the chip renders a
|
|
38
|
-
* value-placeholder in that slot so it still visually has three segments.
|
|
28
|
+
* True if the in-progress (field, operator, value) triple has all segments.
|
|
29
|
+
* No-value operators (is_null/is_not_null) are complete without a value.
|
|
39
30
|
*/
|
|
40
31
|
export declare const isBuildingComplete: (field: FieldMetadata | null, operator: FilterOperator | null, value: string | number | boolean | Array<string | number | boolean> | null | undefined) => boolean;
|
|
41
32
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* preview built for `a` can be reused as-is for `b`.
|
|
33
|
+
* True when two operators share value shape (multi-select/between/no-value
|
|
34
|
+
* categories match) — a value preview built for `a` is reusable for `b`.
|
|
45
35
|
*/
|
|
46
36
|
export declare const isValueShapeCompatible: (a: FilterOperator | null, b: FilterOperator | null) => boolean;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type SegmentVariant } from '../FilterInputField/FilterInputChip/segmentVariant';
|
|
2
|
+
import type { MenuState } from '../types';
|
|
3
|
+
/** Open-menu state for each chip segment (segment click, Backspace cascade). */
|
|
4
|
+
export declare const SEGMENT_TO_MENU: Record<SegmentVariant, MenuState>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { SEGMENT_VARIANT } from "../FilterInputField/FilterInputChip/segmentVariant.js";
|
|
2
|
+
const SEGMENT_TO_MENU = {
|
|
3
|
+
[SEGMENT_VARIANT.attribute]: 'field',
|
|
4
|
+
[SEGMENT_VARIANT.operator]: 'operator',
|
|
5
|
+
[SEGMENT_VARIANT.value]: 'value'
|
|
6
|
+
};
|
|
7
|
+
export { SEGMENT_TO_MENU };
|
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
import type { ExprNode, FieldMetadata } from '../types';
|
|
2
2
|
/**
|
|
3
|
-
* Serialize an expression tree to
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* `"2XX"` become `"2"` in the emitted string. Omitting `fields` keeps the
|
|
9
|
-
* UI-facing values verbatim — useful for clipboard round-trip, debug
|
|
10
|
-
* display, or any case where the output feeds `parseExpression` back. If
|
|
11
|
-
* you forget `fields` on the path to the backend, the placeholder `X`s
|
|
12
|
-
* ride through silently — name the backend call-site helper explicitly
|
|
13
|
-
* (e.g. `const query = serializeExpression(expr, fields)`) to make the
|
|
14
|
-
* intent obvious at callsite.
|
|
3
|
+
* Serialize an expression tree to canonical text. Top-level conditions sort
|
|
4
|
+
* by field name. Pass `fields` for backend output — per-field `serializeValue`
|
|
5
|
+
* runs first (e.g. `"2XX"` → `"2"`). Omitting `fields` keeps UI-facing values
|
|
6
|
+
* verbatim (clipboard round-trip, parseExpression input). Forgetting `fields`
|
|
7
|
+
* on the backend path silently emits placeholder Xs.
|
|
15
8
|
*/
|
|
16
9
|
export declare const serializeExpression: (expr: ExprNode | null, fields?: FieldMetadata[]) => string;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Condition, FieldMetadata, FieldValueOption } from '../types';
|
|
2
|
+
/** Find a field value option matching by label or value (case-insensitive) */
|
|
3
|
+
export declare const findMatchingFieldValue: (fieldValues: FieldValueOption[], text: string) => FieldValueOption | undefined;
|
|
4
|
+
/** Check if a single value matches any option in the field's values list */
|
|
5
|
+
export declare const isValidFieldValue: (fieldValues: FieldValueOption[], v: string | number | boolean) => boolean;
|
|
6
|
+
/** Return indices of values that don't match any field option. Empty array = all valid. */
|
|
7
|
+
export declare const getInvalidValueIndices: (field: FieldMetadata, values: Array<string | number | boolean>) => number[];
|
|
8
|
+
/** Check if condition value(s) are valid for the given field. Returns true if error. */
|
|
9
|
+
export declare const validateValueForField: (field: FieldMetadata, value: Condition["value"]) => boolean;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { getFieldValues, hasStaticAllowlist } from "./fields.js";
|
|
2
|
+
const findMatchingFieldValue = (fieldValues, text)=>fieldValues.find((v)=>v.label.toLowerCase() === text.toLowerCase() || String(v.value).toLowerCase() === text.toLowerCase());
|
|
3
|
+
const isValidFieldValue = (fieldValues, v)=>fieldValues.some((opt)=>opt.value === v || String(opt.value).toLowerCase() === String(v).toLowerCase());
|
|
4
|
+
const getInvalidValueIndices = (field, values)=>{
|
|
5
|
+
if (field.validate) return values.reduce((acc, v, idx)=>{
|
|
6
|
+
if (field.validate(v)) acc.push(idx);
|
|
7
|
+
return acc;
|
|
8
|
+
}, []);
|
|
9
|
+
if (!hasStaticAllowlist(field)) return [];
|
|
10
|
+
const fv = getFieldValues(field);
|
|
11
|
+
if (0 === fv.length) return [];
|
|
12
|
+
return values.reduce((acc, v, idx)=>{
|
|
13
|
+
if (!isValidFieldValue(fv, v)) acc.push(idx);
|
|
14
|
+
return acc;
|
|
15
|
+
}, []);
|
|
16
|
+
};
|
|
17
|
+
const validateValueForField = (field, value)=>{
|
|
18
|
+
if (null == value) return false;
|
|
19
|
+
const values = Array.isArray(value) ? value : [
|
|
20
|
+
value
|
|
21
|
+
];
|
|
22
|
+
return getInvalidValueIndices(field, values).length > 0;
|
|
23
|
+
};
|
|
24
|
+
export { findMatchingFieldValue, getInvalidValueIndices, isValidFieldValue, validateValueForField };
|