react-conditional-ui 1.2.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/dist/index.cjs ADDED
@@ -0,0 +1,1839 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ ConditionDataProvider: () => ConditionDataProvider,
34
+ ConditionParser: () => ConditionDataProvider,
35
+ ConditionalUI: () => ConditionalUI,
36
+ DEFAULT_OPERATORS: () => DEFAULT_OPERATORS,
37
+ Input: () => Input,
38
+ Output: () => Output,
39
+ useConditionDataProvider: () => useConditionDataProvider,
40
+ useConditionalInput: () => useConditionalInput,
41
+ useConditionalOutput: () => useConditionalOutput
42
+ });
43
+ module.exports = __toCommonJS(index_exports);
44
+
45
+ // src/components/Input.tsx
46
+ var import_react3 = require("react");
47
+ var import_TextField = __toESM(require("@mui/material/TextField"), 1);
48
+ var import_InputAdornment = __toESM(require("@mui/material/InputAdornment"), 1);
49
+ var import_IconButton = __toESM(require("@mui/material/IconButton"), 1);
50
+ var import_KeyboardReturn = __toESM(require("@mui/icons-material/KeyboardReturn"), 1);
51
+ var import_ErrorOutline = __toESM(require("@mui/icons-material/ErrorOutline"), 1);
52
+ var import_Tooltip = __toESM(require("@mui/material/Tooltip"), 1);
53
+
54
+ // src/hooks/useConditionalInput.ts
55
+ var import_react2 = require("react");
56
+
57
+ // src/condition-structure/Field.ts
58
+ var Field = class _Field {
59
+ constructor(raw, value, label) {
60
+ this.raw = raw;
61
+ this.value = value;
62
+ this.label = label;
63
+ this.isValid = value !== "";
64
+ }
65
+ static invalid(raw) {
66
+ return new _Field(raw, "", raw);
67
+ }
68
+ };
69
+
70
+ // src/condition-structure/Operator.ts
71
+ var Operator = class _Operator {
72
+ constructor(raw, value, label) {
73
+ this.raw = raw;
74
+ this.value = value;
75
+ this.label = label;
76
+ this.isValid = value !== "";
77
+ }
78
+ static invalid(raw) {
79
+ return new _Operator(raw, "", raw);
80
+ }
81
+ };
82
+
83
+ // src/condition-structure/Value.ts
84
+ var Value = class _Value {
85
+ constructor(raw, opts = { isValid: raw.length > 0 }) {
86
+ this.raw = raw;
87
+ this.isValid = opts.isValid;
88
+ this.errorMessage = opts.errorMessage ?? null;
89
+ this.matchedOption = opts.matchedOption ?? null;
90
+ this.value = this.matchedOption?.value ?? raw;
91
+ this.label = this.matchedOption?.label ?? raw;
92
+ }
93
+ static valid(raw, matchedOption) {
94
+ return new _Value(raw, { isValid: true, matchedOption });
95
+ }
96
+ static invalid(raw, errorMessage) {
97
+ return new _Value(raw, { isValid: false, errorMessage });
98
+ }
99
+ static empty() {
100
+ return new _Value("", { isValid: false, errorMessage: "Missing value" });
101
+ }
102
+ };
103
+
104
+ // src/fuzzy/operators.ts
105
+ var DEFAULT_OPERATORS = [
106
+ {
107
+ label: "equals",
108
+ value: "eq",
109
+ aliases: ["equals", "equal", "equal to", "is equal to", "eq", "=", "==", "is"]
110
+ },
111
+ {
112
+ label: "not equals",
113
+ value: "ne",
114
+ aliases: [
115
+ "not equals",
116
+ "not equal",
117
+ "not equal to",
118
+ "is not equal to",
119
+ "ne",
120
+ "!=",
121
+ "is not",
122
+ "isn't",
123
+ "isnt",
124
+ "doesn't equal",
125
+ "does not equal"
126
+ ]
127
+ },
128
+ {
129
+ label: "greater than",
130
+ value: "gt",
131
+ aliases: [
132
+ "greater than",
133
+ "gt",
134
+ ">",
135
+ "more than",
136
+ "bigger than",
137
+ "above",
138
+ "exceeds",
139
+ "higher than"
140
+ ]
141
+ },
142
+ {
143
+ label: "greater than or equal to",
144
+ value: "gte",
145
+ aliases: [
146
+ "greater than or equal to",
147
+ "greater than or equal",
148
+ "gte",
149
+ ">=",
150
+ "at least",
151
+ "no less than",
152
+ "not less than",
153
+ "not smaller than",
154
+ "not below",
155
+ "not under"
156
+ ]
157
+ },
158
+ {
159
+ label: "less than",
160
+ value: "lt",
161
+ aliases: [
162
+ "less than",
163
+ "lt",
164
+ "<",
165
+ "fewer than",
166
+ "smaller than",
167
+ "below",
168
+ "under",
169
+ "lower than"
170
+ ]
171
+ },
172
+ {
173
+ label: "less than or equal to",
174
+ value: "lte",
175
+ aliases: [
176
+ "less than or equal to",
177
+ "less than or equal",
178
+ "lte",
179
+ "<=",
180
+ "at most",
181
+ "no more than",
182
+ "not greater than",
183
+ "not bigger than",
184
+ "not above",
185
+ "not more than"
186
+ ]
187
+ },
188
+ {
189
+ label: "contains",
190
+ value: "contains",
191
+ aliases: ["contains", "includes", "has", "not missing"]
192
+ }
193
+ ];
194
+
195
+ // src/hooks/useConditionDataProvider.ts
196
+ var import_react = require("react");
197
+
198
+ // src/fuzzy/match-engine.ts
199
+ var import_fuse = __toESM(require("fuse.js"), 1);
200
+
201
+ // src/logger.ts
202
+ var import_debug = __toESM(require("debug"), 1);
203
+ var BASE = "react-conditional-ui";
204
+ var createLogger = (namespace) => (0, import_debug.default)(`${BASE}:${namespace}`);
205
+
206
+ // src/fuzzy/match-engine.ts
207
+ var log = createLogger("match-engine");
208
+ var valueLog = createLogger("value");
209
+ var TYPE_ALLOWED_OPS = {
210
+ number: /* @__PURE__ */ new Set(["eq", "ne", "gt", "lt", "gte", "lte"]),
211
+ enum: /* @__PURE__ */ new Set(["eq", "ne"]),
212
+ text: /* @__PURE__ */ new Set(["eq", "ne", "contains", "starts_with"])
213
+ };
214
+ var MatchEngine = class _MatchEngine {
215
+ constructor(fields, operators) {
216
+ this.fields = fields;
217
+ this.operators = operators;
218
+ this.fieldFuse = new import_fuse.default(fields, {
219
+ keys: ["label"],
220
+ threshold: 0.4,
221
+ includeScore: true
222
+ });
223
+ const flatAliases = operators.flatMap(
224
+ (op) => op.aliases.map((alias) => ({ alias: alias.toLowerCase(), operator: op }))
225
+ );
226
+ this.opFuse = new import_fuse.default(flatAliases, {
227
+ keys: ["alias"],
228
+ threshold: 0.4,
229
+ includeScore: true
230
+ });
231
+ this.perFieldOpFuse = /* @__PURE__ */ new Map();
232
+ for (const field of fields) {
233
+ const restricted = this.resolveOpsForField(field, operators);
234
+ if (restricted !== operators) {
235
+ const aliases = restricted.flatMap(
236
+ (op) => op.aliases.map((alias) => ({ alias: alias.toLowerCase(), operator: op }))
237
+ );
238
+ this.perFieldOpFuse.set(
239
+ field.value,
240
+ new import_fuse.default(aliases, { keys: ["alias"], threshold: 0.4, includeScore: true })
241
+ );
242
+ }
243
+ }
244
+ }
245
+ matchField(candidate) {
246
+ const results = this.fieldFuse.search(candidate);
247
+ if (results.length > 0 && (results[0].score ?? 1) <= 0.4) {
248
+ log(
249
+ "field match: %s -> %s (score: %f)",
250
+ candidate,
251
+ results[0].item.value,
252
+ results[0].score
253
+ );
254
+ return { option: results[0].item, score: results[0].score ?? 1 };
255
+ }
256
+ return null;
257
+ }
258
+ matchOperator(candidate, field) {
259
+ const fuse = (field && this.perFieldOpFuse.get(field.value)) ?? this.opFuse;
260
+ const results = fuse.search(candidate);
261
+ if (results.length > 0 && (results[0].score ?? 1) <= 0.4) {
262
+ const alias = results[0].item.alias;
263
+ const candidateWords = candidate.split(/\s+/).length;
264
+ const aliasWords = alias.split(/\s+/).length;
265
+ if (candidateWords !== aliasWords) return null;
266
+ if (candidate.length < alias.length * 0.5) return null;
267
+ log(
268
+ "operator match: %s -> %s (score: %f)",
269
+ candidate,
270
+ results[0].item.operator.value,
271
+ results[0].score
272
+ );
273
+ return { option: results[0].item.operator, score: results[0].score ?? 1 };
274
+ }
275
+ return null;
276
+ }
277
+ static matchValue(raw, fieldConfig) {
278
+ const knownValues = fieldConfig?.fieldValues;
279
+ const validateValue = fieldConfig?.validateValue;
280
+ const fieldType = fieldConfig?.type;
281
+ if (knownValues && knownValues.length > 0) {
282
+ return _MatchEngine.matchKnownValue(raw, knownValues, validateValue);
283
+ }
284
+ if (raw.length === 0) return Value.empty();
285
+ if (validateValue) {
286
+ const result = validateValue(raw);
287
+ if (result !== true) return Value.invalid(raw, result);
288
+ return Value.valid(raw);
289
+ }
290
+ if (fieldType === "number" && !isFinite(Number(raw))) {
291
+ valueLog("numeric validation failed: raw=%s", raw);
292
+ return Value.invalid(raw, "Expected a number");
293
+ }
294
+ return Value.valid(raw);
295
+ }
296
+ static matchKnownValue(raw, knownValues, validateValue) {
297
+ const lower = raw.toLowerCase();
298
+ const exact = knownValues.find(
299
+ (v) => v.value.toLowerCase() === lower || v.label.toLowerCase() === lower
300
+ );
301
+ if (exact) {
302
+ valueLog("exact match: raw=%s -> %s", raw, exact.value);
303
+ return _MatchEngine.applyValidator(raw, exact, validateValue);
304
+ }
305
+ if (raw.length === 0) return Value.invalid(raw, "Value not recognized");
306
+ const fuse = new import_fuse.default(knownValues, {
307
+ keys: ["label", "value"],
308
+ threshold: 0.4,
309
+ includeScore: true
310
+ });
311
+ const results = fuse.search(raw);
312
+ if (results.length > 0 && (results[0].score ?? 1) <= 0.4) {
313
+ valueLog(
314
+ "fuzzy match: raw=%s -> %s (score: %f)",
315
+ raw,
316
+ results[0].item.value,
317
+ results[0].score
318
+ );
319
+ return _MatchEngine.applyValidator(raw, results[0].item, validateValue);
320
+ }
321
+ valueLog("no match: raw=%s", raw);
322
+ return Value.invalid(raw, "Value not recognized");
323
+ }
324
+ static applyValidator(raw, matched, validateValue) {
325
+ if (validateValue) {
326
+ const result = validateValue(raw);
327
+ if (result !== true) return Value.invalid(raw, result);
328
+ }
329
+ return Value.valid(raw, matched);
330
+ }
331
+ resolveOpsForField(field, allOps) {
332
+ if (field.operators) return field.operators;
333
+ if (field.type) {
334
+ const allowed = TYPE_ALLOWED_OPS[field.type];
335
+ return allOps.filter((op) => allowed.has(op.value));
336
+ }
337
+ return allOps;
338
+ }
339
+ };
340
+
341
+ // src/fuzzy/score.ts
342
+ var AMBIGUOUS_OP_WORDS = /* @__PURE__ */ new Set(["is", "has"]);
343
+ var AMBIGUITY_PENALTY = 0.35;
344
+ var LINKING_VERB_PENALTY = 0.35;
345
+ var LENGTH_BONUS_PER_WORD = 0.01;
346
+ var GAP_PENALTY_PER_WORD = 0.3;
347
+ var UNPARSED_PENALTY = 10;
348
+ var FREETEXT_WORD_COST = 0.1;
349
+ function adjustOperatorScore(baseScore, words, start, end) {
350
+ let score = baseScore;
351
+ const firstWord = words[start];
352
+ if (end - start === 1 && AMBIGUOUS_OP_WORDS.has(firstWord)) {
353
+ score += AMBIGUITY_PENALTY;
354
+ } else if (end - start > 1 && AMBIGUOUS_OP_WORDS.has(firstWord)) {
355
+ score += LINKING_VERB_PENALTY;
356
+ }
357
+ score -= (end - start) * LENGTH_BONUS_PER_WORD;
358
+ if (start > 0) {
359
+ score += start * GAP_PENALTY_PER_WORD;
360
+ }
361
+ return score;
362
+ }
363
+ function scoreConditions(conditions, segmentCount) {
364
+ const unparsed = segmentCount - conditions.length;
365
+ let score = unparsed * UNPARSED_PENALTY;
366
+ for (const c of conditions) {
367
+ score += c.score;
368
+ if (!c.field.isValid) score += UNPARSED_PENALTY;
369
+ if (!c.operator.isValid) score += UNPARSED_PENALTY;
370
+ if (!c.value.isValid) score += UNPARSED_PENALTY;
371
+ if (c.value.isValid && c.value.matchedOption === null) {
372
+ score += c.value.raw.split(/\s+/).length * FREETEXT_WORD_COST;
373
+ }
374
+ }
375
+ return score;
376
+ }
377
+
378
+ // src/fuzzy/word-utils.ts
379
+ var NOISE_WORDS = /* @__PURE__ */ new Set([
380
+ "when",
381
+ "if",
382
+ "the",
383
+ "where",
384
+ "show",
385
+ "that",
386
+ "then",
387
+ "a",
388
+ "an",
389
+ "for",
390
+ "while",
391
+ "can",
392
+ "expect",
393
+ "check",
394
+ "verify",
395
+ "ensure",
396
+ "find",
397
+ "get",
398
+ "list",
399
+ "filter",
400
+ "only",
401
+ "all",
402
+ "with",
403
+ "having",
404
+ "whose",
405
+ "which",
406
+ "given",
407
+ "assuming",
408
+ "suppose",
409
+ "i",
410
+ "want",
411
+ "need",
412
+ "like",
413
+ "please",
414
+ "me",
415
+ "my"
416
+ ]);
417
+ function powerSet(items) {
418
+ const result = [];
419
+ const total = 1 << items.length;
420
+ for (let mask = 1; mask < total; mask++) {
421
+ const subset = [];
422
+ for (let i = 0; i < items.length; i++) {
423
+ if (mask & 1 << i) subset.push(items[i]);
424
+ }
425
+ result.push(subset);
426
+ }
427
+ return result;
428
+ }
429
+ function splitAtIndices(words, indices) {
430
+ const segments = [];
431
+ let start = 0;
432
+ for (const idx of indices) {
433
+ segments.push(words.slice(start, idx).join(" "));
434
+ start = idx + 1;
435
+ }
436
+ segments.push(words.slice(start).join(" "));
437
+ return segments;
438
+ }
439
+ function stripLeadingNoise(words) {
440
+ let i = 0;
441
+ while (i < words.length && NOISE_WORDS.has(words[i])) i++;
442
+ const result = words.slice(i);
443
+ return result;
444
+ }
445
+
446
+ // src/conditions/parser.ts
447
+ var log2 = createLogger("parser");
448
+ var TYPE_ALLOWED_OPS2 = {
449
+ number: /* @__PURE__ */ new Set(["eq", "ne", "gt", "lt", "gte", "lte"]),
450
+ enum: /* @__PURE__ */ new Set(["eq", "ne"]),
451
+ text: /* @__PURE__ */ new Set(["eq", "ne", "contains", "starts_with"])
452
+ };
453
+ var ConditionParser = class {
454
+ constructor(engine) {
455
+ this.fields = engine.fields;
456
+ this.operators = engine.operators;
457
+ this.engine = engine;
458
+ }
459
+ parse(text) {
460
+ const input = text.trim().toLowerCase();
461
+ if (!input) return null;
462
+ const allWords = input.split(/\s+/);
463
+ const words = stripLeadingNoise(allWords);
464
+ if (words.length === 0) return null;
465
+ const fieldResult = this.identifyField(words);
466
+ if (!fieldResult) return null;
467
+ if (fieldResult.remaining.length === 0) {
468
+ return {
469
+ field: fieldResult.field,
470
+ operator: Operator.invalid(""),
471
+ value: Value.empty(),
472
+ score: fieldResult.fieldScore
473
+ };
474
+ }
475
+ const { operator, value, operatorScore } = this.resolveOperator(
476
+ fieldResult.remaining,
477
+ fieldResult.fieldOption
478
+ );
479
+ const result = {
480
+ field: fieldResult.field,
481
+ operator,
482
+ value,
483
+ score: fieldResult.fieldScore + operatorScore
484
+ };
485
+ log2(
486
+ "result: field=%s(%s) op=%s(%s) value=%s (valid: %o)",
487
+ result.field.value,
488
+ result.field.label,
489
+ result.operator.value,
490
+ result.operator.label,
491
+ result.value.value,
492
+ {
493
+ field: result.field.isValid,
494
+ op: result.operator.isValid,
495
+ value: result.value.isValid
496
+ }
497
+ );
498
+ return result;
499
+ }
500
+ identifyField(words) {
501
+ let best = null;
502
+ for (let i = words.length; i >= 1; i--) {
503
+ const candidate = words.slice(0, i).join(" ");
504
+ const match = this.engine.matchField(candidate);
505
+ if (match && (!best || match.score < best.match.score)) {
506
+ best = { candidate, match, wordCount: i };
507
+ }
508
+ }
509
+ if (!best) return null;
510
+ return {
511
+ field: new Field(best.candidate, best.match.option.value, best.match.option.label),
512
+ fieldOption: best.match.option,
513
+ fieldScore: best.match.score,
514
+ remaining: words.slice(best.wordCount)
515
+ };
516
+ }
517
+ getOperatorCandidates(words, fieldOption) {
518
+ const candidates = [];
519
+ for (let start = 0; start < words.length; start++) {
520
+ for (let end = start + 1; end <= words.length; end++) {
521
+ const opRaw = words.slice(start, end).join(" ");
522
+ const opMatch = this.engine.matchOperator(opRaw, fieldOption);
523
+ if (!opMatch) continue;
524
+ const adjustedScore = adjustOperatorScore(opMatch.score, words, start, end);
525
+ candidates.push({
526
+ match: opMatch,
527
+ raw: opRaw,
528
+ endIdx: end,
529
+ adjustedScore
530
+ });
531
+ }
532
+ }
533
+ candidates.sort((a, b) => a.adjustedScore - b.adjustedScore);
534
+ if (candidates.length > 0) {
535
+ log2(
536
+ "operator candidates: %o",
537
+ candidates.map(
538
+ (c) => `${c.raw} -> ${c.match.option.value} (${c.adjustedScore.toFixed(3)})`
539
+ )
540
+ );
541
+ }
542
+ return candidates;
543
+ }
544
+ resolveOperator(words, fieldOption) {
545
+ const candidates = this.getOperatorCandidates(words, fieldOption);
546
+ if (candidates.length === 0) {
547
+ return {
548
+ operator: Operator.invalid(words.join(" ")),
549
+ value: Value.empty(),
550
+ operatorScore: 1
551
+ };
552
+ }
553
+ for (const candidate of candidates) {
554
+ const valueRaw2 = words.slice(candidate.endIdx).join(" ");
555
+ const value = MatchEngine.matchValue(valueRaw2, fieldOption);
556
+ if (value.isValid) {
557
+ const op = candidate.match.option;
558
+ return {
559
+ operator: new Operator(candidate.raw, op.value, op.label),
560
+ value,
561
+ operatorScore: candidate.adjustedScore
562
+ };
563
+ }
564
+ log2(
565
+ "operator '%s' (%s) rejected \u2014 value '%s' invalid, trying next",
566
+ candidate.raw,
567
+ candidate.match.option.value,
568
+ valueRaw2
569
+ );
570
+ }
571
+ const best = candidates[0];
572
+ const bestOp = best.match.option;
573
+ const valueRaw = words.slice(best.endIdx).join(" ");
574
+ return {
575
+ operator: new Operator(best.raw, bestOp.value, bestOp.label),
576
+ value: MatchEngine.matchValue(valueRaw, fieldOption),
577
+ operatorScore: best.adjustedScore
578
+ };
579
+ }
580
+ resolveOpsForField(field, allOps) {
581
+ if (field.operators) return field.operators;
582
+ if (field.type) {
583
+ const allowed = TYPE_ALLOWED_OPS2[field.type];
584
+ return allOps.filter((op) => allowed.has(op.value));
585
+ }
586
+ return allOps;
587
+ }
588
+ allowedOpsForField(field) {
589
+ return this.resolveOpsForField(field, this.operators);
590
+ }
591
+ prefixMatch(partial, candidates) {
592
+ const matches = this.prefixMatches(partial, candidates);
593
+ return matches.length > 0 ? matches[0] : null;
594
+ }
595
+ prefixMatches(partial, candidates, limit = 6) {
596
+ const lower = partial.toLowerCase();
597
+ const results = [];
598
+ for (const candidate of candidates) {
599
+ const cl = candidate.toLowerCase();
600
+ if (cl.startsWith(lower) && cl !== lower) {
601
+ results.push({ completion: cl.slice(lower.length), display: candidate });
602
+ if (results.length >= limit) break;
603
+ }
604
+ }
605
+ return results;
606
+ }
607
+ };
608
+
609
+ // src/consts.ts
610
+ var AND_CONJUNCTION = "and";
611
+ var OR_CONJUNCTION = "or";
612
+
613
+ // src/fuzzy/providers/suggestions.ts
614
+ var SuggestionsProvider = class {
615
+ constructor(parser, segmentResolver) {
616
+ this.parser = parser;
617
+ this.segmentResolver = segmentResolver;
618
+ }
619
+ getCompletions(text, limit = 6) {
620
+ const { last, before } = this.splitForSuggestion(text);
621
+ const trailingSpace = /\s$/.test(text) ? " " : "";
622
+ const direct = this.completionsForSegment(last + trailingSpace, limit);
623
+ if (direct.length > 0) return direct;
624
+ if (!before) return [];
625
+ const { conditions } = this.segmentResolver.resolve(before);
626
+ const previous = conditions[conditions.length - 1];
627
+ if (!previous) return [];
628
+ const inherited = `${previous.field.raw} ${previous.operator.raw} ${last}${trailingSpace}`;
629
+ return this.completionsForSegment(inherited, limit);
630
+ }
631
+ getSuggestion(text) {
632
+ if (!text.trim()) return null;
633
+ const results = this.getCompletions(text, 1);
634
+ if (results.length === 0) return null;
635
+ const r = results[0];
636
+ if (r.completion === r.display && /\s$/.test(text)) return null;
637
+ return r;
638
+ }
639
+ completionsForSegment(text, limit) {
640
+ const input = text.trimStart().toLowerCase();
641
+ if (!input) {
642
+ return this.parser.fields.slice(0, limit).map((f) => ({ completion: f.label, display: f.label }));
643
+ }
644
+ const endsWithSpace = /\s$/.test(input);
645
+ const words = input.split(/\s+/).filter(Boolean);
646
+ if (words.length === 0) return [];
647
+ const fieldResult = this.parser.identifyField(words);
648
+ if (!fieldResult) {
649
+ if (endsWithSpace) return [];
650
+ return this.parser.prefixMatches(
651
+ words.join(" "),
652
+ this.parser.fields.map((f) => f.label),
653
+ limit
654
+ );
655
+ }
656
+ const fieldOption = fieldResult.fieldOption;
657
+ if (fieldResult.remaining.length === 0) {
658
+ if (endsWithSpace) {
659
+ const ops = this.parser.allowedOpsForField(fieldOption);
660
+ return ops.slice(0, limit).map((op) => ({ completion: op.label, display: op.label }));
661
+ }
662
+ return this.parser.prefixMatches(
663
+ words.join(" "),
664
+ this.parser.fields.map((f) => f.label),
665
+ limit
666
+ );
667
+ }
668
+ const candidates = this.parser.getOperatorCandidates(fieldResult.remaining, fieldOption);
669
+ const bestOp = candidates[0];
670
+ if (!bestOp || bestOp.endIdx > fieldResult.remaining.length) {
671
+ if (endsWithSpace) return [];
672
+ const ops = this.parser.allowedOpsForField(fieldOption);
673
+ const aliases = ops.flatMap((op) => op.aliases);
674
+ const opPartial = fieldResult.remaining.join(" ");
675
+ return this.parser.prefixMatches(opPartial, aliases, limit);
676
+ }
677
+ const valueRaw = fieldResult.remaining.slice(bestOp.endIdx).join(" ");
678
+ if (!valueRaw) {
679
+ if (endsWithSpace) {
680
+ const fieldValues2 = fieldOption.fieldValues;
681
+ if (!fieldValues2?.length) return [];
682
+ return fieldValues2.slice(0, limit).map((v) => ({ completion: v.label, display: v.label }));
683
+ }
684
+ const ops = this.parser.allowedOpsForField(fieldOption);
685
+ const aliases = ops.flatMap((op) => op.aliases);
686
+ const opPartial = fieldResult.remaining.join(" ");
687
+ return this.parser.prefixMatches(opPartial, aliases, limit);
688
+ }
689
+ if (endsWithSpace) return [];
690
+ const fieldValues = fieldOption.fieldValues;
691
+ if (!fieldValues?.length) return [];
692
+ return this.parser.prefixMatches(
693
+ valueRaw,
694
+ fieldValues.map((v) => v.label),
695
+ limit
696
+ );
697
+ }
698
+ splitForSuggestion(text) {
699
+ const words = text.split(/\s+/);
700
+ for (let i = words.length - 1; i > 0; i--) {
701
+ const lower = words[i].toLowerCase();
702
+ if (lower === AND_CONJUNCTION || lower === OR_CONJUNCTION) {
703
+ const last = words.slice(i + 1).join(" ");
704
+ if (!last) break;
705
+ return {
706
+ before: words.slice(0, i).join(" "),
707
+ last
708
+ };
709
+ }
710
+ }
711
+ return { last: text, before: null };
712
+ }
713
+ };
714
+
715
+ // src/fuzzy/providers/diagnostics.ts
716
+ var DiagnosticsProvider = class {
717
+ constructor(parser, segmentResolver) {
718
+ this.parser = parser;
719
+ this.segmentResolver = segmentResolver;
720
+ }
721
+ diagnose(text) {
722
+ const input = text.trim();
723
+ if (!input) return [{ start: 0, end: text.length || 1, message: "Empty condition" }];
724
+ const { segments, conditions } = this.segmentResolver.resolve(input);
725
+ const diagnostics = [];
726
+ if (conditions.length === 0) {
727
+ return [
728
+ { start: 0, end: input.length, message: "Could not understand this condition" }
729
+ ];
730
+ }
731
+ for (let i = 0; i < segments.length; i++) {
732
+ const seg = segments[i];
733
+ const offset = input.toLowerCase().indexOf(seg.toLowerCase());
734
+ const condition = conditions[i];
735
+ if (!condition) {
736
+ diagnostics.push({
737
+ start: offset,
738
+ end: offset + seg.length,
739
+ message: "Could not understand this condition"
740
+ });
741
+ continue;
742
+ }
743
+ if (!condition.operator.isValid) {
744
+ const fieldEnd = offset + condition.field.raw.length;
745
+ const fieldConfig = this.parser.fields.find(
746
+ (f) => f.value === condition.field.value
747
+ );
748
+ const hasRestriction = fieldConfig?.operators || fieldConfig?.type;
749
+ diagnostics.push({
750
+ start: fieldEnd,
751
+ end: offset + seg.length,
752
+ message: hasRestriction ? `Operator not supported for ${condition.field.label}` : "Unknown operator"
753
+ });
754
+ }
755
+ if (condition.operator.isValid && !condition.value.isValid) {
756
+ if (!condition.value.raw) {
757
+ diagnostics.push({
758
+ start: offset + seg.length,
759
+ end: offset + seg.length + 1,
760
+ message: "Missing value"
761
+ });
762
+ } else {
763
+ const valStart = offset + seg.toLowerCase().lastIndexOf(condition.value.raw.toLowerCase());
764
+ diagnostics.push({
765
+ start: valStart,
766
+ end: valStart + condition.value.raw.length,
767
+ message: condition.value.errorMessage ?? `Value not recognized for ${condition.field.label}`
768
+ });
769
+ }
770
+ }
771
+ }
772
+ return diagnostics;
773
+ }
774
+ };
775
+
776
+ // src/fuzzy/segments.ts
777
+ var import_debug2 = require("debug");
778
+
779
+ // src/id.ts
780
+ var counter = 0;
781
+ var generateId = () => {
782
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
783
+ return crypto.randomUUID();
784
+ }
785
+ return `id-${Date.now()}-${++counter}`;
786
+ };
787
+
788
+ // src/fuzzy/segments.ts
789
+ var SegmentResolver = class {
790
+ constructor(parser) {
791
+ this.parser = parser;
792
+ }
793
+ resolve(text) {
794
+ const originalWords = text.split(/\s+/);
795
+ const lowerWords = originalWords.map((w) => w.toLowerCase());
796
+ const conjIndices = [];
797
+ for (let i = 0; i < lowerWords.length; i++) {
798
+ if (lowerWords[i] === AND_CONJUNCTION || lowerWords[i] === OR_CONJUNCTION) {
799
+ conjIndices.push({ index: i, conjunction: lowerWords[i] });
800
+ }
801
+ }
802
+ if (conjIndices.length === 0) {
803
+ const single = this.parser.parse(text);
804
+ return {
805
+ segments: [text],
806
+ conditions: single ? [single] : [],
807
+ connector: AND_CONJUNCTION
808
+ };
809
+ }
810
+ const noSplit = this.parser.parse(text);
811
+ let best = {
812
+ segments: [text],
813
+ conditions: noSplit ? [noSplit] : [],
814
+ connector: AND_CONJUNCTION
815
+ };
816
+ let bestScore = scoreConditions(best.conditions, 1);
817
+ for (const subset of powerSet(conjIndices)) {
818
+ const connector = subset[0].conjunction;
819
+ if (subset.some((s) => s.conjunction !== connector)) continue;
820
+ const splitIndices = subset.map((s) => s.index);
821
+ const segments = splitAtIndices(originalWords, splitIndices);
822
+ if (segments.some((s) => !s)) continue;
823
+ const conditions = this.parseSegments(segments);
824
+ const score = scoreConditions(conditions, segments.length);
825
+ if (score < bestScore) {
826
+ bestScore = score;
827
+ best = { segments, conditions, connector };
828
+ }
829
+ }
830
+ if (best.conditions.length === 1 && best.conditions[0].value.isValid && best.conditions[0].value.matchedOption === null) {
831
+ const valueWords = best.conditions[0].value.raw.split(/\s+/);
832
+ const conjInValue = valueWords.find(
833
+ (w) => w.toLowerCase() === AND_CONJUNCTION || w.toLowerCase() === OR_CONJUNCTION
834
+ );
835
+ if (conjInValue) {
836
+ const connector = conjInValue.toLowerCase();
837
+ const matching = conjIndices.filter((c) => c.conjunction === connector);
838
+ for (const subset of powerSet(matching).reverse()) {
839
+ const splitIndices = subset.map((s) => s.index);
840
+ const segments = splitAtIndices(originalWords, splitIndices);
841
+ if (segments.some((s) => !s)) continue;
842
+ const conditions = this.parseSegments(segments);
843
+ if (conditions.length === segments.length && conditions.every((c) => c.field.isValid && c.operator.isValid)) {
844
+ return { segments, conditions, connector };
845
+ }
846
+ }
847
+ }
848
+ }
849
+ return best;
850
+ }
851
+ parseConditions(text) {
852
+ const input = text.trim();
853
+ if (!input) return null;
854
+ const { conditions, connector } = this.resolve(input);
855
+ if (conditions.length === 0) return null;
856
+ const entries = conditions.map((condition) => ({
857
+ id: generateId(),
858
+ condition,
859
+ connector
860
+ }));
861
+ return { id: generateId(), entries };
862
+ }
863
+ parseSegments(segments) {
864
+ const conditions = [];
865
+ for (const segment of segments) {
866
+ const previous = conditions[conditions.length - 1] ?? null;
867
+ const result = this.getInherited(segment, previous) ?? this.parser.parse(segment);
868
+ if (result) conditions.push(result);
869
+ }
870
+ return conditions;
871
+ }
872
+ getInherited(segment, previous) {
873
+ if (!previous) return null;
874
+ const direct = this.parser.parse(segment);
875
+ if (direct && direct.operator.isValid) return direct;
876
+ const inheritField = `${previous.field.raw} ${segment}`;
877
+ const fieldOnly = this.parser.parse(inheritField);
878
+ if (fieldOnly && fieldOnly.operator.isValid) {
879
+ (0, import_debug2.log)("inheriting field for segment: %s -> %s", segment, inheritField);
880
+ return fieldOnly;
881
+ }
882
+ const inheritAll = `${previous.field.raw} ${previous.operator.raw} ${segment}`;
883
+ const full = this.parser.parse(inheritAll);
884
+ if (full) {
885
+ (0, import_debug2.log)("inheriting field+op for segment: %s -> %s", segment, inheritAll);
886
+ return full;
887
+ }
888
+ return direct;
889
+ }
890
+ };
891
+
892
+ // src/condition-data-provider.ts
893
+ var ConditionDataProvider = class {
894
+ constructor(fields, operators) {
895
+ const engine = new MatchEngine(fields, operators);
896
+ const parser = new ConditionParser(engine);
897
+ this.segments = new SegmentResolver(parser);
898
+ this.suggestions = new SuggestionsProvider(parser, this.segments);
899
+ this.diagnostics = new DiagnosticsProvider(parser, this.segments);
900
+ }
901
+ parseComplexCondition(text) {
902
+ return this.segments.parseConditions(text);
903
+ }
904
+ getCompletions(text, limit = 6) {
905
+ return this.suggestions.getCompletions(text, limit);
906
+ }
907
+ getSuggestion(text) {
908
+ return this.suggestions.getSuggestion(text);
909
+ }
910
+ diagnose(text) {
911
+ return this.diagnostics.diagnose(text);
912
+ }
913
+ };
914
+
915
+ // src/hooks/useConditionDataProvider.ts
916
+ function useConditionDataProvider({
917
+ fields,
918
+ operators = DEFAULT_OPERATORS
919
+ }) {
920
+ const provider = (0, import_react.useMemo)(
921
+ () => new ConditionDataProvider(fields, operators),
922
+ [fields, operators]
923
+ );
924
+ const parseComplexCondition = (0, import_react.useCallback)(
925
+ (text) => provider.parseComplexCondition(text),
926
+ [provider]
927
+ );
928
+ const getCompletions = (0, import_react.useCallback)(
929
+ (text, limit) => provider.getCompletions(text, limit),
930
+ [provider]
931
+ );
932
+ const getSuggestion = (0, import_react.useCallback)((text) => provider.getSuggestion(text), [provider]);
933
+ const diagnose = (0, import_react.useCallback)((text) => provider.diagnose(text), [provider]);
934
+ return { provider, parseComplexCondition, getCompletions, getSuggestion, diagnose };
935
+ }
936
+
937
+ // src/hooks/useConditionalInput.ts
938
+ function useConditionalInput({
939
+ fields,
940
+ operators = DEFAULT_OPERATORS,
941
+ value,
942
+ onChange,
943
+ onSubmit
944
+ }) {
945
+ const [internal, setInternal] = (0, import_react2.useState)("");
946
+ const [diagnostics, setDiagnostics] = (0, import_react2.useState)([]);
947
+ const text = value ?? internal;
948
+ const { parseComplexCondition, getSuggestion, getCompletions, diagnose } = useConditionDataProvider({
949
+ fields,
950
+ operators
951
+ });
952
+ const handleChange = (0, import_react2.useCallback)(
953
+ (next) => {
954
+ if (value === void 0) setInternal(next);
955
+ onChange?.(next);
956
+ setDiagnostics([]);
957
+ },
958
+ [value, onChange]
959
+ );
960
+ const handleSubmit = (0, import_react2.useCallback)(() => {
961
+ const group = parseComplexCondition(text);
962
+ if (group) {
963
+ const hasInvalid = group.entries.some(
964
+ (e) => !e.condition.field.isValid || !e.condition.operator.isValid || !e.condition.value.isValid
965
+ );
966
+ if (!hasInvalid) {
967
+ onSubmit?.(group);
968
+ setDiagnostics([]);
969
+ if (value === void 0) setInternal("");
970
+ onChange?.("");
971
+ return;
972
+ }
973
+ }
974
+ setDiagnostics(diagnose(text));
975
+ }, [text, parseComplexCondition, diagnose, value, onChange, onSubmit]);
976
+ return { text, diagnostics, handleChange, handleSubmit, getSuggestion, getCompletions };
977
+ }
978
+
979
+ // src/components/Input.tsx
980
+ var import_jsx_runtime = require("react/jsx-runtime");
981
+ function Input(props) {
982
+ if (isStandalone(props)) {
983
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StandaloneInput, { ...props });
984
+ }
985
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
986
+ InputView,
987
+ {
988
+ value: props.value,
989
+ onChange: props.onChange,
990
+ onSubmit: props.onSubmit,
991
+ getSuggestion: props.getSuggestion,
992
+ getCompletions: props.getCompletions,
993
+ diagnostics: props.diagnostics,
994
+ placeholder: props.placeholder,
995
+ className: props.className,
996
+ style: props.style
997
+ }
998
+ );
999
+ }
1000
+ function isStandalone(props) {
1001
+ return props.fields !== void 0;
1002
+ }
1003
+ function buildOverlaySegments(value, diagnostics) {
1004
+ const sorted = [...diagnostics].sort((a, b) => a.start - b.start);
1005
+ const segments = [];
1006
+ let cursor = 0;
1007
+ for (const d of sorted) {
1008
+ if (d.start > cursor) segments.push({ text: value.slice(cursor, d.start) });
1009
+ segments.push({ text: value.slice(d.start, d.end), diagnostic: d });
1010
+ cursor = d.end;
1011
+ }
1012
+ if (cursor < value.length) segments.push({ text: value.slice(cursor) });
1013
+ return segments;
1014
+ }
1015
+ var GhostInput = (0, import_react3.forwardRef)(function GhostInput2({ ghost, diagnosticSegments, inputValue, onCursorOffset, className, ...inputProps }, ref) {
1016
+ const innerRef = (0, import_react3.useRef)(null);
1017
+ const mirrorRef = (0, import_react3.useRef)(null);
1018
+ const ghostRef = (0, import_react3.useRef)(null);
1019
+ const squigglyRef = (0, import_react3.useRef)(null);
1020
+ (0, import_react3.useLayoutEffect)(() => {
1021
+ if (!mirrorRef.current || !innerRef.current) return;
1022
+ const padding = parseFloat(getComputedStyle(innerRef.current).paddingLeft) || 0;
1023
+ const left = mirrorRef.current.offsetWidth + padding;
1024
+ if (ghostRef.current) ghostRef.current.style.left = `${left}px`;
1025
+ if (squigglyRef.current) {
1026
+ squigglyRef.current.style.paddingLeft = `${padding}px`;
1027
+ squigglyRef.current.style.paddingRight = `${padding}px`;
1028
+ }
1029
+ onCursorOffset?.(left);
1030
+ });
1031
+ const setRefs = (0, import_react3.useCallback)(
1032
+ (el) => {
1033
+ innerRef.current = el;
1034
+ if (typeof ref === "function") ref(el);
1035
+ else if (ref) ref.current = el;
1036
+ },
1037
+ [ref]
1038
+ );
1039
+ const showSquiggly = diagnosticSegments && diagnosticSegments.some((s) => s.diagnostic);
1040
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rcui-input-inner", children: [
1041
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { ref: setRefs, className, ...inputProps }),
1042
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ref: mirrorRef, className: "rcui-mirror", "aria-hidden": "true", children: inputValue }),
1043
+ ghost && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ref: ghostRef, className: "rcui-ghost", "aria-hidden": "true", children: ghost }),
1044
+ showSquiggly && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ref: squigglyRef, className: "rcui-squiggly-layer", "aria-hidden": "true", children: diagnosticSegments.map(
1045
+ (seg, i) => seg.diagnostic ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rcui-squiggly", title: seg.diagnostic.message, children: seg.text }, i) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: seg.text }, i)
1046
+ ) })
1047
+ ] });
1048
+ });
1049
+ function StandaloneInput({
1050
+ fields,
1051
+ operators,
1052
+ value: controlledValue,
1053
+ onChange: controlledOnChange,
1054
+ onSubmit: onGroupParsed,
1055
+ placeholder,
1056
+ className,
1057
+ style
1058
+ }) {
1059
+ const { text, diagnostics, handleChange, handleSubmit, getSuggestion, getCompletions } = useConditionalInput({
1060
+ fields,
1061
+ operators,
1062
+ value: controlledValue,
1063
+ onChange: controlledOnChange,
1064
+ onSubmit: onGroupParsed
1065
+ });
1066
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1067
+ InputView,
1068
+ {
1069
+ value: text,
1070
+ onChange: handleChange,
1071
+ onSubmit: handleSubmit,
1072
+ getSuggestion,
1073
+ getCompletions,
1074
+ diagnostics,
1075
+ placeholder,
1076
+ className,
1077
+ style
1078
+ }
1079
+ );
1080
+ }
1081
+ function InputView({
1082
+ value,
1083
+ onChange,
1084
+ onSubmit,
1085
+ placeholder = "e.g. age greater than 18, ctrl+space for suggestions",
1086
+ getSuggestion,
1087
+ getCompletions,
1088
+ diagnostics = [],
1089
+ className,
1090
+ style
1091
+ }) {
1092
+ const [completions, setCompletions] = (0, import_react3.useState)([]);
1093
+ const [activeIndex, setActiveIndex] = (0, import_react3.useState)(-1);
1094
+ const [cursorLeft, setCursorLeft] = (0, import_react3.useState)(0);
1095
+ const listRef = (0, import_react3.useRef)(null);
1096
+ const ghost = getSuggestion && value ? getSuggestion(value)?.completion ?? null : null;
1097
+ function closeCompletions() {
1098
+ setCompletions([]);
1099
+ setActiveIndex(-1);
1100
+ }
1101
+ function openCompletions() {
1102
+ if (!getCompletions) return;
1103
+ const items = getCompletions(value);
1104
+ setCompletions(items);
1105
+ setActiveIndex(items.length > 0 ? 0 : -1);
1106
+ }
1107
+ function acceptCompletion(item) {
1108
+ onChange(value + item.completion);
1109
+ closeCompletions();
1110
+ }
1111
+ function handleChange(e) {
1112
+ onChange(e.target.value);
1113
+ closeCompletions();
1114
+ }
1115
+ function acceptGhost() {
1116
+ if (ghost) onChange(value + ghost);
1117
+ }
1118
+ function handleKeyDown(e) {
1119
+ if (e.key === " " && e.ctrlKey) {
1120
+ e.preventDefault();
1121
+ openCompletions();
1122
+ return;
1123
+ }
1124
+ if (completions.length > 0) {
1125
+ if (e.key === "ArrowDown") {
1126
+ e.preventDefault();
1127
+ setActiveIndex((i) => (i + 1) % completions.length);
1128
+ return;
1129
+ }
1130
+ if (e.key === "ArrowUp") {
1131
+ e.preventDefault();
1132
+ setActiveIndex((i) => (i - 1 + completions.length) % completions.length);
1133
+ return;
1134
+ }
1135
+ if (e.key === "Enter") {
1136
+ e.preventDefault();
1137
+ if (activeIndex >= 0) {
1138
+ acceptCompletion(completions[activeIndex]);
1139
+ } else {
1140
+ closeCompletions();
1141
+ onSubmit();
1142
+ }
1143
+ return;
1144
+ }
1145
+ if (e.key === "Escape") {
1146
+ e.preventDefault();
1147
+ closeCompletions();
1148
+ return;
1149
+ }
1150
+ if (e.key === "Tab") {
1151
+ e.preventDefault();
1152
+ if (activeIndex >= 0) {
1153
+ acceptCompletion(completions[activeIndex]);
1154
+ }
1155
+ return;
1156
+ }
1157
+ }
1158
+ if (e.key === "Enter") {
1159
+ e.preventDefault();
1160
+ onSubmit();
1161
+ return;
1162
+ }
1163
+ if (e.key === "Tab" && ghost) {
1164
+ e.preventDefault();
1165
+ acceptGhost();
1166
+ }
1167
+ }
1168
+ const hasErrors = diagnostics.length > 0;
1169
+ const errorSummary = diagnostics.map((d) => d.message).join("; ");
1170
+ const diagnosticSegments = hasErrors ? buildOverlaySegments(value, diagnostics) : null;
1171
+ const ghostInputProps = {
1172
+ ghost,
1173
+ diagnosticSegments,
1174
+ inputValue: value,
1175
+ onCursorOffset: setCursorLeft
1176
+ };
1177
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: ["rcui-input-wrapper", className].filter(Boolean).join(" "), style, children: [
1178
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1179
+ import_TextField.default,
1180
+ {
1181
+ fullWidth: true,
1182
+ variant: "outlined",
1183
+ size: "small",
1184
+ placeholder,
1185
+ value,
1186
+ onChange: handleChange,
1187
+ onKeyDown: handleKeyDown,
1188
+ onBlur: () => setTimeout(closeCompletions, 150),
1189
+ className: "rcui-input",
1190
+ slotProps: {
1191
+ input: {
1192
+ inputComponent: GhostInput,
1193
+ inputProps: ghostInputProps,
1194
+ endAdornment: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_InputAdornment.default, { position: "end", children: hasErrors ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Tooltip.default, { title: errorSummary, arrow: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_IconButton.default, { size: "small", onClick: onSubmit, edge: "end", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1195
+ import_ErrorOutline.default,
1196
+ {
1197
+ fontSize: "small",
1198
+ className: "rcui-adornment-error"
1199
+ }
1200
+ ) }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_IconButton.default, { size: "small", onClick: onSubmit, edge: "end", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1201
+ import_KeyboardReturn.default,
1202
+ {
1203
+ fontSize: "small",
1204
+ className: "rcui-adornment-enter"
1205
+ }
1206
+ ) }) })
1207
+ }
1208
+ }
1209
+ }
1210
+ ),
1211
+ completions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1212
+ "ul",
1213
+ {
1214
+ ref: listRef,
1215
+ className: "rcui-completions",
1216
+ role: "listbox",
1217
+ style: { left: cursorLeft },
1218
+ children: completions.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1219
+ "li",
1220
+ {
1221
+ id: `rcui-completion-${i}`,
1222
+ role: "option",
1223
+ "aria-selected": i === activeIndex,
1224
+ className: `rcui-completion-item${i === activeIndex ? " rcui-completion-item--active" : ""}`,
1225
+ onMouseDown: (e) => {
1226
+ e.preventDefault();
1227
+ acceptCompletion(item);
1228
+ },
1229
+ onMouseEnter: () => setActiveIndex(i),
1230
+ children: item.display
1231
+ },
1232
+ item.display
1233
+ ))
1234
+ }
1235
+ )
1236
+ ] });
1237
+ }
1238
+
1239
+ // src/components/Output.tsx
1240
+ var import_Chip3 = __toESM(require("@mui/material/Chip"), 1);
1241
+ var import_Paper = __toESM(require("@mui/material/Paper"), 1);
1242
+ var import_Typography = __toESM(require("@mui/material/Typography"), 1);
1243
+ var import_sortable3 = require("@dnd-kit/sortable");
1244
+ var import_core2 = require("@dnd-kit/core");
1245
+
1246
+ // src/components/OutputRow.tsx
1247
+ var import_react4 = require("react");
1248
+ var import_sortable = require("@dnd-kit/sortable");
1249
+ var import_utilities = require("@dnd-kit/utilities");
1250
+ var import_Chip = __toESM(require("@mui/material/Chip"), 1);
1251
+ var import_Popover = __toESM(require("@mui/material/Popover"), 1);
1252
+ var import_List = __toESM(require("@mui/material/List"), 1);
1253
+ var import_ListItemButton = __toESM(require("@mui/material/ListItemButton"), 1);
1254
+ var import_ListItemText = __toESM(require("@mui/material/ListItemText"), 1);
1255
+ var import_TextField2 = __toESM(require("@mui/material/TextField"), 1);
1256
+ var import_IconButton2 = __toESM(require("@mui/material/IconButton"), 1);
1257
+ var import_Close = __toESM(require("@mui/icons-material/Close"), 1);
1258
+ var import_DragIndicator = __toESM(require("@mui/icons-material/DragIndicator"), 1);
1259
+ var import_jsx_runtime2 = require("react/jsx-runtime");
1260
+ function OutputRow({
1261
+ id,
1262
+ condition,
1263
+ fields,
1264
+ operators,
1265
+ onUpdate,
1266
+ onRemove
1267
+ }) {
1268
+ const { attributes, listeners, setNodeRef, transform, transition, isDragging, isOver } = (0, import_sortable.useSortable)({ id });
1269
+ const style = {
1270
+ transform: import_utilities.CSS.Transform.toString(transform),
1271
+ transition,
1272
+ opacity: isDragging ? 0.3 : 1
1273
+ };
1274
+ const [popover, setPopover] = (0, import_react4.useState)(null);
1275
+ const [valueEdit, setValueEdit] = (0, import_react4.useState)("");
1276
+ const openPopover = (target, anchor) => {
1277
+ setPopover({ target, anchor });
1278
+ if (target === "value") setValueEdit(condition.value.raw ?? "");
1279
+ };
1280
+ const closePopover = () => setPopover(null);
1281
+ const editable = !!onUpdate;
1282
+ const resolveFieldValues = (f) => f.fieldValues;
1283
+ const resolveOperators = (f) => f.operators ?? operators;
1284
+ const selectField = (f) => {
1285
+ onUpdate?.({
1286
+ ...condition,
1287
+ field: new Field(f.label, f.value, f.label),
1288
+ value: MatchEngine.matchValue(condition.value.raw, f)
1289
+ });
1290
+ closePopover();
1291
+ };
1292
+ const selectOperator = (op) => {
1293
+ onUpdate?.({
1294
+ ...condition,
1295
+ operator: new Operator(op.label, op.value, op.label)
1296
+ });
1297
+ closePopover();
1298
+ };
1299
+ const submitValue = (raw) => {
1300
+ const fieldConfig = fields.find((f) => f.value === condition.field.value);
1301
+ onUpdate?.({
1302
+ ...condition,
1303
+ value: MatchEngine.matchValue(raw, fieldConfig)
1304
+ });
1305
+ closePopover();
1306
+ };
1307
+ const currentField = fields.find((f) => f.value === condition.field.value);
1308
+ const fieldValues = currentField ? resolveFieldValues(currentField) : void 0;
1309
+ const activeOperators = currentField ? resolveOperators(currentField) : operators;
1310
+ const rowClass = ["rcui-row", isOver && !isDragging ? "rcui-row--over" : ""].filter(Boolean).join(" ");
1311
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { ref: setNodeRef, style, className: rowClass, children: [
1312
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1313
+ "div",
1314
+ {
1315
+ ...attributes,
1316
+ ...listeners,
1317
+ className: "rcui-drag-handle",
1318
+ "aria-label": "drag handle",
1319
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_DragIndicator.default, { fontSize: "small" })
1320
+ }
1321
+ ),
1322
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1323
+ import_Chip.default,
1324
+ {
1325
+ label: condition.field.label,
1326
+ color: condition.field.isValid ? "primary" : "error",
1327
+ variant: "filled",
1328
+ onClick: editable ? (e) => openPopover("field", e.currentTarget) : void 0,
1329
+ className: condition.field.isValid ? "rcui-chip-field" : "rcui-chip-field--error"
1330
+ }
1331
+ ),
1332
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1333
+ import_Chip.default,
1334
+ {
1335
+ label: condition.operator.label,
1336
+ color: condition.operator.isValid ? "secondary" : "error",
1337
+ variant: "filled",
1338
+ onClick: editable ? (e) => openPopover("operator", e.currentTarget) : void 0,
1339
+ className: condition.operator.isValid ? "rcui-chip-operator" : "rcui-chip-operator--error"
1340
+ }
1341
+ ),
1342
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1343
+ import_Chip.default,
1344
+ {
1345
+ label: condition.value.label || "\u2026",
1346
+ variant: "filled",
1347
+ onClick: editable ? (e) => openPopover("value", e.currentTarget) : void 0,
1348
+ className: "rcui-chip-value"
1349
+ }
1350
+ ),
1351
+ onRemove && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_IconButton2.default, { size: "small", onClick: onRemove, "aria-label": "remove condition", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_Close.default, { fontSize: "small" }) }),
1352
+ editable && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1353
+ import_Popover.default,
1354
+ {
1355
+ open: !!popover,
1356
+ anchorEl: popover?.anchor,
1357
+ onClose: closePopover,
1358
+ anchorOrigin: { vertical: "bottom", horizontal: "left" },
1359
+ children: [
1360
+ popover?.target === "field" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_List.default, { dense: true, children: fields.map((f) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1361
+ import_ListItemButton.default,
1362
+ {
1363
+ selected: f.value === condition.field.value,
1364
+ onClick: () => selectField(f),
1365
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ListItemText.default, { primary: f.label })
1366
+ },
1367
+ f.value
1368
+ )) }),
1369
+ popover?.target === "operator" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_List.default, { dense: true, children: activeOperators.map((op) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1370
+ import_ListItemButton.default,
1371
+ {
1372
+ selected: op.value === condition.operator.value,
1373
+ onClick: () => selectOperator(op),
1374
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ListItemText.default, { primary: op.label })
1375
+ },
1376
+ op.value
1377
+ )) }),
1378
+ popover?.target === "value" && (fieldValues ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_List.default, { dense: true, children: fieldValues.map((v) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1379
+ import_ListItemButton.default,
1380
+ {
1381
+ selected: v.value === condition.value.value,
1382
+ onClick: () => submitValue(v.value),
1383
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ListItemText.default, { primary: v.label })
1384
+ },
1385
+ v.value
1386
+ )) }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "rcui-popover-value-edit", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1387
+ import_TextField2.default,
1388
+ {
1389
+ size: "small",
1390
+ autoFocus: true,
1391
+ value: valueEdit,
1392
+ onChange: (e) => setValueEdit(e.target.value),
1393
+ onKeyDown: (e) => {
1394
+ if (e.key === "Enter") submitValue(valueEdit);
1395
+ },
1396
+ placeholder: "Enter value"
1397
+ }
1398
+ ) }))
1399
+ ]
1400
+ }
1401
+ )
1402
+ ] });
1403
+ }
1404
+
1405
+ // src/components/ConnectorChip.tsx
1406
+ var import_Chip2 = __toESM(require("@mui/material/Chip"), 1);
1407
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1408
+ function ConnectorChip({ connector, onToggle }) {
1409
+ const chipClass = ["rcui-connector-chip", onToggle ? "rcui-connector-chip--clickable" : ""].filter(Boolean).join(" ");
1410
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "rcui-connector", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1411
+ import_Chip2.default,
1412
+ {
1413
+ label: connector.toUpperCase(),
1414
+ size: "small",
1415
+ variant: "filled",
1416
+ onClick: onToggle,
1417
+ className: chipClass
1418
+ }
1419
+ ) });
1420
+ }
1421
+
1422
+ // src/components/OutputDndContext.tsx
1423
+ var import_react5 = require("react");
1424
+ var import_core = require("@dnd-kit/core");
1425
+ var import_sortable2 = require("@dnd-kit/sortable");
1426
+
1427
+ // src/components/output-drag-end.ts
1428
+ var UNGROUP_ZONE_ID = "ungrouped-drop-zone";
1429
+ var GROUP_DROPPABLE_PREFIX = "group:";
1430
+ function findGroupByEntryId(groups, entryId) {
1431
+ for (const group of groups) {
1432
+ if (group.entries.some((e) => e.id === entryId)) return group;
1433
+ }
1434
+ return null;
1435
+ }
1436
+ function applyOutputDragEnd(groups, activeId, overId, mutations) {
1437
+ if (!overId || activeId === overId) return;
1438
+ const sourceGroup = findGroupByEntryId(groups, activeId);
1439
+ if (!sourceGroup) return;
1440
+ if (overId === UNGROUP_ZONE_ID) {
1441
+ mutations.ungroupEntry(sourceGroup.id, activeId);
1442
+ return;
1443
+ }
1444
+ if (overId.startsWith(GROUP_DROPPABLE_PREFIX)) {
1445
+ const targetGroupId = overId.slice(GROUP_DROPPABLE_PREFIX.length);
1446
+ if (targetGroupId !== sourceGroup.id) {
1447
+ mutations.moveBetweenGroups(sourceGroup.id, targetGroupId, activeId, null);
1448
+ }
1449
+ return;
1450
+ }
1451
+ const targetGroup = findGroupByEntryId(groups, overId);
1452
+ if (targetGroup && sourceGroup.id === targetGroup.id) {
1453
+ mutations.reorderWithinGroup(sourceGroup.id, activeId, overId);
1454
+ } else if (targetGroup) {
1455
+ mutations.moveBetweenGroups(sourceGroup.id, targetGroup.id, activeId, overId);
1456
+ } else {
1457
+ mutations.ungroupEntry(sourceGroup.id, activeId);
1458
+ }
1459
+ }
1460
+
1461
+ // src/components/OutputDndContext.tsx
1462
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1463
+ function OutputDndContext({ groups, mutations, children, renderOverlay }) {
1464
+ const [activeEntry, setActiveEntry] = (0, import_react5.useState)(null);
1465
+ const sensors = (0, import_core.useSensors)(
1466
+ (0, import_core.useSensor)(import_core.PointerSensor, { activationConstraint: { distance: 5 } }),
1467
+ (0, import_core.useSensor)(import_core.KeyboardSensor, { coordinateGetter: import_sortable2.sortableKeyboardCoordinates })
1468
+ );
1469
+ const handleDragStart = (0, import_react5.useCallback)(
1470
+ (event) => {
1471
+ const entryId = String(event.active.id);
1472
+ for (const group of groups) {
1473
+ const entry = group.entries.find((e) => e.id === entryId);
1474
+ if (entry) {
1475
+ setActiveEntry(entry);
1476
+ return;
1477
+ }
1478
+ }
1479
+ },
1480
+ [groups]
1481
+ );
1482
+ const handleDragEnd = (0, import_react5.useCallback)(
1483
+ (event) => {
1484
+ setActiveEntry(null);
1485
+ const over = event.over;
1486
+ applyOutputDragEnd(
1487
+ groups,
1488
+ String(event.active.id),
1489
+ over ? String(over.id) : null,
1490
+ mutations
1491
+ );
1492
+ },
1493
+ [groups, mutations]
1494
+ );
1495
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1496
+ import_core.DndContext,
1497
+ {
1498
+ sensors,
1499
+ collisionDetection: import_core.pointerWithin,
1500
+ onDragStart: handleDragStart,
1501
+ onDragEnd: handleDragEnd,
1502
+ children: [
1503
+ children,
1504
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_core.DragOverlay, { dropAnimation: null, children: activeEntry && renderOverlay ? renderOverlay(activeEntry) : null })
1505
+ ]
1506
+ }
1507
+ );
1508
+ }
1509
+
1510
+ // src/hooks/useConditionalOutput.ts
1511
+ var import_react6 = require("react");
1512
+ function useConditionalOutput({
1513
+ groups: controlledGroups,
1514
+ onGroupsChange
1515
+ } = {}) {
1516
+ const [internalGroups, setInternalGroups] = (0, import_react6.useState)([]);
1517
+ const isControlled = controlledGroups !== void 0;
1518
+ const groups = isControlled ? controlledGroups : internalGroups;
1519
+ const onGroupsChangeRef = (0, import_react6.useRef)(onGroupsChange);
1520
+ (0, import_react6.useEffect)(() => {
1521
+ onGroupsChangeRef.current = onGroupsChange;
1522
+ }, [onGroupsChange]);
1523
+ const didMount = (0, import_react6.useRef)(false);
1524
+ (0, import_react6.useEffect)(() => {
1525
+ if (!didMount.current) {
1526
+ didMount.current = true;
1527
+ onGroupsChangeRef.current?.(groups);
1528
+ }
1529
+ }, []);
1530
+ const commit = (0, import_react6.useCallback)(
1531
+ (next) => {
1532
+ if (!isControlled) setInternalGroups(next);
1533
+ onGroupsChangeRef.current?.(next);
1534
+ },
1535
+ [isControlled]
1536
+ );
1537
+ const addGroup = (0, import_react6.useCallback)(
1538
+ (group) => {
1539
+ commit([...groups, group]);
1540
+ },
1541
+ [groups, commit]
1542
+ );
1543
+ const removeEntry = (0, import_react6.useCallback)(
1544
+ (groupId, entryId) => {
1545
+ commit(
1546
+ groups.map(
1547
+ (g) => g.id === groupId ? { ...g, entries: g.entries.filter((e) => e.id !== entryId) } : g
1548
+ ).filter((g) => g.entries.length > 0)
1549
+ );
1550
+ },
1551
+ [groups, commit]
1552
+ );
1553
+ const toggleConnector = (0, import_react6.useCallback)(
1554
+ (groupId, entryId) => {
1555
+ commit(
1556
+ groups.map((g) => {
1557
+ if (g.id !== groupId) return g;
1558
+ return {
1559
+ ...g,
1560
+ entries: g.entries.map(
1561
+ (e) => e.id === entryId ? {
1562
+ ...e,
1563
+ connector: e.connector === "and" ? "or" : "and"
1564
+ } : e
1565
+ )
1566
+ };
1567
+ })
1568
+ );
1569
+ },
1570
+ [groups, commit]
1571
+ );
1572
+ const updateCondition = (0, import_react6.useCallback)(
1573
+ (groupId, entryId, condition) => {
1574
+ commit(
1575
+ groups.map((g) => {
1576
+ if (g.id !== groupId) return g;
1577
+ return {
1578
+ ...g,
1579
+ entries: g.entries.map((e) => e.id === entryId ? { ...e, condition } : e)
1580
+ };
1581
+ })
1582
+ );
1583
+ },
1584
+ [groups, commit]
1585
+ );
1586
+ const updateGroupConfig = (0, import_react6.useCallback)(
1587
+ (groupId, config) => {
1588
+ commit(
1589
+ groups.map(
1590
+ (g) => g.id === groupId ? { ...g, config: { ...g.config, ...config } } : g
1591
+ )
1592
+ );
1593
+ },
1594
+ [groups, commit]
1595
+ );
1596
+ const reorderWithinGroup = (0, import_react6.useCallback)(
1597
+ (groupId, activeId, overId) => {
1598
+ commit(
1599
+ groups.map((g) => {
1600
+ if (g.id !== groupId) return g;
1601
+ const oldIdx = g.entries.findIndex((e) => e.id === activeId);
1602
+ const newIdx = g.entries.findIndex((e) => e.id === overId);
1603
+ if (oldIdx === -1 || newIdx === -1) return g;
1604
+ const entries = [...g.entries];
1605
+ const [moved] = entries.splice(oldIdx, 1);
1606
+ entries.splice(newIdx, 0, moved);
1607
+ return { ...g, entries };
1608
+ })
1609
+ );
1610
+ },
1611
+ [groups, commit]
1612
+ );
1613
+ const moveBetweenGroups = (0, import_react6.useCallback)(
1614
+ (sourceGroupId, targetGroupId, entryId, overEntryId) => {
1615
+ const sourceGroup = groups.find((g) => g.id === sourceGroupId);
1616
+ const targetGroup = groups.find((g) => g.id === targetGroupId);
1617
+ if (!sourceGroup || !targetGroup) return;
1618
+ const entry = sourceGroup.entries.find((e) => e.id === entryId);
1619
+ if (!entry) return;
1620
+ const targetConnector = targetGroup.entries.length > 0 ? targetGroup.entries[0].connector : "and";
1621
+ const movedEntry = { ...entry, connector: targetConnector };
1622
+ let newGroups = groups.map((g) => {
1623
+ if (g.id === sourceGroupId) {
1624
+ return { ...g, entries: g.entries.filter((e) => e.id !== entryId) };
1625
+ }
1626
+ if (g.id === targetGroupId) {
1627
+ const entries = [...g.entries];
1628
+ if (overEntryId) {
1629
+ const idx = entries.findIndex((e) => e.id === overEntryId);
1630
+ entries.splice(idx + 1, 0, movedEntry);
1631
+ } else {
1632
+ entries.push(movedEntry);
1633
+ }
1634
+ return { ...g, entries };
1635
+ }
1636
+ return g;
1637
+ });
1638
+ commit(newGroups.filter((g) => g.entries.length > 0));
1639
+ },
1640
+ [groups, commit]
1641
+ );
1642
+ const ungroupEntry = (0, import_react6.useCallback)(
1643
+ (groupId, entryId) => {
1644
+ const group = groups.find((g) => g.id === groupId);
1645
+ if (!group) return;
1646
+ const entry = group.entries.find((e) => e.id === entryId);
1647
+ if (!entry) return;
1648
+ const newGroup = {
1649
+ id: generateId(),
1650
+ entries: [{ ...entry, connector: "and" }]
1651
+ };
1652
+ let newGroups = groups.map(
1653
+ (g) => g.id === groupId ? { ...g, entries: g.entries.filter((e) => e.id !== entryId) } : g
1654
+ ).filter((g) => g.entries.length > 0);
1655
+ newGroups.push(newGroup);
1656
+ commit(newGroups);
1657
+ },
1658
+ [groups, commit]
1659
+ );
1660
+ const setGroups = (0, import_react6.useCallback)((next) => commit(next), [commit]);
1661
+ const mutations = {
1662
+ addGroup,
1663
+ removeEntry,
1664
+ toggleConnector,
1665
+ updateCondition,
1666
+ updateGroupConfig,
1667
+ reorderWithinGroup,
1668
+ moveBetweenGroups,
1669
+ ungroupEntry,
1670
+ setGroups
1671
+ };
1672
+ return { groups, mutations };
1673
+ }
1674
+
1675
+ // src/components/Output.tsx
1676
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1677
+ var DEFAULT_GROUP_CONFIG = {
1678
+ editable: true,
1679
+ removable: true,
1680
+ variant: "outlined"
1681
+ };
1682
+ function Output({
1683
+ fields,
1684
+ operators,
1685
+ groups: controlledGroups,
1686
+ onGroupsChange,
1687
+ defaultGroupConfig,
1688
+ className,
1689
+ style
1690
+ }) {
1691
+ const { groups, mutations } = useConditionalOutput({
1692
+ groups: controlledGroups,
1693
+ onGroupsChange
1694
+ });
1695
+ const readOnly = controlledGroups !== void 0 && !onGroupsChange;
1696
+ const effectiveDefault = readOnly ? { editable: false, removable: false, ...defaultGroupConfig } : defaultGroupConfig;
1697
+ const rootClass = ["rcui-output", className].filter(Boolean).join(" ");
1698
+ const renderOverlay = (entry) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "rcui-overlay", children: [
1699
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_Chip3.default, { label: entry.condition.field.label, size: "small", className: "rcui-chip-field" }),
1700
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1701
+ import_Chip3.default,
1702
+ {
1703
+ label: entry.condition.operator.label,
1704
+ size: "small",
1705
+ className: "rcui-chip-operator"
1706
+ }
1707
+ ),
1708
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1709
+ import_Chip3.default,
1710
+ {
1711
+ label: entry.condition.value.label || "\u2026",
1712
+ size: "small",
1713
+ className: "rcui-chip-value"
1714
+ }
1715
+ )
1716
+ ] });
1717
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(OutputDndContext, { groups, mutations, renderOverlay, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(DropZone, { className: rootClass, style, children: groups.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_Typography.default, { variant: "body2", color: "text.secondary", children: "Parsed condition will appear here\u2026" }) : groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1718
+ GroupCard,
1719
+ {
1720
+ group,
1721
+ fields,
1722
+ operators,
1723
+ config: resolveConfig(group.config, effectiveDefault),
1724
+ mutations
1725
+ },
1726
+ group.id
1727
+ )) }) });
1728
+ }
1729
+ function resolveConfig(groupConfig, defaultConfig) {
1730
+ return { ...DEFAULT_GROUP_CONFIG, ...defaultConfig, ...groupConfig };
1731
+ }
1732
+ function GroupCard({
1733
+ group,
1734
+ fields,
1735
+ operators,
1736
+ config,
1737
+ mutations
1738
+ }) {
1739
+ const allIds = group.entries.map((e) => e.id);
1740
+ const isMulti = group.entries.length > 1;
1741
+ const { setNodeRef, isOver } = (0, import_core2.useDroppable)({ id: `group:${group.id}` });
1742
+ const entries = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_sortable3.SortableContext, { items: allIds, strategy: import_sortable3.verticalListSortingStrategy, children: [
1743
+ config.label && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_Typography.default, { variant: "caption", color: "text.secondary", className: "rcui-group-label", children: config.label }),
1744
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 4 }, children: group.entries.map((entry, idx) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
1745
+ idx > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1746
+ ConnectorChip,
1747
+ {
1748
+ connector: entry.connector,
1749
+ onToggle: config.editable ? () => mutations.toggleConnector(group.id, entry.id) : void 0
1750
+ }
1751
+ ),
1752
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1753
+ OutputRow,
1754
+ {
1755
+ id: entry.id,
1756
+ condition: entry.condition,
1757
+ fields,
1758
+ operators,
1759
+ onUpdate: config.editable ? (c) => mutations.updateCondition(group.id, entry.id, c) : void 0,
1760
+ onRemove: config.removable ? () => mutations.removeEntry(group.id, entry.id) : void 0
1761
+ }
1762
+ )
1763
+ ] }, entry.id)) })
1764
+ ] });
1765
+ if (!isMulti) {
1766
+ const singleClass = ["rcui-droppable-single", isOver ? "rcui-droppable-single--over" : ""].filter(Boolean).join(" ");
1767
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { ref: setNodeRef, className: singleClass, children: entries });
1768
+ }
1769
+ const paperClass = ["rcui-group-paper", isOver ? "rcui-group-paper--over" : ""].filter(Boolean).join(" ");
1770
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1771
+ import_Paper.default,
1772
+ {
1773
+ ref: setNodeRef,
1774
+ variant: config.variant,
1775
+ className: paperClass,
1776
+ children: entries
1777
+ }
1778
+ );
1779
+ }
1780
+ function DropZone({
1781
+ children,
1782
+ className,
1783
+ style
1784
+ }) {
1785
+ const { setNodeRef, isOver } = (0, import_core2.useDroppable)({ id: UNGROUP_ZONE_ID });
1786
+ const zoneClass = ["rcui-drop-zone", isOver ? "rcui-drop-zone--over" : "", className].filter(Boolean).join(" ");
1787
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { ref: setNodeRef, className: zoneClass, style, children });
1788
+ }
1789
+
1790
+ // src/components/ConditionalUI.tsx
1791
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1792
+ function ConditionalUI({
1793
+ fields,
1794
+ operators = DEFAULT_OPERATORS,
1795
+ value,
1796
+ onChange,
1797
+ onConditionsChange,
1798
+ className,
1799
+ style
1800
+ }) {
1801
+ const { groups, mutations } = useConditionalOutput({
1802
+ onGroupsChange: onConditionsChange
1803
+ });
1804
+ const rootClass = ["rcui-root", className].filter(Boolean).join(" ");
1805
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: rootClass, style, children: [
1806
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1807
+ Input,
1808
+ {
1809
+ fields,
1810
+ operators,
1811
+ value,
1812
+ onChange,
1813
+ onSubmit: mutations.addGroup
1814
+ }
1815
+ ),
1816
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1817
+ Output,
1818
+ {
1819
+ groups,
1820
+ fields,
1821
+ operators,
1822
+ onGroupsChange: mutations.setGroups
1823
+ }
1824
+ )
1825
+ ] });
1826
+ }
1827
+ // Annotate the CommonJS export names for ESM import in node:
1828
+ 0 && (module.exports = {
1829
+ ConditionDataProvider,
1830
+ ConditionParser,
1831
+ ConditionalUI,
1832
+ DEFAULT_OPERATORS,
1833
+ Input,
1834
+ Output,
1835
+ useConditionDataProvider,
1836
+ useConditionalInput,
1837
+ useConditionalOutput
1838
+ });
1839
+ //# sourceMappingURL=index.cjs.map