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