@ctrl-ui/react 0.8.6 → 0.8.7

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.
Files changed (152) hide show
  1. package/dist/action-bar.js +1 -1
  2. package/dist/activity.js +1 -1
  3. package/dist/blocks/coding-agent.js +16 -19
  4. package/dist/blocks/design-canvas-data.d.ts +32 -0
  5. package/dist/blocks/design-canvas-data.js +100 -0
  6. package/dist/blocks/design-canvas-inspector.d.ts +8 -0
  7. package/dist/blocks/design-canvas-inspector.js +238 -0
  8. package/dist/blocks/design-canvas.d.ts +17 -0
  9. package/dist/blocks/design-canvas.js +346 -0
  10. package/dist/blocks/file-explorer.js +2 -2
  11. package/dist/blocks/settings.js +2 -2
  12. package/dist/chat-composer-attachment.d.ts +9 -3
  13. package/dist/chat-composer-attachment.js +27 -10
  14. package/dist/chat-composer-editor.js +20 -4
  15. package/dist/chat-composer.d.ts +1 -1
  16. package/dist/chat-composer.js +4 -3
  17. package/dist/chat-layout.js +32 -19
  18. package/dist/context.js +1 -1
  19. package/dist/dynamic-notification.js +1 -1
  20. package/dist/environment-variables.js +1 -1
  21. package/dist/filter-bar/context.d.ts +32 -0
  22. package/dist/filter-bar/context.js +11 -0
  23. package/dist/filter-bar/editor.d.ts +14 -0
  24. package/dist/filter-bar/editor.js +204 -0
  25. package/dist/filter-bar/model.d.ts +9 -0
  26. package/dist/filter-bar/model.js +48 -0
  27. package/dist/filter-bar/motion.d.ts +10 -0
  28. package/dist/filter-bar/motion.js +84 -0
  29. package/dist/filter-bar/parts.d.ts +19 -0
  30. package/dist/filter-bar/parts.js +233 -0
  31. package/dist/filter-bar/types.d.ts +53 -0
  32. package/dist/filter-bar/types.js +0 -0
  33. package/dist/filter-bar.d.ts +27 -0
  34. package/dist/filter-bar.js +304 -0
  35. package/dist/hooks/use-chat-composer.d.ts +3 -1
  36. package/dist/hooks/use-chat-composer.js +2 -2
  37. package/dist/hooks/use-dropzone.d.ts +7 -4
  38. package/dist/hooks/use-dropzone.js +34 -3
  39. package/dist/inline-attachment.js +8 -14
  40. package/dist/inline-citation.js +1 -1
  41. package/dist/knob-contracts/chat-layout-knobs.d.ts +1 -1
  42. package/dist/knob-contracts/chat-layout-knobs.js +2 -1
  43. package/dist/knob-contracts/choice-knobs.d.ts +1 -1
  44. package/dist/knob-contracts/choice-knobs.js +1 -0
  45. package/dist/knob-contracts/collapsible-knobs.d.ts +1 -1
  46. package/dist/knob-contracts/collapsible-knobs.js +1 -1
  47. package/dist/knob-contracts/filter-bar-knobs.d.ts +4 -0
  48. package/dist/knob-contracts/filter-bar-knobs.js +16 -0
  49. package/dist/knob-contracts/infinite-canvas-knobs.d.ts +1 -1
  50. package/dist/knob-contracts/infinite-canvas-knobs.js +3 -0
  51. package/dist/knob-contracts/page-layout-knobs.d.ts +1 -1
  52. package/dist/knob-contracts/page-layout-knobs.js +3 -1
  53. package/dist/knob-contracts/popup-knobs.d.ts +1 -1
  54. package/dist/knob-contracts/popup-knobs.js +1 -0
  55. package/dist/knob-contracts/table-of-contents-knobs.d.ts +1 -1
  56. package/dist/knob-contracts/table-of-contents-knobs.js +3 -5
  57. package/dist/lib/phone-input-format.js +1 -1
  58. package/dist/markdown-block.js +11 -7
  59. package/dist/scripts/contrast-eval.js +5 -4
  60. package/dist/styles/index.css +5 -4
  61. package/dist/styles/recipes/button-group.css +1 -1
  62. package/dist/styles/recipes/button.css +2 -2
  63. package/dist/styles/recipes/chat-composer.css +14 -6
  64. package/dist/styles/recipes/chat-layout.css +29 -2
  65. package/dist/styles/recipes/choice.css +10 -3
  66. package/dist/styles/recipes/code-diff.css +16 -4
  67. package/dist/styles/recipes/code.css +11 -5
  68. package/dist/styles/recipes/collapsible.css +37 -0
  69. package/dist/styles/recipes/color-picker.css +12 -2
  70. package/dist/styles/recipes/dockable-panel.css +11 -3
  71. package/dist/styles/recipes/field-parts.css +62 -0
  72. package/dist/styles/recipes/field.css +28 -6
  73. package/dist/styles/recipes/filter-bar.css +174 -0
  74. package/dist/styles/recipes/infinite-canvas.css +51 -3
  75. package/dist/styles/recipes/inline-attachment.css +6 -1
  76. package/dist/styles/recipes/page-layout.css +18 -0
  77. package/dist/styles/recipes/pagination.css +1 -1
  78. package/dist/styles/recipes/popup-controls.css +16 -0
  79. package/dist/styles/recipes/popup-drawer.css +34 -32
  80. package/dist/styles/recipes/popup-lists.css +11 -2
  81. package/dist/styles/recipes/popup.css +10 -12
  82. package/dist/styles/recipes/range.css +1 -1
  83. package/dist/styles/recipes/resizable.css +34 -0
  84. package/dist/styles/recipes/spinner.css +4 -0
  85. package/dist/styles/recipes/table-of-contents.css +42 -42
  86. package/dist/styles/recipes/tabs.css +21 -0
  87. package/dist/styles/recipes/track-highlight.css +2 -1
  88. package/dist/styles/recipes/transcript-divider.css +2 -2
  89. package/dist/styles/theme.css +250 -91
  90. package/dist/ui/alert-dialog.js +1 -1
  91. package/dist/ui/autocomplete.js +5 -1
  92. package/dist/ui/button.js +2 -2
  93. package/dist/ui/checkbox-group.js +1 -1
  94. package/dist/ui/code-diff.js +1 -1
  95. package/dist/ui/code.js +3 -3
  96. package/dist/ui/collapsible.js +1 -1
  97. package/dist/ui/color-picker.js +1 -1
  98. package/dist/ui/combobox-disabled-selection.d.ts +1 -1
  99. package/dist/ui/combobox-disabled-selection.js +2 -1
  100. package/dist/ui/combobox.d.ts +5 -23
  101. package/dist/ui/combobox.js +26 -14
  102. package/dist/ui/command.js +9 -1
  103. package/dist/ui/context-menu.js +1 -1
  104. package/dist/ui/dialog.js +1 -1
  105. package/dist/ui/dockable-panel.js +18 -10
  106. package/dist/ui/drawer.d.ts +7 -4
  107. package/dist/ui/drawer.js +36 -23
  108. package/dist/ui/dropdown-menu.js +1 -1
  109. package/dist/ui/dropzone.d.ts +3 -1
  110. package/dist/ui/dropzone.js +3 -1
  111. package/dist/ui/field.js +1 -1
  112. package/dist/ui/hover-card.js +1 -1
  113. package/dist/ui/infinite-canvas.d.ts +11 -0
  114. package/dist/ui/infinite-canvas.js +82 -7
  115. package/dist/ui/input-group.d.ts +2 -1
  116. package/dist/ui/input-group.js +12 -3
  117. package/dist/ui/input.js +9 -0
  118. package/dist/ui/item.js +1 -1
  119. package/dist/ui/markdown-elements.js +11 -8
  120. package/dist/ui/menubar.js +1 -1
  121. package/dist/ui/morphing-panel.js +1 -1
  122. package/dist/ui/navigation-menu.js +1 -1
  123. package/dist/ui/number-field.d.ts +1 -1
  124. package/dist/ui/number-field.js +13 -2
  125. package/dist/ui/phone-input.js +2 -2
  126. package/dist/ui/popover.js +1 -1
  127. package/dist/ui/radio-group.d.ts +1 -0
  128. package/dist/ui/radio-group.js +11 -1
  129. package/dist/ui/resizable.d.ts +18 -1
  130. package/dist/ui/resizable.js +127 -7
  131. package/dist/ui/responsive-dialog.js +6 -3
  132. package/dist/ui/rich-tooltip.js +1 -1
  133. package/dist/ui/scroll-area.d.ts +4 -1
  134. package/dist/ui/scroll-area.js +2 -1
  135. package/dist/ui/select.d.ts +2 -1
  136. package/dist/ui/select.js +58 -33
  137. package/dist/ui/sheet.js +1 -1
  138. package/dist/ui/sidebar-menu.js +1 -1
  139. package/dist/ui/sidebar-provider.js +2 -2
  140. package/dist/ui/sidebar-resize-rail.js +1 -1
  141. package/dist/ui/sidebar.js +1 -1
  142. package/dist/ui/table-of-contents.d.ts +4 -3
  143. package/dist/ui/table-of-contents.js +126 -47
  144. package/dist/ui/table.js +11 -7
  145. package/dist/ui/toast.js +1 -1
  146. package/dist/ui/toggle.d.ts +1 -1
  147. package/dist/ui/toggle.js +2 -1
  148. package/dist/ui/tooltip.js +1 -1
  149. package/dist/ui/tree.js +1 -1
  150. package/dist/ui/trigger-menu.js +7 -3
  151. package/dist/user-ask.js +1 -1
  152. package/package.json +1 -1
