@upbound/monarch-blocks 0.1.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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +58 -0
  3. package/dist/cel-filter-bar.d.ts +187 -0
  4. package/dist/cel-filter-bar.js +1850 -0
  5. package/dist/cel-filter-bar.js.map +1 -0
  6. package/dist/chart.d.ts +52 -0
  7. package/dist/chart.js +332 -0
  8. package/dist/chart.js.map +1 -0
  9. package/dist/chat-interface.d.ts +143 -0
  10. package/dist/chat-interface.js +676 -0
  11. package/dist/chat-interface.js.map +1 -0
  12. package/dist/code-block.d.ts +21 -0
  13. package/dist/code-block.js +201 -0
  14. package/dist/code-block.js.map +1 -0
  15. package/dist/data-table.d.ts +505 -0
  16. package/dist/data-table.js +4725 -0
  17. package/dist/data-table.js.map +1 -0
  18. package/dist/filter-bar.d.ts +80 -0
  19. package/dist/filter-bar.js +590 -0
  20. package/dist/filter-bar.js.map +1 -0
  21. package/dist/floating-widget.d.ts +36 -0
  22. package/dist/floating-widget.js +218 -0
  23. package/dist/floating-widget.js.map +1 -0
  24. package/dist/index.d.ts +18 -0
  25. package/dist/index.js +6217 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/page-header.d.ts +12 -0
  28. package/dist/page-header.js +120 -0
  29. package/dist/page-header.js.map +1 -0
  30. package/dist/relative-time.d.ts +60 -0
  31. package/dist/relative-time.js +328 -0
  32. package/dist/relative-time.js.map +1 -0
  33. package/dist/section-header.d.ts +11 -0
  34. package/dist/section-header.js +123 -0
  35. package/dist/section-header.js.map +1 -0
  36. package/dist/section-nav.d.ts +50 -0
  37. package/dist/section-nav.js +143 -0
  38. package/dist/section-nav.js.map +1 -0
  39. package/dist/timeline.d.ts +8 -0
  40. package/dist/timeline.js +95 -0
  41. package/dist/timeline.js.map +1 -0
  42. package/dist/types-BULiU2qC.d.ts +170 -0
  43. package/package.json +58 -0
