@charcuterie/ui 2.12.1 → 2.13.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.
@@ -0,0 +1,64 @@
1
+ import type { Tree } from "@charcuterie/logic";
2
+ import type { ReactNode } from "react";
3
+ export type QueryBuilderLabels = {
4
+ addGroup?: string;
5
+ addLeaf?: string;
6
+ removeGroup?: string;
7
+ removeLeaf?: string;
8
+ };
9
+ export type QueryBuilderProps<Combinator, Leaf> = {
10
+ /**
11
+ * The combinator choices offered in every group's "Match" select.
12
+ * The `value` is opaque — `and`/`or` for a mail rule, `any`/`all`/
13
+ * `none` for a DSL — and the `label` is what a user reads.
14
+ */
15
+ combinatorOptions: readonly {
16
+ label: string;
17
+ value: Combinator;
18
+ }[];
19
+ /** The value a fresh leaf is born with when "+ Condition" is pressed. */
20
+ createLeafValue: () => Leaf;
21
+ labels?: QueryBuilderLabels;
22
+ /**
23
+ * The leaf UI, owned by the app. `QueryBuilder` knows nothing about
24
+ * what a condition *is* — a field/operator/value triple, a
25
+ * non-uniform per-kind shape — only that it can be rendered and
26
+ * patched. The delete control around each leaf is the builder's.
27
+ */
28
+ renderLeaf: (args: {
29
+ nodeId: string;
30
+ onChange: (value: Leaf) => void;
31
+ value: Leaf;
32
+ }) => ReactNode;
33
+ /**
34
+ * The `Tree` core from `useTree`, owned by the consumer — the same
35
+ * arrangement as a `Listbox`'s parent owning `useVisibility`.
36
+ * `QueryBuilder` subscribes to it and drives it; it never creates
37
+ * one, so a consumer keeps `serialize()` and the initial tree.
38
+ */
39
+ tree: Tree<Combinator, Leaf>;
40
+ };
41
+ /**
42
+ * The generic, nestable AND/OR — and any-combinator — group editor.
43
+ *
44
+ * Two apps in the fleet grow the same widget: Mail Sifter's nested
45
+ * mail rules (leaf = field/operator/value, combinators AND/OR) and
46
+ * mux-magic's job DSL (non-uniform leaves, combinators any/all/none).
47
+ * Both are a recursive group of conditions with a combinator, so the
48
+ * value **and** the combinator are opaque here — `Combinator` and
49
+ * `Leaf` never appear except passed through to the consumer's
50
+ * `renderLeaf` and its `combinatorOptions`.
51
+ *
52
+ * State lives entirely in the `Tree` core from `@charcuterie/logic`;
53
+ * this component holds none of its own. It subscribes with
54
+ * `useStoreValue`, so a `patchLeaf` deep in the tree re-renders only
55
+ * the rows whose node identity changed — the same identity discipline
56
+ * the state layer is built on.
57
+ *
58
+ * The recursion and the per-leaf row each live in their own file
59
+ * (`QueryBuilderGroup`, `QueryBuilderRow`), not exported: a group
60
+ * renders groups, and both are rendered inside a `.map`, which is
61
+ * exactly the shape the member-file rule is for.
62
+ */
63
+ export declare const QueryBuilder: <Combinator, Leaf>({ combinatorOptions, createLeafValue, labels, renderLeaf, tree, }: QueryBuilderProps<Combinator, Leaf>) => ReactNode;
64
+ //# sourceMappingURL=QueryBuilder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QueryBuilder.d.ts","sourceRoot":"","sources":["../../src/QueryBuilder/QueryBuilder.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AAK9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,iBAAiB,CAAC,UAAU,EAAE,IAAI,IAAI;IAChD;;;;OAIG;IACH,iBAAiB,EAAE,SAAS;QAC1B,KAAK,EAAE,MAAM,CAAA;QACb,KAAK,EAAE,UAAU,CAAA;KAClB,EAAE,CAAA;IACH,yEAAyE;IACzE,eAAe,EAAE,MAAM,IAAI,CAAA;IAC3B,MAAM,CAAC,EAAE,kBAAkB,CAAA;IAC3B;;;;;OAKG;IACH,UAAU,EAAE,CAAC,IAAI,EAAE;QACjB,MAAM,EAAE,MAAM,CAAA;QACd,QAAQ,EAAE,CAAC,KAAK,EAAE,IAAI,KAAK,IAAI,CAAA;QAC/B,KAAK,EAAE,IAAI,CAAA;KACZ,KAAK,SAAS,CAAA;IACf;;;;;OAKG;IACH,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAA;CAC7B,CAAA;AASD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,YAAY,GAAI,UAAU,EAAE,IAAI,EAAE,mEAM5C,iBAAiB,CAAC,UAAU,EAAE,IAAI,CAAC,KAAG,SAiBxC,CAAA"}
@@ -0,0 +1,37 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { selectRootGroup, useStoreValue, } from "@charcuterie/logic";
3
+ import { QueryBuilderGroup } from "./QueryBuilderGroup.js";
4
+ const DEFAULT_LABELS = {
5
+ addGroup: "Add group",
6
+ addLeaf: "Add condition",
7
+ removeGroup: "Remove group",
8
+ removeLeaf: "Remove condition",
9
+ };
10
+ /**
11
+ * The generic, nestable AND/OR — and any-combinator — group editor.
12
+ *
13
+ * Two apps in the fleet grow the same widget: Mail Sifter's nested
14
+ * mail rules (leaf = field/operator/value, combinators AND/OR) and
15
+ * mux-magic's job DSL (non-uniform leaves, combinators any/all/none).
16
+ * Both are a recursive group of conditions with a combinator, so the
17
+ * value **and** the combinator are opaque here — `Combinator` and
18
+ * `Leaf` never appear except passed through to the consumer's
19
+ * `renderLeaf` and its `combinatorOptions`.
20
+ *
21
+ * State lives entirely in the `Tree` core from `@charcuterie/logic`;
22
+ * this component holds none of its own. It subscribes with
23
+ * `useStoreValue`, so a `patchLeaf` deep in the tree re-renders only
24
+ * the rows whose node identity changed — the same identity discipline
25
+ * the state layer is built on.
26
+ *
27
+ * The recursion and the per-leaf row each live in their own file
28
+ * (`QueryBuilderGroup`, `QueryBuilderRow`), not exported: a group
29
+ * renders groups, and both are rendered inside a `.map`, which is
30
+ * exactly the shape the member-file rule is for.
31
+ */
32
+ export const QueryBuilder = ({ combinatorOptions, createLeafValue, labels, renderLeaf, tree, }) => {
33
+ const state = useStoreValue(tree);
34
+ const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
35
+ return (_jsx(QueryBuilderGroup, { combinatorOptions: combinatorOptions, createLeafValue: createLeafValue, depth: 0, labels: resolvedLabels, node: selectRootGroup(state), renderLeaf: renderLeaf, state: state, tree: tree }));
36
+ };
37
+ //# sourceMappingURL=QueryBuilder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QueryBuilder.js","sourceRoot":"","sources":["../../src/QueryBuilder/QueryBuilder.tsx"],"names":[],"mappings":";AACA,OAAO,EACL,eAAe,EACf,aAAa,GACd,MAAM,oBAAoB,CAAA;AAG3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAyB,CAAA;AA0C3D,MAAM,cAAc,GAAG;IACrB,QAAQ,EAAE,WAAW;IACrB,OAAO,EAAE,eAAe;IACxB,WAAW,EAAE,cAAc;IAC3B,UAAU,EAAE,kBAAkB;CACQ,CAAA;AAExC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAmB,EAC7C,iBAAiB,EACjB,eAAe,EACf,MAAM,EACN,UAAU,EACV,IAAI,GACgC,EAAa,EAAE;IACnD,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,CAAA;IAEjC,MAAM,cAAc,GAAG,EAAE,GAAG,cAAc,EAAE,GAAG,MAAM,EAAE,CAAA;IAEvD,OAAO,CACL,KAAC,iBAAiB,IAChB,iBAAiB,EAAE,iBAAiB,EACpC,eAAe,EAAE,eAAe,EAChC,KAAK,EAAE,CAAC,EACR,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,eAAe,CAAC,KAAK,CAAC,EAC5B,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,IAAI,GACV,CACH,CAAA;AACH,CAAC,CAAA"}
@@ -0,0 +1,37 @@
1
+ import type { Tree, TreeGroupNode, TreeState } from "@charcuterie/logic";
2
+ import type { ReactNode } from "react";
3
+ import type { QueryBuilderLabels } from "./QueryBuilder.tsx";
4
+ export type QueryBuilderGroupProps<Combinator, Leaf> = {
5
+ combinatorOptions: readonly {
6
+ label: string;
7
+ value: Combinator;
8
+ }[];
9
+ createLeafValue: () => Leaf;
10
+ /** Root is `0`; nested groups deepen and gain the accent rail. */
11
+ depth: number;
12
+ labels: Required<QueryBuilderLabels>;
13
+ node: TreeGroupNode<Combinator>;
14
+ renderLeaf: (args: {
15
+ nodeId: string;
16
+ onChange: (value: Leaf) => void;
17
+ value: Leaf;
18
+ }) => ReactNode;
19
+ state: TreeState<Combinator, Leaf>;
20
+ tree: Tree<Combinator, Leaf>;
21
+ };
22
+ /**
23
+ * One match group: a "Match" combinator select, its children (leaf
24
+ * rows and nested groups), and a toolbar to add either. Its own file
25
+ * because it renders itself recursively for nested groups and is
26
+ * itself rendered inside the parent's `.map` — the member-file case.
27
+ *
28
+ * The combinator is opaque, so the native `Select` (which speaks
29
+ * strings) is bridged by `String(value)`: options carry the
30
+ * stringified combinator, and a change is mapped back to the real
31
+ * `Combinator` before it reaches `setCombinator`. That keeps
32
+ * `Combinator` fully generic while still using the platform control
33
+ * that brings type-ahead, the mobile wheel, and form semantics for
34
+ * free.
35
+ */
36
+ export declare const QueryBuilderGroup: <Combinator, Leaf>({ combinatorOptions, createLeafValue, depth, labels, node, renderLeaf, state, tree, }: QueryBuilderGroupProps<Combinator, Leaf>) => ReactNode;
37
+ //# sourceMappingURL=QueryBuilderGroup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QueryBuilderGroup.d.ts","sourceRoot":"","sources":["../../src/QueryBuilder/QueryBuilderGroup.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,IAAI,EACJ,aAAa,EACb,SAAS,EACV,MAAM,oBAAoB,CAAA;AAE3B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAQtC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAG5D,MAAM,MAAM,sBAAsB,CAAC,UAAU,EAAE,IAAI,IAAI;IACrD,iBAAiB,EAAE,SAAS;QAC1B,KAAK,EAAE,MAAM,CAAA;QACb,KAAK,EAAE,UAAU,CAAA;KAClB,EAAE,CAAA;IACH,eAAe,EAAE,MAAM,IAAI,CAAA;IAC3B,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,QAAQ,CAAC,kBAAkB,CAAC,CAAA;IACpC,IAAI,EAAE,aAAa,CAAC,UAAU,CAAC,CAAA;IAC/B,UAAU,EAAE,CAAC,IAAI,EAAE;QACjB,MAAM,EAAE,MAAM,CAAA;QACd,QAAQ,EAAE,CAAC,KAAK,EAAE,IAAI,KAAK,IAAI,CAAA;QAC/B,KAAK,EAAE,IAAI,CAAA;KACZ,KAAK,SAAS,CAAA;IACf,KAAK,EAAE,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,CAAA;IAClC,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAA;CAC7B,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,GAAI,UAAU,EAAE,IAAI,EAAE,uFASjD,sBAAsB,CAAC,UAAU,EAAE,IAAI,CAAC,KAAG,SA6H7C,CAAA"}
@@ -0,0 +1,57 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { selectChildNodes } from "@charcuterie/logic";
3
+ import { Button } from "../Button/Button.js";
4
+ import { Card } from "../Card/Card.js";
5
+ import { Field } from "../Field/Field.js";
6
+ import { IconButton } from "../IconButton/IconButton.js";
7
+ import { Select } from "../Select/Select.js";
8
+ import { toClassName } from "../toClassName.js";
9
+ import { QueryBuilderRow } from "./QueryBuilderRow.js";
10
+ /**
11
+ * One match group: a "Match" combinator select, its children (leaf
12
+ * rows and nested groups), and a toolbar to add either. Its own file
13
+ * because it renders itself recursively for nested groups and is
14
+ * itself rendered inside the parent's `.map` — the member-file case.
15
+ *
16
+ * The combinator is opaque, so the native `Select` (which speaks
17
+ * strings) is bridged by `String(value)`: options carry the
18
+ * stringified combinator, and a change is mapped back to the real
19
+ * `Combinator` before it reaches `setCombinator`. That keeps
20
+ * `Combinator` fully generic while still using the platform control
21
+ * that brings type-ahead, the mobile wheel, and form semantics for
22
+ * free.
23
+ */
24
+ export const QueryBuilderGroup = ({ combinatorOptions, createLeafValue, depth, labels, node, renderLeaf, state, tree, }) => {
25
+ const children = selectChildNodes(state, node.id);
26
+ const selectOptions = combinatorOptions.map((option) => ({
27
+ label: option.label,
28
+ value: String(option.value),
29
+ }));
30
+ const card = (_jsx(Card
31
+ // Nested groups sit on the sunken surface so the depth reads as
32
+ // recession, not another raised card floating at the same level.
33
+ , {
34
+ // Nested groups sit on the sunken surface so the depth reads as
35
+ // recession, not another raised card floating at the same level.
36
+ surface: depth === 0 ? "raised" : "sunken", children: _jsxs("div", { className: "flex flex-col gap-3", children: [_jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsx("div", { className: "min-w-52", children: _jsx(Field, { label: "Match", children: _jsx(Select, { onChange: (nextValue) => {
37
+ const match = combinatorOptions.find((option) => String(option.value) === nextValue);
38
+ if (match) {
39
+ tree.setCombinator(node.id, match.value);
40
+ }
41
+ }, options: selectOptions, value: String(node.combinator) }) }) }), depth === 0 ? null : (_jsx(IconButton, { appearance: "ghost", intent: "danger", label: labels.removeGroup, onClick: () => {
42
+ tree.removeNode(node.id);
43
+ }, size: "sm", children: "\u2715" }))] }), children.length === 0 ? null : (_jsx("div", { className: "flex flex-col gap-2", children: children.map((child) => child.kind === "group" ? (_jsx(QueryBuilderGroup, { combinatorOptions: combinatorOptions, createLeafValue: createLeafValue, depth: depth + 1, labels: labels, node: child, renderLeaf: renderLeaf, state: state, tree: tree }, child.id)) : (_jsx(QueryBuilderRow, { label: labels.removeLeaf, nodeId: child.id, onChange: (value) => {
44
+ tree.patchLeaf(child.id, value);
45
+ }, onRemove: () => {
46
+ tree.removeNode(child.id);
47
+ }, renderLeaf: renderLeaf, value: child.value }, child.id))) })), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx(Button, { appearance: "outline", intent: "neutral", onClick: () => {
48
+ tree.addLeaf(node.id, createLeafValue());
49
+ }, size: "sm", children: `+ ${labels.addLeaf}` }), _jsx(Button, { appearance: "outline", intent: "neutral", onClick: () => {
50
+ tree.addGroup(node.id);
51
+ }, size: "sm", children: `+ ${labels.addGroup}` })] })] }) }));
52
+ // Depth is shown with a semantic-token inline-start rail. The colour
53
+ // utility paints all four sides, but only the inline-start edge has
54
+ // a width, so only it renders — no raw palette colour anywhere.
55
+ return depth === 0 ? (card) : (_jsx("div", { className: toClassName("border-intent-accent-border border-s-2 ps-3"), children: card }));
56
+ };
57
+ //# sourceMappingURL=QueryBuilderGroup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QueryBuilderGroup.js","sourceRoot":"","sources":["../../src/QueryBuilder/QueryBuilderGroup.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { ReactNode } from "react";
2
+ export type QueryBuilderRowProps<Leaf> = {
3
+ /** The accessible name of the row's delete control. */
4
+ label: string;
5
+ nodeId: string;
6
+ onChange: (value: Leaf) => void;
7
+ onRemove: () => void;
8
+ renderLeaf: (args: {
9
+ nodeId: string;
10
+ onChange: (value: Leaf) => void;
11
+ value: Leaf;
12
+ }) => ReactNode;
13
+ value: Leaf;
14
+ };
15
+ /**
16
+ * A single leaf row: the app's own leaf UI, plus the builder's delete
17
+ * control. Its own file for the same reason `ListboxOption` is —
18
+ * rendered inside the group's `.map`, so it cannot be an inline
19
+ * component — and it is a member file (`QueryBuilder*.tsx`, not
20
+ * `QueryBuilder/QueryBuilder.tsx`), so it stays out of the barrel and
21
+ * the component count by rule.
22
+ *
23
+ * The delete control is an `IconButton` because a bare glyph is not an
24
+ * accessible name; its required `label` is what makes each row's
25
+ * remove button findable and unique per condition.
26
+ */
27
+ export declare const QueryBuilderRow: <Leaf>({ label, nodeId, onChange, onRemove, renderLeaf, value, }: QueryBuilderRowProps<Leaf>) => ReactNode;
28
+ //# sourceMappingURL=QueryBuilderRow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QueryBuilderRow.d.ts","sourceRoot":"","sources":["../../src/QueryBuilder/QueryBuilderRow.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC,MAAM,MAAM,oBAAoB,CAAC,IAAI,IAAI;IACvC,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,IAAI,KAAK,IAAI,CAAA;IAC/B,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,UAAU,EAAE,CAAC,IAAI,EAAE;QACjB,MAAM,EAAE,MAAM,CAAA;QACd,QAAQ,EAAE,CAAC,KAAK,EAAE,IAAI,KAAK,IAAI,CAAA;QAC/B,KAAK,EAAE,IAAI,CAAA;KACZ,KAAK,SAAS,CAAA;IACf,KAAK,EAAE,IAAI,CAAA;CACZ,CAAA;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,eAAe,GAAI,IAAI,EAAG,2DAOpC,oBAAoB,CAAC,IAAI,CAAC,KAAG,SAgB/B,CAAA"}
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { IconButton } from "../IconButton/IconButton.js";
3
+ /**
4
+ * A single leaf row: the app's own leaf UI, plus the builder's delete
5
+ * control. Its own file for the same reason `ListboxOption` is —
6
+ * rendered inside the group's `.map`, so it cannot be an inline
7
+ * component — and it is a member file (`QueryBuilder*.tsx`, not
8
+ * `QueryBuilder/QueryBuilder.tsx`), so it stays out of the barrel and
9
+ * the component count by rule.
10
+ *
11
+ * The delete control is an `IconButton` because a bare glyph is not an
12
+ * accessible name; its required `label` is what makes each row's
13
+ * remove button findable and unique per condition.
14
+ */
15
+ export const QueryBuilderRow = ({ label, nodeId, onChange, onRemove, renderLeaf, value, }) => (_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("div", { className: "grow", children: renderLeaf({ nodeId, onChange, value }) }), _jsx(IconButton, { appearance: "ghost", intent: "danger", label: label, onClick: onRemove, size: "sm", children: "\u2715" })] }));
16
+ //# sourceMappingURL=QueryBuilderRow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QueryBuilderRow.js","sourceRoot":"","sources":["../../src/QueryBuilder/QueryBuilderRow.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA8B,CAAA;AAgBzD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAQ,EACrC,KAAK,EACL,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,KAAK,GACsB,EAAa,EAAE,CAAC,CAC3C,eAAK,SAAS,EAAC,wBAAwB,aACrC,cAAK,SAAS,EAAC,MAAM,YAClB,UAAU,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,GACpC,EAEN,KAAC,UAAU,IACT,UAAU,EAAC,OAAO,EAClB,MAAM,EAAC,QAAQ,EACf,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,QAAQ,EACjB,IAAI,EAAC,IAAI,uBAGE,IACT,CACP,CAAA"}
package/dist/index.d.ts CHANGED
@@ -98,6 +98,8 @@ export type { ProgressBarProps, ProgressBarSize, } from "./ProgressBar/ProgressB
98
98
  export { ProgressBar } from "./ProgressBar/ProgressBar.tsx";