package/dist/context.js CHANGED
@@ -1,9 +1,9 @@
1
1
  "use client";
2
2
  import { cn } from "./lib/cn.js";
3
3
  import { Button } from "./ui/button.js";
4
+ import { Popover, PopoverClose, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger } from "./ui/popover.js";
4
5
  import { ScrollArea } from "./ui/scroll-area.js";
5
6
  import { deriveContextModel } from "./context-model.js";
6
- import { Popover, PopoverClose, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger } from "./ui/popover.js";
7
7
  import { createContext, useContext } from "react";
8
8
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
9
9
  import { X } from "lucide-react";
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { cn } from "./lib/cn.js";
3
3
  import { Button } from "./ui/button.js";
4
- import { createDynamicNotificationGlass } from "./dynamic-notification-glass.js";
5
4
  import { createDynamicNotificationLiquid } from "./dynamic-notification-liquid.js";
5
+ import { createDynamicNotificationGlass } from "./dynamic-notification-glass.js";
6
6
  import { useDynamicNotification } from "./hooks/use-dynamic-notification.js";
7
7
  import { createContext, useContext, useEffect, useId, useMemo, useRef } from "react";
8
8
  import { jsx } from "react/jsx-runtime";
@@ -3,8 +3,8 @@ import { cn } from "./lib/cn.js";
3
3
  import { Button } from "./ui/button.js";
