@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.
- package/LICENSE +21 -0
- package/README.md +58 -0
- package/dist/cel-filter-bar.d.ts +187 -0
- package/dist/cel-filter-bar.js +1850 -0
- package/dist/cel-filter-bar.js.map +1 -0
- package/dist/chart.d.ts +52 -0
- package/dist/chart.js +332 -0
- package/dist/chart.js.map +1 -0
- package/dist/chat-interface.d.ts +143 -0
- package/dist/chat-interface.js +676 -0
- package/dist/chat-interface.js.map +1 -0
- package/dist/code-block.d.ts +21 -0
- package/dist/code-block.js +201 -0
- package/dist/code-block.js.map +1 -0
- package/dist/data-table.d.ts +505 -0
- package/dist/data-table.js +4725 -0
- package/dist/data-table.js.map +1 -0
- package/dist/filter-bar.d.ts +80 -0
- package/dist/filter-bar.js +590 -0
- package/dist/filter-bar.js.map +1 -0
- package/dist/floating-widget.d.ts +36 -0
- package/dist/floating-widget.js +218 -0
- package/dist/floating-widget.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +6217 -0
- package/dist/index.js.map +1 -0
- package/dist/page-header.d.ts +12 -0
- package/dist/page-header.js +120 -0
- package/dist/page-header.js.map +1 -0
- package/dist/relative-time.d.ts +60 -0
- package/dist/relative-time.js +328 -0
- package/dist/relative-time.js.map +1 -0
- package/dist/section-header.d.ts +11 -0
- package/dist/section-header.js +123 -0
- package/dist/section-header.js.map +1 -0
- package/dist/section-nav.d.ts +50 -0
- package/dist/section-nav.js +143 -0
- package/dist/section-nav.js.map +1 -0
- package/dist/timeline.d.ts +8 -0
- package/dist/timeline.js +95 -0
- package/dist/timeline.js.map +1 -0
- package/dist/types-BULiU2qC.d.ts +170 -0
- 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
|