@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,1850 @@
|
|
|
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/cel-filter-bar/cel-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/hooks/use-mounted.ts
|
|
71
|
+
import { useSyncExternalStore } from "react";
|
|
72
|
+
function subscribe() {
|
|
73
|
+
return () => {
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function useMounted() {
|
|
77
|
+
return useSyncExternalStore(
|
|
78
|
+
subscribe,
|
|
79
|
+
() => true,
|
|
80
|
+
() => false
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/cel-filter-bar/cel-filter-bar.tsx
|
|
85
|
+
import { Badge } from "@upbound/monarch-core";
|
|
86
|
+
import { Button } from "@upbound/monarch-core";
|
|
87
|
+
import { ButtonGroup, ButtonGroupText } from "@upbound/monarch-core";
|
|
88
|
+
import { Calendar } from "@upbound/monarch-core";
|
|
89
|
+
import {
|
|
90
|
+
Combobox,
|
|
91
|
+
ComboboxChip,
|
|
92
|
+
ComboboxClearAll,
|
|
93
|
+
ComboboxCollection,
|
|
94
|
+
ComboboxContent,
|
|
95
|
+
ComboboxEmpty,
|
|
96
|
+
ComboboxGroup,
|
|
97
|
+
ComboboxInput,
|
|
98
|
+
ComboboxItem,
|
|
99
|
+
ComboboxLabel,
|
|
100
|
+
ComboboxList,
|
|
101
|
+
ComboboxSelectedChips,
|
|
102
|
+
ComboboxSeparator,
|
|
103
|
+
ComboboxTrigger,
|
|
104
|
+
ComboboxValue
|
|
105
|
+
} from "@upbound/monarch-core";
|
|
106
|
+
import { Input } from "@upbound/monarch-core";
|
|
107
|
+
import { InputGroup, InputGroupAddon, InputGroupInput, InputGroupText } from "@upbound/monarch-core";
|
|
108
|
+
import { Label } from "@upbound/monarch-core";
|
|
109
|
+
import { Popover, PopoverContent, PopoverTrigger } from "@upbound/monarch-core";
|
|
110
|
+
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@upbound/monarch-core";
|
|
111
|
+
|
|
112
|
+
// src/hooks/use-cel-filter-editor-state.ts
|
|
113
|
+
import { useCallback, useRef, useState } from "react";
|
|
114
|
+
|
|
115
|
+
// src/lib/cel-filter/constants.ts
|
|
116
|
+
var RELATIVE_DURATION_PRESET_VALUES = [
|
|
117
|
+
"24h",
|
|
118
|
+
"168h",
|
|
119
|
+
"720h"
|
|
120
|
+
];
|
|
121
|
+
var RELATIVE_DURATION_PRESETS = new Set(RELATIVE_DURATION_PRESET_VALUES);
|
|
122
|
+
function isRelativeDurationPreset(value) {
|
|
123
|
+
return RELATIVE_DURATION_PRESETS.has(value);
|
|
124
|
+
}
|
|
125
|
+
var MAX_NESTING_DEPTH = 50;
|
|
126
|
+
var MAX_REGEX_LEN = 1e3;
|
|
127
|
+
var ADD_FILTER_POPOVER_SUPPRESS_MS = 500;
|
|
128
|
+
|
|
129
|
+
// src/hooks/use-cel-filter-editor-state.ts
|
|
130
|
+
function useCelFilterEditorState(initialCommittedIds = []) {
|
|
131
|
+
const [editingConditionId, setEditingConditionId] = useState(null);
|
|
132
|
+
const [committedConditionIds, setCommittedConditionIds] = useState(() => new Set(initialCommittedIds));
|
|
133
|
+
const [activeSurface, setActiveSurface] = useState(null);
|
|
134
|
+
const suppressCloseUntilRef = useRef(null);
|
|
135
|
+
const segmentSwitchRef = useRef(false);
|
|
136
|
+
const isDismissSuppressed = useCallback((id) => {
|
|
137
|
+
const entry = suppressCloseUntilRef.current;
|
|
138
|
+
return entry !== null && entry.id === id && Date.now() < entry.until;
|
|
139
|
+
}, []);
|
|
140
|
+
const commitCondition = useCallback((id) => {
|
|
141
|
+
setCommittedConditionIds((prev) => {
|
|
142
|
+
if (prev.has(id)) {
|
|
143
|
+
return prev;
|
|
144
|
+
}
|
|
145
|
+
const next = new Set(prev);
|
|
146
|
+
next.add(id);
|
|
147
|
+
return next;
|
|
148
|
+
});
|
|
149
|
+
setEditingConditionId((current) => current === id ? null : current);
|
|
150
|
+
setActiveSurface(null);
|
|
151
|
+
segmentSwitchRef.current = false;
|
|
152
|
+
}, []);
|
|
153
|
+
const beginEditing = useCallback((id, surface = "value") => {
|
|
154
|
+
setEditingConditionId(id);
|
|
155
|
+
setActiveSurface(surface);
|
|
156
|
+
}, []);
|
|
157
|
+
const openSurface = useCallback(
|
|
158
|
+
(id, surface) => {
|
|
159
|
+
if (editingConditionId !== id) {
|
|
160
|
+
beginEditing(id, surface);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
setActiveSurface(surface);
|
|
164
|
+
},
|
|
165
|
+
[beginEditing, editingConditionId]
|
|
166
|
+
);
|
|
167
|
+
const closeSurface = useCallback(
|
|
168
|
+
(id) => {
|
|
169
|
+
if (editingConditionId === id) {
|
|
170
|
+
setActiveSurface(null);
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
[editingConditionId]
|
|
174
|
+
);
|
|
175
|
+
const handlePopoverDismiss = useCallback(
|
|
176
|
+
(id) => {
|
|
177
|
+
if (segmentSwitchRef.current) {
|
|
178
|
+
segmentSwitchRef.current = false;
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (isDismissSuppressed(id)) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
suppressCloseUntilRef.current = null;
|
|
185
|
+
commitCondition(id);
|
|
186
|
+
},
|
|
187
|
+
[commitCondition, isDismissSuppressed]
|
|
188
|
+
);
|
|
189
|
+
const toggleSurface = useCallback(
|
|
190
|
+
(id, surface) => {
|
|
191
|
+
const isDraft = editingConditionId === id;
|
|
192
|
+
const popoverVisible = isDraft && activeSurface !== null;
|
|
193
|
+
if (isDraft && activeSurface === surface && popoverVisible) {
|
|
194
|
+
closeSurface(id);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (popoverVisible && activeSurface !== surface) {
|
|
198
|
+
segmentSwitchRef.current = true;
|
|
199
|
+
}
|
|
200
|
+
openSurface(id, surface);
|
|
201
|
+
},
|
|
202
|
+
[activeSurface, closeSurface, editingConditionId, openSurface]
|
|
203
|
+
);
|
|
204
|
+
const suppressDismissFor = useCallback((id, ms = ADD_FILTER_POPOVER_SUPPRESS_MS) => {
|
|
205
|
+
suppressCloseUntilRef.current = {
|
|
206
|
+
id,
|
|
207
|
+
until: Date.now() + ms
|
|
208
|
+
};
|
|
209
|
+
}, []);
|
|
210
|
+
const removeCondition = useCallback(
|
|
211
|
+
(id) => {
|
|
212
|
+
var _a;
|
|
213
|
+
if (((_a = suppressCloseUntilRef.current) == null ? void 0 : _a.id) === id) {
|
|
214
|
+
suppressCloseUntilRef.current = null;
|
|
215
|
+
}
|
|
216
|
+
setCommittedConditionIds((prev) => {
|
|
217
|
+
if (!prev.has(id)) {
|
|
218
|
+
return prev;
|
|
219
|
+
}
|
|
220
|
+
const next = new Set(prev);
|
|
221
|
+
next.delete(id);
|
|
222
|
+
return next;
|
|
223
|
+
});
|
|
224
|
+
setEditingConditionId((current) => current === id ? null : current);
|
|
225
|
+
if (editingConditionId === id) {
|
|
226
|
+
setActiveSurface(null);
|
|
227
|
+
segmentSwitchRef.current = false;
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
[editingConditionId]
|
|
231
|
+
);
|
|
232
|
+
const clearSuppress = useCallback(() => {
|
|
233
|
+
suppressCloseUntilRef.current = null;
|
|
234
|
+
}, []);
|
|
235
|
+
const getController = useCallback(
|
|
236
|
+
(conditionId) => {
|
|
237
|
+
const isDraft = editingConditionId === conditionId;
|
|
238
|
+
const surface = isDraft ? activeSurface : null;
|
|
239
|
+
return {
|
|
240
|
+
isDraft,
|
|
241
|
+
activeSurface: surface,
|
|
242
|
+
popoverVisible: isDraft && activeSurface !== null,
|
|
243
|
+
isDismissSuppressed: () => isDismissSuppressed(conditionId),
|
|
244
|
+
beginEditing: () => beginEditing(conditionId),
|
|
245
|
+
openSurface: (s) => openSurface(conditionId, s),
|
|
246
|
+
closeSurface: () => closeSurface(conditionId),
|
|
247
|
+
commit: () => {
|
|
248
|
+
suppressCloseUntilRef.current = null;
|
|
249
|
+
commitCondition(conditionId);
|
|
250
|
+
},
|
|
251
|
+
handlePopoverDismiss: () => handlePopoverDismiss(conditionId),
|
|
252
|
+
toggleSurface: (s) => toggleSurface(conditionId, s)
|
|
253
|
+
};
|
|
254
|
+
},
|
|
255
|
+
[
|
|
256
|
+
activeSurface,
|
|
257
|
+
beginEditing,
|
|
258
|
+
closeSurface,
|
|
259
|
+
commitCondition,
|
|
260
|
+
editingConditionId,
|
|
261
|
+
handlePopoverDismiss,
|
|
262
|
+
isDismissSuppressed,
|
|
263
|
+
openSurface,
|
|
264
|
+
toggleSurface
|
|
265
|
+
]
|
|
266
|
+
);
|
|
267
|
+
return {
|
|
268
|
+
editingConditionId,
|
|
269
|
+
activeSurface,
|
|
270
|
+
committedConditionIds,
|
|
271
|
+
getController,
|
|
272
|
+
beginEditing,
|
|
273
|
+
suppressDismissFor,
|
|
274
|
+
removeCondition,
|
|
275
|
+
clearSuppress
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// src/lib/cel-filter/date-utils.ts
|
|
280
|
+
var CALENDAR_DAY_PATTERN = /^(\d{4}-\d{2}-\d{2})/;
|
|
281
|
+
var MINUTE_PATTERN = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2})/;
|
|
282
|
+
var TIME_OF_DAY_PATTERN = /^\d{4}-\d{2}-\d{2}T(\d{2}:\d{2})/;
|
|
283
|
+
function calendarDayStartInstant(instant) {
|
|
284
|
+
const match = CALENDAR_DAY_PATTERN.exec(instant);
|
|
285
|
+
return match ? `${match[1]}T00:00:00.000Z` : instant;
|
|
286
|
+
}
|
|
287
|
+
function inclusiveEndInstant(instant, granularity) {
|
|
288
|
+
if (granularity === "minute") {
|
|
289
|
+
const minuteMatch = MINUTE_PATTERN.exec(instant);
|
|
290
|
+
return minuteMatch ? `${minuteMatch[1]}:59.999Z` : instant;
|
|
291
|
+
}
|
|
292
|
+
const match = CALENDAR_DAY_PATTERN.exec(instant);
|
|
293
|
+
return match ? `${match[1]}T23:59:59.999Z` : instant;
|
|
294
|
+
}
|
|
295
|
+
function isRealCalendarDay(instant) {
|
|
296
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})/.exec(instant);
|
|
297
|
+
if (!match) {
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
const [year, month, day] = [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
301
|
+
const date = new Date(Date.UTC(year, month - 1, day));
|
|
302
|
+
return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day;
|
|
303
|
+
}
|
|
304
|
+
function readTimeOfDay(instant) {
|
|
305
|
+
if (!instant) {
|
|
306
|
+
return "";
|
|
307
|
+
}
|
|
308
|
+
const match = TIME_OF_DAY_PATTERN.exec(instant);
|
|
309
|
+
return match ? match[1] : "";
|
|
310
|
+
}
|
|
311
|
+
function withTimeOfDay(instant, timeOfDay) {
|
|
312
|
+
const day = CALENDAR_DAY_PATTERN.exec(instant);
|
|
313
|
+
const time = /^(\d{2}):(\d{2})/.exec(timeOfDay);
|
|
314
|
+
if (!day || !time) {
|
|
315
|
+
return instant;
|
|
316
|
+
}
|
|
317
|
+
return `${day[1]}T${time[1]}:${time[2]}:00.000Z`;
|
|
318
|
+
}
|
|
319
|
+
function formatCalendarDateUtc(date, boundary) {
|
|
320
|
+
const year = date.getFullYear();
|
|
321
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
322
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
323
|
+
const time = boundary === "end" ? "T23:59:59.999Z" : "T00:00:00.000Z";
|
|
324
|
+
return `${year}-${month}-${day}${time}`;
|
|
325
|
+
}
|
|
326
|
+
function parseCalendarDate(value) {
|
|
327
|
+
if (!value) {
|
|
328
|
+
return void 0;
|
|
329
|
+
}
|
|
330
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})/.exec(value);
|
|
331
|
+
if (!match) {
|
|
332
|
+
return void 0;
|
|
333
|
+
}
|
|
334
|
+
return new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]));
|
|
335
|
+
}
|
|
336
|
+
function formatInstantLabel(instant, { withTime = false, locale } = {}) {
|
|
337
|
+
const date = parseCalendarDate(instant);
|
|
338
|
+
if (!date) {
|
|
339
|
+
return "";
|
|
340
|
+
}
|
|
341
|
+
const dateLabel = date.toLocaleDateString(locale);
|
|
342
|
+
const timeOfDay = withTime ? readTimeOfDay(instant) : "";
|
|
343
|
+
return timeOfDay ? `${dateLabel} ${timeOfDay} UTC` : dateLabel;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// src/lib/cel-filter/types.ts
|
|
347
|
+
var isCelFilterGroup = (node) => node.type === "group";
|
|
348
|
+
var isCelFilterCondition = (node) => node.type === "condition";
|
|
349
|
+
var isCheckboxCelValue = (value) => Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
350
|
+
var isMultiTextCelValue = (value) => typeof value === "object" && value !== null && "tokens" in value;
|
|
351
|
+
var isDateCelValue = (value) => {
|
|
352
|
+
if (typeof value !== "object" || value === null) {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
if ("tokens" in value || "key" in value) {
|
|
356
|
+
return false;
|
|
357
|
+
}
|
|
358
|
+
return "preset" in value || "instant" in value || "range" in value || Object.keys(value).length === 0;
|
|
359
|
+
};
|
|
360
|
+
var isMapKeyCelValue = (value) => typeof value === "object" && value !== null && "key" in value;
|
|
361
|
+
|
|
362
|
+
// src/lib/cel-filter/value-utils.ts
|
|
363
|
+
function isRelativeDateCelValue(value) {
|
|
364
|
+
return value.preset != null;
|
|
365
|
+
}
|
|
366
|
+
function readDateCelValue(value) {
|
|
367
|
+
return isDateCelValue(value) ? value : {};
|
|
368
|
+
}
|
|
369
|
+
function readMapKeyCelValue(value) {
|
|
370
|
+
return isMapKeyCelValue(value) ? value : { key: "" };
|
|
371
|
+
}
|
|
372
|
+
function readMultiTextCelValue(value) {
|
|
373
|
+
return isMultiTextCelValue(value) ? value : { tokens: [] };
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// src/lib/cel-filter/compile/escape-cel-string.ts
|
|
377
|
+
function escapeCelString(input) {
|
|
378
|
+
let out = "";
|
|
379
|
+
for (const char of input) {
|
|
380
|
+
switch (char) {
|
|
381
|
+
case "\\":
|
|
382
|
+
out += "\\\\";
|
|
383
|
+
break;
|
|
384
|
+
case '"':
|
|
385
|
+
out += '\\"';
|
|
386
|
+
break;
|
|
387
|
+
case "\n":
|
|
388
|
+
out += "\\n";
|
|
389
|
+
break;
|
|
390
|
+
case "\r":
|
|
391
|
+
out += "\\r";
|
|
392
|
+
break;
|
|
393
|
+
case " ":
|
|
394
|
+
out += "\\t";
|
|
395
|
+
break;
|
|
396
|
+
case "\f":
|
|
397
|
+
out += "\\f";
|
|
398
|
+
break;
|
|
399
|
+
case "\v":
|
|
400
|
+
out += "\\v";
|
|
401
|
+
break;
|
|
402
|
+
case "\b":
|
|
403
|
+
out += "\\b";
|
|
404
|
+
break;
|
|
405
|
+
case "\0":
|
|
406
|
+
out += "\\0";
|
|
407
|
+
break;
|
|
408
|
+
default:
|
|
409
|
+
out += char;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return out;
|
|
413
|
+
}
|
|
414
|
+
function celStringLiteral(input) {
|
|
415
|
+
return `"${escapeCelString(input)}"`;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// src/lib/cel-filter/compile/operators.ts
|
|
419
|
+
var RESERVED_COLUMN_CEL_IDENTIFIERS = {
|
|
420
|
+
namespace: "__namespace__"
|
|
421
|
+
};
|
|
422
|
+
var CEL_IDENTIFIER_TO_COLUMN_ID = Object.fromEntries(
|
|
423
|
+
Object.entries(RESERVED_COLUMN_CEL_IDENTIFIERS).map(([columnId, celIdentifier]) => [celIdentifier, columnId])
|
|
424
|
+
);
|
|
425
|
+
var ENUM_OPERATORS = ["is", "is_not"];
|
|
426
|
+
var TEXT_OPERATORS = [
|
|
427
|
+
"is",
|
|
428
|
+
"is_not",
|
|
429
|
+
"contains",
|
|
430
|
+
"does_not_contain",
|
|
431
|
+
"starts_with",
|
|
432
|
+
"ends_with",
|
|
433
|
+
"matches"
|
|
434
|
+
];
|
|
435
|
+
var DATE_OPERATORS = [
|
|
436
|
+
"is",
|
|
437
|
+
"is_not",
|
|
438
|
+
"is_before",
|
|
439
|
+
"is_after",
|
|
440
|
+
"is_on_or_before",
|
|
441
|
+
"is_on_or_after",
|
|
442
|
+
"is_between",
|
|
443
|
+
"is_not_between"
|
|
444
|
+
];
|
|
445
|
+
var MAP_OPERATORS = ["key_exists", "key_does_not_exist", "has_value", "does_not_have_value"];
|
|
446
|
+
var DATE_RELATIVE_OPERATORS = ["is_before", "is_after", "is_on_or_before", "is_on_or_after"];
|
|
447
|
+
var DATE_WINDOW_OPERATORS = ["is", "is_not"];
|
|
448
|
+
var DATE_RANGE_OPERATORS = ["is_between", "is_not_between"];
|
|
449
|
+
var DATE_END_BOUNDARY_OPERATORS = ["is_on_or_before", "is_after"];
|
|
450
|
+
var MAP_VALUE_OPERATORS = ["has_value", "does_not_have_value"];
|
|
451
|
+
function getAllowedOperatorsForShape(shape) {
|
|
452
|
+
switch (shape) {
|
|
453
|
+
case "checkbox":
|
|
454
|
+
case "radio":
|
|
455
|
+
case "static":
|
|
456
|
+
return ENUM_OPERATORS;
|
|
457
|
+
case "text":
|
|
458
|
+
case "multiText":
|
|
459
|
+
return TEXT_OPERATORS;
|
|
460
|
+
case "date":
|
|
461
|
+
return DATE_OPERATORS;
|
|
462
|
+
case "map":
|
|
463
|
+
return MAP_OPERATORS;
|
|
464
|
+
default:
|
|
465
|
+
return [];
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
function getAllowedOperators(columnDef) {
|
|
469
|
+
if (!columnDef) {
|
|
470
|
+
return [];
|
|
471
|
+
}
|
|
472
|
+
if (columnDef.allowedOperators) {
|
|
473
|
+
return columnDef.allowedOperators;
|
|
474
|
+
}
|
|
475
|
+
const shape = columnDef.filterType === "keyValue" ? "map" : columnDef.filterType;
|
|
476
|
+
return getAllowedOperatorsForShape(shape);
|
|
477
|
+
}
|
|
478
|
+
function isDateRelativeOperator(operator) {
|
|
479
|
+
return DATE_RELATIVE_OPERATORS.includes(operator);
|
|
480
|
+
}
|
|
481
|
+
function isDateWindowOperator(operator) {
|
|
482
|
+
return DATE_WINDOW_OPERATORS.includes(operator);
|
|
483
|
+
}
|
|
484
|
+
function isDateRangeOperator(operator) {
|
|
485
|
+
return DATE_RANGE_OPERATORS.includes(operator);
|
|
486
|
+
}
|
|
487
|
+
function dateBoundaryForOperator(operator) {
|
|
488
|
+
return DATE_END_BOUNDARY_OPERATORS.includes(operator) ? "end" : "start";
|
|
489
|
+
}
|
|
490
|
+
function resolveColumnDefinition(condition, schema) {
|
|
491
|
+
if (!condition.columnId) {
|
|
492
|
+
return void 0;
|
|
493
|
+
}
|
|
494
|
+
return schema.columns.find((column) => column.id === condition.columnId);
|
|
495
|
+
}
|
|
496
|
+
function resolveFilterShape(condition, schema) {
|
|
497
|
+
const columnDef = resolveColumnDefinition(condition, schema);
|
|
498
|
+
if (!columnDef) {
|
|
499
|
+
return void 0;
|
|
500
|
+
}
|
|
501
|
+
if (columnDef.filterType === "keyValue") {
|
|
502
|
+
return "map";
|
|
503
|
+
}
|
|
504
|
+
if (columnDef.filterType === "static") {
|
|
505
|
+
return Array.isArray(condition.value) ? "checkbox" : "radio";
|
|
506
|
+
}
|
|
507
|
+
return columnDef.filterType;
|
|
508
|
+
}
|
|
509
|
+
var OPERATOR_LABELS = {
|
|
510
|
+
is: "is",
|
|
511
|
+
is_not: "is not",
|
|
512
|
+
contains: "contains",
|
|
513
|
+
does_not_contain: "does not contain",
|
|
514
|
+
starts_with: "starts with",
|
|
515
|
+
ends_with: "ends with",
|
|
516
|
+
matches: "matches",
|
|
517
|
+
is_before: "is before",
|
|
518
|
+
is_after: "is after",
|
|
519
|
+
is_on_or_before: "is on or before",
|
|
520
|
+
is_on_or_after: "is on or after",
|
|
521
|
+
is_between: "is between",
|
|
522
|
+
is_not_between: "is not between",
|
|
523
|
+
key_exists: "key exists",
|
|
524
|
+
key_does_not_exist: "key does not exist",
|
|
525
|
+
has_value: "has value",
|
|
526
|
+
does_not_have_value: "does not have value"
|
|
527
|
+
};
|
|
528
|
+
function getOperatorLabel(operator) {
|
|
529
|
+
return OPERATOR_LABELS[operator];
|
|
530
|
+
}
|
|
531
|
+
function getQuantifierLabel(quantifier) {
|
|
532
|
+
return quantifier === "all" ? "all of" : "any of";
|
|
533
|
+
}
|
|
534
|
+
var SINGLE_INSTANCE_FILTER_TYPES = /* @__PURE__ */ new Set(["checkbox", "radio", "static"]);
|
|
535
|
+
function allowsMultipleConditions(filterType) {
|
|
536
|
+
return !SINGLE_INSTANCE_FILTER_TYPES.has(filterType);
|
|
537
|
+
}
|
|
538
|
+
function getDefaultOperator(filterType) {
|
|
539
|
+
switch (filterType) {
|
|
540
|
+
case "checkbox":
|
|
541
|
+
case "radio":
|
|
542
|
+
case "text":
|
|
543
|
+
case "static":
|
|
544
|
+
return "is";
|
|
545
|
+
case "multiText":
|
|
546
|
+
return "contains";
|
|
547
|
+
case "date":
|
|
548
|
+
return "is";
|
|
549
|
+
case "keyValue":
|
|
550
|
+
return "key_exists";
|
|
551
|
+
default:
|
|
552
|
+
return "is";
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
function applyOperatorChange(condition, newOperator, shape) {
|
|
556
|
+
const next = __spreadProps(__spreadValues({}, condition), { operator: newOperator });
|
|
557
|
+
const oldOperator = condition.operator;
|
|
558
|
+
if (shape === "date") {
|
|
559
|
+
const value = condition.value;
|
|
560
|
+
const isRelative = isDateCelValue(value) && isRelativeDateCelValue(value);
|
|
561
|
+
const changesValueShape = isDateRangeOperator(oldOperator) !== isDateRangeOperator(newOperator);
|
|
562
|
+
const relativeNeedsClear = isRelative && (newOperator === "is" || newOperator === "is_not");
|
|
563
|
+
if (changesValueShape || relativeNeedsClear) {
|
|
564
|
+
next.value = void 0;
|
|
565
|
+
return next;
|
|
566
|
+
}
|
|
567
|
+
if (isDateCelValue(value) && value.hasTime && isDateWindowOperator(newOperator)) {
|
|
568
|
+
const _a = value, { hasTime: _hasTime } = _a, dayOnly = __objRest(_a, ["hasTime"]);
|
|
569
|
+
next.value = dayOnly;
|
|
570
|
+
}
|
|
571
|
+
return next;
|
|
572
|
+
}
|
|
573
|
+
if (shape === "map") {
|
|
574
|
+
const value = condition.value;
|
|
575
|
+
if (!isMapKeyCelValue(value)) {
|
|
576
|
+
return next;
|
|
577
|
+
}
|
|
578
|
+
const bothValueOps = MAP_VALUE_OPERATORS.includes(oldOperator) && MAP_VALUE_OPERATORS.includes(newOperator);
|
|
579
|
+
next.value = bothValueOps ? { key: value.key, value: value.value } : { key: value.key };
|
|
580
|
+
return next;
|
|
581
|
+
}
|
|
582
|
+
return next;
|
|
583
|
+
}
|
|
584
|
+
var atomic = (expression) => ({ expression, needsGrouping: false });
|
|
585
|
+
function buildConditionExpression(condition, shape, columnDef) {
|
|
586
|
+
var _a, _b, _c, _d, _e, _f;
|
|
587
|
+
if (shape === "map") {
|
|
588
|
+
return buildMapExpression(condition.columnId, condition.operator, condition.value);
|
|
589
|
+
}
|
|
590
|
+
const columnId = (_b = (_a = columnDef == null ? void 0 : columnDef.id) != null ? _a : condition.columnId) != null ? _b : "";
|
|
591
|
+
const column = (_c = RESERVED_COLUMN_CEL_IDENTIFIERS[columnId]) != null ? _c : columnId;
|
|
592
|
+
switch (shape) {
|
|
593
|
+
case "checkbox":
|
|
594
|
+
return buildCheckboxExpression(column, condition.operator, (_d = condition.value) != null ? _d : []);
|
|
595
|
+
case "radio":
|
|
596
|
+
return buildRadioExpression(column, condition.operator, String((_e = condition.value) != null ? _e : ""));
|
|
597
|
+
case "text":
|
|
598
|
+
return atomic(buildTextTokenFragment(column, condition.operator, String((_f = condition.value) != null ? _f : "")));
|
|
599
|
+
case "multiText":
|
|
600
|
+
return buildMultiTextExpression(column, condition.operator, condition.value);
|
|
601
|
+
case "date":
|
|
602
|
+
return atomic(buildDateExpression(column, condition.operator, condition.value));
|
|
603
|
+
default:
|
|
604
|
+
return atomic("");
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
function buildCheckboxExpression(column, operator, values) {
|
|
608
|
+
const list = `${column} in [${values.map(celStringLiteral).join(", ")}]`;
|
|
609
|
+
return atomic(operator === "is_not" ? `!(${list})` : list);
|
|
610
|
+
}
|
|
611
|
+
function buildRadioExpression(column, operator, value) {
|
|
612
|
+
const literal = celStringLiteral(value);
|
|
613
|
+
return atomic(operator === "is_not" ? `${column} != ${literal}` : `${column} == ${literal}`);
|
|
614
|
+
}
|
|
615
|
+
function buildTextTokenFragment(column, operator, value, forcePositive = false) {
|
|
616
|
+
const literal = celStringLiteral(value);
|
|
617
|
+
switch (operator) {
|
|
618
|
+
case "is":
|
|
619
|
+
return `${column} == ${literal}`;
|
|
620
|
+
case "is_not":
|
|
621
|
+
return forcePositive ? `${column} == ${literal}` : `${column} != ${literal}`;
|
|
622
|
+
case "contains":
|
|
623
|
+
return `${column}.contains(${literal})`;
|
|
624
|
+
case "does_not_contain":
|
|
625
|
+
return forcePositive ? `${column}.contains(${literal})` : `!${column}.contains(${literal})`;
|
|
626
|
+
case "starts_with":
|
|
627
|
+
return `${column}.startsWith(${literal})`;
|
|
628
|
+
case "ends_with":
|
|
629
|
+
return `${column}.endsWith(${literal})`;
|
|
630
|
+
case "matches":
|
|
631
|
+
return `${column}.matches(${literal})`;
|
|
632
|
+
default:
|
|
633
|
+
return "";
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
function isNegatedTextOperator(operator) {
|
|
637
|
+
return operator === "is_not" || operator === "does_not_contain";
|
|
638
|
+
}
|
|
639
|
+
function buildMultiTextExpression(column, operator, value) {
|
|
640
|
+
const tokens = value.tokens;
|
|
641
|
+
if (tokens.length === 1) {
|
|
642
|
+
return atomic(buildTextTokenFragment(column, operator, tokens[0]));
|
|
643
|
+
}
|
|
644
|
+
const connective = value.quantifier === "all" ? " && " : " || ";
|
|
645
|
+
const joined = tokens.map((token) => buildTextTokenFragment(column, operator, token, true)).join(connective);
|
|
646
|
+
if (isNegatedTextOperator(operator)) {
|
|
647
|
+
return atomic(`!(${joined})`);
|
|
648
|
+
}
|
|
649
|
+
return { expression: joined, needsGrouping: value.quantifier !== "all" };
|
|
650
|
+
}
|
|
651
|
+
var DATE_RELATIVE_COMPARATOR_SYMBOLS = {
|
|
652
|
+
is_before: "<",
|
|
653
|
+
is_after: ">",
|
|
654
|
+
is_on_or_before: "<=",
|
|
655
|
+
is_on_or_after: ">="
|
|
656
|
+
};
|
|
657
|
+
function buildCalendarDayRange(column, startInstant, endInstant) {
|
|
658
|
+
const start = `timestamp(${celStringLiteral(startInstant)})`;
|
|
659
|
+
const end = `timestamp(${celStringLiteral(endInstant)})`;
|
|
660
|
+
return `${column} >= ${start} && ${column} <= ${end}`;
|
|
661
|
+
}
|
|
662
|
+
function buildDateExpression(column, operator, value) {
|
|
663
|
+
var _a, _b, _c;
|
|
664
|
+
if (isRelativeDateCelValue(value)) {
|
|
665
|
+
const durationStr = (_a = value.preset) != null ? _a : "";
|
|
666
|
+
if (!durationStr) {
|
|
667
|
+
return "";
|
|
668
|
+
}
|
|
669
|
+
const relativeSymbol = (_b = DATE_RELATIVE_COMPARATOR_SYMBOLS[operator]) != null ? _b : ">";
|
|
670
|
+
return `${column} ${relativeSymbol} now() - duration(${celStringLiteral(durationStr)})`;
|
|
671
|
+
}
|
|
672
|
+
if (isDateRangeOperator(operator) && value.range) {
|
|
673
|
+
const range = buildCalendarDayRange(column, value.range.start, value.range.end);
|
|
674
|
+
return operator === "is_not_between" ? `!(${range})` : range;
|
|
675
|
+
}
|
|
676
|
+
const startInstant = (_c = value.instant) != null ? _c : "";
|
|
677
|
+
const granularity = value.hasTime && !isDateWindowOperator(operator) ? "minute" : "day";
|
|
678
|
+
const endInstant = inclusiveEndInstant(startInstant, granularity);
|
|
679
|
+
const startTimestamp = `timestamp(${celStringLiteral(startInstant)})`;
|
|
680
|
+
const endTimestamp = `timestamp(${celStringLiteral(endInstant)})`;
|
|
681
|
+
switch (operator) {
|
|
682
|
+
case "is":
|
|
683
|
+
return buildCalendarDayRange(column, startInstant, endInstant);
|
|
684
|
+
case "is_not":
|
|
685
|
+
return `!(${buildCalendarDayRange(column, startInstant, endInstant)})`;
|
|
686
|
+
case "is_before":
|
|
687
|
+
return `${column} < ${startTimestamp}`;
|
|
688
|
+
case "is_on_or_before":
|
|
689
|
+
return `${column} <= ${endTimestamp}`;
|
|
690
|
+
case "is_after":
|
|
691
|
+
return `${column} > ${endTimestamp}`;
|
|
692
|
+
case "is_on_or_after":
|
|
693
|
+
return `${column} >= ${startTimestamp}`;
|
|
694
|
+
default:
|
|
695
|
+
return `${column} == ${startTimestamp}`;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
function buildMapExpression(columnId, operator, value) {
|
|
699
|
+
var _a, _b, _c;
|
|
700
|
+
const column = (_a = RESERVED_COLUMN_CEL_IDENTIFIERS[columnId]) != null ? _a : columnId;
|
|
701
|
+
const keyLiteral = celStringLiteral(value.key);
|
|
702
|
+
switch (operator) {
|
|
703
|
+
case "key_exists":
|
|
704
|
+
return atomic(`${keyLiteral} in ${column}`);
|
|
705
|
+
case "key_does_not_exist":
|
|
706
|
+
return atomic(`!(${keyLiteral} in ${column})`);
|
|
707
|
+
case "has_value":
|
|
708
|
+
return atomic(`${column}[${keyLiteral}] == ${celStringLiteral((_b = value.value) != null ? _b : "")}`);
|
|
709
|
+
case "does_not_have_value":
|
|
710
|
+
return atomic(`${column}[${keyLiteral}] != ${celStringLiteral((_c = value.value) != null ? _c : "")}`);
|
|
711
|
+
default:
|
|
712
|
+
return atomic("");
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
function denormalizeColumnIdWithReservedNames(columnId) {
|
|
716
|
+
var _a;
|
|
717
|
+
return (_a = CEL_IDENTIFIER_TO_COLUMN_ID[columnId]) != null ? _a : columnId;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// src/lib/cel-filter/compile/validate-condition.ts
|
|
721
|
+
function statusFromIssues(issues) {
|
|
722
|
+
if (issues.some((issue) => issue.severity === "invalid")) {
|
|
723
|
+
return "invalid";
|
|
724
|
+
}
|
|
725
|
+
if (issues.some((issue) => issue.severity === "incomplete")) {
|
|
726
|
+
return "incomplete";
|
|
727
|
+
}
|
|
728
|
+
return "valid";
|
|
729
|
+
}
|
|
730
|
+
var SEVERITY_BY_CODE = {
|
|
731
|
+
missing_column: "incomplete",
|
|
732
|
+
missing_value: "incomplete",
|
|
733
|
+
missing_key: "incomplete",
|
|
734
|
+
missing_tokens: "incomplete",
|
|
735
|
+
missing_quantifier: "incomplete",
|
|
736
|
+
missing_instant: "incomplete",
|
|
737
|
+
missing_range: "incomplete",
|
|
738
|
+
missing_duration: "incomplete",
|
|
739
|
+
unknown_column: "invalid",
|
|
740
|
+
illegal_operator: "invalid",
|
|
741
|
+
invalid_date_mode: "invalid",
|
|
742
|
+
regex_too_long: "invalid",
|
|
743
|
+
invalid_timestamp: "invalid",
|
|
744
|
+
invalid_date_range: "invalid",
|
|
745
|
+
nesting_depth_exceeded: "invalid"
|
|
746
|
+
};
|
|
747
|
+
var MESSAGES = {
|
|
748
|
+
missing_column: "Select a column for this filter.",
|
|
749
|
+
missing_value: "Enter a value for this filter.",
|
|
750
|
+
missing_key: "Enter a key for this filter.",
|
|
751
|
+
missing_tokens: "Enter at least one value for this filter.",
|
|
752
|
+
missing_quantifier: "Choose whether to match any or all values.",
|
|
753
|
+
missing_instant: "Select a date for this filter.",
|
|
754
|
+
missing_range: "Select a start and end date for this filter.",
|
|
755
|
+
missing_duration: "Select a duration for this filter.",
|
|
756
|
+
unknown_column: "This column is not available for filtering.",
|
|
757
|
+
illegal_operator: "This operator is not allowed for this filter.",
|
|
758
|
+
invalid_date_mode: "This operator cannot be used with this date mode.",
|
|
759
|
+
regex_too_long: `Pattern exceeds the maximum length of ${MAX_REGEX_LEN} characters.`,
|
|
760
|
+
invalid_timestamp: "Enter a valid date.",
|
|
761
|
+
invalid_date_range: "The start must be on or before the end.",
|
|
762
|
+
nesting_depth_exceeded: "This filter is nested too deeply."
|
|
763
|
+
};
|
|
764
|
+
var RFC3339_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/;
|
|
765
|
+
function isValidRfc3339(value) {
|
|
766
|
+
return RFC3339_RE.test(value) && !Number.isNaN(Date.parse(value)) && isRealCalendarDay(value);
|
|
767
|
+
}
|
|
768
|
+
function makeIssue({ condition, code, field }) {
|
|
769
|
+
return {
|
|
770
|
+
code,
|
|
771
|
+
message: MESSAGES[code],
|
|
772
|
+
severity: SEVERITY_BY_CODE[code],
|
|
773
|
+
nodeId: condition.id,
|
|
774
|
+
nodeType: "condition",
|
|
775
|
+
field
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
function validateCondition(condition, schema) {
|
|
779
|
+
if (!condition.columnId) {
|
|
780
|
+
return [makeIssue({ condition, code: "missing_column", field: "columnId" })];
|
|
781
|
+
}
|
|
782
|
+
const columnDef = resolveColumnDefinition(condition, schema);
|
|
783
|
+
if (!columnDef) {
|
|
784
|
+
return [makeIssue({ condition, code: "unknown_column", field: "columnId" })];
|
|
785
|
+
}
|
|
786
|
+
return validateColumnCondition(condition, columnDef);
|
|
787
|
+
}
|
|
788
|
+
function validateColumnCondition(condition, columnDef) {
|
|
789
|
+
const allowed = getAllowedOperators(columnDef);
|
|
790
|
+
if (!allowed.includes(condition.operator)) {
|
|
791
|
+
return [makeIssue({ condition, code: "illegal_operator", field: "operator" })];
|
|
792
|
+
}
|
|
793
|
+
switch (columnDef.filterType) {
|
|
794
|
+
case "checkbox":
|
|
795
|
+
case "static":
|
|
796
|
+
return validateCheckbox(condition);
|
|
797
|
+
case "radio":
|
|
798
|
+
case "text":
|
|
799
|
+
return validateSingleString(condition, columnDef.filterType);
|
|
800
|
+
case "multiText":
|
|
801
|
+
return validateMultiText(condition);
|
|
802
|
+
case "date":
|
|
803
|
+
return validateDate(condition);
|
|
804
|
+
case "keyValue":
|
|
805
|
+
return validateMapCondition(condition);
|
|
806
|
+
default:
|
|
807
|
+
return [];
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
function validateCheckbox(condition) {
|
|
811
|
+
const value = condition.value;
|
|
812
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
813
|
+
return [makeIssue({ condition, code: "missing_value", field: "value" })];
|
|
814
|
+
}
|
|
815
|
+
return [];
|
|
816
|
+
}
|
|
817
|
+
function validateSingleString(condition, filterType) {
|
|
818
|
+
const value = condition.value;
|
|
819
|
+
if (typeof value !== "string" || value === "") {
|
|
820
|
+
return [makeIssue({ condition, code: "missing_value", field: "value" })];
|
|
821
|
+
}
|
|
822
|
+
if (filterType === "text" && condition.operator === "matches" && value.length > MAX_REGEX_LEN) {
|
|
823
|
+
return [makeIssue({ condition, code: "regex_too_long", field: "pattern" })];
|
|
824
|
+
}
|
|
825
|
+
return [];
|
|
826
|
+
}
|
|
827
|
+
function validateMultiText(condition) {
|
|
828
|
+
const value = condition.value;
|
|
829
|
+
if (!isMultiTextCelValue(value) || value.tokens.length === 0) {
|
|
830
|
+
return [makeIssue({ condition, code: "missing_tokens", field: "tokens" })];
|
|
831
|
+
}
|
|
832
|
+
const issues = [];
|
|
833
|
+
const multi = value;
|
|
834
|
+
if (multi.tokens.length > 1 && !multi.quantifier) {
|
|
835
|
+
issues.push(makeIssue({ condition, code: "missing_quantifier", field: "quantifier" }));
|
|
836
|
+
}
|
|
837
|
+
if (condition.operator === "matches" && multi.tokens.some((token) => token.length > MAX_REGEX_LEN)) {
|
|
838
|
+
issues.push(makeIssue({ condition, code: "regex_too_long", field: "pattern" }));
|
|
839
|
+
}
|
|
840
|
+
return issues;
|
|
841
|
+
}
|
|
842
|
+
function validateDate(condition) {
|
|
843
|
+
const value = condition.value;
|
|
844
|
+
if (!isDateCelValue(value)) {
|
|
845
|
+
return [makeIssue({ condition, code: "missing_instant", field: "instant" })];
|
|
846
|
+
}
|
|
847
|
+
const date = value;
|
|
848
|
+
if (isRelativeDateCelValue(date)) {
|
|
849
|
+
return validateRelativeDate(condition, date);
|
|
850
|
+
}
|
|
851
|
+
return validateAbsoluteDate(condition, date);
|
|
852
|
+
}
|
|
853
|
+
function validateAbsoluteDate(condition, value) {
|
|
854
|
+
const issues = [];
|
|
855
|
+
if (isDateRangeOperator(condition.operator)) {
|
|
856
|
+
const range = value.range;
|
|
857
|
+
if (!range || !range.start || !range.end) {
|
|
858
|
+
issues.push(makeIssue({ condition, code: "missing_range", field: "range" }));
|
|
859
|
+
return issues;
|
|
860
|
+
}
|
|
861
|
+
if (!isValidRfc3339(range.start)) {
|
|
862
|
+
issues.push(makeIssue({ condition, code: "invalid_timestamp", field: "start" }));
|
|
863
|
+
}
|
|
864
|
+
if (!isValidRfc3339(range.end)) {
|
|
865
|
+
issues.push(makeIssue({ condition, code: "invalid_timestamp", field: "end" }));
|
|
866
|
+
}
|
|
867
|
+
if (issues.length === 0 && Date.parse(range.start) > Date.parse(range.end)) {
|
|
868
|
+
issues.push(makeIssue({ condition, code: "invalid_date_range", field: "range" }));
|
|
869
|
+
}
|
|
870
|
+
return issues;
|
|
871
|
+
}
|
|
872
|
+
if (!value.instant) {
|
|
873
|
+
issues.push(makeIssue({ condition, code: "missing_instant", field: "instant" }));
|
|
874
|
+
return issues;
|
|
875
|
+
}
|
|
876
|
+
if (!isValidRfc3339(value.instant)) {
|
|
877
|
+
issues.push(makeIssue({ condition, code: "invalid_timestamp", field: "instant" }));
|
|
878
|
+
}
|
|
879
|
+
return issues;
|
|
880
|
+
}
|
|
881
|
+
function validateRelativeDate(condition, value) {
|
|
882
|
+
const issues = [];
|
|
883
|
+
if (!isDateRelativeOperator(condition.operator)) {
|
|
884
|
+
issues.push(makeIssue({ condition, code: "invalid_date_mode", field: "value" }));
|
|
885
|
+
return issues;
|
|
886
|
+
}
|
|
887
|
+
if (!value.preset) {
|
|
888
|
+
issues.push(makeIssue({ condition, code: "missing_duration", field: "preset" }));
|
|
889
|
+
}
|
|
890
|
+
return issues;
|
|
891
|
+
}
|
|
892
|
+
function validateMapCondition(condition) {
|
|
893
|
+
const issues = [];
|
|
894
|
+
const value = isMapKeyCelValue(condition.value) ? condition.value : void 0;
|
|
895
|
+
if (!value || value.key === "") {
|
|
896
|
+
issues.push(makeIssue({ condition, code: "missing_key", field: "key" }));
|
|
897
|
+
}
|
|
898
|
+
const requiresValue = condition.operator === "has_value" || condition.operator === "does_not_have_value";
|
|
899
|
+
if (requiresValue && (!value || !value.value)) {
|
|
900
|
+
issues.push(makeIssue({ condition, code: "missing_value", field: "value" }));
|
|
901
|
+
}
|
|
902
|
+
return issues;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
// src/lib/cel-filter/compile/apply-display-rules.ts
|
|
906
|
+
function applyDisplayRules(raw, meta) {
|
|
907
|
+
const issues = raw.issues.filter((issue) => {
|
|
908
|
+
if (issue.nodeType === "group") {
|
|
909
|
+
return true;
|
|
910
|
+
}
|
|
911
|
+
if (issue.nodeId === meta.editingConditionId) {
|
|
912
|
+
return false;
|
|
913
|
+
}
|
|
914
|
+
return meta.committedConditionIds.has(issue.nodeId);
|
|
915
|
+
});
|
|
916
|
+
const status = statusFromIssues(issues);
|
|
917
|
+
const expression = status === "valid" ? raw.expression : void 0;
|
|
918
|
+
return {
|
|
919
|
+
status,
|
|
920
|
+
expression,
|
|
921
|
+
issues,
|
|
922
|
+
isEmitReady: false
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// src/lib/cel-filter/schema-utils.ts
|
|
927
|
+
var COMMIT_FILTER_TYPES = /* @__PURE__ */ new Set(["text", "multiText", "keyValue", "date"]);
|
|
928
|
+
function isMultiTextColumn(columnId, schema) {
|
|
929
|
+
var _a;
|
|
930
|
+
return ((_a = schema.columns.find((column) => column.id === columnId)) == null ? void 0 : _a.filterType) === "multiText";
|
|
931
|
+
}
|
|
932
|
+
function getColumnEmitOn(columnDef) {
|
|
933
|
+
if (columnDef.emitOn) {
|
|
934
|
+
return columnDef.emitOn;
|
|
935
|
+
}
|
|
936
|
+
return COMMIT_FILTER_TYPES.has(columnDef.filterType) ? "commit" : "change";
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
// src/lib/cel-filter/compile/compute-is-emit-ready.ts
|
|
940
|
+
function resolveEditingColumn(tree, schema, editingConditionId) {
|
|
941
|
+
const condition = tree.root.children.find(
|
|
942
|
+
(child) => isCelFilterCondition(child) && child.id === editingConditionId
|
|
943
|
+
);
|
|
944
|
+
return condition && schema.columns.find((column) => column.id === condition.columnId);
|
|
945
|
+
}
|
|
946
|
+
function computeIsEmitReady(raw, editingConditionId, schema, tree, meta) {
|
|
947
|
+
if (raw.status !== "valid") return false;
|
|
948
|
+
if (!editingConditionId) return true;
|
|
949
|
+
const columnDef = resolveEditingColumn(tree, schema, editingConditionId);
|
|
950
|
+
if (!columnDef || getColumnEmitOn(columnDef) === "change") return true;
|
|
951
|
+
const popoverClosed = meta.activeSurface === null;
|
|
952
|
+
return popoverClosed && meta.committedConditionIds.has(editingConditionId);
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
// src/lib/cel-filter/compile/compile-cel-filter-tree.ts
|
|
956
|
+
function combineGroupFragments(fragments) {
|
|
957
|
+
if (fragments.length === 0) {
|
|
958
|
+
return { expression: "", needsGrouping: false };
|
|
959
|
+
}
|
|
960
|
+
if (fragments.length === 1) {
|
|
961
|
+
return fragments[0];
|
|
962
|
+
}
|
|
963
|
+
return {
|
|
964
|
+
expression: fragments.map((fragment) => fragment.needsGrouping ? `(${fragment.expression})` : fragment.expression).join(" && "),
|
|
965
|
+
needsGrouping: false
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
function compileGroup(group, schema) {
|
|
969
|
+
const depthIssues = group.depth > MAX_NESTING_DEPTH ? [
|
|
970
|
+
{
|
|
971
|
+
code: "nesting_depth_exceeded",
|
|
972
|
+
message: "This filter is nested too deeply.",
|
|
973
|
+
severity: "invalid",
|
|
974
|
+
nodeId: group.id,
|
|
975
|
+
nodeType: "group"
|
|
976
|
+
}
|
|
977
|
+
] : [];
|
|
978
|
+
return group.children.reduce(
|
|
979
|
+
(acc, child) => {
|
|
980
|
+
if (isCelFilterGroup(child)) {
|
|
981
|
+
const nested = compileGroup(child, schema);
|
|
982
|
+
acc.issues.push(...nested.issues);
|
|
983
|
+
if (nested.fragments.length > 0) {
|
|
984
|
+
acc.fragments.push(combineGroupFragments(nested.fragments));
|
|
985
|
+
}
|
|
986
|
+
return acc;
|
|
987
|
+
}
|
|
988
|
+
if (isCelFilterCondition(child)) {
|
|
989
|
+
const conditionIssues = validateCondition(child, schema);
|
|
990
|
+
if (conditionIssues.length > 0) {
|
|
991
|
+
acc.issues.push(...conditionIssues);
|
|
992
|
+
return acc;
|
|
993
|
+
}
|
|
994
|
+
const shape = resolveFilterShape(child, schema);
|
|
995
|
+
if (!shape) {
|
|
996
|
+
return acc;
|
|
997
|
+
}
|
|
998
|
+
const columnDef = resolveColumnDefinition(child, schema);
|
|
999
|
+
acc.fragments.push(buildConditionExpression(child, shape, columnDef));
|
|
1000
|
+
}
|
|
1001
|
+
return acc;
|
|
1002
|
+
},
|
|
1003
|
+
{ issues: depthIssues, fragments: [] }
|
|
1004
|
+
);
|
|
1005
|
+
}
|
|
1006
|
+
function compileCelFilterTree(tree, schema) {
|
|
1007
|
+
const { issues, fragments } = compileGroup(tree.root, schema);
|
|
1008
|
+
const status = statusFromIssues(issues);
|
|
1009
|
+
if (status === "valid") {
|
|
1010
|
+
return { status, expression: combineGroupFragments(fragments).expression, issues, isEmitReady: true };
|
|
1011
|
+
}
|
|
1012
|
+
return { status, issues, isEmitReady: false };
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
// src/lib/cel-filter/tree-utils.ts
|
|
1016
|
+
function createCelNodeId() {
|
|
1017
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
1018
|
+
return crypto.randomUUID();
|
|
1019
|
+
}
|
|
1020
|
+
return `cel-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
1021
|
+
}
|
|
1022
|
+
function createEmptyCelFilterTree() {
|
|
1023
|
+
return {
|
|
1024
|
+
root: {
|
|
1025
|
+
id: createCelNodeId(),
|
|
1026
|
+
type: "group",
|
|
1027
|
+
combinator: "and",
|
|
1028
|
+
depth: 0,
|
|
1029
|
+
children: []
|
|
1030
|
+
}
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
function createDefaultCondition({ schema, columnId }) {
|
|
1034
|
+
const columnDef = schema.columns.find((column) => column.id === columnId);
|
|
1035
|
+
if (!columnDef) {
|
|
1036
|
+
throw new Error(`createDefaultCondition: column "${columnId}" is not in schema.columns`);
|
|
1037
|
+
}
|
|
1038
|
+
return {
|
|
1039
|
+
id: createCelNodeId(),
|
|
1040
|
+
type: "condition",
|
|
1041
|
+
columnId,
|
|
1042
|
+
operator: getDefaultOperator(columnDef.filterType),
|
|
1043
|
+
value: columnDef.defaultValue
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
function appendCondition(tree, condition) {
|
|
1047
|
+
return { root: __spreadProps(__spreadValues({}, tree.root), { children: [...tree.root.children, condition] }) };
|
|
1048
|
+
}
|
|
1049
|
+
function replaceCondition(tree, conditionId, updater) {
|
|
1050
|
+
return {
|
|
1051
|
+
root: __spreadProps(__spreadValues({}, tree.root), {
|
|
1052
|
+
children: tree.root.children.map(
|
|
1053
|
+
(child) => isCelFilterCondition(child) && child.id === conditionId ? updater(child) : child
|
|
1054
|
+
)
|
|
1055
|
+
})
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
function removeConditionById(tree, conditionId) {
|
|
1059
|
+
return {
|
|
1060
|
+
root: __spreadProps(__spreadValues({}, tree.root), {
|
|
1061
|
+
children: tree.root.children.filter((child) => !(isCelFilterCondition(child) && child.id === conditionId))
|
|
1062
|
+
})
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// src/lib/cel-filter/merge-contextual-into-cel-tree.ts
|
|
1067
|
+
function upsertCondition(tree, matches, build) {
|
|
1068
|
+
var _a;
|
|
1069
|
+
const index = tree.root.children.findIndex((child) => isCelFilterCondition(child) && matches(child));
|
|
1070
|
+
const found = tree.root.children[index];
|
|
1071
|
+
const existing = index >= 0 && isCelFilterCondition(found) ? found : void 0;
|
|
1072
|
+
const condition = __spreadValues({
|
|
1073
|
+
id: (_a = existing == null ? void 0 : existing.id) != null ? _a : createCelNodeId()
|
|
1074
|
+
}, build(existing));
|
|
1075
|
+
const children = index >= 0 ? tree.root.children.map((child, i) => i === index ? condition : child) : [...tree.root.children, condition];
|
|
1076
|
+
return { root: __spreadProps(__spreadValues({}, tree.root), { children }) };
|
|
1077
|
+
}
|
|
1078
|
+
function mergeKeyValueToken(tree, columnId, token) {
|
|
1079
|
+
const [key, ...rest] = token.split("=");
|
|
1080
|
+
const mapValue = rest.join("=");
|
|
1081
|
+
return upsertCondition(
|
|
1082
|
+
tree,
|
|
1083
|
+
(condition) => condition.columnId === columnId && isMapKeyCelValue(condition.value) && condition.value.key === key,
|
|
1084
|
+
() => ({
|
|
1085
|
+
type: "condition",
|
|
1086
|
+
columnId,
|
|
1087
|
+
operator: mapValue ? "has_value" : "key_exists",
|
|
1088
|
+
value: mapValue ? { key, value: mapValue } : { key }
|
|
1089
|
+
})
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
1092
|
+
function mergeContextualIntoCelTree(tree, schema, columnId, values) {
|
|
1093
|
+
if (values.length === 0) {
|
|
1094
|
+
return tree;
|
|
1095
|
+
}
|
|
1096
|
+
const columnDef = schema.columns.find((column) => column.id === columnId);
|
|
1097
|
+
if (!columnDef) {
|
|
1098
|
+
return tree;
|
|
1099
|
+
}
|
|
1100
|
+
const matchesColumn = (condition) => condition.columnId === columnId;
|
|
1101
|
+
switch (columnDef.filterType) {
|
|
1102
|
+
case "checkbox":
|
|
1103
|
+
return upsertCondition(tree, matchesColumn, (existing) => ({
|
|
1104
|
+
type: "condition",
|
|
1105
|
+
columnId,
|
|
1106
|
+
operator: "is",
|
|
1107
|
+
value: Array.from(
|
|
1108
|
+
/* @__PURE__ */ new Set([...existing && isCheckboxCelValue(existing.value) ? existing.value : [], ...values])
|
|
1109
|
+
)
|
|
1110
|
+
}));
|
|
1111
|
+
case "keyValue":
|
|
1112
|
+
return values.reduce((acc, token) => mergeKeyValueToken(acc, columnId, token), tree);
|
|
1113
|
+
case "multiText":
|
|
1114
|
+
return upsertCondition(tree, matchesColumn, () => ({
|
|
1115
|
+
type: "condition",
|
|
1116
|
+
columnId,
|
|
1117
|
+
operator: "contains",
|
|
1118
|
+
value: { tokens: values, quantifier: "any" }
|
|
1119
|
+
}));
|
|
1120
|
+
case "date":
|
|
1121
|
+
return upsertCondition(tree, matchesColumn, () => ({
|
|
1122
|
+
type: "condition",
|
|
1123
|
+
columnId,
|
|
1124
|
+
operator: "is",
|
|
1125
|
+
value: { instant: calendarDayStartInstant(values[0]) }
|
|
1126
|
+
}));
|
|
1127
|
+
default:
|
|
1128
|
+
return upsertCondition(tree, matchesColumn, () => ({
|
|
1129
|
+
type: "condition",
|
|
1130
|
+
columnId,
|
|
1131
|
+
operator: "is",
|
|
1132
|
+
value: values[0]
|
|
1133
|
+
}));
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
// src/cel-filter-bar/cel-filter-bar.tsx
|
|
1138
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
1139
|
+
var RELATIVE_PRESET_LABELS = {
|
|
1140
|
+
"24h": "Last 24 hours",
|
|
1141
|
+
"168h": "Last 7 days",
|
|
1142
|
+
"720h": "Last 30 days"
|
|
1143
|
+
};
|
|
1144
|
+
var SEGMENT_SELECT_TRIGGER_CLASSNAME = "hover:border-input! focus-visible:border-input focus-visible:ring-0 hover:bg-muted focus-visible:bg-muted aria-expanded:bg-muted";
|
|
1145
|
+
function CelFilterBar({
|
|
1146
|
+
tree,
|
|
1147
|
+
onChange,
|
|
1148
|
+
schema,
|
|
1149
|
+
onCompileResultChange,
|
|
1150
|
+
onRestrictedOptionSelect,
|
|
1151
|
+
showClearButton = false,
|
|
1152
|
+
className
|
|
1153
|
+
}) {
|
|
1154
|
+
const editor = useCelFilterEditorState();
|
|
1155
|
+
const onCompileResultChangeRef = React.useRef(onCompileResultChange);
|
|
1156
|
+
React.useEffect(() => {
|
|
1157
|
+
onCompileResultChangeRef.current = onCompileResultChange;
|
|
1158
|
+
});
|
|
1159
|
+
const rawResult = React.useMemo(() => compileCelFilterTree(tree, schema), [tree, schema]);
|
|
1160
|
+
const displayResult = React.useMemo(
|
|
1161
|
+
() => applyDisplayRules(rawResult, {
|
|
1162
|
+
editingConditionId: editor.editingConditionId,
|
|
1163
|
+
committedConditionIds: editor.committedConditionIds
|
|
1164
|
+
}),
|
|
1165
|
+
[rawResult, editor.editingConditionId, editor.committedConditionIds]
|
|
1166
|
+
);
|
|
1167
|
+
const isEmitReady = React.useMemo(
|
|
1168
|
+
() => computeIsEmitReady(rawResult, editor.editingConditionId, schema, tree, {
|
|
1169
|
+
activeSurface: editor.activeSurface,
|
|
1170
|
+
committedConditionIds: editor.committedConditionIds
|
|
1171
|
+
}),
|
|
1172
|
+
[rawResult, editor.editingConditionId, editor.activeSurface, editor.committedConditionIds, schema, tree]
|
|
1173
|
+
);
|
|
1174
|
+
React.useEffect(() => {
|
|
1175
|
+
var _a;
|
|
1176
|
+
(_a = onCompileResultChangeRef.current) == null ? void 0 : _a.call(onCompileResultChangeRef, __spreadProps(__spreadValues({}, displayResult), { isEmitReady }));
|
|
1177
|
+
}, [displayResult, isEmitReady]);
|
|
1178
|
+
const conditions = tree.root.children.filter(isCelFilterCondition);
|
|
1179
|
+
function handleAddFilter(columnId) {
|
|
1180
|
+
const condition = createDefaultCondition({ schema, columnId });
|
|
1181
|
+
onChange(appendCondition(tree, condition));
|
|
1182
|
+
editor.beginEditing(condition.id);
|
|
1183
|
+
editor.suppressDismissFor(condition.id);
|
|
1184
|
+
}
|
|
1185
|
+
function handleRemove(conditionId) {
|
|
1186
|
+
onChange(removeConditionById(tree, conditionId));
|
|
1187
|
+
editor.removeCondition(conditionId);
|
|
1188
|
+
}
|
|
1189
|
+
function handleClearAll() {
|
|
1190
|
+
onChange({ root: __spreadProps(__spreadValues({}, tree.root), { children: [] }) });
|
|
1191
|
+
editor.clearSuppress();
|
|
1192
|
+
}
|
|
1193
|
+
return /* @__PURE__ */ jsxs("div", { "data-slot": "cel-filter-bar", className: cn("flex flex-wrap items-center gap-2", className), children: [
|
|
1194
|
+
/* @__PURE__ */ jsx(CelAddFilterButton, { schema, conditions, onSelect: handleAddFilter }),
|
|
1195
|
+
conditions.map((condition) => {
|
|
1196
|
+
const columnDef = resolveColumnDefinition(condition, schema);
|
|
1197
|
+
if (!columnDef) return null;
|
|
1198
|
+
return /* @__PURE__ */ jsx(
|
|
1199
|
+
CelFilterPill,
|
|
1200
|
+
{
|
|
1201
|
+
condition,
|
|
1202
|
+
columnDef,
|
|
1203
|
+
schema,
|
|
1204
|
+
tree,
|
|
1205
|
+
onChange,
|
|
1206
|
+
controller: editor.getController(condition.id),
|
|
1207
|
+
issues: displayResult.issues.filter((issue) => issue.nodeId === condition.id),
|
|
1208
|
+
onRemove: () => handleRemove(condition.id),
|
|
1209
|
+
onRestrictedOptionSelect
|
|
1210
|
+
},
|
|
1211
|
+
condition.id
|
|
1212
|
+
);
|
|
1213
|
+
}),
|
|
1214
|
+
showClearButton && conditions.length > 0 && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: handleClearAll, children: "Clear all" })
|
|
1215
|
+
] });
|
|
1216
|
+
}
|
|
1217
|
+
function CelAddFilterButton({
|
|
1218
|
+
schema,
|
|
1219
|
+
conditions,
|
|
1220
|
+
onSelect
|
|
1221
|
+
}) {
|
|
1222
|
+
var _a, _b;
|
|
1223
|
+
const usedSingleInstanceColumnIds = new Set(
|
|
1224
|
+
conditions.map((condition) => resolveColumnDefinition(condition, schema)).filter(
|
|
1225
|
+
(columnDef) => !!columnDef && !allowsMultipleConditions(columnDef.filterType)
|
|
1226
|
+
).map((columnDef) => columnDef.id)
|
|
1227
|
+
);
|
|
1228
|
+
const availableColumns = schema.columns.filter((column) => !usedSingleInstanceColumnIds.has(column.id));
|
|
1229
|
+
const groupMap = /* @__PURE__ */ new Map();
|
|
1230
|
+
for (const column of availableColumns) {
|
|
1231
|
+
const key = (_a = column.group) != null ? _a : "";
|
|
1232
|
+
const list = (_b = groupMap.get(key)) != null ? _b : [];
|
|
1233
|
+
list.push(column);
|
|
1234
|
+
groupMap.set(key, list);
|
|
1235
|
+
}
|
|
1236
|
+
const groups = Array.from(groupMap.entries()).map(([value, items]) => ({ value, items }));
|
|
1237
|
+
return /* @__PURE__ */ jsxs(
|
|
1238
|
+
Combobox,
|
|
1239
|
+
{
|
|
1240
|
+
items: groups,
|
|
1241
|
+
itemToStringValue: (item) => item.title,
|
|
1242
|
+
itemToStringLabel: (item) => item.title,
|
|
1243
|
+
onValueChange: (column) => {
|
|
1244
|
+
if (column) onSelect(column.id);
|
|
1245
|
+
},
|
|
1246
|
+
children: [
|
|
1247
|
+
/* @__PURE__ */ jsxs(ComboboxTrigger, { "aria-label": "Add filter", render: /* @__PURE__ */ jsx(Button, { variant: "outline" }), children: [
|
|
1248
|
+
/* @__PURE__ */ jsx(Icon, { name: "plus", "data-icon": "inline-start" }),
|
|
1249
|
+
"Add filter"
|
|
1250
|
+
] }),
|
|
1251
|
+
/* @__PURE__ */ jsxs(ComboboxContent, { align: "start", className: "w-64", children: [
|
|
1252
|
+
/* @__PURE__ */ jsx(ComboboxInput, { showTrigger: false, placeholder: "Search filters\u2026" }),
|
|
1253
|
+
/* @__PURE__ */ jsx(ComboboxEmpty, { children: "No filters found." }),
|
|
1254
|
+
/* @__PURE__ */ jsx(ComboboxList, { children: (group) => /* @__PURE__ */ jsxs(ComboboxGroup, { items: group.items, children: [
|
|
1255
|
+
group.value && /* @__PURE__ */ jsx(ComboboxLabel, { children: group.value }),
|
|
1256
|
+
/* @__PURE__ */ jsx(ComboboxCollection, { children: (column) => /* @__PURE__ */ jsx(ComboboxItem, { value: column, children: column.title }, column.id) })
|
|
1257
|
+
] }, group.value) })
|
|
1258
|
+
] })
|
|
1259
|
+
]
|
|
1260
|
+
}
|
|
1261
|
+
);
|
|
1262
|
+
}
|
|
1263
|
+
function formatConditionSummary(condition, columnDef, locale) {
|
|
1264
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1265
|
+
const shape = columnDef.filterType === "keyValue" ? "map" : columnDef.filterType;
|
|
1266
|
+
switch (shape) {
|
|
1267
|
+
case "checkbox": {
|
|
1268
|
+
const value = isCheckboxCelValue(condition.value) ? condition.value : [];
|
|
1269
|
+
if (value.length === 0) return "Any";
|
|
1270
|
+
if (value.length === 1) {
|
|
1271
|
+
return (_c = (_b = (_a = columnDef.options) == null ? void 0 : _a.find((opt) => opt.value === value[0])) == null ? void 0 : _b.label) != null ? _c : value[0];
|
|
1272
|
+
}
|
|
1273
|
+
return `${value.length} ${columnDef.pluralTitle}`;
|
|
1274
|
+
}
|
|
1275
|
+
case "radio":
|
|
1276
|
+
case "static": {
|
|
1277
|
+
const value = typeof condition.value === "string" ? condition.value : "";
|
|
1278
|
+
if (!value) return "Any";
|
|
1279
|
+
return (_f = (_e = (_d = columnDef.options) == null ? void 0 : _d.find((opt) => opt.value === value)) == null ? void 0 : _e.label) != null ? _f : value;
|
|
1280
|
+
}
|
|
1281
|
+
case "text":
|
|
1282
|
+
return typeof condition.value === "string" && condition.value ? condition.value : "Any";
|
|
1283
|
+
case "multiText": {
|
|
1284
|
+
const value = readMultiTextCelValue(condition.value);
|
|
1285
|
+
if (value.tokens.length === 0) return "Any";
|
|
1286
|
+
if (value.tokens.length === 1) return value.tokens[0];
|
|
1287
|
+
return `${value.tokens.length} values`;
|
|
1288
|
+
}
|
|
1289
|
+
case "date": {
|
|
1290
|
+
const value = readDateCelValue(condition.value);
|
|
1291
|
+
if (isRelativeDateCelValue(value)) return value.preset ? RELATIVE_PRESET_LABELS[value.preset] : "Any";
|
|
1292
|
+
if (value.range)
|
|
1293
|
+
return `${formatInstantLabel(value.range.start, { locale })} \u2013 ${formatInstantLabel(value.range.end, { locale })}`;
|
|
1294
|
+
if (value.instant) return formatInstantLabel(value.instant, { withTime: value.hasTime, locale });
|
|
1295
|
+
return "Any";
|
|
1296
|
+
}
|
|
1297
|
+
case "map": {
|
|
1298
|
+
const value = readMapKeyCelValue(condition.value);
|
|
1299
|
+
if (!value.key) return "Any";
|
|
1300
|
+
return value.value ? `${value.key}=${value.value}` : value.key;
|
|
1301
|
+
}
|
|
1302
|
+
default:
|
|
1303
|
+
return "Any";
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
function CelFilterPill({
|
|
1307
|
+
condition,
|
|
1308
|
+
columnDef,
|
|
1309
|
+
schema,
|
|
1310
|
+
tree,
|
|
1311
|
+
onChange,
|
|
1312
|
+
controller,
|
|
1313
|
+
issues,
|
|
1314
|
+
onRemove,
|
|
1315
|
+
onRestrictedOptionSelect
|
|
1316
|
+
}) {
|
|
1317
|
+
var _a;
|
|
1318
|
+
const shape = (_a = resolveFilterShape(condition, schema)) != null ? _a : columnDef.filterType;
|
|
1319
|
+
const mounted = useMounted();
|
|
1320
|
+
const locale = mounted ? void 0 : "en-US";
|
|
1321
|
+
if (shape === "checkbox") {
|
|
1322
|
+
return /* @__PURE__ */ jsx(
|
|
1323
|
+
CelCheckboxComboboxPill,
|
|
1324
|
+
{
|
|
1325
|
+
condition,
|
|
1326
|
+
columnDef,
|
|
1327
|
+
tree,
|
|
1328
|
+
onChange,
|
|
1329
|
+
controller,
|
|
1330
|
+
issues,
|
|
1331
|
+
onRemove,
|
|
1332
|
+
onRestrictedOptionSelect
|
|
1333
|
+
}
|
|
1334
|
+
);
|
|
1335
|
+
}
|
|
1336
|
+
if (shape === "radio") {
|
|
1337
|
+
return /* @__PURE__ */ jsx(
|
|
1338
|
+
CelRadioComboboxPill,
|
|
1339
|
+
{
|
|
1340
|
+
condition,
|
|
1341
|
+
columnDef,
|
|
1342
|
+
tree,
|
|
1343
|
+
onChange,
|
|
1344
|
+
controller,
|
|
1345
|
+
issues,
|
|
1346
|
+
onRemove,
|
|
1347
|
+
onRestrictedOptionSelect
|
|
1348
|
+
}
|
|
1349
|
+
);
|
|
1350
|
+
}
|
|
1351
|
+
function updateValue(value) {
|
|
1352
|
+
onChange(replaceCondition(tree, condition.id, (c) => __spreadProps(__spreadValues({}, c), { value })));
|
|
1353
|
+
}
|
|
1354
|
+
function updateOperator(operator) {
|
|
1355
|
+
const hadValue = condition.value !== void 0;
|
|
1356
|
+
const next = applyOperatorChange(condition, operator, shape);
|
|
1357
|
+
onChange(replaceCondition(tree, condition.id, () => next));
|
|
1358
|
+
if (hadValue && next.value === void 0) controller.beginEditing();
|
|
1359
|
+
}
|
|
1360
|
+
const allowedOperators = getAllowedOperators(columnDef);
|
|
1361
|
+
return /* @__PURE__ */ jsxs(
|
|
1362
|
+
Popover,
|
|
1363
|
+
{
|
|
1364
|
+
open: controller.popoverVisible,
|
|
1365
|
+
onOpenChange: (open) => {
|
|
1366
|
+
if (open) controller.beginEditing();
|
|
1367
|
+
else if (!controller.isDismissSuppressed()) controller.handlePopoverDismiss();
|
|
1368
|
+
},
|
|
1369
|
+
children: [
|
|
1370
|
+
/* @__PURE__ */ jsxs(ButtonGroup, { children: [
|
|
1371
|
+
/* @__PURE__ */ jsx(ButtonGroupText, { children: columnDef.title }),
|
|
1372
|
+
/* @__PURE__ */ jsx(
|
|
1373
|
+
CelOperatorSegment,
|
|
1374
|
+
{
|
|
1375
|
+
columnTitle: columnDef.title,
|
|
1376
|
+
operator: condition.operator,
|
|
1377
|
+
allowedOperators,
|
|
1378
|
+
hideOnSingleOperator: columnDef.hideOnSingleOperator,
|
|
1379
|
+
onChangeOperator: updateOperator
|
|
1380
|
+
}
|
|
1381
|
+
),
|
|
1382
|
+
shape === "multiText" && /* @__PURE__ */ jsx(
|
|
1383
|
+
CelQuantifierSegment,
|
|
1384
|
+
{
|
|
1385
|
+
columnTitle: columnDef.title,
|
|
1386
|
+
value: readMultiTextCelValue(condition.value),
|
|
1387
|
+
onChangeValue: updateValue
|
|
1388
|
+
}
|
|
1389
|
+
),
|
|
1390
|
+
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "outline", className: "gap-1 font-normal", children: [
|
|
1391
|
+
/* @__PURE__ */ jsx("span", { className: cn(issues.length > 0 && "text-destructive"), children: formatConditionSummary(condition, columnDef, locale) }),
|
|
1392
|
+
/* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: "sm", className: "text-muted-foreground" })
|
|
1393
|
+
] }) }),
|
|
1394
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "icon", "aria-label": `Remove ${columnDef.title} filter`, onClick: onRemove, children: /* @__PURE__ */ jsx(Icon, { name: "xmark" }) })
|
|
1395
|
+
] }),
|
|
1396
|
+
/* @__PURE__ */ jsxs(PopoverContent, { align: "start", className: shape === "date" ? "w-fit" : "w-72", children: [
|
|
1397
|
+
shape === "text" && /* @__PURE__ */ jsx(CelTextValueEditor, { condition, onChangeValue: updateValue, onCommit: controller.commit }),
|
|
1398
|
+
shape === "multiText" && /* @__PURE__ */ jsx(CelMultiTextValueEditor, { condition, onChangeValue: updateValue }),
|
|
1399
|
+
shape === "date" && /* @__PURE__ */ jsx(CelDateValueEditor, { condition, onChangeValue: updateValue }),
|
|
1400
|
+
shape === "map" && /* @__PURE__ */ jsx(CelKeyValueValueEditor, { condition, onChangeValue: updateValue, onCommit: controller.commit }),
|
|
1401
|
+
issues.length > 0 && /* @__PURE__ */ jsx("p", { className: "text-body-sm text-destructive", children: issues.map((issue) => issue.message).join(" ") })
|
|
1402
|
+
] })
|
|
1403
|
+
]
|
|
1404
|
+
}
|
|
1405
|
+
);
|
|
1406
|
+
}
|
|
1407
|
+
function CelOperatorSegment({
|
|
1408
|
+
columnTitle,
|
|
1409
|
+
operator,
|
|
1410
|
+
allowedOperators,
|
|
1411
|
+
hideOnSingleOperator,
|
|
1412
|
+
onChangeOperator
|
|
1413
|
+
}) {
|
|
1414
|
+
if (allowedOperators.length > 1) {
|
|
1415
|
+
return /* @__PURE__ */ jsxs(Select, { value: operator, onValueChange: (value) => onChangeOperator(value), children: [
|
|
1416
|
+
/* @__PURE__ */ jsx(SelectTrigger, { "aria-label": `${columnTitle} operator`, className: SEGMENT_SELECT_TRIGGER_CLASSNAME, children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
1417
|
+
/* @__PURE__ */ jsx(SelectContent, { position: "popper", children: /* @__PURE__ */ jsx(SelectGroup, { children: allowedOperators.map((op) => /* @__PURE__ */ jsx(SelectItem, { value: op, children: getOperatorLabel(op) }, op)) }) })
|
|
1418
|
+
] });
|
|
1419
|
+
}
|
|
1420
|
+
if (allowedOperators.length === 1 && !hideOnSingleOperator) {
|
|
1421
|
+
return /* @__PURE__ */ jsx(ButtonGroupText, { className: "text-muted-foreground font-normal", children: getOperatorLabel(allowedOperators[0]) });
|
|
1422
|
+
}
|
|
1423
|
+
return null;
|
|
1424
|
+
}
|
|
1425
|
+
function CelQuantifierSegment({
|
|
1426
|
+
columnTitle,
|
|
1427
|
+
value,
|
|
1428
|
+
onChangeValue
|
|
1429
|
+
}) {
|
|
1430
|
+
var _a;
|
|
1431
|
+
if (value.tokens.length <= 1) return null;
|
|
1432
|
+
return /* @__PURE__ */ jsxs(
|
|
1433
|
+
Select,
|
|
1434
|
+
{
|
|
1435
|
+
value: (_a = value.quantifier) != null ? _a : "any",
|
|
1436
|
+
onValueChange: (next) => {
|
|
1437
|
+
if (next === "any" || next === "all") onChangeValue(__spreadProps(__spreadValues({}, value), { quantifier: next }));
|
|
1438
|
+
},
|
|
1439
|
+
children: [
|
|
1440
|
+
/* @__PURE__ */ jsx(SelectTrigger, { "aria-label": `${columnTitle} quantifier`, className: SEGMENT_SELECT_TRIGGER_CLASSNAME, children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
1441
|
+
/* @__PURE__ */ jsx(SelectContent, { position: "popper", children: /* @__PURE__ */ jsxs(SelectGroup, { children: [
|
|
1442
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "any", children: getQuantifierLabel("any") }),
|
|
1443
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "all", children: getQuantifierLabel("all") })
|
|
1444
|
+
] }) })
|
|
1445
|
+
]
|
|
1446
|
+
}
|
|
1447
|
+
);
|
|
1448
|
+
}
|
|
1449
|
+
function CelCheckboxComboboxPill({
|
|
1450
|
+
condition,
|
|
1451
|
+
columnDef,
|
|
1452
|
+
tree,
|
|
1453
|
+
onChange,
|
|
1454
|
+
controller,
|
|
1455
|
+
issues,
|
|
1456
|
+
onRemove,
|
|
1457
|
+
onRestrictedOptionSelect
|
|
1458
|
+
}) {
|
|
1459
|
+
var _a;
|
|
1460
|
+
const options = (_a = columnDef.options) != null ? _a : [];
|
|
1461
|
+
const value = isCheckboxCelValue(condition.value) ? condition.value : [];
|
|
1462
|
+
const selected = options.filter((option) => value.includes(option.value));
|
|
1463
|
+
const allowedOperators = getAllowedOperators(columnDef);
|
|
1464
|
+
function updateOperator(operator) {
|
|
1465
|
+
onChange(replaceCondition(tree, condition.id, (c) => applyOperatorChange(c, operator, "checkbox")));
|
|
1466
|
+
}
|
|
1467
|
+
function applySelection(nextSelected) {
|
|
1468
|
+
const current = new Set(value);
|
|
1469
|
+
const nextValues = new Set(nextSelected.map((option) => option.value));
|
|
1470
|
+
for (const option of nextSelected) {
|
|
1471
|
+
if (!current.has(option.value) && option.disabled) {
|
|
1472
|
+
onRestrictedOptionSelect == null ? void 0 : onRestrictedOptionSelect({ columnId: columnDef.id, value: option.value, label: option.label });
|
|
1473
|
+
nextValues.delete(option.value);
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
onChange(replaceCondition(tree, condition.id, (c) => __spreadProps(__spreadValues({}, c), { value: Array.from(nextValues) })));
|
|
1477
|
+
const added = nextSelected.find((option) => !current.has(option.value) && !option.disabled && option.operator);
|
|
1478
|
+
if (added == null ? void 0 : added.operator) updateOperator(added.operator);
|
|
1479
|
+
}
|
|
1480
|
+
return /* @__PURE__ */ jsxs(
|
|
1481
|
+
Combobox,
|
|
1482
|
+
{
|
|
1483
|
+
items: options,
|
|
1484
|
+
itemToStringValue: (item) => item.label,
|
|
1485
|
+
multiple: true,
|
|
1486
|
+
value: selected,
|
|
1487
|
+
onValueChange: (next) => applySelection(next),
|
|
1488
|
+
open: controller.popoverVisible,
|
|
1489
|
+
onOpenChange: (open) => {
|
|
1490
|
+
if (open) controller.beginEditing();
|
|
1491
|
+
else if (!controller.isDismissSuppressed()) controller.handlePopoverDismiss();
|
|
1492
|
+
},
|
|
1493
|
+
children: [
|
|
1494
|
+
/* @__PURE__ */ jsxs(ButtonGroup, { children: [
|
|
1495
|
+
/* @__PURE__ */ jsx(ButtonGroupText, { children: columnDef.title }),
|
|
1496
|
+
/* @__PURE__ */ jsx(
|
|
1497
|
+
CelOperatorSegment,
|
|
1498
|
+
{
|
|
1499
|
+
columnTitle: columnDef.title,
|
|
1500
|
+
operator: condition.operator,
|
|
1501
|
+
allowedOperators,
|
|
1502
|
+
hideOnSingleOperator: columnDef.hideOnSingleOperator,
|
|
1503
|
+
onChangeOperator: updateOperator
|
|
1504
|
+
}
|
|
1505
|
+
),
|
|
1506
|
+
/* @__PURE__ */ jsx(
|
|
1507
|
+
ComboboxTrigger,
|
|
1508
|
+
{
|
|
1509
|
+
"aria-label": `${columnDef.title} value`,
|
|
1510
|
+
render: /* @__PURE__ */ jsx(Button, { variant: "outline", className: "gap-1 font-normal [&>svg:last-child]:size-(--icon-sm)" }),
|
|
1511
|
+
children: /* @__PURE__ */ jsx("span", { className: cn(issues.length > 0 && "text-destructive"), children: formatConditionSummary(condition, columnDef) })
|
|
1512
|
+
}
|
|
1513
|
+
),
|
|
1514
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "icon", "aria-label": `Remove ${columnDef.title} filter`, onClick: onRemove, children: /* @__PURE__ */ jsx(Icon, { name: "xmark" }) })
|
|
1515
|
+
] }),
|
|
1516
|
+
/* @__PURE__ */ jsxs(ComboboxContent, { className: "min-w-64", collisionAvoidance: { side: "none", align: "shift" }, children: [
|
|
1517
|
+
/* @__PURE__ */ jsx(ComboboxInput, { showTrigger: false, placeholder: `Search ${columnDef.title.toLowerCase()}\u2026` }),
|
|
1518
|
+
selected.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1519
|
+
/* @__PURE__ */ jsxs(ComboboxSelectedChips, { children: [
|
|
1520
|
+
/* @__PURE__ */ jsx(ComboboxValue, { children: (items) => /* @__PURE__ */ jsx(Fragment, { children: items.map((item) => /* @__PURE__ */ jsx(ComboboxChip, { children: item.label }, item.value)) }) }),
|
|
1521
|
+
/* @__PURE__ */ jsx(ComboboxClearAll, { onClick: () => applySelection([]) })
|
|
1522
|
+
] }),
|
|
1523
|
+
/* @__PURE__ */ jsx(ComboboxSeparator, {})
|
|
1524
|
+
] }),
|
|
1525
|
+
/* @__PURE__ */ jsxs(ComboboxEmpty, { children: [
|
|
1526
|
+
"No ",
|
|
1527
|
+
columnDef.title.toLowerCase(),
|
|
1528
|
+
" found."
|
|
1529
|
+
] }),
|
|
1530
|
+
/* @__PURE__ */ jsx(ComboboxList, { children: (item) => /* @__PURE__ */ jsx(ComboboxItem, { value: item, className: cn(item.disabled && "text-muted-foreground"), children: item.label }, item.value) }),
|
|
1531
|
+
issues.length > 0 && /* @__PURE__ */ jsx("p", { className: "text-body-sm text-destructive", children: issues.map((issue) => issue.message).join(" ") })
|
|
1532
|
+
] })
|
|
1533
|
+
]
|
|
1534
|
+
}
|
|
1535
|
+
);
|
|
1536
|
+
}
|
|
1537
|
+
function CelRadioComboboxPill({
|
|
1538
|
+
condition,
|
|
1539
|
+
columnDef,
|
|
1540
|
+
tree,
|
|
1541
|
+
onChange,
|
|
1542
|
+
controller,
|
|
1543
|
+
issues,
|
|
1544
|
+
onRemove,
|
|
1545
|
+
onRestrictedOptionSelect
|
|
1546
|
+
}) {
|
|
1547
|
+
var _a, _b;
|
|
1548
|
+
const options = (_a = columnDef.options) != null ? _a : [];
|
|
1549
|
+
const value = typeof condition.value === "string" ? condition.value : "";
|
|
1550
|
+
const selected = (_b = options.find((option) => option.value === value)) != null ? _b : null;
|
|
1551
|
+
const allowedOperators = getAllowedOperators(columnDef);
|
|
1552
|
+
function updateOperator(operator) {
|
|
1553
|
+
onChange(replaceCondition(tree, condition.id, (c) => applyOperatorChange(c, operator, "radio")));
|
|
1554
|
+
}
|
|
1555
|
+
function applySelection(option) {
|
|
1556
|
+
if (!option) return;
|
|
1557
|
+
if (option.disabled) {
|
|
1558
|
+
onRestrictedOptionSelect == null ? void 0 : onRestrictedOptionSelect({ columnId: columnDef.id, value: option.value, label: option.label });
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1561
|
+
onChange(replaceCondition(tree, condition.id, (c) => __spreadProps(__spreadValues({}, c), { value: option.value })));
|
|
1562
|
+
if (option.operator) updateOperator(option.operator);
|
|
1563
|
+
controller.commit();
|
|
1564
|
+
}
|
|
1565
|
+
return /* @__PURE__ */ jsxs(
|
|
1566
|
+
Combobox,
|
|
1567
|
+
{
|
|
1568
|
+
items: options,
|
|
1569
|
+
itemToStringValue: (item) => item.label,
|
|
1570
|
+
value: selected,
|
|
1571
|
+
onValueChange: (next) => applySelection(next),
|
|
1572
|
+
open: controller.popoverVisible,
|
|
1573
|
+
onOpenChange: (open) => {
|
|
1574
|
+
if (open) controller.beginEditing();
|
|
1575
|
+
else if (!controller.isDismissSuppressed()) controller.handlePopoverDismiss();
|
|
1576
|
+
},
|
|
1577
|
+
children: [
|
|
1578
|
+
/* @__PURE__ */ jsxs(ButtonGroup, { children: [
|
|
1579
|
+
/* @__PURE__ */ jsx(ButtonGroupText, { children: columnDef.title }),
|
|
1580
|
+
/* @__PURE__ */ jsx(
|
|
1581
|
+
CelOperatorSegment,
|
|
1582
|
+
{
|
|
1583
|
+
columnTitle: columnDef.title,
|
|
1584
|
+
operator: condition.operator,
|
|
1585
|
+
allowedOperators,
|
|
1586
|
+
hideOnSingleOperator: columnDef.hideOnSingleOperator,
|
|
1587
|
+
onChangeOperator: updateOperator
|
|
1588
|
+
}
|
|
1589
|
+
),
|
|
1590
|
+
/* @__PURE__ */ jsx(
|
|
1591
|
+
ComboboxTrigger,
|
|
1592
|
+
{
|
|
1593
|
+
"aria-label": `${columnDef.title} value`,
|
|
1594
|
+
render: /* @__PURE__ */ jsx(Button, { variant: "outline", className: "gap-1 font-normal [&>svg:last-child]:size-(--icon-sm)" }),
|
|
1595
|
+
children: /* @__PURE__ */ jsx("span", { className: cn(issues.length > 0 && "text-destructive"), children: formatConditionSummary(condition, columnDef) })
|
|
1596
|
+
}
|
|
1597
|
+
),
|
|
1598
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "icon", "aria-label": `Remove ${columnDef.title} filter`, onClick: onRemove, children: /* @__PURE__ */ jsx(Icon, { name: "xmark" }) })
|
|
1599
|
+
] }),
|
|
1600
|
+
/* @__PURE__ */ jsxs(ComboboxContent, { className: "min-w-64", collisionAvoidance: { side: "none", align: "shift" }, children: [
|
|
1601
|
+
/* @__PURE__ */ jsx(ComboboxInput, { showTrigger: false, placeholder: `Search ${columnDef.title.toLowerCase()}\u2026` }),
|
|
1602
|
+
/* @__PURE__ */ jsxs(ComboboxEmpty, { children: [
|
|
1603
|
+
"No ",
|
|
1604
|
+
columnDef.title.toLowerCase(),
|
|
1605
|
+
" found."
|
|
1606
|
+
] }),
|
|
1607
|
+
/* @__PURE__ */ jsx(ComboboxList, { children: (item) => /* @__PURE__ */ jsx(ComboboxItem, { value: item, className: cn(item.disabled && "text-muted-foreground"), children: item.label }, item.value) }),
|
|
1608
|
+
issues.length > 0 && /* @__PURE__ */ jsx("p", { className: "text-body-sm text-destructive", children: issues.map((issue) => issue.message).join(" ") })
|
|
1609
|
+
] })
|
|
1610
|
+
]
|
|
1611
|
+
}
|
|
1612
|
+
);
|
|
1613
|
+
}
|
|
1614
|
+
function CelTextValueEditor({
|
|
1615
|
+
condition,
|
|
1616
|
+
onChangeValue,
|
|
1617
|
+
onCommit
|
|
1618
|
+
}) {
|
|
1619
|
+
const value = typeof condition.value === "string" ? condition.value : "";
|
|
1620
|
+
return /* @__PURE__ */ jsx(
|
|
1621
|
+
Input,
|
|
1622
|
+
{
|
|
1623
|
+
autoFocus: true,
|
|
1624
|
+
value,
|
|
1625
|
+
placeholder: "Enter a value\u2026",
|
|
1626
|
+
onChange: (e) => onChangeValue(e.target.value),
|
|
1627
|
+
onKeyDown: (e) => {
|
|
1628
|
+
if (e.key === "Enter") onCommit();
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
);
|
|
1632
|
+
}
|
|
1633
|
+
function CelMultiTextValueEditor({
|
|
1634
|
+
condition,
|
|
1635
|
+
onChangeValue
|
|
1636
|
+
}) {
|
|
1637
|
+
const value = readMultiTextCelValue(condition.value);
|
|
1638
|
+
const [draft, setDraft] = React.useState("");
|
|
1639
|
+
function addToken() {
|
|
1640
|
+
var _a;
|
|
1641
|
+
const token = draft.trim();
|
|
1642
|
+
if (!token || value.tokens.includes(token)) {
|
|
1643
|
+
setDraft("");
|
|
1644
|
+
return;
|
|
1645
|
+
}
|
|
1646
|
+
const nextTokens = [...value.tokens, token];
|
|
1647
|
+
const quantifier = nextTokens.length > 1 ? (_a = value.quantifier) != null ? _a : "any" : value.quantifier;
|
|
1648
|
+
onChangeValue(__spreadProps(__spreadValues({}, value), { tokens: nextTokens, quantifier }));
|
|
1649
|
+
setDraft("");
|
|
1650
|
+
}
|
|
1651
|
+
function removeToken(token) {
|
|
1652
|
+
onChangeValue(__spreadProps(__spreadValues({}, value), { tokens: value.tokens.filter((t) => t !== token) }));
|
|
1653
|
+
}
|
|
1654
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
1655
|
+
value.tokens.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: value.tokens.map((token) => /* @__PURE__ */ jsxs(Badge, { variant: "secondary", className: "gap-1", children: [
|
|
1656
|
+
token,
|
|
1657
|
+
/* @__PURE__ */ jsx(
|
|
1658
|
+
"button",
|
|
1659
|
+
{
|
|
1660
|
+
type: "button",
|
|
1661
|
+
"aria-label": `Remove ${token}`,
|
|
1662
|
+
onClick: () => removeToken(token),
|
|
1663
|
+
className: "cursor-pointer",
|
|
1664
|
+
children: /* @__PURE__ */ jsx(Icon, { name: "xmark", size: "xs" })
|
|
1665
|
+
}
|
|
1666
|
+
)
|
|
1667
|
+
] }, token)) }),
|
|
1668
|
+
/* @__PURE__ */ jsx(
|
|
1669
|
+
Input,
|
|
1670
|
+
{
|
|
1671
|
+
autoFocus: true,
|
|
1672
|
+
value: draft,
|
|
1673
|
+
placeholder: "Type a value, press Enter\u2026",
|
|
1674
|
+
onChange: (e) => setDraft(e.target.value),
|
|
1675
|
+
onKeyDown: (e) => {
|
|
1676
|
+
if (e.key === "Enter") {
|
|
1677
|
+
e.preventDefault();
|
|
1678
|
+
addToken();
|
|
1679
|
+
} else if (e.key === "Backspace" && draft === "" && value.tokens.length > 0) {
|
|
1680
|
+
removeToken(value.tokens[value.tokens.length - 1]);
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
)
|
|
1685
|
+
] });
|
|
1686
|
+
}
|
|
1687
|
+
function CelKeyValueValueEditor({
|
|
1688
|
+
condition,
|
|
1689
|
+
onChangeValue,
|
|
1690
|
+
onCommit
|
|
1691
|
+
}) {
|
|
1692
|
+
var _a;
|
|
1693
|
+
const value = readMapKeyCelValue(condition.value);
|
|
1694
|
+
const needsValue = condition.operator === "has_value" || condition.operator === "does_not_have_value";
|
|
1695
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
1696
|
+
/* @__PURE__ */ jsxs(InputGroup, { children: [
|
|
1697
|
+
/* @__PURE__ */ jsx(InputGroupAddon, { children: /* @__PURE__ */ jsx(InputGroupText, { children: "Key" }) }),
|
|
1698
|
+
/* @__PURE__ */ jsx(
|
|
1699
|
+
InputGroupInput,
|
|
1700
|
+
{
|
|
1701
|
+
autoFocus: true,
|
|
1702
|
+
value: value.key,
|
|
1703
|
+
onChange: (e) => onChangeValue(__spreadProps(__spreadValues({}, value), { key: e.target.value })),
|
|
1704
|
+
onKeyDown: (e) => {
|
|
1705
|
+
if (e.key === "Enter" && !needsValue) onCommit();
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
)
|
|
1709
|
+
] }),
|
|
1710
|
+
needsValue && /* @__PURE__ */ jsxs(InputGroup, { children: [
|
|
1711
|
+
/* @__PURE__ */ jsx(InputGroupAddon, { children: /* @__PURE__ */ jsx(InputGroupText, { children: "Value" }) }),
|
|
1712
|
+
/* @__PURE__ */ jsx(
|
|
1713
|
+
InputGroupInput,
|
|
1714
|
+
{
|
|
1715
|
+
value: (_a = value.value) != null ? _a : "",
|
|
1716
|
+
onChange: (e) => onChangeValue(__spreadProps(__spreadValues({}, value), { value: e.target.value })),
|
|
1717
|
+
onKeyDown: (e) => {
|
|
1718
|
+
if (e.key === "Enter") onCommit();
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
)
|
|
1722
|
+
] })
|
|
1723
|
+
] });
|
|
1724
|
+
}
|
|
1725
|
+
function CelDateValueEditor({
|
|
1726
|
+
condition,
|
|
1727
|
+
onChangeValue
|
|
1728
|
+
}) {
|
|
1729
|
+
const value = readDateCelValue(condition.value);
|
|
1730
|
+
const operator = condition.operator;
|
|
1731
|
+
const relativeCapable = isDateRelativeOperator(operator);
|
|
1732
|
+
const isRange = isDateRangeOperator(operator);
|
|
1733
|
+
const showTime = !isDateWindowOperator(operator) && !isRange;
|
|
1734
|
+
const isRelative = isRelativeDateCelValue(value);
|
|
1735
|
+
function pickInstant(date) {
|
|
1736
|
+
if (!date) return;
|
|
1737
|
+
const boundary = dateBoundaryForOperator(operator);
|
|
1738
|
+
const instant = formatCalendarDateUtc(date, showTime ? boundary : "start");
|
|
1739
|
+
onChangeValue({ instant });
|
|
1740
|
+
}
|
|
1741
|
+
function pickRange(range) {
|
|
1742
|
+
if (!(range == null ? void 0 : range.from)) return;
|
|
1743
|
+
const start = calendarDayStartInstant(formatCalendarDateUtc(range.from, "start"));
|
|
1744
|
+
const end = range.to ? formatCalendarDateUtc(range.to, "end") : formatCalendarDateUtc(range.from, "end");
|
|
1745
|
+
onChangeValue({ range: { start, end } });
|
|
1746
|
+
}
|
|
1747
|
+
function pickTime(timeOfDay) {
|
|
1748
|
+
if (!value.instant) return;
|
|
1749
|
+
onChangeValue({ instant: withTimeOfDay(value.instant, timeOfDay), hasTime: true });
|
|
1750
|
+
}
|
|
1751
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
1752
|
+
/* @__PURE__ */ jsxs("div", { className: "flex", children: [
|
|
1753
|
+
relativeCapable && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 border-r pr-2", children: [
|
|
1754
|
+
/* @__PURE__ */ jsx("p", { className: "text-body-sm text-muted-foreground px-2 font-medium", children: "Quick select" }),
|
|
1755
|
+
RELATIVE_DURATION_PRESET_VALUES.map((preset) => /* @__PURE__ */ jsx(
|
|
1756
|
+
Button,
|
|
1757
|
+
{
|
|
1758
|
+
type: "button",
|
|
1759
|
+
variant: isRelative && value.preset === preset ? "secondary" : "ghost",
|
|
1760
|
+
size: "sm",
|
|
1761
|
+
className: "justify-start",
|
|
1762
|
+
onClick: () => onChangeValue({ preset }),
|
|
1763
|
+
children: RELATIVE_PRESET_LABELS[preset]
|
|
1764
|
+
},
|
|
1765
|
+
preset
|
|
1766
|
+
))
|
|
1767
|
+
] }),
|
|
1768
|
+
isRange ? /* @__PURE__ */ jsx(
|
|
1769
|
+
Calendar,
|
|
1770
|
+
{
|
|
1771
|
+
mode: "range",
|
|
1772
|
+
selected: value.range ? { from: parseCalendarDate(value.range.start), to: parseCalendarDate(value.range.end) } : void 0,
|
|
1773
|
+
onSelect: pickRange
|
|
1774
|
+
}
|
|
1775
|
+
) : /* @__PURE__ */ jsx(
|
|
1776
|
+
Calendar,
|
|
1777
|
+
{
|
|
1778
|
+
mode: "single",
|
|
1779
|
+
selected: isRelative ? void 0 : parseCalendarDate(value.instant),
|
|
1780
|
+
onSelect: pickInstant
|
|
1781
|
+
}
|
|
1782
|
+
)
|
|
1783
|
+
] }),
|
|
1784
|
+
showTime && !isRelative && /* @__PURE__ */ jsxs("div", { className: "flex w-full items-center gap-2", children: [
|
|
1785
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "cel-date-time", className: "text-body-sm text-muted-foreground shrink-0", children: "Time (UTC)" }),
|
|
1786
|
+
/* @__PURE__ */ jsx(
|
|
1787
|
+
Input,
|
|
1788
|
+
{
|
|
1789
|
+
id: "cel-date-time",
|
|
1790
|
+
type: "time",
|
|
1791
|
+
className: "w-full",
|
|
1792
|
+
value: readTimeOfDay(value.instant),
|
|
1793
|
+
onChange: (e) => pickTime(e.target.value),
|
|
1794
|
+
disabled: !value.instant
|
|
1795
|
+
}
|
|
1796
|
+
)
|
|
1797
|
+
] })
|
|
1798
|
+
] });
|
|
1799
|
+
}
|
|
1800
|
+
export {
|
|
1801
|
+
CelFilterBar,
|
|
1802
|
+
RELATIVE_DURATION_PRESET_VALUES,
|
|
1803
|
+
allowsMultipleConditions,
|
|
1804
|
+
appendCondition,
|
|
1805
|
+
applyDisplayRules,
|
|
1806
|
+
applyOperatorChange,
|
|
1807
|
+
calendarDayStartInstant,
|
|
1808
|
+
celStringLiteral,
|
|
1809
|
+
compileCelFilterTree,
|
|
1810
|
+
computeIsEmitReady,
|
|
1811
|
+
createCelNodeId,
|
|
1812
|
+
createDefaultCondition,
|
|
1813
|
+
createEmptyCelFilterTree,
|
|
1814
|
+
dateBoundaryForOperator,
|
|
1815
|
+
denormalizeColumnIdWithReservedNames,
|
|
1816
|
+
escapeCelString,
|
|
1817
|
+
formatCalendarDateUtc,
|
|
1818
|
+
formatInstantLabel,
|
|
1819
|
+
getAllowedOperators,
|
|
1820
|
+
getColumnEmitOn,
|
|
1821
|
+
getDefaultOperator,
|
|
1822
|
+
getOperatorLabel,
|
|
1823
|
+
getQuantifierLabel,
|
|
1824
|
+
isCelFilterCondition,
|
|
1825
|
+
isCelFilterGroup,
|
|
1826
|
+
isCheckboxCelValue,
|
|
1827
|
+
isDateCelValue,
|
|
1828
|
+
isDateRangeOperator,
|
|
1829
|
+
isDateRelativeOperator,
|
|
1830
|
+
isDateWindowOperator,
|
|
1831
|
+
isMapKeyCelValue,
|
|
1832
|
+
isMultiTextCelValue,
|
|
1833
|
+
isMultiTextColumn,
|
|
1834
|
+
isRealCalendarDay,
|
|
1835
|
+
isRelativeDateCelValue,
|
|
1836
|
+
isRelativeDurationPreset,
|
|
1837
|
+
mergeContextualIntoCelTree,
|
|
1838
|
+
parseCalendarDate,
|
|
1839
|
+
readDateCelValue,
|
|
1840
|
+
readMapKeyCelValue,
|
|
1841
|
+
readMultiTextCelValue,
|
|
1842
|
+
readTimeOfDay,
|
|
1843
|
+
removeConditionById,
|
|
1844
|
+
replaceCondition,
|
|
1845
|
+
resolveColumnDefinition,
|
|
1846
|
+
resolveFilterShape,
|
|
1847
|
+
validateCondition,
|
|
1848
|
+
withTimeOfDay
|
|
1849
|
+
};
|
|
1850
|
+
//# sourceMappingURL=cel-filter-bar.js.map
|