4
4
  import { DUPLICATE_ENVIRONMENT_VARIABLE_MESSAGE, collectEnvironmentVariables, rowsToEnvFileText } from "./lib/env-file.js";
5
5
  import { useEnvironmentVariables } from "./hooks/use-environment-variables.js";
6
- import { Input } from "./ui/input.js";
7
6
  import { InputGroup, InputGroupAddon, InputGroupInput } from "./ui/input-group.js";
7
+ import { Input } from "./ui/input.js";
8
8
  import { createContext, use, useId } from "react";
9
9
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
10
10
  import { AlertTriangleIcon, EyeIcon, EyeOffIcon, FileUpIcon, PlusIcon, RotateCcwIcon, SaveIcon, Trash2Icon } from "lucide-react";
@@ -0,0 +1,32 @@
1
+ import { FilterBarDraft, FilterBarField, FilterBarItem, FilterBarOperator, FilterBarScalar, FilterBarStage, FilterBarValue } from "./types.js";
2
+ import { CSSProperties, KeyboardEvent } from "react";
3
+ //#region src/filter-bar/context.d.ts
4
+ export type FilterBarExit = {
5
+ item: FilterBarItem;
6
+ style: CSSProperties;
7
+ };
8
+ export type FilterBarContextValue = {
9
+ fields: readonly FilterBarField[];
10
+ operators: readonly FilterBarOperator[];
11
+ items: readonly FilterBarItem[];
12
+ draft: FilterBarDraft | null;
13
+ exits: FilterBarExit[];
14
+ disabled: boolean;
15
+ readOnly: boolean;
16
+ register: (key: string, element: HTMLElement | null) => void;
17
+ start: (owner: string, anchor: HTMLElement, stage?: FilterBarStage, item?: FilterBarItem, query?: string) => void;
18
+ close: (restoreFocus?: boolean) => void;
19
+ back: () => void;
20
+ setQuery: (query: string) => void;
21
+ selectField: (fieldId: string) => void;
22
+ selectOperator: (operatorId: string) => void;
23
+ selectValues: (values: FilterBarScalar[]) => void;
24
+ commit: (value: FilterBarValue) => void;
25
+ remove: (item: FilterBarItem) => void;
26
+ clear: () => void;
27
+ releaseExit: (id: string) => void;
28
+ navigate: (event: KeyboardEvent<HTMLElement>, itemId?: string, part?: string) => void;
29
+ };
30
+ export declare const FilterBarContext: import("react").Context<FilterBarContextValue | null>;
31
+ export declare function useFilterBarContext(): FilterBarContextValue;
32
+ //#endregion
@@ -0,0 +1,11 @@
1
+ "use client";
2
+ import { createContext, use } from "react";
3
+ //#region src/filter-bar/context.tsx
4
+ const FilterBarContext = createContext(null);
5
+ function useFilterBarContext() {
6
+ const context = use(FilterBarContext);
7
+ if (!context) throw new Error("FilterBar parts must be used inside FilterBar.Root.");
8
+ return context;
9
+ }
10
+ //#endregion
11
+ export { FilterBarContext, useFilterBarContext };
@@ -0,0 +1,14 @@
1
+ import { ComboboxInputProps } from "../ui/combobox.js";
2
+ import { ReactNode, RefObject } from "react";
3
+ //#region src/filter-bar/editor.d.ts
4
+ export declare function FilterBarEditor({ owner, mode, anchorRef, children }: {
5
+ owner: string;
6
+ mode: "input" | "button";
7
+ anchorRef?: RefObject<HTMLInputElement | null>;
8
+ children: ReactNode;
9
+ }): import("react").JSX.Element;
10
+ export declare function FilterBarInlineInput({ owner, inputRef, className, ...props }: Omit<ComboboxInputProps, "ref"> & {
11
+ owner: string;
12
+ inputRef: RefObject<HTMLInputElement | null>;
13
+ }): import("react").JSX.Element;
14
+ //#endregion
@@ -0,0 +1,204 @@
1
+ "use client";
2
+ import { Button } from "../ui/button.js";
3
+ import { useFilterBarContext } from "./context.js";
4
+ import { allowsCustomValue, fieldOperators, fieldOptions, parseCustomValue } from "./model.js";
5
+ import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList } from "../ui/combobox.js";
6
+ import { useRef } from "react";
7
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
8
+ //#region src/filter-bar/editor.tsx
9
+ function valueChoice(value, label) {
10
+ return {
11
+ id: `${typeof value}:${value}`,
12
+ value,
13
+ label
14
+ };
15
+ }
16
+ function sameChoice(first, second) {
17
+ return first.id === second.id;
18
+ }
19
+ function valueChoices(field, query) {
20
+ const choices = fieldOptions(field).map((option) => ({
21
+ ...valueChoice(option.value, option.label),
22
+ icon: option.icon,
23
+ disabled: option.disabled
24
+ }));
25
+ const customValue = allowsCustomValue(field) ? parseCustomValue(field, query) : void 0;
26
+ if (customValue !== void 0 && !choices.some((choice) => choice.value === customValue)) choices.push(valueChoice(customValue, `Use “${customValue}”`));
27
+ return choices;
28
+ }
29
+ function editorChoices(context, draft) {
30
+ if (!draft || draft.stage === "field") return context.fields.filter((candidate) => fieldOperators(candidate, context.operators).length > 0).map((candidate) => ({
31
+ id: candidate.id,
32
+ label: candidate.label,
33
+ icon: candidate.icon
34
+ }));
35
+ const field = context.fields.find((candidate) => candidate.id === draft.fieldId);
36
+ if (!field) return [];
37
+ if (draft.stage === "operator") return fieldOperators(field, context.operators).map((operator) => ({
38
+ id: operator.id,
39
+ label: operator.label
40
+ }));
41
+ return valueChoices(field, draft.query);
42
+ }
43
+ function searchLabel(stage, field) {
44
+ if (stage === "field") return "Filter field";
45
+ if (stage === "operator") return "Filter operator";
46
+ return `${field?.label ?? "Filter"} value`;
47
+ }
48
+ function handleSelection(context, draft, choice) {
49
+ if (!choice || !draft) return;
50
+ const field = context.fields.find((candidate) => candidate.id === draft.fieldId);
51
+ if (draft.stage === "value" && (field?.loading || field?.error)) return;
52
+ if (Array.isArray(choice)) context.selectValues(choice.flatMap((selected) => selected.value === void 0 ? [] : [selected.value]));
53
+ else if (draft.stage === "field") context.selectField(choice.id);
54
+ else if (draft.stage === "operator") context.selectOperator(choice.id);
55
+ else if (choice.value !== void 0) context.commit(choice.value);
56
+ }
57
+ function handleEditorKeyDown(context, draft, event) {
58
+ if (event.nativeEvent.isComposing) return false;
59
+ const backwards = event.key === "Escape" || event.key === "Backspace" && draft?.query === "";
60
+ if (draft && backwards) {
61
+ event.preventDefault();
62
+ event.stopPropagation();
63
+ context.back();
64
+ return true;
65
+ }
66
+ const operator = context.operators.find((candidate) => candidate.id === draft?.operatorId);
67
+ const field = context.fields.find((candidate) => candidate.id === draft?.fieldId);
68
+ if (!(draft?.stage === "value" && operator?.arity === "many" && event.key === "Enter" && (event.metaKey || event.ctrlKey)) || !draft || draft.values.length === 0 || field?.loading || field?.error) return false;
69
+ event.preventDefault();
70
+ context.commit(draft.values);
71
+ return true;
72
+ }
73
+ function EditorOptions({ field, invalidNumber }) {
74
+ if (field?.loading || field?.error) return /* @__PURE__ */ jsx("div", {
75
+ className: "p-3 text-caption",
76
+ role: field.error ? "alert" : "status",
77
+ children: field.error ?? "Loading values…"
78
+ });
79
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(ComboboxEmpty, { children: invalidNumber ? "Enter a finite number." : "No matching options." }), /* @__PURE__ */ jsx(ComboboxList, { children: (choice) => /* @__PURE__ */ jsx(ComboboxItem, {
80
+ value: choice,
81
+ disabled: choice.disabled,
82
+ children: /* @__PURE__ */ jsxs("span", {
83
+ className: "flex min-w-0 items-center gap-2",
84
+ children: [choice.icon, /* @__PURE__ */ jsx("span", {
85
+ className: "truncate",
86
+ children: choice.label
87
+ })]
88
+ })
89
+ }, choice.id) })] });
90
+ }
91
+ function handleOpenChange(context, draft, anchorRef, owner, open, eventDetails) {
92
+ if (open && !draft && anchorRef?.current && eventDetails.reason !== "input-change") context.start(owner, anchorRef.current);
93
+ if (open || !draft) return;
94
+ if (eventDetails.reason === "item-press" || eventDetails.reason === "escape-key") eventDetails.cancel();
95
+ else context.close(false);
96
+ }
97
+ function editorView(context, draft) {
98
+ const field = context.fields.find((candidate) => candidate.id === draft?.fieldId);
99
+ const operator = context.operators.find((candidate) => candidate.id === draft?.operatorId);
100
+ const stage = draft?.stage ?? "field";
101
+ const many = stage === "value" && operator?.arity === "many";
102
+ const query = draft?.query ?? "";
103
+ const choices = editorChoices(context, draft);
104
+ const selected = (draft?.values ?? []).map((value) => choices.find((choice) => choice.value === value) ?? valueChoice(value, field?.formatValue?.(value) ?? String(value)));
105
+ const customNumber = field?.type === "number" && allowsCustomValue(field);
106
+ return {
107
+ field,
108
+ stage,
109
+ many,
110
+ query,
111
+ choices,
112
+ selected,
113
+ invalidNumber: stage === "value" && Boolean(customNumber) && query.trim() !== "" && field !== void 0 && parseCustomValue(field, query) === void 0,
114
+ remote: stage === "value" && field?.optionsMode === "remote",
115
+ unavailable: stage === "value" && Boolean(field?.loading || field?.error),
116
+ label: searchLabel(stage, field)
117
+ };
118
+ }
119
+ function FilterBarEditor({ owner, mode, anchorRef, children }) {
120
+ const context = useFilterBarContext();
121
+ const searchRef = useRef(null);
122
+ const draft = context.draft?.owner === owner ? context.draft : null;
123
+ const { field, stage, many, query, choices, selected, invalidNumber, remote, unavailable, label } = editorView(context, draft);
124
+ const canCommitMany = selected.length > 0 && !unavailable && !context.disabled && !context.readOnly;
125
+ return /* @__PURE__ */ jsxs(Combobox, {
126
+ items: choices,
127
+ multiple: many,
128
+ value: many ? selected : null,
129
+ inputValue: query,
130
+ open: draft !== null,
131
+ disabled: context.disabled,
132
+ readOnly: context.readOnly,
133
+ modal: false,
134
+ isItemEqualToValue: sameChoice,
135
+ filter: remote ? null : void 0,
136
+ onValueChange: (choice) => handleSelection(context, draft, choice),
137
+ onInputValueChange: (nextQuery, eventDetails) => {
138
+ if (eventDetails.reason !== "input-change" && eventDetails.reason !== "input-clear") return;
139
+ if (draft) context.setQuery(nextQuery);
140
+ else if (anchorRef?.current) context.start(owner, anchorRef.current, "field", void 0, nextQuery);
141
+ },
142
+ onOpenChange: (open, eventDetails) => handleOpenChange(context, draft, anchorRef, owner, open, eventDetails),
143
+ children: [children, /* @__PURE__ */ jsxs(ComboboxContent, {
144
+ anchor: draft?.anchor,
145
+ initialFocus: mode === "input" ? false : searchRef,
146
+ finalFocus: false,
147
+ className: "w-72 max-h-(--available-height) overflow-y-auto",
148
+ onKeyDown: (event) => {
149
+ if (event.key === "Escape" && !event.defaultPrevented) {
150
+ event.preventDefault();
151
+ context.back();
152
+ }
153
+ },
154
+ children: [
155
+ mode === "button" && /* @__PURE__ */ jsx("div", {
156
+ className: "p-2",
157
+ children: /* @__PURE__ */ jsx(ComboboxInput, {
158
+ ref: searchRef,
159
+ size: "sm",
160
+ "aria-label": label,
161
+ placeholder: label,
162
+ inputMode: field?.type === "number" && stage === "value" ? "decimal" : void 0,
163
+ onKeyDown: (event) => handleEditorKeyDown(context, draft, event)
164
+ })
165
+ }),
166
+ /* @__PURE__ */ jsx(EditorOptions, {
167
+ field: stage === "value" ? field : void 0,
168
+ invalidNumber
169
+ }),
170
+ many && /* @__PURE__ */ jsx("div", {
171
+ className: "flex justify-end p-2",
172
+ children: /* @__PURE__ */ jsx(Button, {
173
+ disabled: !canCommitMany,
174
+ onClick: () => context.commit(draft?.values ?? []),
175
+ children: "Done"
176
+ })
177
+ })
178
+ ]
179
+ })]
180
+ });
181
+ }
182
+ function FilterBarInlineInput({ owner, inputRef, className, ...props }) {
183
+ const context = useFilterBarContext();
184
+ const draft = context.draft?.owner === owner ? context.draft : null;
185
+ function handleKeyDown(event) {
186
+ props.onKeyDown?.(event);
187
+ if (event.defaultPrevented || event.nativeEvent.isComposing) return;
188
+ if (handleEditorKeyDown(context, draft, event) || event.currentTarget.value !== "") return;
189
+ const last = context.items.at(-1);
190
+ if (event.key === "Backspace" && !draft && last) {
191
+ event.preventDefault();
192
+ context.remove(last);
193
+ } else context.navigate(event);
194
+ }
195
+ return /* @__PURE__ */ jsx(ComboboxInput, {
196
+ ref: inputRef,
197
+ size: "sm",
198
+ className,
199
+ ...props,
200
+ onKeyDown: handleKeyDown
201
+ });
202
+ }
203
+ //#endregion
204
+ export { FilterBarEditor, FilterBarInlineInput };
@@ -0,0 +1,9 @@
1
+ import { FilterBarField, FilterBarItem, FilterBarOperator, FilterBarOption, FilterBarScalar } from "./types.js";
2
+ //#region src/filter-bar/model.d.ts
3
+ export declare function fieldOperators(field: FilterBarField, operators: readonly FilterBarOperator[]): FilterBarOperator[];
4
+ export declare function fieldOptions(field: FilterBarField): readonly FilterBarOption[];
5
+ export declare function parseCustomValue(field: FilterBarField, query: string): FilterBarScalar | undefined;
6
+ export declare function allowsCustomValue(field: FilterBarField): boolean;
7
+ export declare function formatFilterValue(item: FilterBarItem, field: FilterBarField | undefined): string;
8
+ export declare function filterNavigationKeys(items: readonly FilterBarItem[]): string[];
9
+ //#endregion
@@ -0,0 +1,48 @@
1
+ //#region src/filter-bar/model.ts
2
+ const booleanOptions = [{
3
+ value: true,
4
+ label: "True"
5
+ }, {
6
+ value: false,
7
+ label: "False"
8
+ }];
9
+ function fieldOperators(field, operators) {
10
+ return operators.filter((operator) => !field.operators || field.operators.includes(operator.id));
11
+ }
12
+ function fieldOptions(field) {
13
+ return field.options ?? (field.type === "boolean" ? booleanOptions : []);
14
+ }
15
+ function parseCustomValue(field, query) {
16
+ if (!query.trim()) return void 0;
17
+ if (field.type === "number") {
18
+ if (!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i.test(query.trim())) return void 0;
19
+ const number = Number(query);
20
+ return Number.isFinite(number) ? number : void 0;
21
+ }
22
+ if (field.type === "boolean") {
23
+ if (query.toLowerCase() === "true") return true;
24
+ if (query.toLowerCase() === "false") return false;
25
+ return;
26
+ }
27
+ return query.trim();
28
+ }
29
+ function allowsCustomValue(field) {
30
+ return field.allowCustomValue ?? (field.options === void 0 && field.type !== "boolean" && field.optionsMode !== "remote");
31
+ }
32
+ function formatFilterValue(item, field) {
33
+ if (item.value === null) return "";
34
+ return (Array.isArray(item.value) ? item.value : [item.value]).map((value) => field?.formatValue?.(value) ?? fieldOptionsForLabel(field, value)).join(", ");
35
+ }
36
+ function fieldOptionsForLabel(field, value) {
37
+ return (field ? fieldOptions(field).find((option) => option.value === value)?.label : void 0) ?? String(value);
38
+ }
39
+ function filterNavigationKeys(items) {
40
+ return items.flatMap((item) => [
41
+ "field",
42
+ "operator",
43
+ ...item.value === null ? [] : ["value"],
44
+ "remove"
45
+ ].map((segment) => `${item.id}:${segment}`));
46
+ }
47
+ //#endregion
48
+ export { allowsCustomValue, fieldOperators, fieldOptions, filterNavigationKeys, formatFilterValue, parseCustomValue };
@@ -0,0 +1,10 @@
1
+ //#region src/filter-bar/motion.d.ts
2
+ export declare function filterMotionEnabled(element: HTMLElement): boolean;
3
+ export declare function useFilterBarMotion(): {
4
+ register(key: string, element: HTMLElement | null): void;
5
+ capture(): void;
6
+ cancel: () => void;
7
+ play(): void;
8
+ rootRef: import("react").RefObject<HTMLFieldSetElement | null>;
9
+ };
10
+ //#endregion
@@ -0,0 +1,84 @@
1
+ "use client";
2
+ import { useLayoutEffect, useRef, useState } from "react";
3
+ //#region src/filter-bar/motion.ts
4
+ function filterMotionEnabled(element) {
5
+ return !window.matchMedia("(prefers-reduced-motion: reduce)").matches && Number.parseFloat(getComputedStyle(element).getPropertyValue("--duration-fast")) > 0;
6
+ }
7
+ function animateMovement(element, before, after, duration, easing) {
8
+ const x = before.left - after.left;
9
+ const y = before.top - after.top;
10
+ if (Math.abs(x) < 1 && Math.abs(y) < 1) return null;
11
+ return element.animate([{ transform: `translate(${x}px, ${y}px)` }, { transform: "translate(0, 0)" }], {
12
+ duration,
13
+ easing
14
+ });
15
+ }
16
+ function animateLayout(elements, before, after, animations, duration, easing) {
17
+ for (const [key, bounds] of after) {
18
+ const origin = before.get(key);
19
+ const element = elements.get(key);
20
+ if (!origin || !element) continue;
21
+ const animation = animateMovement(element, origin, bounds, duration, easing);
22
+ if (animation) animations.set(key, animation);
23
+ }
24
+ }
25
+ function useFilterBarMotion() {
26
+ const rootRef = useRef(null);
27
+ const [motion] = useState(() => {
28
+ const elements = /* @__PURE__ */ new Map();
29
+ const animations = /* @__PURE__ */ new Map();
30
+ let captured = null;
31
+ function measure() {
32
+ return new Map([...elements].map(([key, element]) => [key, element.getBoundingClientRect()]));
33
+ }
34
+ function cancel() {
35
+ for (const animation of animations.values()) animation.cancel();
36
+ animations.clear();
37
+ }
38
+ return {
39
+ register(key, element) {
40
+ if (element) elements.set(key, element);
41
+ else elements.delete(key);
42
+ },
43
+ capture() {
44
+ captured = measure();
45
+ },
46
+ cancel,
47
+ play() {
48
+ const root = rootRef.current;
49
+ if (!root || !filterMotionEnabled(root)) {
50
+ cancel();
51
+ captured = null;
52
+ return;
53
+ }
54
+ if (!captured) return;
55
+ const origin = captured;
56
+ captured = null;
57
+ cancel();
58
+ const next = measure();
59
+ const computed = getComputedStyle(root);
60
+ const duration = Number.parseFloat(computed.getPropertyValue("--duration-fast"));
61
+ const easing = computed.getPropertyValue("--ease-standard").trim();
62
+ animateLayout(elements, origin, next, animations, duration, easing);
63
+ }
64
+ };
65
+ });
66
+ useLayoutEffect(() => motion.play());
67
+ useLayoutEffect(() => {
68
+ const media = window.matchMedia("(prefers-reduced-motion: reduce)");
69
+ function cancelReducedMotion() {
70
+ if (media.matches) motion.cancel();
71
+ }
72
+ media.addEventListener("change", cancelReducedMotion);
73
+ return () => {
74
+ media.removeEventListener("change", cancelReducedMotion);
75
+ motion.cancel();
76
+ };
77
+ }, [motion]);
78
+ return {
79
+ rootRef,
80
+ ...motion
81
+ };
82
+ }
83
+ //#endregion
84
+ export { filterMotionEnabled, useFilterBarMotion };
@@ -0,0 +1,19 @@
1
+ import { ButtonProps } from "../ui/button.js";
2
+ import { FilterBarKnobStyle } from "../knob-contracts/filter-bar-knobs.js";
3
+ import { FilterBarInlineInput } from "./editor.js";
4
+ import { FilterBarItem } from "./types.js";
5
+ import { CSSProperties, ComponentProps, ReactNode } from "react";
6
+ //#region src/filter-bar/parts.d.ts
7
+ export type FilterBarChipProps = ComponentProps<"div"> & {
8
+ item: FilterBarItem;
9
+ readOnly?: boolean;
10
+ style?: CSSProperties & FilterBarKnobStyle;
11
+ };
12
+ export declare function FilterBarChip(props: FilterBarChipProps): import("react").JSX.Element;
13
+ export declare function FilterBarChips({ renderChip }: {
14
+ renderChip?: (item: FilterBarItem) => ReactNode;
15
+ }): import("react").JSX.Element;
16
+ export declare function FilterBarAddButton({ className, children, onClick, onKeyDown, ref, ...props }: ButtonProps): import("react").JSX.Element;
17
+ export declare function FilterBarInput({ className, ...props }: Omit<Parameters<typeof FilterBarInlineInput>[0], "owner" | "inputRef">): import("react").JSX.Element;
18
+ export declare function FilterBarClear({ children, onClick, ...props }: ButtonProps): import("react").JSX.Element | null;
19
+ //#endregion