@mastra/playground-ui 37.0.0-alpha.5 → 37.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/CHANGELOG.md +127 -0
  2. package/dist/components/Columns.cjs.js +1 -1
  3. package/dist/components/Columns.es.js +1 -1
  4. package/dist/components/RuleBuilder.cjs.js +10 -0
  5. package/dist/components/RuleBuilder.cjs.js.map +1 -0
  6. package/dist/components/RuleBuilder.d.ts +2 -0
  7. package/dist/components/RuleBuilder.es.js +2 -0
  8. package/dist/components/RuleBuilder.es.js.map +1 -0
  9. package/dist/index.cjs.js +65 -908
  10. package/dist/index.cjs.js.map +1 -1
  11. package/dist/index.es.js +18 -842
  12. package/dist/index.es.js.map +1 -1
  13. package/dist/{multi-column-B9TVKcLl.cjs → multi-column-BLp7eL6P.cjs} +1 -2
  14. package/dist/{multi-column-B9TVKcLl.cjs.map → multi-column-BLp7eL6P.cjs.map} +1 -1
  15. package/dist/{multi-column-p8sEagdM.js → multi-column-DeSb1YDE.js} +1 -2
  16. package/dist/{multi-column-p8sEagdM.js.map → multi-column-DeSb1YDE.js.map} +1 -1
  17. package/dist/rule-builder-Zs6Ywp5-.js +563 -0
  18. package/dist/rule-builder-Zs6Ywp5-.js.map +1 -0
  19. package/dist/rule-builder-vwKEUlZU.cjs +594 -0
  20. package/dist/rule-builder-vwKEUlZU.cjs.map +1 -0
  21. package/dist/src/ds/components/RuleBuilder/index.d.ts +2 -0
  22. package/dist/src/utils/colors.d.ts +1 -0
  23. package/dist/src/utils/errors.d.ts +2 -0
  24. package/dist/src/utils/file.d.ts +1 -0
  25. package/dist/src/utils/formatting.d.ts +1 -0
  26. package/dist/src/utils/json-schema.d.ts +1 -0
  27. package/dist/src/utils/number.d.ts +1 -0
  28. package/dist/src/utils/query-utils.d.ts +1 -0
  29. package/dist/src/utils/rule-engine.d.ts +2 -0
  30. package/dist/src/utils/string.d.ts +1 -0
  31. package/dist/src/utils/truncate-string.d.ts +1 -0
  32. package/dist/utils/colors.cjs.js +15 -0
  33. package/dist/utils/colors.cjs.js.map +1 -0
  34. package/dist/utils/colors.d.ts +2 -0
  35. package/dist/utils/colors.es.js +11 -0
  36. package/dist/utils/colors.es.js.map +1 -0
  37. package/dist/utils/errors.cjs.js +33 -0
  38. package/dist/utils/errors.cjs.js.map +1 -0
  39. package/dist/utils/errors.d.ts +2 -0
  40. package/dist/utils/errors.es.js +22 -0
  41. package/dist/utils/errors.es.js.map +1 -0
  42. package/dist/utils/file.cjs.js +113 -0
  43. package/dist/utils/file.cjs.js.map +1 -0
  44. package/dist/utils/file.d.ts +2 -0
  45. package/dist/utils/file.es.js +100 -0
  46. package/dist/utils/file.es.js.map +1 -0
  47. package/dist/utils/formatting.cjs.js +47 -0
  48. package/dist/utils/formatting.cjs.js.map +1 -0
  49. package/dist/utils/formatting.d.ts +2 -0
  50. package/dist/utils/formatting.es.js +41 -0
  51. package/dist/utils/formatting.es.js.map +1 -0
  52. package/dist/utils/json-schema.cjs.js +3 -0
  53. package/dist/utils/json-schema.cjs.js.map +1 -0
  54. package/dist/utils/json-schema.d.ts +2 -0
  55. package/dist/utils/json-schema.es.js +2 -0
  56. package/dist/utils/json-schema.es.js.map +1 -0
  57. package/dist/utils/number.cjs.js +10 -0
  58. package/dist/utils/number.cjs.js.map +1 -0
  59. package/dist/utils/number.d.ts +2 -0
  60. package/dist/utils/number.es.js +6 -0
  61. package/dist/utils/number.es.js.map +1 -0
  62. package/dist/utils/query-utils.cjs.js +98 -0
  63. package/dist/utils/query-utils.cjs.js.map +1 -0
  64. package/dist/utils/query-utils.d.ts +2 -0
  65. package/dist/utils/query-utils.es.js +88 -0
  66. package/dist/utils/query-utils.es.js.map +1 -0
  67. package/dist/utils/rule-engine.cjs.js +29 -0
  68. package/dist/utils/rule-engine.cjs.js.map +1 -0
  69. package/dist/utils/rule-engine.d.ts +2 -0
  70. package/dist/utils/rule-engine.es.js +21 -0
  71. package/dist/utils/rule-engine.es.js.map +1 -0
  72. package/dist/utils/string.cjs.js +11 -0
  73. package/dist/utils/string.cjs.js.map +1 -0
  74. package/dist/utils/string.d.ts +2 -0
  75. package/dist/utils/string.es.js +7 -0
  76. package/dist/utils/string.es.js.map +1 -0
  77. package/dist/utils/truncate-string.cjs.js +11 -0
  78. package/dist/utils/truncate-string.cjs.js.map +1 -0
  79. package/dist/utils/truncate-string.d.ts +2 -0
  80. package/dist/utils/truncate-string.es.js +7 -0
  81. package/dist/utils/truncate-string.es.js.map +1 -0
  82. package/package.json +8 -8