99
99
  export type { ProgressThreshold } from "./ProgressBar/progressValue.ts";
100
100
  export { getProgressIntent, toProgressPercent, toProgressValue, } from "./ProgressBar/progressValue.ts";
101
+ export type { QueryBuilderLabels, QueryBuilderProps, } from "./QueryBuilder/QueryBuilder.tsx";
102
+ export { QueryBuilder } from "./QueryBuilder/QueryBuilder.tsx";
101
103
  export type { RadioGroupProps, RadioItem, } from "./RadioGroup/RadioGroup.tsx";
102
104
  export { RadioGroup } from "./RadioGroup/RadioGroup.tsx";
103
105
  export type { RailLandmark, RailProps, RailSide, } from "./Rail/Rail.tsx";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -64,6 +64,7 @@ export { Modal } from "./Modal/Modal.js";
64
64
  export { Popover } from "./Popover/Popover.js";
65
65
  export { ProgressBar } from "./ProgressBar/ProgressBar.js";
66
66
  export { getProgressIntent, toProgressPercent, toProgressValue, } from "./ProgressBar/progressValue.js";
67
+ export { QueryBuilder } from "./QueryBuilder/QueryBuilder.js";
67
68
  export { RadioGroup } from "./RadioGroup/RadioGroup.js";