@@ -0,0 +1,80 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as React from 'react';
3
+
4
+ type FilterType = 'checkbox' | 'radio' | 'text' | 'multiText' | 'date';
5
+ interface FilterOption {
6
+ value: string;
7
+ label: string;
8
+ icon?: React.ReactNode;
9
+ }
10
+ interface FilterDefinitionBase {
11
+ key: string;
12
+ label: string;
13
+ }
14
+ interface CheckboxFilterDefinition extends FilterDefinitionBase {
15
+ type?: 'checkbox';
16
+ options: FilterOption[];
17
+ /**
18
+ * When `true`, the chip's value popup becomes a searchable Combobox
19
+ * (multi-select with a search input). Use for long option lists where a
20
+ * basic dropdown becomes hard to scan. When `false` or omitted, the chip
21
+ * uses the standard checkbox dropdown.
22
+ */
23
+ searchable?: boolean;
24
+ }
25
+ interface RadioFilterDefinition extends FilterDefinitionBase {
26
+ type: 'radio';
27
+ options: FilterOption[];
28
+ /** Same searchable-Combobox swap as `CheckboxFilterDefinition`, constrained to a single selection. */
29
+ searchable?: boolean;
30
+ }
31
+ interface TextFilterDefinition extends FilterDefinitionBase {
32
+ type: 'text';
33
+ placeholder?: string;
34
+ }
35
+ interface MultiTextFilterDefinition extends FilterDefinitionBase {
36
+ type: 'multiText';
37
+ placeholder?: string;
38
+ }
39
+ interface DateFilterDefinition extends FilterDefinitionBase {
40
+ type: 'date';
41
+ /**
42
+ * `true` always shows Start/End time inputs, `false` never shows them,
43
+ * and omitted auto-detects from whether the current value already carries
44
+ * a non-midnight time component.
45
+ */
46
+ includeTime?: boolean;
47
+ }
48
+ type FilterDefinition = CheckboxFilterDefinition | RadioFilterDefinition | TextFilterDefinition | MultiTextFilterDefinition | DateFilterDefinition;
49
+ type CheckboxFilterValue = string[];
50
+ type RadioFilterValue = string;
51
+ type TextFilterValue = string;
52
+ type MultiTextFilterValue = string[];
53
+ type DateFilterValue = {
54
+ startDate: Date | null;
55
+ endDate: Date | null;
56
+ };
57
+ type FilterValue = CheckboxFilterValue | RadioFilterValue | TextFilterValue | MultiTextFilterValue | DateFilterValue;
58
+ interface ActiveFilter {
59
+ key: string;
60
+ value?: FilterValue;
61
+ }
62
+ interface FilterBarProps extends React.ComponentProps<'div'> {
63
+ filters: FilterDefinition[];
64
+ activeFilters: ActiveFilter[];
65
+ onAddFilter: (key: string) => void;
66
+ onRemoveFilter: (key: string) => void;
67
+ onChangeFilter: (key: string, value: FilterValue) => void;
68
+ }
69
+ declare function FilterBar({ filters, activeFilters, onAddFilter, onRemoveFilter, onChangeFilter, className, children, ...props }: FilterBarProps): react_jsx_runtime.JSX.Element;
70
+ interface FilterChipProps {
71
+ definition: FilterDefinition;
72
+ value: FilterValue | undefined;
73
+ /** Opens the chip's value popup as soon as it mounts — used right after the filter is added. */
74
+ autoOpen?: boolean;
75
+ onChangeValue: (value: FilterValue) => void;
76
+ onRemove: () => void;
77
+ }
78
+ declare function FilterChip({ definition, value, autoOpen, onChangeValue, onRemove }: FilterChipProps): react_jsx_runtime.JSX.Element;
79
+
80
+ export { type ActiveFilter, type CheckboxFilterDefinition, type CheckboxFilterValue, type DateFilterDefinition, type DateFilterValue, FilterBar, type FilterBarProps, FilterChip, type FilterDefinition, type FilterOption, type FilterType, type FilterValue, type MultiTextFilterDefinition, type MultiTextFilterValue, type RadioFilterDefinition, type RadioFilterValue, type TextFilterDefinition, type TextFilterValue };
@@ -0,0 +1,590 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ var __objRest = (source, exclude) => {
21
+ var target = {};
22
+ for (var prop in source)
23
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
24
+ target[prop] = source[prop];
25
+ if (source != null && __getOwnPropSymbols)
26
+ for (var prop of __getOwnPropSymbols(source)) {
27
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
28
+ target[prop] = source[prop];
29
+ }
30
+ return target;
31
+ };
32
+
33
+ // src/filter-bar/filter-bar.tsx
34
+ import * as React from "react";
35
+ import { Icon } from "@upbound/monarch-core";
36
+
37
+ // src/lib/utils.ts
38
+ import { clsx } from "clsx";
39
+ import { extendTailwindMerge } from "tailwind-merge";
40
+ var twMerge = extendTailwindMerge({
41
+ extend: {
42
+ classGroups: {
43
+ "font-size": [
44
+ {
45
+ text: [
46
+ "display-hero",
47
+ "display-kpi-sm",
48
+ "display-kpi",
49
+ "display-kpi-lg",
50
+ "display-feature",
51
+ "h1",
52
+ "h2",
53
+ "h3",
54
+ "h4",
55
+ "body-lg",
56
+ "body",
57
+ "body-sm",
58
+ "caption",
59
+ "eyebrow"
60
+ ]
61
+ }
62
+ ]
63
+ }
64
+ }
65
+ });
66
+ function cn(...inputs) {
67
+ return twMerge(clsx(inputs));
68
+ }
69
+
70
+ // src/filter-bar/filter-bar.tsx
71
+ import { Badge } from "@upbound/monarch-core";
72
+ import { Button } from "@upbound/monarch-core";
73
+ import { ButtonGroup, ButtonGroupText } from "@upbound/monarch-core";
74
+ import { Calendar } from "@upbound/monarch-core";
75
+ import {
76
+ Combobox,
77
+ ComboboxChip,
78
+ ComboboxClearAll,
79
+ ComboboxContent,
80
+ ComboboxEmpty,
81
+ ComboboxInput,
82
+ ComboboxItem,
83
+ ComboboxList,
84
+ ComboboxSelectedChips,
85
+ ComboboxSeparator,
86
+ ComboboxTrigger,
87
+ ComboboxValue
88
+ } from "@upbound/monarch-core";
89
+ import {
90
+ DropdownMenu,
91
+ DropdownMenuCheckboxItem,
92
+ DropdownMenuContent,
93
+ DropdownMenuItem,
94
+ DropdownMenuRadioGroup,
95
+ DropdownMenuRadioItem,
96
+ DropdownMenuTrigger
97
+ } from "@upbound/monarch-core";
98
+ import { Input } from "@upbound/monarch-core";
99
+ import { Label } from "@upbound/monarch-core";
100
+ import { Popover, PopoverContent, PopoverTrigger } from "@upbound/monarch-core";
101
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
102
+ function asCheckboxValue(value) {
103
+ return Array.isArray(value) ? value : [];
104
+ }
105
+ function asRadioValue(value) {
106
+ return typeof value === "string" ? value : "";
107
+ }
108
+ function asTextValue(value) {
109
+ return typeof value === "string" ? value : "";
110
+ }
111
+ function asMultiTextValue(value) {
112
+ return Array.isArray(value) ? value : [];
113
+ }
114
+ function asDateValue(value) {
115
+ if (value && typeof value === "object" && !Array.isArray(value)) return value;
116
+ return { startDate: null, endDate: null };
117
+ }
118
+ function hasExplicitTime(date) {
119
+ if (!date) return false;
120
+ return date.getHours() !== 0 || date.getMinutes() !== 0;
121
+ }
122
+ function formatTimeForInput(date) {
123
+ if (!date) return "";
124
+ return `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`;
125
+ }
126
+ function applyTimeToDate(date, time) {
127
+ const [hours, minutes] = time.split(":").map(Number);
128
+ const next = new Date(date);
129
+ next.setHours(hours || 0, minutes || 0, 0, 0);
130
+ return next;
131
+ }
132
+ function formatDateLabel(date) {
133
+ return date.toLocaleDateString(void 0, { month: "short", day: "numeric" });
134
+ }
135
+ function FilterBar(_a) {
136
+ var _b = _a, {
137
+ filters,
138
+ activeFilters,
139
+ onAddFilter,
140
+ onRemoveFilter,
141
+ onChangeFilter,
142
+ className,
143
+ children
144
+ } = _b, props = __objRest(_b, [
145
+ "filters",
146
+ "activeFilters",
147
+ "onAddFilter",
148
+ "onRemoveFilter",
149
+ "onChangeFilter",
150
+ "className",
151
+ "children"
152
+ ]);
153
+ const usedKeys = new Set(activeFilters.map((f) => f.key));
154
+ const availableFilters = filters.filter((f) => !usedKeys.has(f.key));
155
+ const shouldShowFilters = availableFilters.length > 0 || activeFilters.length > 0;
156
+ const [pendingOpenKey, setPendingOpenKey] = React.useState(null);
157
+ function handleAddFilter(key) {
158
+ onAddFilter(key);
159
+ setPendingOpenKey(key);
160
+ }
161
+ return /* @__PURE__ */ jsxs(
162
+ "div",
163
+ __spreadProps(__spreadValues({
164
+ "data-slot": "filter-bar",
165
+ "data-visible-filters": shouldShowFilters,
166
+ className: cn("flex flex-wrap items-center gap-2", className)
167
+ }, props), {
168
+ children: [
169
+ availableFilters.length > 0 && /* @__PURE__ */ jsxs(DropdownMenu, { children: [
170
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "outline", children: [
171
+ /* @__PURE__ */ jsx(Icon, { name: "plus", "data-icon": "inline-start" }),
172
+ "Add filter"
173
+ ] }) }),
174
+ /* @__PURE__ */ jsx(
175
+ DropdownMenuContent,
176
+ {
177
+ align: "start",
178
+ className: "w-48",
179
+ onCloseAutoFocus: (e) => e.preventDefault(),
180
+ children: availableFilters.map((filter) => /* @__PURE__ */ jsx(DropdownMenuItem, { onSelect: () => handleAddFilter(filter.key), children: filter.label }, filter.key))
181
+ }
182
+ )
183
+ ] }),
184
+ activeFilters.map((active) => {
185
+ const definition = filters.find((f) => f.key === active.key);
186
+ if (!definition) return null;
187
+ return /* @__PURE__ */ jsx(
188
+ FilterChip,
189
+ {
190
+ definition,
191
+ value: active.value,
192
+ autoOpen: active.key === pendingOpenKey,
193
+ onChangeValue: (value) => onChangeFilter(active.key, value),
194
+ onRemove: () => onRemoveFilter(active.key)
195
+ },
196
+ active.key
197
+ );
198
+ }),
199
+ children
200
+ ]
201
+ })
202
+ );
203
+ }
204
+ function FilterChip({ definition, value, autoOpen, onChangeValue, onRemove }) {
205
+ var _a;
206
+ const type = (_a = definition.type) != null ? _a : "checkbox";
207
+ switch (type) {
208
+ case "checkbox":
209
+ return /* @__PURE__ */ jsx(
210
+ CheckboxFilterChip,
211
+ {
212
+ definition,
213
+ value,
214
+ autoOpen,
215
+ onChangeValue,
216
+ onRemove
217
+ }
218
+ );
219
+ case "radio":
220
+ return /* @__PURE__ */ jsx(
221
+ RadioFilterChip,
222
+ {
223
+ definition,
224
+ value,
225
+ autoOpen,
226
+ onChangeValue,
227
+ onRemove
228
+ }
229
+ );
230
+ case "text":
231
+ return /* @__PURE__ */ jsx(
232
+ TextFilterChip,
233
+ {
234
+ definition,
235
+ value,
236
+ autoOpen,
237
+ onChangeValue,
238
+ onRemove
239
+ }
240
+ );
241
+ case "multiText":
242
+ return /* @__PURE__ */ jsx(
243
+ MultiTextFilterChip,
244
+ {
245
+ definition,
246
+ value,
247
+ autoOpen,
248
+ onChangeValue,
249
+ onRemove
250
+ }
251
+ );
252
+ case "date":
253
+ return /* @__PURE__ */ jsx(
254
+ DateFilterChip,
255
+ {
256
+ definition,
257
+ value,
258
+ autoOpen,
259
+ onChangeValue,
260
+ onRemove
261
+ }
262
+ );
263
+ }
264
+ }
265
+ function FilterChipRemoveButton({ label, onRemove }) {
266
+ return /* @__PURE__ */ jsx(Button, { variant: "outline", size: "icon", "aria-label": `Remove ${label} filter`, onClick: onRemove, children: /* @__PURE__ */ jsx(Icon, { name: "xmark" }) });
267
+ }
268
+ function CheckboxFilterChip({
269
+ definition,
270
+ value,
271
+ autoOpen,
272
+ onChangeValue,
273
+ onRemove
274
+ }) {
275
+ var _a, _b;
276
+ const { label, options, searchable = false } = definition;
277
+ const values = asCheckboxValue(value);
278
+ const displayLabel = values.length === 0 ? "Any" : values.length === 1 ? (_b = (_a = options.find((o) => o.value === values[0])) == null ? void 0 : _a.label) != null ? _b : values[0] : `${values.length} selected`;
279
+ function toggle(optionValue) {
280
+ const next = values.includes(optionValue) ? values.filter((v) => v !== optionValue) : [...values, optionValue];
281
+ onChangeValue(next);
282
+ }
283
+ return /* @__PURE__ */ jsxs(ButtonGroup, { children: [
284
+ /* @__PURE__ */ jsx(ButtonGroupText, { children: label }),
285
+ searchable ? /* @__PURE__ */ jsxs(
286
+ Combobox,
287
+ {
288
+ items: options,
289
+ itemToStringValue: (item) => item.label,
290
+ multiple: true,
291
+ defaultOpen: autoOpen,
292
+ value: options.filter((o) => values.includes(o.value)),
293
+ onValueChange: (selected) => onChangeValue(selected.map((s) => s.value)),
294
+ children: [
295
+ /* @__PURE__ */ jsx(
296
+ ComboboxTrigger,
297
+ {
298
+ render: /* @__PURE__ */ jsx(Button, { variant: "outline", className: "gap-1 font-normal [&>svg:last-child]:size-(--icon-sm)" }),
299
+ children: /* @__PURE__ */ jsx("span", { className: values.length === 0 ? "text-muted-foreground" : "", children: displayLabel })
300
+ }
301
+ ),
302
+ /* @__PURE__ */ jsxs(ComboboxContent, { className: "min-w-64", collisionAvoidance: { side: "none", align: "shift" }, children: [
303
+ /* @__PURE__ */ jsx(ComboboxInput, { showTrigger: false, placeholder: `Search ${label.toLowerCase()}\u2026` }),
304
+ values.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
305
+ /* @__PURE__ */ jsxs(ComboboxSelectedChips, { children: [
306
+ /* @__PURE__ */ jsx(ComboboxValue, { children: (selected) => /* @__PURE__ */ jsx(Fragment, { children: selected.map((item) => /* @__PURE__ */ jsx(ComboboxChip, { children: item.label }, item.value)) }) }),
307
+ /* @__PURE__ */ jsx(ComboboxClearAll, { onClick: () => onChangeValue([]) })
308
+ ] }),
309
+ /* @__PURE__ */ jsx(ComboboxSeparator, {})
310
+ ] }),
311
+ /* @__PURE__ */ jsxs(ComboboxEmpty, { children: [
312
+ "No ",
313
+ label.toLowerCase(),
314
+ " found."
315
+ ] }),
316
+ /* @__PURE__ */ jsx(ComboboxList, { children: (item) => /* @__PURE__ */ jsxs(ComboboxItem, { value: item, children: [
317
+ item.icon,
318
+ item.label
319
+ ] }, item.value) })
320
+ ] })
321
+ ]
322
+ }
323
+ ) : /* @__PURE__ */ jsxs(DropdownMenu, { defaultOpen: autoOpen, children: [
324
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "outline", className: "gap-1 font-normal", children: [
325
+ /* @__PURE__ */ jsx("span", { className: values.length === 0 ? "text-muted-foreground" : "", children: displayLabel }),
326
+ /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: "sm", className: "text-muted-foreground" })
327
+ ] }) }),
328
+ /* @__PURE__ */ jsx(DropdownMenuContent, { align: "start", className: "w-48", children: options.map((opt) => /* @__PURE__ */ jsxs(
329
+ DropdownMenuCheckboxItem,
330
+ {
331
+ checked: values.includes(opt.value),
332
+ onCheckedChange: () => toggle(opt.value),
333
+ children: [
334
+ opt.icon,
335
+ opt.label
336
+ ]
337
+ },
338
+ opt.value
339
+ )) })
340
+ ] }),
341
+ /* @__PURE__ */ jsx(FilterChipRemoveButton, { label, onRemove })
342
+ ] });
343
+ }
344
+ function RadioFilterChip({
345
+ definition,
346
+ value,
347
+ autoOpen,
348
+ onChangeValue,
349
+ onRemove
350
+ }) {
351
+ var _a, _b, _c;
352
+ const { label, options, searchable = false } = definition;
353
+ const selectedValue = asRadioValue(value);
354
+ const displayLabel = selectedValue ? (_b = (_a = options.find((o) => o.value === selectedValue)) == null ? void 0 : _a.label) != null ? _b : selectedValue : "Any";
355
+ const selected = (_c = options.find((o) => o.value === selectedValue)) != null ? _c : null;
356
+ return /* @__PURE__ */ jsxs(ButtonGroup, { children: [
357
+ /* @__PURE__ */ jsx(ButtonGroupText, { children: label }),
358
+ searchable ? /* @__PURE__ */ jsxs(
359
+ Combobox,
360
+ {
361
+ items: options,
362
+ itemToStringValue: (item) => item.label,
363
+ defaultOpen: autoOpen,
364
+ value: selected,
365
+ onValueChange: (next) => {
366
+ var _a2;
367
+ return onChangeValue((_a2 = next == null ? void 0 : next.value) != null ? _a2 : "");
368
+ },
369
+ children: [
370
+ /* @__PURE__ */ jsx(
371
+ ComboboxTrigger,
372
+ {
373
+ render: /* @__PURE__ */ jsx(Button, { variant: "outline", className: "gap-1 font-normal [&>svg:last-child]:size-(--icon-sm)" }),
374
+ children: /* @__PURE__ */ jsx("span", { className: selectedValue ? "" : "text-muted-foreground", children: displayLabel })
375
+ }
376
+ ),
377
+ /* @__PURE__ */ jsxs(ComboboxContent, { className: "min-w-64", collisionAvoidance: { side: "none", align: "shift" }, children: [
378
+ /* @__PURE__ */ jsx(ComboboxInput, { showTrigger: false, placeholder: `Search ${label.toLowerCase()}\u2026` }),
379
+ /* @__PURE__ */ jsxs(ComboboxEmpty, { children: [
380
+ "No ",
381
+ label.toLowerCase(),
382
+ " found."
383
+ ] }),
384
+ /* @__PURE__ */ jsx(ComboboxList, { children: (item) => /* @__PURE__ */ jsxs(ComboboxItem, { value: item, children: [
385
+ item.icon,
386
+ item.label
387
+ ] }, item.value) })
388
+ ] })
389
+ ]
390
+ }
391
+ ) : /* @__PURE__ */ jsxs(DropdownMenu, { defaultOpen: autoOpen, children: [
392
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "outline", className: "gap-1 font-normal", children: [
393
+ /* @__PURE__ */ jsx("span", { className: selectedValue ? "" : "text-muted-foreground", children: displayLabel }),
394
+ /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: "sm", className: "text-muted-foreground" })
395
+ ] }) }),
396
+ /* @__PURE__ */ jsx(DropdownMenuContent, { align: "start", className: "w-48", children: /* @__PURE__ */ jsx(DropdownMenuRadioGroup, { value: selectedValue, onValueChange: onChangeValue, children: options.map((opt) => /* @__PURE__ */ jsxs(DropdownMenuRadioItem, { value: opt.value, children: [
397
+ opt.icon,
398
+ opt.label
399
+ ] }, opt.value)) }) })
400
+ ] }),
401
+ /* @__PURE__ */ jsx(FilterChipRemoveButton, { label, onRemove })
402
+ ] });
403
+ }
404
+ function TextFilterChip({
405
+ definition,
406
+ value,
407
+ autoOpen,
408
+ onChangeValue,
409
+ onRemove
410
+ }) {
411
+ const { label, placeholder } = definition;
412
+ const text = asTextValue(value);
413
+ const [open, setOpen] = React.useState(!!autoOpen);
414
+ return /* @__PURE__ */ jsxs(ButtonGroup, { children: [
415
+ /* @__PURE__ */ jsx(ButtonGroupText, { children: label }),
416
+ /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
417
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", className: "gap-1 font-normal", children: /* @__PURE__ */ jsx("span", { className: text ? "" : "text-muted-foreground", children: text || "Any" }) }) }),
418
+ /* @__PURE__ */ jsx(PopoverContent, { align: "start", children: /* @__PURE__ */ jsx(
419
+ Input,
420
+ {
421
+ autoFocus: true,
422
+ value: text,
423
+ placeholder: placeholder != null ? placeholder : "Enter a value\u2026",
424
+ onChange: (e) => onChangeValue(e.target.value),
425
+ onKeyDown: (e) => {
426
+ if (e.key === "Enter") setOpen(false);
427
+ }
428
+ }
429
+ ) })
430
+ ] }),
431
+ /* @__PURE__ */ jsx(FilterChipRemoveButton, { label, onRemove })
432
+ ] });
433
+ }
434
+ function MultiTextFilterChip({
435
+ definition,
436
+ value,
437
+ autoOpen,
438
+ onChangeValue,
439
+ onRemove
440
+ }) {
441
+ const { label, placeholder } = definition;
442
+ const tokens = asMultiTextValue(value);
443
+ const [draft, setDraft] = React.useState("");
444
+ const displayLabel = tokens.length === 0 ? "Any" : tokens.length === 1 ? tokens[0] : `${tokens.length} values`;
445
+ function addToken() {
446
+ const token = draft.trim();
447
+ if (!token || tokens.includes(token)) {
448
+ setDraft("");
449
+ return;
450
+ }
451
+ onChangeValue([...tokens, token]);
452
+ setDraft("");
453
+ }
454
+ function removeToken(token) {
455
+ onChangeValue(tokens.filter((t) => t !== token));
456
+ }
457
+ return /* @__PURE__ */ jsxs(ButtonGroup, { children: [
458
+ /* @__PURE__ */ jsx(ButtonGroupText, { children: label }),
459
+ /* @__PURE__ */ jsxs(Popover, { defaultOpen: autoOpen, children: [
460
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", className: "gap-1 font-normal", children: /* @__PURE__ */ jsx("span", { className: tokens.length === 0 ? "text-muted-foreground" : "", children: displayLabel }) }) }),
461
+ /* @__PURE__ */ jsx(PopoverContent, { align: "start", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
462
+ tokens.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: tokens.map((token) => /* @__PURE__ */ jsxs(Badge, { variant: "secondary", className: "gap-1", children: [
463
+ token,
464
+ /* @__PURE__ */ jsx(
465
+ "button",
466
+ {
467
+ type: "button",
468
+ "aria-label": `Remove ${token}`,
469
+ onClick: () => removeToken(token),
470
+ className: "cursor-pointer",
471
+ children: /* @__PURE__ */ jsx(Icon, { name: "xmark", size: "xs" })
472
+ }
473
+ )
474
+ ] }, token)) }),
475
+ /* @__PURE__ */ jsx(
476
+ Input,
477
+ {
478
+ autoFocus: true,
479
+ value: draft,
480
+ placeholder: placeholder != null ? placeholder : "Type a value, press Enter\u2026",
481
+ onChange: (e) => setDraft(e.target.value),
482
+ onKeyDown: (e) => {
483
+ if (e.key === "Enter") {
484
+ e.preventDefault();
485
+ addToken();
486
+ } else if (e.key === "Backspace" && draft === "" && tokens.length > 0) {
487
+ removeToken(tokens[tokens.length - 1]);
488
+ }
489
+ }
490
+ }
491
+ )
492
+ ] }) })
493
+ ] }),
494
+ /* @__PURE__ */ jsx(FilterChipRemoveButton, { label, onRemove })
495
+ ] });
496
+ }
497
+ function DateFilterChip({
498
+ definition,
499
+ value,
500
+ autoOpen,
501
+ onChangeValue,
502
+ onRemove
503
+ }) {
504
+ const { label, includeTime } = definition;
505
+ const { startDate, endDate } = asDateValue(value);
506
+ const showTimeInputs = includeTime != null ? includeTime : hasExplicitTime(startDate) || hasExplicitTime(endDate);
507
+ const displayLabel = !startDate ? "Any" : !endDate || startDate.getTime() === endDate.getTime() ? formatDateLabel(startDate) : `${formatDateLabel(startDate)} \u2013 ${formatDateLabel(endDate)}`;
508
+ function pickRange(range) {
509
+ var _a;
510
+ if (!(range == null ? void 0 : range.from)) {
511
+ onChangeValue({ startDate: null, endDate: null });
512
+ return;
513
+ }
514
+ onChangeValue({ startDate: range.from, endDate: (_a = range.to) != null ? _a : range.from });
515
+ }
516
+ function pickStartTime(time) {
517
+ if (!startDate) return;
518
+ onChangeValue({ startDate: applyTimeToDate(startDate, time), endDate });
519
+ }
520
+ function pickEndTime(time) {
521
+ if (!endDate) return;
522
+ onChangeValue({ startDate, endDate: applyTimeToDate(endDate, time) });
523
+ }
524
+ return /* @__PURE__ */ jsxs(ButtonGroup, { children: [
525
+ /* @__PURE__ */ jsx(ButtonGroupText, { children: label }),
526
+ /* @__PURE__ */ jsxs(Popover, { defaultOpen: autoOpen, children: [
527
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", className: "gap-1 font-normal", children: /* @__PURE__ */ jsx("span", { className: startDate ? "" : "text-muted-foreground", children: displayLabel }) }) }),
528
+ /* @__PURE__ */ jsx(PopoverContent, { align: "start", className: "w-fit", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
529
+ /* @__PURE__ */ jsx(
530
+ Calendar,
531
+ {
532
+ mode: "range",
533
+ selected: { from: startDate != null ? startDate : void 0, to: endDate != null ? endDate : void 0 },
534
+ onSelect: pickRange
535
+ }
536
+ ),
537
+ showTimeInputs && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
538
+ /* @__PURE__ */ jsxs("div", { className: "flex w-full items-center gap-2", children: [
539
+ /* @__PURE__ */ jsx(
540
+ Label,
541
+ {
542
+ htmlFor: `${definition.key}-start-time`,
543
+ className: "text-body-sm text-muted-foreground w-10 shrink-0",
544
+ children: "Start"
545
+ }
546
+ ),
547
+ /* @__PURE__ */ jsx(
548
+ Input,
549
+ {
550
+ id: `${definition.key}-start-time`,
551
+ type: "time",
552
+ className: "w-full",
553
+ value: formatTimeForInput(startDate),
554
+ onChange: (e) => pickStartTime(e.target.value),
555
+ disabled: !startDate
556
+ }
557
+ )
558
+ ] }),
559
+ /* @__PURE__ */ jsxs("div", { className: "flex w-full items-center gap-2", children: [
560
+ /* @__PURE__ */ jsx(
561
+ Label,
562
+ {
563
+ htmlFor: `${definition.key}-end-time`,
564
+ className: "text-body-sm text-muted-foreground w-10 shrink-0",
565
+ children: "End"
566
+ }
567
+ ),
568
+ /* @__PURE__ */ jsx(
569
+ Input,
570
+ {
571
+ id: `${definition.key}-end-time`,
572
+ type: "time",
573
+ className: "w-full",
574
+ value: formatTimeForInput(endDate),
575
+ onChange: (e) => pickEndTime(e.target.value),
576
+ disabled: !endDate
577
+ }
578
+ )
579
+ ] })
580
+ ] })
581
+ ] }) })
582
+ ] }),
583
+ /* @__PURE__ */ jsx(FilterChipRemoveButton, { label, onRemove })
584
+ ] });
585
+ }
586
+ export {
587
+ FilterBar,
588
+ FilterChip
589
+ };
590
+ //# sourceMappingURL=filter-bar.js.map