@@ -0,0 +1,563 @@
1
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
+ import { Type, ToggleLeft, Hash, List, Braces, Ban, Check, ListX, ChevronsLeft, ChevronsRight, ChevronLeft, ChevronRight, Text, TextSearch, EqualNot, Equal, X, Plus, Component } from 'lucide-react';
3
+ import * as React from 'react';
4
+ import { createDefaultRule, isRule, createDefaultRuleGroup } from './utils/rule-engine.es.js';
5
+ import { S as Select, d as SelectTrigger, e as SelectValue, a as SelectContent, c as SelectItem } from './select-BSf4VfTX.js';
6
+ import { c as cn } from './cn-BakJzhuv.js';
7
+ import { Icon } from './icons/Icon.es.js';
8
+ import { I as Input } from './input-CmF5qOKQ.js';
9
+ import { B as Button } from './Button-DUxP65eY.js';
10
+
11
+ const getPropertyType = (property) => {
12
+ if (Array.isArray(property.type)) {
13
+ return property.type.find((t) => t !== "null") || "string";
14
+ }
15
+ return property.type || "string";
16
+ };
17
+ const hasNestedChildren = (property) => {
18
+ const type = getPropertyType(property);
19
+ if (type === "object" && property.properties) {
20
+ return Object.keys(property.properties).length > 0;
21
+ }
22
+ if (type === "array" && property.items) {
23
+ const itemType = getPropertyType(property.items);
24
+ return itemType === "object" && !!property.items.properties;
25
+ }
26
+ return false;
27
+ };
28
+ const getFieldOptionsFromProperties = (properties, parentPath = "") => {
29
+ if (!properties) return [];
30
+ return Object.entries(properties).map(([key, property]) => {
31
+ const path = parentPath ? `${parentPath}.${key}` : key;
32
+ const type = getPropertyType(property);
33
+ const hasChildren = hasNestedChildren(property);
34
+ return {
35
+ path,
36
+ label: property.title || key,
37
+ type,
38
+ hasChildren,
39
+ children: type === "object" ? property.properties : void 0,
40
+ items: type === "array" ? property.items : void 0
41
+ };
42
+ });
43
+ };
44
+ const getFieldOptionsFromSchema = (schema) => {
45
+ return getFieldOptionsFromProperties(schema.properties);
46
+ };
47
+ const getChildFieldOptions = (fieldOption, currentPath) => {
48
+ if (fieldOption.type === "object" && fieldOption.children) {
49
+ return getFieldOptionsFromProperties(fieldOption.children, currentPath);
50
+ }
51
+ if (fieldOption.type === "array" && fieldOption.items) {
52
+ const itemType = getPropertyType(fieldOption.items);
53
+ if (itemType === "object" && fieldOption.items.properties) {
54
+ return getFieldOptionsFromProperties(fieldOption.items.properties, currentPath);
55
+ }
56
+ }
57
+ return [];
58
+ };
59
+ const parseFieldPath = (path) => {
60
+ return path.split(".").filter(Boolean);
61
+ };
62
+ const getFieldOptionAtPath = (schema, path) => {
63
+ const segments = parseFieldPath(path);
64
+ if (segments.length === 0) return void 0;
65
+ let currentProperties = schema.properties;
66
+ let currentPath = "";
67
+ let result;
68
+ for (let i = 0; i < segments.length; i++) {
69
+ const segment = segments[i];
70
+ if (!currentProperties || !currentProperties[segment]) {
71
+ return void 0;
72
+ }
73
+ const property = currentProperties[segment];
74
+ currentPath = currentPath ? `${currentPath}.${segment}` : segment;
75
+ const type = getPropertyType(property);
76
+ const hasChildren = hasNestedChildren(property);
77
+ result = {
78
+ path: currentPath,
79
+ label: property.title || segment,
80
+ type,
81
+ hasChildren,
82
+ children: type === "object" ? property.properties : void 0,
83
+ items: type === "array" ? property.items : void 0
84
+ };
85
+ if (type === "object" && property.properties) {
86
+ currentProperties = property.properties;
87
+ } else if (type === "array" && property.items?.properties) {
88
+ currentProperties = property.items.properties;
89
+ } else {
90
+ currentProperties = void 0;
91
+ }
92
+ }
93
+ return result;
94
+ };
95
+
96
+ const getFieldTypeIcon = (type) => {
97
+ switch (type) {
98
+ case "object":
99
+ return /* @__PURE__ */ jsx(Braces, {});
100
+ case "array":
101
+ return /* @__PURE__ */ jsx(List, {});
102
+ case "number":
103
+ case "integer":
104
+ return /* @__PURE__ */ jsx(Hash, {});
105
+ case "boolean":
106
+ return /* @__PURE__ */ jsx(ToggleLeft, {});
107
+ case "string":
108
+ default:
109
+ return /* @__PURE__ */ jsx(Type, {});
110
+ }
111
+ };
112
+ const FieldLevelSelect = ({
113
+ options,
114
+ value,
115
+ onChange,
116
+ placeholder = "Select field",
117
+ className
118
+ }) => {
119
+ return /* @__PURE__ */ jsx("div", { className: cn("relative", className), children: /* @__PURE__ */ jsxs(Select, { value, onValueChange: onChange, children: [
120
+ /* @__PURE__ */ jsx(SelectTrigger, { className: "min-w-[140px] text-neutral6 bg-surface4", size: "sm", children: /* @__PURE__ */ jsx(SelectValue, { placeholder }) }),
121
+ /* @__PURE__ */ jsx(SelectContent, { children: options.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option.path, children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
122
+ /* @__PURE__ */ jsx(Icon, { size: "sm", className: "text-neutral3", children: getFieldTypeIcon(option.type) }),
123
+ option.label,
124
+ option.hasChildren && /* @__PURE__ */ jsx("span", { className: "text-neutral3", children: "..." })
125
+ ] }) }, option.path)) })
126
+ ] }) });
127
+ };
128
+ const RuleFieldSelect = ({ schema, value, onChange, className }) => {
129
+ const pathSegments = React.useMemo(() => parseFieldPath(value), [value]);
130
+ const selectors = React.useMemo(() => {
131
+ const result = [];
132
+ const rootOptions = getFieldOptionsFromSchema(schema);
133
+ if (rootOptions.length === 0) return result;
134
+ result.push({
135
+ options: rootOptions,
136
+ value: pathSegments[0] || "",
137
+ basePath: ""
138
+ });
139
+ let currentPath = "";
140
+ for (let i = 0; i < pathSegments.length; i++) {
141
+ const segment = pathSegments[i];
142
+ currentPath = currentPath ? `${currentPath}.${segment}` : segment;
143
+ const fieldOption = getFieldOptionAtPath(schema, currentPath);
144
+ if (!fieldOption || !fieldOption.hasChildren) break;
145
+ const childOptions = getChildFieldOptions(fieldOption, currentPath);
146
+ if (childOptions.length === 0) break;
147
+ const nextSegment = pathSegments[i + 1];
148
+ const nextPath = nextSegment ? `${currentPath}.${nextSegment}` : "";
149
+ result.push({
150
+ options: childOptions,
151
+ value: nextPath,
152
+ basePath: currentPath
153
+ });
154
+ }
155
+ return result;
156
+ }, [schema, pathSegments]);
157
+ const handleChange = React.useCallback(
158
+ (levelIndex, newValue) => {
159
+ onChange(newValue);
160
+ },
161
+ [onChange]
162
+ );
163
+ if (selectors.length === 0) {
164
+ return /* @__PURE__ */ jsx("div", { className: cn("text-sm text-neutral3", className), children: "No fields available" });
165
+ }
166
+ return /* @__PURE__ */ jsx("div", { className: cn("flex items-center gap-1", className), children: selectors.map((selector, index) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
167
+ index > 0 && /* @__PURE__ */ jsx("span", { className: "text-neutral3", children: "." }),
168
+ /* @__PURE__ */ jsx(
169
+ FieldLevelSelect,
170
+ {
171
+ options: selector.options,
172
+ value: selector.value,
173
+ onChange: (newValue) => handleChange(index, newValue),
174
+ placeholder: index === 0 ? "Select field" : "Select property"
175
+ }
176
+ )
177
+ ] }, `${selector.basePath}-${index}`)) });
178
+ };
179
+
180
+ const OPERATOR_LABELS = {
181
+ equals: "equals",
182
+ not_equals: "not equals",
183
+ contains: "contains",
184
+ not_contains: "not contains",
185
+ greater_than: "greater than",
186
+ less_than: "less than",
187
+ greater_than_or_equal: "greater than or equal",
188
+ less_than_or_equal: "less than or equal",
189
+ in: "in",
190
+ not_in: "not in",
191
+ exists: "exists",
192
+ not_exists: "not exists"
193
+ };
194
+ const OPERATORS = [
195
+ "equals",
196
+ "not_equals",
197
+ "contains",
198
+ "not_contains",
199
+ "greater_than",
200
+ "less_than",
201
+ "greater_than_or_equal",
202
+ "less_than_or_equal",
203
+ "in",
204
+ "not_in",
205
+ "exists",
206
+ "not_exists"
207
+ ];
208
+
209
+ const OPERATOR_ICONS = {
210
+ equals: /* @__PURE__ */ jsx(Equal, {}),
211
+ not_equals: /* @__PURE__ */ jsx(EqualNot, {}),
212
+ contains: /* @__PURE__ */ jsx(TextSearch, {}),
213
+ not_contains: /* @__PURE__ */ jsx(Text, {}),
214
+ greater_than: /* @__PURE__ */ jsx(ChevronRight, {}),
215
+ less_than: /* @__PURE__ */ jsx(ChevronLeft, {}),
216
+ greater_than_or_equal: /* @__PURE__ */ jsx(ChevronsRight, {}),
217
+ less_than_or_equal: /* @__PURE__ */ jsx(ChevronsLeft, {}),
218
+ in: /* @__PURE__ */ jsx(List, {}),
219
+ not_in: /* @__PURE__ */ jsx(ListX, {}),
220
+ exists: /* @__PURE__ */ jsx(Check, {}),
221
+ not_exists: /* @__PURE__ */ jsx(Ban, {})
222
+ };
223
+ const RuleOperatorSelect = ({ value, onChange, operators, className }) => {
224
+ const operatorList = operators ?? OPERATORS;
225
+ return /* @__PURE__ */ jsx("div", { className: cn("relative", className), children: /* @__PURE__ */ jsxs(Select, { value, onValueChange: onChange, children: [
226
+ /* @__PURE__ */ jsx(SelectTrigger, { className: "min-w-[150px] text-neutral6 bg-surface4", size: "sm", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select operator" }) }),
227
+ /* @__PURE__ */ jsx(SelectContent, { children: operatorList.map((operator) => /* @__PURE__ */ jsx(SelectItem, { value: operator, children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
228
+ /* @__PURE__ */ jsx(Icon, { size: "sm", className: "text-neutral3", children: OPERATOR_ICONS[operator] }),
229
+ OPERATOR_LABELS[operator]
230
+ ] }) }, operator)) })
231
+ ] }) });
232
+ };
233
+
234
+ const parseValue = (stringValue) => {
235
+ const trimmed = stringValue.trim();
236
+ if (trimmed === "") return "";
237
+ if (trimmed === "true") return true;
238
+ if (trimmed === "false") return false;
239
+ if (trimmed === "null") return null;
240
+ const lowerTrimmed = trimmed.toLowerCase();
241
+ if (lowerTrimmed === "infinity" || lowerTrimmed === "-infinity" || lowerTrimmed === "nan") {
242
+ return trimmed;
243
+ }
244
+ const num = Number(trimmed);
245
+ if (!Number.isNaN(num)) return num;
246
+ return stringValue;
247
+ };
248
+ const parseArrayValue = (stringValue) => {
249
+ if (stringValue.trim() === "") return [];
250
+ return stringValue.split(",").map((item) => parseValue(item.trim()));
251
+ };
252
+ const valueToString = (value) => {
253
+ if (value === null) return "null";
254
+ if (value === void 0) return "";
255
+ if (Array.isArray(value)) return value.map(valueToString).join(", ");
256
+ return String(value);
257
+ };
258
+ const booleanToString = (value) => {
259
+ if (value === true) return "true";
260
+ if (value === false) return "false";
261
+ return "";
262
+ };
263
+ const BooleanValueInput = ({ value, onChange, className }) => {
264
+ const stringValue = booleanToString(value);
265
+ const handleChange = React.useCallback(
266
+ (newValue) => {
267
+ onChange(newValue === "true");
268
+ },
269
+ [onChange]
270
+ );
271
+ return /* @__PURE__ */ jsxs("div", { className: cn("relative", className), children: [
272
+ /* @__PURE__ */ jsx(Icon, { size: "sm", className: "absolute left-2 top-1/2 -translate-y-1/2 text-neutral3 pointer-events-none z-10", children: /* @__PURE__ */ jsx(ToggleLeft, {}) }),
273
+ /* @__PURE__ */ jsxs(Select, { value: stringValue, onValueChange: handleChange, children: [
274
+ /* @__PURE__ */ jsx(SelectTrigger, { className: "min-w-[140px] bg-surface4 pl-7", size: "sm", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select value" }) }),
275
+ /* @__PURE__ */ jsxs(SelectContent, { children: [
276
+ /* @__PURE__ */ jsx(SelectItem, { value: "true", children: "true" }),
277
+ /* @__PURE__ */ jsx(SelectItem, { value: "false", children: "false" })
278
+ ] })
279
+ ] })
280
+ ] });
281
+ };
282
+ const NumberValueInput = ({ value, onChange, placeholder, className }) => {
283
+ const displayValue = value === void 0 || value === "" ? "" : String(value);
284
+ const handleChange = React.useCallback(
285
+ (e) => {
286
+ const inputValue = e.target.value;
287
+ if (inputValue === "") {
288
+ onChange("");
289
+ } else {
290
+ const num = Number(inputValue);
291
+ onChange(Number.isNaN(num) ? inputValue : num);
292
+ }
293
+ },
294
+ [onChange]
295
+ );
296
+ return /* @__PURE__ */ jsxs("div", { className: cn("relative", className), children: [
297
+ /* @__PURE__ */ jsx(Icon, { size: "sm", className: "absolute left-2 top-1/2 -translate-y-1/2 text-neutral3 pointer-events-none", children: /* @__PURE__ */ jsx(Hash, {}) }),
298
+ /* @__PURE__ */ jsx(
299
+ Input,
300
+ {
301
+ type: "number",
302
+ value: displayValue,
303
+ onChange: handleChange,
304
+ placeholder: placeholder || "Enter number",
305
+ className: "min-w-[140px] bg-surface4 pl-7",
306
+ size: "sm"
307
+ }
308
+ )
309
+ ] });
310
+ };
311
+ const TextValueInput = ({ value, onChange, placeholder, className }) => {
312
+ const displayValue = valueToString(value);
313
+ const handleChange = React.useCallback(
314
+ (e) => {
315
+ onChange(parseValue(e.target.value));
316
+ },
317
+ [onChange]
318
+ );
319
+ return /* @__PURE__ */ jsxs("div", { className: cn("relative", className), children: [
320
+ /* @__PURE__ */ jsx(Icon, { size: "sm", className: "absolute left-2 top-1/2 -translate-y-1/2 text-neutral3 pointer-events-none", children: /* @__PURE__ */ jsx(Type, {}) }),
321
+ /* @__PURE__ */ jsx(
322
+ Input,
323
+ {
324
+ value: displayValue,
325
+ onChange: handleChange,
326
+ placeholder: placeholder || "Enter value",
327
+ className: "min-w-[140px] bg-surface4 pl-7",
328
+ size: "sm"
329
+ }
330
+ )
331
+ ] });
332
+ };
333
+ const ArrayValueInput = ({ value, onChange, placeholder, className }) => {
334
+ const displayValue = React.useMemo(() => {
335
+ if (Array.isArray(value)) {
336
+ return value.map(valueToString).join(", ");
337
+ }
338
+ return valueToString(value);
339
+ }, [value]);
340
+ const handleChange = React.useCallback(
341
+ (e) => {
342
+ onChange(parseArrayValue(e.target.value));
343
+ },
344
+ [onChange]
345
+ );
346
+ return /* @__PURE__ */ jsx(
347
+ Input,
348
+ {
349
+ value: displayValue,
350
+ onChange: handleChange,
351
+ placeholder: placeholder || "Enter values (comma-separated)",
352
+ className: cn("min-w-[160px] bg-surface4", className),
353
+ size: "sm"
354
+ }
355
+ );
356
+ };
357
+ const RuleValueInput = ({
358
+ value,
359
+ onChange,
360
+ operator,
361
+ fieldType,
362
+ placeholder,
363
+ className
364
+ }) => {
365
+ const isArrayOperator = operator === "in" || operator === "not_in";
366
+ if (isArrayOperator) {
367
+ return /* @__PURE__ */ jsx(ArrayValueInput, { value, onChange, placeholder, className });
368
+ }
369
+ if (fieldType === "boolean") {
370
+ return /* @__PURE__ */ jsx(BooleanValueInput, { value, onChange, placeholder, className });
371
+ }
372
+ if (fieldType === "number" || fieldType === "integer") {
373
+ return /* @__PURE__ */ jsx(NumberValueInput, { value, onChange, placeholder, className });
374
+ }
375
+ return /* @__PURE__ */ jsx(TextValueInput, { value, onChange, placeholder, className });
376
+ };
377
+
378
+ const PRIMITIVE_TYPES = /* @__PURE__ */ new Set(["string", "number", "boolean", "integer"]);
379
+ const RuleRow = ({ schema, rule, onChange, onRemove, className }) => {
380
+ const fieldType = React.useMemo(() => {
381
+ return getFieldOptionAtPath(schema, rule.field)?.type;
382
+ }, [schema, rule.field]);
383
+ const isPrimitive = fieldType !== void 0 && PRIMITIVE_TYPES.has(fieldType);
384
+ const isArray = fieldType === "array";
385
+ const isArrayOperator = rule.operator === "in" || rule.operator === "not_in";
386
+ const isExistenceOperator = rule.operator === "exists" || rule.operator === "not_exists";
387
+ const showComparator = isPrimitive || isArray;
388
+ const showValueInput = !isExistenceOperator && (isPrimitive || isArray && isArrayOperator);
389
+ const handleFieldChange = React.useCallback(
390
+ (field) => {
391
+ onChange({ ...rule, field });
392
+ },
393
+ [rule, onChange]
394
+ );
395
+ const handleOperatorChange = React.useCallback(
396
+ (operator) => {
397
+ const isNewExistence = operator === "exists" || operator === "not_exists";
398
+ if (isNewExistence) {
399
+ onChange({ ...rule, operator, value: void 0 });
400
+ return;
401
+ }
402
+ const isArrayOperator2 = operator === "in" || operator === "not_in";
403
+ const wasArrayOperator = rule.operator === "in" || rule.operator === "not_in";
404
+ let newValue = rule.value;
405
+ if (isArrayOperator2 && !wasArrayOperator) {
406
+ newValue = rule.value !== void 0 ? [rule.value] : [];
407
+ } else if (!isArrayOperator2 && wasArrayOperator) {
408
+ newValue = Array.isArray(rule.value) ? rule.value[0] : rule.value;
409
+ }
410
+ onChange({ ...rule, operator, value: newValue });
411
+ },
412
+ [rule, onChange]
413
+ );
414
+ const handleValueChange = React.useCallback(
415
+ (value) => {
416
+ onChange({ ...rule, value });
417
+ },
418
+ [rule, onChange]
419
+ );
420
+ return /* @__PURE__ */ jsxs("div", { className: "flex justify-between gap-2", children: [
421
+ /* @__PURE__ */ jsxs("div", { className: cn("flex flex-wrap items-center gap-2", className), children: [
422
+ /* @__PURE__ */ jsx(RuleFieldSelect, { schema, value: rule.field, onChange: handleFieldChange }),
423
+ showComparator && /* @__PURE__ */ jsxs(Fragment, { children: [
424
+ /* @__PURE__ */ jsx(
425
+ RuleOperatorSelect,
426
+ {
427
+ value: rule.operator,
428
+ onChange: handleOperatorChange,
429
+ operators: isArray ? ["in", "not_in"] : void 0
430
+ }
431
+ ),
432
+ showValueInput && /* @__PURE__ */ jsx(
433
+ RuleValueInput,
434
+ {
435
+ value: rule.value,
436
+ onChange: handleValueChange,
437
+ operator: rule.operator,
438
+ fieldType
439
+ }
440
+ )
441
+ ] })
442
+ ] }),
443
+ /* @__PURE__ */ jsx(Button, { type: "button", onClick: onRemove, tooltip: "Remove rule", size: "icon-sm", variant: "ghost", children: /* @__PURE__ */ jsx(X, {}) })
444
+ ] });
445
+ };
446
+
447
+ const DEFAULT_MAX_DEPTH = 3;
448
+ const RuleGroupView = ({ schema, group, onChange, onRemove, depth, maxDepth }) => {
449
+ const isRoot = depth === 0;
450
+ const handleToggleOperator = () => {
451
+ onChange({ ...group, operator: group.operator === "AND" ? "OR" : "AND" });
452
+ };
453
+ const handleConditionChange = (index, condition) => {
454
+ onChange({
455
+ ...group,
456
+ conditions: group.conditions.map((c, i) => i === index ? condition : c)
457
+ });
458
+ };
459
+ const handleRemoveCondition = (index) => {
460
+ const newConditions = group.conditions.filter((_, i) => i !== index);
461
+ if (newConditions.length === 0 && onRemove) {
462
+ onRemove();
463
+ } else {
464
+ onChange({ ...group, conditions: newConditions });
465
+ }
466
+ };
467
+ const handleAddRule = () => {
468
+ onChange({ ...group, conditions: [...group.conditions, createDefaultRule()] });
469
+ };
470
+ const handleAddGroup = () => {
471
+ onChange({
472
+ ...group,
473
+ conditions: [
474
+ ...group.conditions,
475
+ createDefaultRuleGroup(group.operator === "AND" ? "OR" : "AND")
476
+ ]
477
+ });
478
+ };
479
+ return /* @__PURE__ */ jsxs("div", { className: cn(isRoot ? "bg-surface2" : "pl-6 bg-surface3"), children: [
480
+ !isRoot && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between pl-3 pr-4 py-1.5 border-b border-border1 border-dashed", children: [
481
+ /* @__PURE__ */ jsx("span", { className: "text-ui-xs text-neutral3", children: "Group" }),
482
+ onRemove && /* @__PURE__ */ jsx(Button, { type: "button", onClick: onRemove, tooltip: "Remove group", size: "icon-sm", variant: "ghost", children: /* @__PURE__ */ jsx(X, {}) })
483
+ ] }),
484
+ group.conditions.map((condition, index) => /* @__PURE__ */ jsx("div", { className: "border-b border-border1 border-dashed last:border-b-0", children: /* @__PURE__ */ jsxs("div", { className: cn("relative", isRule(condition) && "p-4 border-l-4 border-border1"), children: [
485
+ index > 0 && /* @__PURE__ */ jsx(
486
+ "button",
487
+ {
488
+ type: "button",
489
+ onClick: handleToggleOperator,
490
+ className: cn(
491
+ "absolute left-1/2 -translate-x-1/2 z-10 -translate-y-1/2 top-0 text-ui-xs px-3 py-0.5 rounded-full cursor-pointer",
492
+ group.operator === "OR" ? "bg-accent6Dark text-accent6 hover:bg-accent6Dark/70" : "bg-accent3Dark text-accent3 hover:bg-accent3Dark/70"
493
+ ),
494
+ children: group.operator.toLowerCase()
495
+ }
496
+ ),
497
+ isRule(condition) ? /* @__PURE__ */ jsx(
498
+ RuleRow,
499
+ {
500
+ schema,
501
+ rule: condition,
502
+ onChange: (updatedRule) => handleConditionChange(index, updatedRule),
503
+ onRemove: () => handleRemoveCondition(index)
504
+ }
505
+ ) : /* @__PURE__ */ jsx(
506
+ RuleGroupView,
507
+ {
508
+ schema,
509
+ group: condition,
510
+ onChange: (updatedGroup) => handleConditionChange(index, updatedGroup),
511
+ onRemove: () => handleRemoveCondition(index),
512
+ depth: depth + 1,
513
+ maxDepth
514
+ }
515
+ )
516
+ ] }) }, index)),
517
+ /* @__PURE__ */ jsxs("div", { className: "p-2 flex gap-1", children: [
518
+ /* @__PURE__ */ jsxs(Button, { type: "button", onClick: handleAddRule, variant: "ghost", size: "sm", children: [
519
+ /* @__PURE__ */ jsx(Icon, { children: /* @__PURE__ */ jsx(Plus, {}) }),
520
+ "Add rule"
521
+ ] }),
522
+ depth < maxDepth - 1 && /* @__PURE__ */ jsxs(Button, { type: "button", onClick: handleAddGroup, variant: "ghost", size: "sm", children: [
523
+ /* @__PURE__ */ jsx(Icon, { children: /* @__PURE__ */ jsx(Component, {}) }),
524
+ "Add group"
525
+ ] })
526
+ ] })
527
+ ] });
528
+ };
529
+ const RuleBuilder = ({
530
+ schema,
531
+ ruleGroup,
532
+ onChange,
533
+ maxDepth = DEFAULT_MAX_DEPTH,
534
+ className
535
+ }) => {
536
+ const handleGroupChange = React.useCallback(
537
+ (group) => {
538
+ onChange(group);
539
+ },
540
+ [onChange]
541
+ );
542
+ const handleAddFirstRule = React.useCallback(() => {
543
+ onChange({ operator: "AND", conditions: [createDefaultRule()] });
544
+ }, [onChange]);
545
+ if (!ruleGroup) {
546
+ return /* @__PURE__ */ jsxs(
547
+ "button",
548
+ {
549
+ type: "button",
550
+ onClick: handleAddFirstRule,
551
+ className: "flex items-center justify-center gap-2 text-ui-sm text-neutral3 hover:text-neutral6 w-full border border-dashed border-border1 p-2 rounded-md",
552
+ children: [
553
+ /* @__PURE__ */ jsx(Icon, { children: /* @__PURE__ */ jsx(Plus, {}) }),
554
+ "Add conditional rule"
555
+ ]
556
+ }
557
+ );
558
+ }
559
+ return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsx(RuleGroupView, { schema, group: ruleGroup, onChange: handleGroupChange, depth: 0, maxDepth }) });
560
+ };
561
+
562
+ export { OPERATORS as O, RuleBuilder as R, OPERATOR_LABELS as a, RuleFieldSelect as b, RuleOperatorSelect as c, RuleRow as d, RuleValueInput as e, getFieldOptionAtPath as f, getChildFieldOptions as g, getFieldOptionsFromSchema as h, parseFieldPath as p };
563
+ //# sourceMappingURL=rule-builder-Zs6Ywp5-.js.map