68
69
  export { Rail } from "./Rail/Rail.js";
69
70
  export { AnchorLink } from "./RouterLink/AnchorLink.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@charcuterie/ui",
3
- "version": "2.12.1",
3
+ "version": "2.13.0",
4
4
  "description": "The Charcuterie components. Tokens for look, @charcuterie/logic for state, ARIA that agents can actually drive.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -48,7 +48,7 @@
48
48
  "typecheck": "tsc --noEmit"
49
49
  },
50
50
  "dependencies": {
51
- "@charcuterie/logic": "1.4.0",
51
+ "@charcuterie/logic": "1.5.0",
52
52
  "@charcuterie/tokens": "1.5.0",
53
53
  "@floating-ui/react": "^0.27.20",
54
54
  "@tanstack/react-virtual": "^3.13.12"
@@ -0,0 +1,63 @@
1
+ import { Canvas, Controls, Meta } from "@storybook/addon-docs/blocks";
2
+
3
+ import * as QueryBuilderStories from "./QueryBuilder.stories";
4
+
5
+ <Meta of={QueryBuilderStories} />
6
+
7
+ # QueryBuilder
8
+
9
+ The generic, nestable **AND/OR — and any-combinator — group editor**. A recursive group of
10
+ conditions, each group carrying a combinator the app owns and each leaf carrying a value the
11
+ app owns. Two apps in the fleet grow the same widget from opposite ends: Mail Sifter's nested
12
+ mail rules (leaf = field/operator/value, combinators AND/OR) and mux-magic's job DSL
13
+ (non-uniform leaves, combinators any/all/none). So both the **leaf value** and the **group
14
+ combinator** are fully opaque here — `Combinator` and `Leaf` only ever pass through to the
15
+ consumer's `renderLeaf` and `combinatorOptions`.
16
+
17
+ <Canvas of={QueryBuilderStories.Default} />
18
+
19
+ ## Props
20
+
21
+ <Controls />
22
+
23
+ ## The state is a `Tree`, and the consumer owns it
24
+
25
+ `QueryBuilder` holds no state of its own. It takes a `Tree` core from
26
+ [`useTree`](?path=/docs/state-usetree--docs) and subscribes to it, the same way a `Listbox`'s
27
+ parent owns `useVisibility` and hands it down. That is what leaves `serialize()` — the
28
+ YAML/DB round-trip — and the initial tree in the consumer's hands.
29
+
30
+ ```tsx
31
+ const tree = useTree<Combinator, Leaf>({
32
+ defaultCombinator: "and",
33
+ initialTree,
34
+ })
35
+
36
+ <QueryBuilder
37
+ combinatorOptions={COMBINATOR_OPTIONS}
38
+ createLeafValue={() => ({ field: "subject", operator: "contains", value: "" })}
39
+ renderLeaf={({ value, onChange }) => <ConditionFields value={value} onChange={onChange} />}
40
+ tree={tree}
41
+ />
42
+ ```
43
+
44
+ Because the tree is normalized and addressed by id, a `patchLeaf` deep in the tree re-renders
45
+ only the rows whose node identity changed — the same `useSyncExternalStore` identity
46
+ discipline the whole state layer rests on.
47
+
48
+ ## Nesting, and depth
49
+
50
+ A group renders leaf rows and nested groups, recursively. The nested group wears a
51
+ semantic-token inline-start rail so depth reads without a raw palette colour, and it moves to
52
+ the sunken surface so it recedes rather than floating at the same level as its parent.
53
+
54
+ <Canvas of={QueryBuilderStories.AllStates} />
55
+
56
+ ## Driving it
57
+
58
+ Every control is nameable, not a `data-testid`: the combinator select is named `Match` by the
59
+ field label, the leaf controls by their own labels, and each delete is an `IconButton` whose
60
+ required `label` makes it findable. Add a condition, nest a group, flip a combinator, delete a
61
+ row — all reachable by role and name.
62
+
63
+ <Canvas of={QueryBuilderStories.Interactive} />
@@ -0,0 +1,243 @@
1
+ import type { SerializedTree } from "@charcuterie/logic"
2
+ import { createTree, useTree } from "@charcuterie/logic"
3
+ import type { Meta, StoryObj } from "@storybook/react"
4
+ import type { ReactNode } from "react"
5
+ import { StorySection } from "../board.storyHelpers.tsx"
6
+ import { Select } from "../Select/Select.tsx"
7
+ import { QueryBuilder } from "./QueryBuilder.tsx"
8
+
9
+ /**
10
+ * The concrete demo leaf — a field/operator/value triple, Mail
11
+ * Sifter's shape. `QueryBuilder` never sees this type; it flows
12
+ * through `renderLeaf` and `createLeafValue` untouched, which is what
13
+ * lets mux-magic pass a non-uniform per-kind shape instead.
14
+ */
15
+ type DemoLeaf = {
16
+ field: string
17
+ operator: string
18
+ value: string
19
+ }
20
+
21
+ type Combinator = "and" | "or"
22
+
23
+ const COMBINATOR_OPTIONS: readonly {
24
+ label: string
25
+ value: Combinator
26
+ }[] = [
27
+ { label: "ALL — every condition", value: "and" },
28
+ { label: "ANY — one condition", value: "or" },
29
+ ]
30
+
31
+ const FIELD_OPTIONS = [
32
+ { label: "Subject", value: "subject" },
33
+ { label: "From", value: "from" },
34
+ { label: "Body", value: "body" },
35
+ ] as const
36
+
37
+ const OPERATOR_OPTIONS = [
38
+ { label: "contains", value: "contains" },
39
+ { label: "is", value: "is" },
40
+ { label: "starts with", value: "startsWith" },
41
+ ] as const
42
+
43
+ const createLeafValue = (): DemoLeaf => ({
44
+ field: "subject",
45
+ operator: "contains",
46
+ value: "",
47
+ })
48
+
49
+ const renderLeaf = ({
50
+ onChange,
51
+ value,
52
+ }: {
53
+ nodeId: string
54
+ onChange: (value: DemoLeaf) => void
55
+ value: DemoLeaf
56
+ }): ReactNode => (
57
+ <div className="flex flex-wrap items-center gap-2">
58
+ <Select
59
+ label="Field"
60
+ onChange={(field) => {
61
+ onChange({ ...value, field })
62
+ }}
63
+ options={FIELD_OPTIONS}
64
+ size="sm"
65
+ value={value.field}
66
+ />
67
+
68
+ <Select
69
+ label="Operator"
70
+ onChange={(operator) => {
71
+ onChange({ ...value, operator })
72
+ }}
73
+ options={OPERATOR_OPTIONS}
74
+ size="sm"
75
+ value={value.operator}
76
+ />
77
+
78
+ <input
79
+ aria-label="Value"
80
+ className="min-w-40 grow rounded-md border border-border-default bg-surface-raised px-3 py-1.5 text-content-primary text-sm"
81
+ onChange={(changeEvent) => {
82
+ onChange({
83
+ ...value,
84
+ value: changeEvent.currentTarget.value,
85
+ })
86
+ }}
87
+ value={value.value}
88
+ />
89
+ </div>
90
+ )
91
+
92
+ const FLAT_TREE: SerializedTree<Combinator, DemoLeaf> = {
93
+ children: [
94
+ {
95
+ kind: "leaf",
96
+ value: {
97
+ field: "from",
98
+ operator: "contains",
99
+ value: "@newsletter",
100
+ },
101
+ },
102
+ {
103
+ kind: "leaf",
104
+ value: {
105
+ field: "subject",
106
+ operator: "contains",
107
+ value: "sale",
108
+ },
109
+ },
110
+ ],
111
+ combinator: "and",
112
+ kind: "group",
113
+ }
114
+
115
+ const NESTED_TREE: SerializedTree<Combinator, DemoLeaf> = {
116
+ children: [
117
+ {
118
+ kind: "leaf",
119
+ value: {
120
+ field: "from",
121
+ operator: "is",
122
+ value: "billing@acme.test",
123
+ },
124
+ },
125
+ {
126
+ children: [
127
+ {
128
+ kind: "leaf",
129
+ value: {
130
+ field: "subject",
131
+ operator: "contains",
132
+ value: "invoice",
133
+ },
134
+ },
135
+ {
136
+ kind: "leaf",
137
+ value: {
138
+ field: "subject",
139
+ operator: "contains",
140
+ value: "receipt",
141
+ },
142
+ },
143
+ ],
144
+ combinator: "or",
145
+ kind: "group",
146
+ },
147
+ ],
148
+ combinator: "and",
149
+ kind: "group",
150
+ }
151
+
152
+ const QueryBuilderHarness = ({
153
+ initialTree,
154
+ }: {
155
+ initialTree?: SerializedTree<Combinator, DemoLeaf>
156
+ }): ReactNode => {
157
+ const tree = useTree<Combinator, DemoLeaf>({
158
+ defaultCombinator: "and",
159
+ initialTree,
160
+ })
161
+
162
+ return (
163
+ <QueryBuilder
164
+ combinatorOptions={COMBINATOR_OPTIONS}
165
+ createLeafValue={createLeafValue}
166
+ renderLeaf={renderLeaf}
167
+ tree={tree}
168
+ />
169
+ )
170
+ }
171
+
172
+ const meta = {
173
+ title: "Components/QueryBuilder",
174
+ component: QueryBuilder,
175
+ parameters: { layout: "padded" },
176
+ args: {
177
+ combinatorOptions: COMBINATOR_OPTIONS,
178
+ createLeafValue,
179
+ renderLeaf,
180
+ tree: createTree<Combinator, DemoLeaf>({
181
+ defaultCombinator: "and",
182
+ }),
183
+ },
184
+ } satisfies Meta<typeof QueryBuilder<Combinator, DemoLeaf>>
185
+
186
+ export default meta
187
+
188
+ type Story = StoryObj<typeof meta>
189
+
190
+ /**
191
+ * A flat group of two conditions joined by ALL. Change the combinator,
192
+ * edit a condition, or add another with "+ Add condition".
193
+ */
194
+ export const Default: Story = {
195
+ render: () => (
196
+ <QueryBuilderHarness initialTree={FLAT_TREE} />
197
+ ),
198
+ }
199
+
200
+ /**
201
+ * Two shapes side by side: a flat rule, and a nested one where a
202
+ * sub-group joined by ANY sits inside a group joined by ALL. The
203
+ * nested group wears the inline-start accent rail that marks depth.
204
+ */
205
+ export const AllStates: Story = {
206
+ render: () => (
207
+ <div className="flex flex-col gap-6">
208
+ <StorySection title="Flat — ALL of two conditions">
209
+ <QueryBuilderHarness initialTree={FLAT_TREE} />
210
+ </StorySection>
211
+
212
+ <StorySection title="Nested — ALL, with an ANY sub-group">
213
+ <QueryBuilderHarness initialTree={NESTED_TREE} />
214
+ </StorySection>
215
+ </div>
216
+ ),
217
+ }
218
+
219
+ /**
220
+ * Drive it: add a condition, add a nested group, flip a combinator,
221
+ * delete a row. Starts from a single condition so every control is
222
+ * unique and nameable.
223
+ */
224
+ export const Interactive: Story = {
225
+ render: () => (
226
+ <QueryBuilderHarness
227
+ initialTree={{
228
+ children: [
229
+ {
230
+ kind: "leaf",
231
+ value: {
232
+ field: "subject",
233
+ operator: "contains",
234
+ value: "urgent",
235
+ },
236
+ },
237
+ ],
238
+ combinator: "and",
239
+ kind: "group",
240
+ }}
241
+ />
242
+ ),
243
+ }
@@ -0,0 +1,132 @@
1
+ import { composeStories } from "@storybook/react"
2
+ import { expect, userEvent, waitFor } from "storybook/test"
3
+ import { test } from "vitest"
4
+
5
+ import { expectNoAxeViolations } from "../expectNoAxeViolations.testHelpers.ts"
6
+ import { mountStory } from "../mountStory.testHelpers.ts"
7
+ import { expectAgentDrivable } from "../testing/index.ts"
8
+ import * as stories from "./QueryBuilder.stories.tsx"
9
+
10
+ const { AllStates, Default, Interactive } =
11
+ composeStories(stories)
12
+
13
+ test("the root group exposes its combinator as a named 'Match' control", async () => {
14
+ const { canvas, canvasElement } =
15
+ await mountStory(Default)
16
+
17
+ // One root group, so exactly one combinator select — findable by
18
+ // the label `Field` wires, not a testid.
19
+ expectAgentDrivable(canvas, {
20
+ name: "Match",
21
+ role: "combobox",
22
+ })
23
+
24
+ await expectNoAxeViolations(canvasElement)
25
+ })
26
+
27
+ test("'+ Add condition' appends a new leaf row", async () => {
28
+ const { canvas, canvasElement } =
29
+ await mountStory(Interactive)
30
+
31
+ // Interactive starts from a single condition, so every control is
32
+ // unique and nameable.
33
+ await expect(
34
+ canvas.getAllByRole("textbox", { name: "Value" }),
35
+ ).toHaveLength(1)
36
+
37
+ await userEvent.click(
38
+ expectAgentDrivable(canvas, {
39
+ name: "+ Add condition",
40
+ role: "button",
41
+ }),
42
+ )
43
+
44
+ await waitFor(() => {
45
+ expect(
46
+ canvas.getAllByRole("textbox", { name: "Value" }),
47
+ ).toHaveLength(2)
48
+ })
49
+
50
+ await expectNoAxeViolations(canvasElement)
51
+ })
52
+
53
+ test("'+ Add group' nests a second group with its own combinator", async () => {
54
+ const { canvas, canvasElement } =
55
+ await mountStory(Interactive)
56
+
57
+ await expect(
58
+ canvas.getAllByRole("combobox", { name: "Match" }),
59
+ ).toHaveLength(1)
60
+
61
+ await userEvent.click(
62
+ expectAgentDrivable(canvas, {
63
+ name: "+ Add group",
64
+ role: "button",
65
+ }),
66
+ )
67
+
68
+ await waitFor(() => {
69
+ expect(
70
+ canvas.getAllByRole("combobox", { name: "Match" }),
71
+ ).toHaveLength(2)
72
+ })
73
+
74
+ // Only the nested group carries a remove control, so it is unique.
75
+ expectAgentDrivable(canvas, {
76
+ name: "Remove group",
77
+ role: "button",
78
+ })
79
+
80
+ await expectNoAxeViolations(canvasElement)
81
+ })
82
+
83
+ test("changing the combinator select updates its value", async () => {
84
+ const { canvas } = await mountStory(Interactive)
85
+
86
+ const combinator = expectAgentDrivable(canvas, {
87
+ name: "Match",
88
+ role: "combobox",
89
+ })
90
+
91
+ await expect(combinator).toHaveValue("and")
92
+
93
+ await userEvent.selectOptions(
94
+ combinator,
95
+ "ANY — one condition",
96
+ )
97
+
98
+ await waitFor(() => {
99
+ expect(combinator).toHaveValue("or")
100
+ })
101
+ })
102
+
103
+ test("removing a condition takes its row away", async () => {
104
+ const { canvas } = await mountStory(Interactive)
105
+
106
+ await userEvent.click(
107
+ expectAgentDrivable(canvas, {
108
+ name: "Remove condition",
109
+ role: "button",
110
+ }),
111
+ )
112
+
113
+ await waitFor(() => {
114
+ expect(
115
+ canvas.queryAllByRole("textbox", { name: "Value" }),
116
+ ).toHaveLength(0)
117
+ })
118
+ })
119
+
120
+ test("a nested tree renders every group's combinator and passes axe", async () => {
121
+ const { canvas, canvasElement } =
122
+ await mountStory(AllStates)
123
+
124
+ // Flat harness (1 group) + nested harness (root + one sub-group) =
125
+ // three combinator selects, which is the nesting rendering.
126
+ await expect(
127
+ canvas.getAllByRole("combobox", { name: "Match" })
128
+ .length,
129
+ ).toBeGreaterThanOrEqual(3)
130
+
131
+ await expectNoAxeViolations(canvasElement)
132
+ })
@@ -0,0 +1,102 @@
1
+ import type { Tree } from "@charcuterie/logic"
2
+ import {
3
+ selectRootGroup,
4
+ useStoreValue,
5
+ } from "@charcuterie/logic"
6
+ import type { ReactNode } from "react"
7
+
8
+ import { QueryBuilderGroup } from "./QueryBuilderGroup.tsx"
9
+
10
+ export type QueryBuilderLabels = {
11
+ addGroup?: string
12
+ addLeaf?: string
13
+ removeGroup?: string
14
+ removeLeaf?: string
15
+ }
16
+
17
+ export type QueryBuilderProps<Combinator, Leaf> = {
18
+ /**
19
+ * The combinator choices offered in every group's "Match" select.
20
+ * The `value` is opaque — `and`/`or` for a mail rule, `any`/`all`/
21
+ * `none` for a DSL — and the `label` is what a user reads.
22
+ */
23
+ combinatorOptions: readonly {
24
+ label: string
25
+ value: Combinator
26
+ }[]
27
+ /** The value a fresh leaf is born with when "+ Condition" is pressed. */
28
+ createLeafValue: () => Leaf
29
+ labels?: QueryBuilderLabels
30
+ /**
31
+ * The leaf UI, owned by the app. `QueryBuilder` knows nothing about
32
+ * what a condition *is* — a field/operator/value triple, a
33
+ * non-uniform per-kind shape — only that it can be rendered and
34
+ * patched. The delete control around each leaf is the builder's.
35
+ */
36
+ renderLeaf: (args: {
37
+ nodeId: string
38
+ onChange: (value: Leaf) => void
39
+ value: Leaf
40
+ }) => ReactNode
41
+ /**
42
+ * The `Tree` core from `useTree`, owned by the consumer — the same
43
+ * arrangement as a `Listbox`'s parent owning `useVisibility`.
44
+ * `QueryBuilder` subscribes to it and drives it; it never creates
45
+ * one, so a consumer keeps `serialize()` and the initial tree.
46
+ */
47
+ tree: Tree<Combinator, Leaf>
48
+ }
49
+
50
+ const DEFAULT_LABELS = {
51
+ addGroup: "Add group",
52
+ addLeaf: "Add condition",
53
+ removeGroup: "Remove group",
54
+ removeLeaf: "Remove condition",
55
+ } satisfies Required<QueryBuilderLabels>
56
+
57
+ /**
58
+ * The generic, nestable AND/OR — and any-combinator — group editor.
59
+ *
60
+ * Two apps in the fleet grow the same widget: Mail Sifter's nested
61
+ * mail rules (leaf = field/operator/value, combinators AND/OR) and
62
+ * mux-magic's job DSL (non-uniform leaves, combinators any/all/none).
63
+ * Both are a recursive group of conditions with a combinator, so the
64
+ * value **and** the combinator are opaque here — `Combinator` and
65
+ * `Leaf` never appear except passed through to the consumer's
66
+ * `renderLeaf` and its `combinatorOptions`.
67
+ *
68
+ * State lives entirely in the `Tree` core from `@charcuterie/logic`;
69
+ * this component holds none of its own. It subscribes with
70
+ * `useStoreValue`, so a `patchLeaf` deep in the tree re-renders only
71
+ * the rows whose node identity changed — the same identity discipline
72
+ * the state layer is built on.
73
+ *
74
+ * The recursion and the per-leaf row each live in their own file
75
+ * (`QueryBuilderGroup`, `QueryBuilderRow`), not exported: a group
76
+ * renders groups, and both are rendered inside a `.map`, which is
77
+ * exactly the shape the member-file rule is for.
78
+ */
79
+ export const QueryBuilder = <Combinator, Leaf>({
80
+ combinatorOptions,
81
+ createLeafValue,
82
+ labels,
83
+ renderLeaf,
84
+ tree,
85
+ }: QueryBuilderProps<Combinator, Leaf>): ReactNode => {
86
+ const state = useStoreValue(tree)
87
+
88
+ const resolvedLabels = { ...DEFAULT_LABELS, ...labels }
89
+
90
+ return (
91
+ <QueryBuilderGroup
92
+ combinatorOptions={combinatorOptions}
93
+ createLeafValue={createLeafValue}
94
+ depth={0}
95
+ labels={resolvedLabels}
96
+ node={selectRootGroup(state)}
97
+ renderLeaf={renderLeaf}
98
+ state={state}
99
+ tree={tree}
100
+ />
101
+ )
102
+ }
@@ -0,0 +1,185 @@
1
+ import type {
2
+ Tree,
3
+ TreeGroupNode,
4
+ TreeState,
5
+ } from "@charcuterie/logic"
6
+ import { selectChildNodes } from "@charcuterie/logic"
7
+ import type { ReactNode } from "react"
8
+
9
+ import { Button } from "../Button/Button.tsx"
10
+ import { Card } from "../Card/Card.tsx"
11
+ import { Field } from "../Field/Field.tsx"
12
+ import { IconButton } from "../IconButton/IconButton.tsx"
13
+ import { Select } from "../Select/Select.tsx"
14
+ import { toClassName } from "../toClassName.ts"
15
+ import type { QueryBuilderLabels } from "./QueryBuilder.tsx"
16
+ import { QueryBuilderRow } from "./QueryBuilderRow.tsx"
17
+
18
+ export type QueryBuilderGroupProps<Combinator, Leaf> = {
19
+ combinatorOptions: readonly {
20
+ label: string
21
+ value: Combinator
22
+ }[]
23
+ createLeafValue: () => Leaf
24
+ /** Root is `0`; nested groups deepen and gain the accent rail. */
25
+ depth: number
26
+ labels: Required<QueryBuilderLabels>
27
+ node: TreeGroupNode<Combinator>
28
+ renderLeaf: (args: {
29
+ nodeId: string
30
+ onChange: (value: Leaf) => void
31
+ value: Leaf
32
+ }) => ReactNode
33
+ state: TreeState<Combinator, Leaf>
34
+ tree: Tree<Combinator, Leaf>
35
+ }
36
+
37
+ /**
38
+ * One match group: a "Match" combinator select, its children (leaf
39
+ * rows and nested groups), and a toolbar to add either. Its own file
40
+ * because it renders itself recursively for nested groups and is
41
+ * itself rendered inside the parent's `.map` — the member-file case.
42
+ *
43
+ * The combinator is opaque, so the native `Select` (which speaks
44
+ * strings) is bridged by `String(value)`: options carry the
45
+ * stringified combinator, and a change is mapped back to the real
46
+ * `Combinator` before it reaches `setCombinator`. That keeps
47
+ * `Combinator` fully generic while still using the platform control
48
+ * that brings type-ahead, the mobile wheel, and form semantics for
49
+ * free.
50
+ */
51
+ export const QueryBuilderGroup = <Combinator, Leaf>({
52
+ combinatorOptions,
53
+ createLeafValue,
54
+ depth,
55
+ labels,
56
+ node,
57
+ renderLeaf,
58
+ state,
59
+ tree,
60
+ }: QueryBuilderGroupProps<Combinator, Leaf>): ReactNode => {
61
+ const children = selectChildNodes(state, node.id)
62
+
63
+ const selectOptions = combinatorOptions.map((option) => ({
64
+ label: option.label,
65
+ value: String(option.value),
66
+ }))
67
+
68
+ const card = (
69
+ <Card
70
+ // Nested groups sit on the sunken surface so the depth reads as
71
+ // recession, not another raised card floating at the same level.
72
+ surface={depth === 0 ? "raised" : "sunken"}
73
+ >
74
+ <div className="flex flex-col gap-3">
75
+ <div className="flex items-start justify-between gap-2">
76
+ <div className="min-w-52">
77
+ <Field label="Match">
78
+ <Select
79
+ onChange={(nextValue) => {
80
+ const match = combinatorOptions.find(
81
+ (option) =>
82
+ String(option.value) === nextValue,
83
+ )
84
+
85
+ if (match) {
86
+ tree.setCombinator(node.id, match.value)
87
+ }
88
+ }}
89
+ options={selectOptions}
90
+ value={String(node.combinator)}
91
+ />
92
+ </Field>
93
+ </div>
94
+
95
+ {depth === 0 ? null : (
96
+ <IconButton
97
+ appearance="ghost"
98
+ intent="danger"
99
+ label={labels.removeGroup}
100
+ onClick={() => {
101
+ tree.removeNode(node.id)
102
+ }}
103
+ size="sm"
104
+ >
105
+ ✕
106
+ </IconButton>
107
+ )}
108
+ </div>
109
+
110
+ {children.length === 0 ? null : (
111
+ <div className="flex flex-col gap-2">
112
+ {children.map((child) =>
113
+ child.kind === "group" ? (
114
+ <QueryBuilderGroup
115
+ combinatorOptions={combinatorOptions}
116
+ createLeafValue={createLeafValue}
117
+ depth={depth + 1}
118
+ key={child.id}
119
+ labels={labels}
120
+ node={child}
121
+ renderLeaf={renderLeaf}
122
+ state={state}
123
+ tree={tree}
124
+ />
125
+ ) : (
126
+ <QueryBuilderRow
127
+ key={child.id}
128
+ label={labels.removeLeaf}
129
+ nodeId={child.id}
130
+ onChange={(value) => {
131
+ tree.patchLeaf(child.id, value)
132
+ }}
133
+ onRemove={() => {
134
+ tree.removeNode(child.id)
135
+ }}
136
+ renderLeaf={renderLeaf}
137
+ value={child.value}
138
+ />
139
+ ),
140
+ )}
141
+ </div>
142
+ )}
143
+
144
+ <div className="flex flex-wrap gap-2">
145
+ <Button
146
+ appearance="outline"
147
+ intent="neutral"
148
+ onClick={() => {
149
+ tree.addLeaf(node.id, createLeafValue())
150
+ }}
151
+ size="sm"
152
+ >
153
+ {`+ ${labels.addLeaf}`}
154
+ </Button>
155
+
156
+ <Button
157
+ appearance="outline"
158
+ intent="neutral"
159
+ onClick={() => {
160
+ tree.addGroup(node.id)
161
+ }}
162
+ size="sm"
163
+ >
164
+ {`+ ${labels.addGroup}`}
165
+ </Button>
166
+ </div>
167
+ </div>
168
+ </Card>
169
+ )
170
+
171
+ // Depth is shown with a semantic-token inline-start rail. The colour
172
+ // utility paints all four sides, but only the inline-start edge has
173
+ // a width, so only it renders — no raw palette colour anywhere.
174
+ return depth === 0 ? (
175
+ card
176
+ ) : (
177
+ <div
178
+ className={toClassName(
179
+ "border-intent-accent-border border-s-2 ps-3",
180
+ )}
181
+ >
182
+ {card}
183
+ </div>
184
+ )
185
+ }
@@ -0,0 +1,54 @@
1
+ import type { ReactNode } from "react"
2
+
3
+ import { IconButton } from "../IconButton/IconButton.tsx"
4
+
5
+ export type QueryBuilderRowProps<Leaf> = {
6
+ /** The accessible name of the row's delete control. */
7
+ label: string
8
+ nodeId: string
9
+ onChange: (value: Leaf) => void
10
+ onRemove: () => void
11
+ renderLeaf: (args: {
12
+ nodeId: string
13
+ onChange: (value: Leaf) => void
14
+ value: Leaf
15
+ }) => ReactNode
16
+ value: Leaf
17
+ }
18
+
19
+ /**
20
+ * A single leaf row: the app's own leaf UI, plus the builder's delete
21
+ * control. Its own file for the same reason `ListboxOption` is —
22
+ * rendered inside the group's `.map`, so it cannot be an inline
23
+ * component — and it is a member file (`QueryBuilder*.tsx`, not
24
+ * `QueryBuilder/QueryBuilder.tsx`), so it stays out of the barrel and
25
+ * the component count by rule.
26
+ *
27
+ * The delete control is an `IconButton` because a bare glyph is not an
28
+ * accessible name; its required `label` is what makes each row's
29
+ * remove button findable and unique per condition.
30
+ */
31
+ export const QueryBuilderRow = <Leaf,>({
32
+ label,
33
+ nodeId,
34
+ onChange,
35
+ onRemove,
36
+ renderLeaf,
37
+ value,
38
+ }: QueryBuilderRowProps<Leaf>): ReactNode => (
39
+ <div className="flex items-start gap-2">
40
+ <div className="grow">
41
+ {renderLeaf({ nodeId, onChange, value })}
42
+ </div>
43
+
44
+ <IconButton
45
+ appearance="ghost"
46
+ intent="danger"
47
+ label={label}
48
+ onClick={onRemove}
49
+ size="sm"
50
+ >
51
+ ✕
52
+ </IconButton>
53
+ </div>
54
+ )
package/src/index.ts CHANGED
@@ -173,6 +173,11 @@ export {
173
173
  toProgressPercent,
174
174
  toProgressValue,
175
175
  } from "./ProgressBar/progressValue.ts"
176
+ export type {
177
+ QueryBuilderLabels,
178
+ QueryBuilderProps,
179
+ } from "./QueryBuilder/QueryBuilder.tsx"
180
+ export { QueryBuilder } from "./QueryBuilder/QueryBuilder.tsx"
176
181
  export type {
177
182
  RadioGroupProps,
178
183
  RadioItem,
@@ -364,7 +364,16 @@ test("the barrel is the only place components are re-exported", async () => {
364
364
  // `useToolbarOverflow.ts` and `chooseVisibleCount.ts` are its
365
365
  // members and stay out of this count by the `<Name>/<Name>.tsx`
366
366
  // rule.
367
- expect(componentNames.length).toBe(43)
367
+ //
368
+ // `QueryBuilder` — the generic nestable AND/OR (any-combinator) group
369
+ // editor Mail Sifter's nested rules and mux-magic's job DSL both need
370
+ // — is the library's first recursive component and its first with a
371
+ // fully opaque value *and* combinator: +1 -> 44. `QueryBuilderGroup`
372
+ // and `QueryBuilderRow` are its members — a group renders groups, and
373
+ // both are rendered inside a `.map` — and stay out of this count and
374
+ // the barrel by the `<Name>/<Name>.tsx` rule, exactly as
375
+ // `SegmentedOption` and `ListboxOption` do.
376
+ expect(componentNames.length).toBe(44)
368
377
 
369
378
  for (const name of componentNames) {
370
379
  expect(barrel).toContain(`export { ${name} }`)