react-conditional-ui 1.2.0 → 1.2.1
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/README.md +2 -0
- package/dist/index.cjs +458 -299
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +3 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +40 -12
- package/dist/index.d.ts +40 -12
- package/dist/index.js +453 -299
- package/dist/index.js.map +1 -1
- package/dist/styles.css +4 -0
- package/package.json +26 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
// src/components/Input.tsx
|
|
2
2
|
import { useCallback as useCallback3, useState as useState2, useRef, useLayoutEffect, forwardRef } from "react";
|
|
3
3
|
import TextField from "@mui/material/TextField";
|
|
4
|
+
import ClickAwayListener from "@mui/material/ClickAwayListener";
|
|
4
5
|
import InputAdornment from "@mui/material/InputAdornment";
|
|
5
6
|
import IconButton from "@mui/material/IconButton";
|
|
7
|
+
import Stack from "@mui/material/Stack";
|
|
8
|
+
import AddIcon from "@mui/icons-material/Add";
|
|
6
9
|
import KeyboardReturnIcon from "@mui/icons-material/KeyboardReturn";
|
|
7
10
|
import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline";
|
|
8
11
|
import Tooltip from "@mui/material/Tooltip";
|
|
@@ -159,74 +162,70 @@ import createDebug from "debug";
|
|
|
159
162
|
var BASE = "react-conditional-ui";
|
|
160
163
|
var createLogger = (namespace) => createDebug(`${BASE}:${namespace}`);
|
|
161
164
|
|
|
162
|
-
// src/fuzzy/
|
|
163
|
-
var log = createLogger("match-engine");
|
|
164
|
-
var valueLog = createLogger("value");
|
|
165
|
+
// src/fuzzy/operator-policy.ts
|
|
165
166
|
var TYPE_ALLOWED_OPS = {
|
|
166
167
|
number: /* @__PURE__ */ new Set(["eq", "ne", "gt", "lt", "gte", "lte"]),
|
|
167
168
|
enum: /* @__PURE__ */ new Set(["eq", "ne"]),
|
|
168
169
|
text: /* @__PURE__ */ new Set(["eq", "ne", "contains", "starts_with"])
|
|
169
170
|
};
|
|
171
|
+
function allowedOperatorsForField(field, allOperators) {
|
|
172
|
+
if (field.operators) return field.operators;
|
|
173
|
+
if (field.type) {
|
|
174
|
+
const allowed = TYPE_ALLOWED_OPS[field.type];
|
|
175
|
+
return allOperators.filter((op) => allowed.has(op.value));
|
|
176
|
+
}
|
|
177
|
+
return allOperators;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// src/fuzzy/match-engine.ts
|
|
181
|
+
var log = createLogger("match-engine");
|
|
182
|
+
var valueLog = createLogger("value");
|
|
183
|
+
var FUSE_THRESHOLD = 0.4;
|
|
184
|
+
var OPERATOR_ALIAS_MIN_COVERAGE = 0.5;
|
|
170
185
|
var MatchEngine = class _MatchEngine {
|
|
171
186
|
constructor(fields, operators) {
|
|
172
187
|
this.fields = fields;
|
|
173
188
|
this.operators = operators;
|
|
174
189
|
this.fieldFuse = new Fuse(fields, {
|
|
175
190
|
keys: ["label"],
|
|
176
|
-
threshold:
|
|
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,
|
|
191
|
+
threshold: FUSE_THRESHOLD,
|
|
185
192
|
includeScore: true
|
|
186
193
|
});
|
|
194
|
+
this.opFuse = this.createOperatorAliasFuse(operators);
|
|
187
195
|
this.perFieldOpFuse = /* @__PURE__ */ new Map();
|
|
188
196
|
for (const field of fields) {
|
|
189
197
|
const restricted = this.resolveOpsForField(field, operators);
|
|
190
198
|
if (restricted !== operators) {
|
|
191
|
-
|
|
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
|
-
);
|
|
199
|
+
this.perFieldOpFuse.set(field.value, this.createOperatorAliasFuse(restricted));
|
|
198
200
|
}
|
|
199
201
|
}
|
|
200
202
|
}
|
|
201
203
|
matchField(candidate) {
|
|
202
204
|
const results = this.fieldFuse.search(candidate);
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
results[0].item.value,
|
|
208
|
-
results[0].score
|
|
209
|
-
);
|
|
210
|
-
return { option: results[0].item, score: results[0].score ?? 1 };
|
|
205
|
+
const best = results[0];
|
|
206
|
+
if (best && _MatchEngine.isWithinThreshold(best.score)) {
|
|
207
|
+
log("field match: %s -> %s (score: %f)", candidate, best.item.value, best.score);
|
|
208
|
+
return { option: best.item, score: best.score ?? 1 };
|
|
211
209
|
}
|
|
212
210
|
return null;
|
|
213
211
|
}
|
|
214
212
|
matchOperator(candidate, field) {
|
|
215
213
|
const fuse = (field && this.perFieldOpFuse.get(field.value)) ?? this.opFuse;
|
|
216
214
|
const results = fuse.search(candidate);
|
|
217
|
-
|
|
218
|
-
|
|
215
|
+
const best = results[0];
|
|
216
|
+
if (best && _MatchEngine.isWithinThreshold(best.score)) {
|
|
217
|
+
const alias = best.item.alias;
|
|
219
218
|
const candidateWords = candidate.split(/\s+/).length;
|
|
220
219
|
const aliasWords = alias.split(/\s+/).length;
|
|
221
220
|
if (candidateWords !== aliasWords) return null;
|
|
222
|
-
if (candidate.length < alias.length *
|
|
221
|
+
if (candidate.length < alias.length * OPERATOR_ALIAS_MIN_COVERAGE) return null;
|
|
223
222
|
log(
|
|
224
223
|
"operator match: %s -> %s (score: %f)",
|
|
225
224
|
candidate,
|
|
226
|
-
|
|
227
|
-
|
|
225
|
+
best.item.operator.value,
|
|
226
|
+
best.score
|
|
228
227
|
);
|
|
229
|
-
return { option:
|
|
228
|
+
return { option: best.item.operator, score: best.score ?? 1 };
|
|
230
229
|
}
|
|
231
230
|
return null;
|
|
232
231
|
}
|
|
@@ -261,18 +260,14 @@ var MatchEngine = class _MatchEngine {
|
|
|
261
260
|
if (raw.length === 0) return Value.invalid(raw, "Value not recognized");
|
|
262
261
|
const fuse = new Fuse(knownValues, {
|
|
263
262
|
keys: ["label", "value"],
|
|
264
|
-
threshold:
|
|
263
|
+
threshold: FUSE_THRESHOLD,
|
|
265
264
|
includeScore: true
|
|
266
265
|
});
|
|
267
266
|
const results = fuse.search(raw);
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
results[0].item.value,
|
|
273
|
-
results[0].score
|
|
274
|
-
);
|
|
275
|
-
return _MatchEngine.applyValidator(raw, results[0].item, validateValue);
|
|
267
|
+
const best = results[0];
|
|
268
|
+
if (best && _MatchEngine.isWithinThreshold(best.score)) {
|
|
269
|
+
valueLog("fuzzy match: raw=%s -> %s (score: %f)", raw, best.item.value, best.score);
|
|
270
|
+
return _MatchEngine.applyValidator(raw, best.item, validateValue);
|
|
276
271
|
}
|
|
277
272
|
valueLog("no match: raw=%s", raw);
|
|
278
273
|
return Value.invalid(raw, "Value not recognized");
|
|
@@ -285,51 +280,22 @@ var MatchEngine = class _MatchEngine {
|
|
|
285
280
|
return Value.valid(raw, matched);
|
|
286
281
|
}
|
|
287
282
|
resolveOpsForField(field, allOps) {
|
|
288
|
-
|
|
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;
|
|
283
|
+
return allowedOperatorsForField(field, allOps);
|
|
294
284
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
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;
|
|
285
|
+
createOperatorAliasFuse(operators) {
|
|
286
|
+
const aliases = operators.flatMap(
|
|
287
|
+
(op) => op.aliases.map((alias) => ({ alias: alias.toLowerCase(), operator: op }))
|
|
288
|
+
);
|
|
289
|
+
return new Fuse(aliases, {
|
|
290
|
+
keys: ["alias"],
|
|
291
|
+
threshold: FUSE_THRESHOLD,
|
|
292
|
+
includeScore: true
|
|
293
|
+
});
|
|
316
294
|
}
|
|
317
|
-
|
|
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
|
-
}
|
|
295
|
+
static isWithinThreshold(score) {
|
|
296
|
+
return (score ?? 1) <= FUSE_THRESHOLD;
|
|
330
297
|
}
|
|
331
|
-
|
|
332
|
-
}
|
|
298
|
+
};
|
|
333
299
|
|
|
334
300
|
// src/fuzzy/word-utils.ts
|
|
335
301
|
var NOISE_WORDS = /* @__PURE__ */ new Set([
|
|
@@ -399,18 +365,113 @@ function stripLeadingNoise(words) {
|
|
|
399
365
|
return result;
|
|
400
366
|
}
|
|
401
367
|
|
|
368
|
+
// src/fuzzy/score.ts
|
|
369
|
+
var AMBIGUOUS_OP_WORDS = /* @__PURE__ */ new Set(["is", "has"]);
|
|
370
|
+
var AMBIGUITY_PENALTY = 0.35;
|
|
371
|
+
var LINKING_VERB_PENALTY = 0.35;
|
|
372
|
+
var LENGTH_BONUS_PER_WORD = 0.01;
|
|
373
|
+
var GAP_PENALTY_PER_WORD = 0.3;
|
|
374
|
+
var UNPARSED_PENALTY = 10;
|
|
375
|
+
var FREETEXT_WORD_COST = 0.1;
|
|
376
|
+
function adjustOperatorScore(baseScore, words, start, end) {
|
|
377
|
+
let score = baseScore;
|
|
378
|
+
const firstWord = words[start];
|
|
379
|
+
if (end - start === 1 && AMBIGUOUS_OP_WORDS.has(firstWord)) {
|
|
380
|
+
score += AMBIGUITY_PENALTY;
|
|
381
|
+
} else if (end - start > 1 && AMBIGUOUS_OP_WORDS.has(firstWord)) {
|
|
382
|
+
score += LINKING_VERB_PENALTY;
|
|
383
|
+
}
|
|
384
|
+
score -= (end - start) * LENGTH_BONUS_PER_WORD;
|
|
385
|
+
if (start > 0) {
|
|
386
|
+
score += start * GAP_PENALTY_PER_WORD;
|
|
387
|
+
}
|
|
388
|
+
return score;
|
|
389
|
+
}
|
|
390
|
+
function scoreConditions(conditions, segmentCount) {
|
|
391
|
+
const unparsed = segmentCount - conditions.length;
|
|
392
|
+
let score = unparsed * UNPARSED_PENALTY;
|
|
393
|
+
for (const c of conditions) {
|
|
394
|
+
score += c.score;
|
|
395
|
+
if (!c.field.isValid) score += UNPARSED_PENALTY;
|
|
396
|
+
if (!c.operator.isValid) score += UNPARSED_PENALTY;
|
|
397
|
+
if (!c.value.isValid) score += UNPARSED_PENALTY;
|
|
398
|
+
if (c.value.isValid && c.value.matchedOption === null) {
|
|
399
|
+
score += c.value.raw.split(/\s+/).length * FREETEXT_WORD_COST;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return score;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// src/conditions/query-helper.ts
|
|
406
|
+
var ConditionQueryHelper = class {
|
|
407
|
+
constructor(engine, fields, operators) {
|
|
408
|
+
this.engine = engine;
|
|
409
|
+
this.fields = fields;
|
|
410
|
+
this.operators = operators;
|
|
411
|
+
}
|
|
412
|
+
getFields() {
|
|
413
|
+
return this.fields;
|
|
414
|
+
}
|
|
415
|
+
findFieldByValue(value) {
|
|
416
|
+
return this.fields.find((field) => field.value === value);
|
|
417
|
+
}
|
|
418
|
+
identifyField(words) {
|
|
419
|
+
let best = null;
|
|
420
|
+
for (let i = words.length; i >= 1; i--) {
|
|
421
|
+
const candidate = words.slice(0, i).join(" ");
|
|
422
|
+
const match = this.engine.matchField(candidate);
|
|
423
|
+
if (match && (!best || match.score < best.match.score)) {
|
|
424
|
+
best = { candidate, match, wordCount: i };
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
if (!best) return null;
|
|
428
|
+
return {
|
|
429
|
+
raw: best.candidate,
|
|
430
|
+
fieldOption: best.match.option,
|
|
431
|
+
fieldScore: best.match.score,
|
|
432
|
+
remaining: words.slice(best.wordCount)
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
getOperatorCandidates(words, fieldOption) {
|
|
436
|
+
const candidates = [];
|
|
437
|
+
for (let start = 0; start < words.length; start++) {
|
|
438
|
+
for (let end = start + 1; end <= words.length; end++) {
|
|
439
|
+
const opRaw = words.slice(start, end).join(" ");
|
|
440
|
+
const opMatch = this.engine.matchOperator(opRaw, fieldOption);
|
|
441
|
+
if (!opMatch) continue;
|
|
442
|
+
candidates.push({
|
|
443
|
+
match: opMatch,
|
|
444
|
+
raw: opRaw,
|
|
445
|
+
endIdx: end,
|
|
446
|
+
adjustedScore: adjustOperatorScore(opMatch.score, words, start, end)
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
candidates.sort((a, b) => a.adjustedScore - b.adjustedScore);
|
|
451
|
+
return candidates;
|
|
452
|
+
}
|
|
453
|
+
allowedOpsForField(field) {
|
|
454
|
+
return allowedOperatorsForField(field, this.operators);
|
|
455
|
+
}
|
|
456
|
+
prefixMatches(partial, candidates, limit = 6) {
|
|
457
|
+
const lower = partial.toLowerCase();
|
|
458
|
+
const results = [];
|
|
459
|
+
for (const candidate of candidates) {
|
|
460
|
+
const cl = candidate.toLowerCase();
|
|
461
|
+
if (cl.startsWith(lower) && cl !== lower) {
|
|
462
|
+
results.push({ completion: cl.slice(lower.length), display: candidate });
|
|
463
|
+
if (results.length >= limit) break;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
return results;
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
|
|
402
470
|
// src/conditions/parser.ts
|
|
403
471
|
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
472
|
var ConditionParser = class {
|
|
410
|
-
constructor(engine) {
|
|
411
|
-
this.
|
|
412
|
-
this.operators = engine.operators;
|
|
413
|
-
this.engine = engine;
|
|
473
|
+
constructor(engine, queryHelper) {
|
|
474
|
+
this.query = queryHelper ?? new ConditionQueryHelper(engine, engine.fields, engine.operators);
|
|
414
475
|
}
|
|
415
476
|
parse(text) {
|
|
416
477
|
const input = text.trim().toLowerCase();
|
|
@@ -418,11 +479,15 @@ var ConditionParser = class {
|
|
|
418
479
|
const allWords = input.split(/\s+/);
|
|
419
480
|
const words = stripLeadingNoise(allWords);
|
|
420
481
|
if (words.length === 0) return null;
|
|
421
|
-
const fieldResult = this.identifyField(words);
|
|
482
|
+
const fieldResult = this.query.identifyField(words);
|
|
422
483
|
if (!fieldResult) return null;
|
|
423
484
|
if (fieldResult.remaining.length === 0) {
|
|
424
485
|
return {
|
|
425
|
-
field:
|
|
486
|
+
field: new Field(
|
|
487
|
+
fieldResult.raw,
|
|
488
|
+
fieldResult.fieldOption.value,
|
|
489
|
+
fieldResult.fieldOption.label
|
|
490
|
+
),
|
|
426
491
|
operator: Operator.invalid(""),
|
|
427
492
|
value: Value.empty(),
|
|
428
493
|
score: fieldResult.fieldScore
|
|
@@ -433,7 +498,11 @@ var ConditionParser = class {
|
|
|
433
498
|
fieldResult.fieldOption
|
|
434
499
|
);
|
|
435
500
|
const result = {
|
|
436
|
-
field:
|
|
501
|
+
field: new Field(
|
|
502
|
+
fieldResult.raw,
|
|
503
|
+
fieldResult.fieldOption.value,
|
|
504
|
+
fieldResult.fieldOption.label
|
|
505
|
+
),
|
|
437
506
|
operator,
|
|
438
507
|
value,
|
|
439
508
|
score: fieldResult.fieldScore + operatorScore
|
|
@@ -453,40 +522,8 @@ var ConditionParser = class {
|
|
|
453
522
|
);
|
|
454
523
|
return result;
|
|
455
524
|
}
|
|
456
|
-
|
|
457
|
-
|
|
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);
|
|
525
|
+
resolveOperator(words, fieldOption) {
|
|
526
|
+
const candidates = this.query.getOperatorCandidates(words, fieldOption);
|
|
490
527
|
if (candidates.length > 0) {
|
|
491
528
|
log2(
|
|
492
529
|
"operator candidates: %o",
|
|
@@ -495,10 +532,6 @@ var ConditionParser = class {
|
|
|
495
532
|
)
|
|
496
533
|
);
|
|
497
534
|
}
|
|
498
|
-
return candidates;
|
|
499
|
-
}
|
|
500
|
-
resolveOperator(words, fieldOption) {
|
|
501
|
-
const candidates = this.getOperatorCandidates(words, fieldOption);
|
|
502
535
|
if (candidates.length === 0) {
|
|
503
536
|
return {
|
|
504
537
|
operator: Operator.invalid(words.join(" ")),
|
|
@@ -533,33 +566,6 @@ var ConditionParser = class {
|
|
|
533
566
|
operatorScore: best.adjustedScore
|
|
534
567
|
};
|
|
535
568
|
}
|
|
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
569
|
};
|
|
564
570
|
|
|
565
571
|
// src/consts.ts
|
|
@@ -568,8 +574,8 @@ var OR_CONJUNCTION = "or";
|
|
|
568
574
|
|
|
569
575
|
// src/fuzzy/providers/suggestions.ts
|
|
570
576
|
var SuggestionsProvider = class {
|
|
571
|
-
constructor(
|
|
572
|
-
this.
|
|
577
|
+
constructor(query, segmentResolver) {
|
|
578
|
+
this.query = query;
|
|
573
579
|
this.segmentResolver = segmentResolver;
|
|
574
580
|
}
|
|
575
581
|
getCompletions(text, limit = 6) {
|
|
@@ -595,40 +601,40 @@ var SuggestionsProvider = class {
|
|
|
595
601
|
completionsForSegment(text, limit) {
|
|
596
602
|
const input = text.trimStart().toLowerCase();
|
|
597
603
|
if (!input) {
|
|
598
|
-
return this.
|
|
604
|
+
return this.query.getFields().slice(0, limit).map((f) => ({ completion: f.label, display: f.label }));
|
|
599
605
|
}
|
|
600
606
|
const endsWithSpace = /\s$/.test(input);
|
|
601
607
|
const words = input.split(/\s+/).filter(Boolean);
|
|
602
608
|
if (words.length === 0) return [];
|
|
603
|
-
const fieldResult = this.
|
|
609
|
+
const fieldResult = this.query.identifyField(words);
|
|
604
610
|
if (!fieldResult) {
|
|
605
611
|
if (endsWithSpace) return [];
|
|
606
|
-
return this.
|
|
612
|
+
return this.query.prefixMatches(
|
|
607
613
|
words.join(" "),
|
|
608
|
-
this.
|
|
614
|
+
this.query.getFields().map((f) => f.label),
|
|
609
615
|
limit
|
|
610
616
|
);
|
|
611
617
|
}
|
|
612
618
|
const fieldOption = fieldResult.fieldOption;
|
|
613
619
|
if (fieldResult.remaining.length === 0) {
|
|
614
620
|
if (endsWithSpace) {
|
|
615
|
-
const ops = this.
|
|
621
|
+
const ops = this.query.allowedOpsForField(fieldOption);
|
|
616
622
|
return ops.slice(0, limit).map((op) => ({ completion: op.label, display: op.label }));
|
|
617
623
|
}
|
|
618
|
-
return this.
|
|
624
|
+
return this.query.prefixMatches(
|
|
619
625
|
words.join(" "),
|
|
620
|
-
this.
|
|
626
|
+
this.query.getFields().map((f) => f.label),
|
|
621
627
|
limit
|
|
622
628
|
);
|
|
623
629
|
}
|
|
624
|
-
const candidates = this.
|
|
630
|
+
const candidates = this.query.getOperatorCandidates(fieldResult.remaining, fieldOption);
|
|
625
631
|
const bestOp = candidates[0];
|
|
626
632
|
if (!bestOp || bestOp.endIdx > fieldResult.remaining.length) {
|
|
627
633
|
if (endsWithSpace) return [];
|
|
628
|
-
const ops = this.
|
|
634
|
+
const ops = this.query.allowedOpsForField(fieldOption);
|
|
629
635
|
const aliases = ops.flatMap((op) => op.aliases);
|
|
630
636
|
const opPartial = fieldResult.remaining.join(" ");
|
|
631
|
-
return this.
|
|
637
|
+
return this.query.prefixMatches(opPartial, aliases, limit);
|
|
632
638
|
}
|
|
633
639
|
const valueRaw = fieldResult.remaining.slice(bestOp.endIdx).join(" ");
|
|
634
640
|
if (!valueRaw) {
|
|
@@ -637,15 +643,15 @@ var SuggestionsProvider = class {
|
|
|
637
643
|
if (!fieldValues2?.length) return [];
|
|
638
644
|
return fieldValues2.slice(0, limit).map((v) => ({ completion: v.label, display: v.label }));
|
|
639
645
|
}
|
|
640
|
-
const ops = this.
|
|
646
|
+
const ops = this.query.allowedOpsForField(fieldOption);
|
|
641
647
|
const aliases = ops.flatMap((op) => op.aliases);
|
|
642
648
|
const opPartial = fieldResult.remaining.join(" ");
|
|
643
|
-
return this.
|
|
649
|
+
return this.query.prefixMatches(opPartial, aliases, limit);
|
|
644
650
|
}
|
|
645
651
|
if (endsWithSpace) return [];
|
|
646
652
|
const fieldValues = fieldOption.fieldValues;
|
|
647
653
|
if (!fieldValues?.length) return [];
|
|
648
|
-
return this.
|
|
654
|
+
return this.query.prefixMatches(
|
|
649
655
|
valueRaw,
|
|
650
656
|
fieldValues.map((v) => v.label),
|
|
651
657
|
limit
|
|
@@ -670,8 +676,8 @@ var SuggestionsProvider = class {
|
|
|
670
676
|
|
|
671
677
|
// src/fuzzy/providers/diagnostics.ts
|
|
672
678
|
var DiagnosticsProvider = class {
|
|
673
|
-
constructor(
|
|
674
|
-
this.
|
|
679
|
+
constructor(query, segmentResolver) {
|
|
680
|
+
this.query = query;
|
|
675
681
|
this.segmentResolver = segmentResolver;
|
|
676
682
|
}
|
|
677
683
|
diagnose(text) {
|
|
@@ -698,9 +704,7 @@ var DiagnosticsProvider = class {
|
|
|
698
704
|
}
|
|
699
705
|
if (!condition.operator.isValid) {
|
|
700
706
|
const fieldEnd = offset + condition.field.raw.length;
|
|
701
|
-
const fieldConfig = this.
|
|
702
|
-
(f) => f.value === condition.field.value
|
|
703
|
-
);
|
|
707
|
+
const fieldConfig = this.query.findFieldByValue(condition.field.value);
|
|
704
708
|
const hasRestriction = fieldConfig?.operators || fieldConfig?.type;
|
|
705
709
|
diagnostics.push({
|
|
706
710
|
start: fieldEnd,
|
|
@@ -749,60 +753,26 @@ var SegmentResolver = class {
|
|
|
749
753
|
resolve(text) {
|
|
750
754
|
const originalWords = text.split(/\s+/);
|
|
751
755
|
const lowerWords = originalWords.map((w) => w.toLowerCase());
|
|
752
|
-
const conjIndices =
|
|
753
|
-
|
|
754
|
-
if (lowerWords[i] === AND_CONJUNCTION || lowerWords[i] === OR_CONJUNCTION) {
|
|
755
|
-
conjIndices.push({ index: i, conjunction: lowerWords[i] });
|
|
756
|
-
}
|
|
757
|
-
}
|
|
756
|
+
const conjIndices = this.findConjunctionIndices(lowerWords);
|
|
757
|
+
const fallback = this.createSingleCandidate(text);
|
|
758
758
|
if (conjIndices.length === 0) {
|
|
759
|
-
|
|
760
|
-
return {
|
|
761
|
-
segments: [text],
|
|
762
|
-
conditions: single ? [single] : [],
|
|
763
|
-
connector: AND_CONJUNCTION
|
|
764
|
-
};
|
|
759
|
+
return fallback;
|
|
765
760
|
}
|
|
766
|
-
|
|
767
|
-
let best = {
|
|
768
|
-
segments: [text],
|
|
769
|
-
conditions: noSplit ? [noSplit] : [],
|
|
770
|
-
connector: AND_CONJUNCTION
|
|
771
|
-
};
|
|
761
|
+
let best = fallback;
|
|
772
762
|
let bestScore = scoreConditions(best.conditions, 1);
|
|
773
763
|
for (const subset of powerSet(conjIndices)) {
|
|
774
764
|
const connector = subset[0].conjunction;
|
|
775
765
|
if (subset.some((s) => s.conjunction !== connector)) continue;
|
|
776
|
-
const
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
const conditions = this.parseSegments(segments);
|
|
766
|
+
const next = this.buildSplitCandidate(originalWords, subset, connector);
|
|
767
|
+
if (!next) continue;
|
|
768
|
+
const { segments, conditions } = next;
|
|
780
769
|
const score = scoreConditions(conditions, segments.length);
|
|
781
770
|
if (score < bestScore) {
|
|
782
771
|
bestScore = score;
|
|
783
772
|
best = { segments, conditions, connector };
|
|
784
773
|
}
|
|
785
774
|
}
|
|
786
|
-
|
|
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;
|
|
775
|
+
return this.trySplitFromValueConjunction(best, originalWords, conjIndices) ?? best;
|
|
806
776
|
}
|
|
807
777
|
parseConditions(text) {
|
|
808
778
|
const input = text.trim();
|
|
@@ -825,6 +795,54 @@ var SegmentResolver = class {
|
|
|
825
795
|
}
|
|
826
796
|
return conditions;
|
|
827
797
|
}
|
|
798
|
+
createSingleCandidate(text) {
|
|
799
|
+
const parsed = this.parser.parse(text);
|
|
800
|
+
return {
|
|
801
|
+
segments: [text],
|
|
802
|
+
conditions: parsed ? [parsed] : [],
|
|
803
|
+
connector: AND_CONJUNCTION
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
findConjunctionIndices(words) {
|
|
807
|
+
const indices = [];
|
|
808
|
+
for (let i = 0; i < words.length; i++) {
|
|
809
|
+
if (words[i] === AND_CONJUNCTION || words[i] === OR_CONJUNCTION) {
|
|
810
|
+
indices.push({ index: i, conjunction: words[i] });
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
return indices;
|
|
814
|
+
}
|
|
815
|
+
buildSplitCandidate(originalWords, splitPoints, connector) {
|
|
816
|
+
const splitIndices = splitPoints.map((s) => s.index);
|
|
817
|
+
const segments = splitAtIndices(originalWords, splitIndices);
|
|
818
|
+
if (segments.some((s) => !s)) return null;
|
|
819
|
+
return {
|
|
820
|
+
segments,
|
|
821
|
+
conditions: this.parseSegments(segments),
|
|
822
|
+
connector
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
trySplitFromValueConjunction(best, originalWords, conjIndices) {
|
|
826
|
+
const onlyCondition = best.conditions[0];
|
|
827
|
+
if (best.conditions.length !== 1 || !onlyCondition?.value.isValid || onlyCondition.value.matchedOption !== null) {
|
|
828
|
+
return null;
|
|
829
|
+
}
|
|
830
|
+
const valueWords = onlyCondition.value.raw.split(/\s+/);
|
|
831
|
+
const conjInValue = valueWords.find(
|
|
832
|
+
(w) => w.toLowerCase() === AND_CONJUNCTION || w.toLowerCase() === OR_CONJUNCTION
|
|
833
|
+
);
|
|
834
|
+
if (!conjInValue) return null;
|
|
835
|
+
const connector = conjInValue.toLowerCase();
|
|
836
|
+
const matching = conjIndices.filter((c) => c.conjunction === connector);
|
|
837
|
+
for (const subset of powerSet(matching).reverse()) {
|
|
838
|
+
const candidate = this.buildSplitCandidate(originalWords, subset, connector);
|
|
839
|
+
if (!candidate) continue;
|
|
840
|
+
if (candidate.conditions.length === candidate.segments.length && candidate.conditions.every((c) => c.field.isValid && c.operator.isValid)) {
|
|
841
|
+
return candidate;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
return null;
|
|
845
|
+
}
|
|
828
846
|
getInherited(segment, previous) {
|
|
829
847
|
if (!previous) return null;
|
|
830
848
|
const direct = this.parser.parse(segment);
|
|
@@ -849,10 +867,11 @@ var SegmentResolver = class {
|
|
|
849
867
|
var ConditionDataProvider = class {
|
|
850
868
|
constructor(fields, operators) {
|
|
851
869
|
const engine = new MatchEngine(fields, operators);
|
|
852
|
-
const
|
|
870
|
+
const query = new ConditionQueryHelper(engine, fields, operators);
|
|
871
|
+
const parser = new ConditionParser(engine, query);
|
|
853
872
|
this.segments = new SegmentResolver(parser);
|
|
854
|
-
this.suggestions = new SuggestionsProvider(
|
|
855
|
-
this.diagnostics = new DiagnosticsProvider(
|
|
873
|
+
this.suggestions = new SuggestionsProvider(query, this.segments);
|
|
874
|
+
this.diagnostics = new DiagnosticsProvider(query, this.segments);
|
|
856
875
|
}
|
|
857
876
|
parseComplexCondition(text) {
|
|
858
877
|
return this.segments.parseConditions(text);
|
|
@@ -935,9 +954,12 @@ function useConditionalInput({
|
|
|
935
954
|
// src/components/Input.tsx
|
|
936
955
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
937
956
|
function Input(props) {
|
|
938
|
-
if (
|
|
939
|
-
return /* @__PURE__ */ jsx(
|
|
957
|
+
if (isManaged(props)) {
|
|
958
|
+
return /* @__PURE__ */ jsx(ManagedInput, { ...props });
|
|
940
959
|
}
|
|
960
|
+
return /* @__PURE__ */ jsx(ControlledInput, { ...props });
|
|
961
|
+
}
|
|
962
|
+
function ControlledInput(props) {
|
|
941
963
|
return /* @__PURE__ */ jsx(
|
|
942
964
|
InputView,
|
|
943
965
|
{
|
|
@@ -953,7 +975,7 @@ function Input(props) {
|
|
|
953
975
|
}
|
|
954
976
|
);
|
|
955
977
|
}
|
|
956
|
-
function
|
|
978
|
+
function isManaged(props) {
|
|
957
979
|
return props.fields !== void 0;
|
|
958
980
|
}
|
|
959
981
|
function buildOverlaySegments(value, diagnostics) {
|
|
@@ -1002,7 +1024,7 @@ var GhostInput = forwardRef(function GhostInput2({ ghost, diagnosticSegments, in
|
|
|
1002
1024
|
) })
|
|
1003
1025
|
] });
|
|
1004
1026
|
});
|
|
1005
|
-
function
|
|
1027
|
+
function ManagedInput({
|
|
1006
1028
|
fields,
|
|
1007
1029
|
operators,
|
|
1008
1030
|
value: controlledValue,
|
|
@@ -1038,7 +1060,7 @@ function InputView({
|
|
|
1038
1060
|
value,
|
|
1039
1061
|
onChange,
|
|
1040
1062
|
onSubmit,
|
|
1041
|
-
placeholder = "e.g. age greater than 18
|
|
1063
|
+
placeholder = "e.g. age greater than 18 \u2014 Control(Option)+Space for suggestions",
|
|
1042
1064
|
getSuggestion,
|
|
1043
1065
|
getCompletions,
|
|
1044
1066
|
diagnostics = [],
|
|
@@ -1049,11 +1071,17 @@ function InputView({
|
|
|
1049
1071
|
const [activeIndex, setActiveIndex] = useState2(-1);
|
|
1050
1072
|
const [cursorLeft, setCursorLeft] = useState2(0);
|
|
1051
1073
|
const listRef = useRef(null);
|
|
1074
|
+
const inputRef = useRef(null);
|
|
1052
1075
|
const ghost = getSuggestion && value ? getSuggestion(value)?.completion ?? null : null;
|
|
1053
1076
|
function closeCompletions() {
|
|
1054
1077
|
setCompletions([]);
|
|
1055
1078
|
setActiveIndex(-1);
|
|
1056
1079
|
}
|
|
1080
|
+
function handleCompletionsClickAway() {
|
|
1081
|
+
if (completions.length === 0) return;
|
|
1082
|
+
closeCompletions();
|
|
1083
|
+
queueMicrotask(() => inputRef.current?.focus());
|
|
1084
|
+
}
|
|
1057
1085
|
function openCompletions() {
|
|
1058
1086
|
if (!getCompletions) return;
|
|
1059
1087
|
const items = getCompletions(value);
|
|
@@ -1072,7 +1100,9 @@ function InputView({
|
|
|
1072
1100
|
if (ghost) onChange(value + ghost);
|
|
1073
1101
|
}
|
|
1074
1102
|
function handleKeyDown(e) {
|
|
1075
|
-
|
|
1103
|
+
const isSpace = e.key === " " || e.code === "Space";
|
|
1104
|
+
const suggestChord = isSpace && !e.shiftKey && !e.metaKey && (e.ctrlKey || e.altKey);
|
|
1105
|
+
if (suggestChord) {
|
|
1076
1106
|
e.preventDefault();
|
|
1077
1107
|
openCompletions();
|
|
1078
1108
|
return;
|
|
@@ -1130,66 +1160,106 @@ function InputView({
|
|
|
1130
1160
|
inputValue: value,
|
|
1131
1161
|
onCursorOffset: setCursorLeft
|
|
1132
1162
|
};
|
|
1133
|
-
return /* @__PURE__ */
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1163
|
+
return /* @__PURE__ */ jsx(ClickAwayListener, { onClickAway: handleCompletionsClickAway, children: /* @__PURE__ */ jsxs(
|
|
1164
|
+
"div",
|
|
1165
|
+
{
|
|
1166
|
+
className: ["rcui-input-wrapper", className].filter(Boolean).join(" "),
|
|
1167
|
+
style,
|
|
1168
|
+
children: [
|
|
1169
|
+
/* @__PURE__ */ jsx(
|
|
1170
|
+
TextField,
|
|
1171
|
+
{
|
|
1172
|
+
fullWidth: true,
|
|
1173
|
+
variant: "outlined",
|
|
1174
|
+
size: "small",
|
|
1175
|
+
placeholder,
|
|
1176
|
+
value,
|
|
1177
|
+
onChange: handleChange,
|
|
1178
|
+
onKeyDown: handleKeyDown,
|
|
1179
|
+
className: "rcui-input",
|
|
1180
|
+
slotProps: {
|
|
1181
|
+
input: {
|
|
1182
|
+
inputRef,
|
|
1183
|
+
inputComponent: GhostInput,
|
|
1184
|
+
inputProps: ghostInputProps,
|
|
1185
|
+
endAdornment: /* @__PURE__ */ jsx(InputAdornment, { position: "end", children: /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", spacing: 0, children: [
|
|
1186
|
+
getCompletions ? /* @__PURE__ */ jsx(Tooltip, { title: "Show suggestions", arrow: true, children: /* @__PURE__ */ jsx(
|
|
1187
|
+
IconButton,
|
|
1188
|
+
{
|
|
1189
|
+
size: "small",
|
|
1190
|
+
"aria-label": "Show suggestions",
|
|
1191
|
+
className: "rcui-adornment-suggestions",
|
|
1192
|
+
onMouseDown: (e) => {
|
|
1193
|
+
e.preventDefault();
|
|
1194
|
+
openCompletions();
|
|
1195
|
+
},
|
|
1196
|
+
children: /* @__PURE__ */ jsx(AddIcon, { fontSize: "small" })
|
|
1197
|
+
}
|
|
1198
|
+
) }) : null,
|
|
1199
|
+
hasErrors ? /* @__PURE__ */ jsx(Tooltip, { title: errorSummary, arrow: true, children: /* @__PURE__ */ jsx(
|
|
1200
|
+
IconButton,
|
|
1201
|
+
{
|
|
1202
|
+
size: "small",
|
|
1203
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
1204
|
+
onClick: onSubmit,
|
|
1205
|
+
edge: "end",
|
|
1206
|
+
children: /* @__PURE__ */ jsx(
|
|
1207
|
+
ErrorOutlineIcon,
|
|
1208
|
+
{
|
|
1209
|
+
fontSize: "small",
|
|
1210
|
+
className: "rcui-adornment-error"
|
|
1211
|
+
}
|
|
1212
|
+
)
|
|
1213
|
+
}
|
|
1214
|
+
) }) : /* @__PURE__ */ jsx(
|
|
1215
|
+
IconButton,
|
|
1216
|
+
{
|
|
1217
|
+
size: "small",
|
|
1218
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
1219
|
+
onClick: onSubmit,
|
|
1220
|
+
edge: "end",
|
|
1221
|
+
children: /* @__PURE__ */ jsx(
|
|
1222
|
+
KeyboardReturnIcon,
|
|
1223
|
+
{
|
|
1224
|
+
fontSize: "small",
|
|
1225
|
+
className: "rcui-adornment-enter"
|
|
1226
|
+
}
|
|
1227
|
+
)
|
|
1228
|
+
}
|
|
1229
|
+
)
|
|
1230
|
+
] }) })
|
|
1161
1231
|
}
|
|
1162
|
-
|
|
1232
|
+
}
|
|
1163
1233
|
}
|
|
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",
|
|
1234
|
+
),
|
|
1235
|
+
completions.length > 0 && /* @__PURE__ */ jsx(
|
|
1236
|
+
"ul",
|
|
1176
1237
|
{
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
"
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1238
|
+
ref: listRef,
|
|
1239
|
+
className: "rcui-completions",
|
|
1240
|
+
role: "listbox",
|
|
1241
|
+
style: { left: cursorLeft },
|
|
1242
|
+
children: completions.map((item, i) => /* @__PURE__ */ jsx(
|
|
1243
|
+
"li",
|
|
1244
|
+
{
|
|
1245
|
+
id: `rcui-completion-${i}`,
|
|
1246
|
+
role: "option",
|
|
1247
|
+
"aria-selected": i === activeIndex,
|
|
1248
|
+
className: `rcui-completion-item${i === activeIndex ? " rcui-completion-item--active" : ""}`,
|
|
1249
|
+
onMouseDown: (e) => {
|
|
1250
|
+
e.preventDefault();
|
|
1251
|
+
acceptCompletion(item);
|
|
1252
|
+
},
|
|
1253
|
+
onMouseEnter: () => setActiveIndex(i),
|
|
1254
|
+
children: item.display
|
|
1255
|
+
},
|
|
1256
|
+
item.display
|
|
1257
|
+
))
|
|
1258
|
+
}
|
|
1259
|
+
)
|
|
1260
|
+
]
|
|
1261
|
+
}
|
|
1262
|
+
) });
|
|
1193
1263
|
}
|
|
1194
1264
|
|
|
1195
1265
|
// src/components/Output.tsx
|
|
@@ -1388,7 +1458,7 @@ import {
|
|
|
1388
1458
|
} from "@dnd-kit/core";
|
|
1389
1459
|
import { sortableKeyboardCoordinates } from "@dnd-kit/sortable";
|
|
1390
1460
|
|
|
1391
|
-
// src/
|
|
1461
|
+
// src/dnd/output-drag-end.ts
|
|
1392
1462
|
var UNGROUP_ZONE_ID = "ungrouped-drop-zone";
|
|
1393
1463
|
var GROUP_DROPPABLE_PREFIX = "group:";
|
|
1394
1464
|
function findGroupByEntryId(groups, entryId) {
|
|
@@ -1643,20 +1713,91 @@ var DEFAULT_GROUP_CONFIG = {
|
|
|
1643
1713
|
removable: true,
|
|
1644
1714
|
variant: "outlined"
|
|
1645
1715
|
};
|
|
1646
|
-
function Output({
|
|
1716
|
+
function Output(props) {
|
|
1717
|
+
if (isManagedOutput(props)) return /* @__PURE__ */ jsx5(ManagedOutput, { ...props });
|
|
1718
|
+
if (isReadOnlyOutput(props)) return /* @__PURE__ */ jsx5(ReadOnlyOutput, { ...props });
|
|
1719
|
+
return /* @__PURE__ */ jsx5(ControlledOutput, { ...props });
|
|
1720
|
+
}
|
|
1721
|
+
function ManagedOutput({
|
|
1647
1722
|
fields,
|
|
1648
1723
|
operators,
|
|
1649
|
-
groups: controlledGroups,
|
|
1650
1724
|
onGroupsChange,
|
|
1651
1725
|
defaultGroupConfig,
|
|
1652
1726
|
className,
|
|
1653
1727
|
style
|
|
1654
1728
|
}) {
|
|
1655
|
-
const { groups, mutations } = useConditionalOutput({
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1729
|
+
const { groups, mutations } = useConditionalOutput({ onGroupsChange });
|
|
1730
|
+
return /* @__PURE__ */ jsx5(
|
|
1731
|
+
OutputView,
|
|
1732
|
+
{
|
|
1733
|
+
groups,
|
|
1734
|
+
mutations,
|
|
1735
|
+
readOnly: false,
|
|
1736
|
+
fields,
|
|
1737
|
+
operators,
|
|
1738
|
+
defaultGroupConfig,
|
|
1739
|
+
className,
|
|
1740
|
+
style
|
|
1741
|
+
}
|
|
1742
|
+
);
|
|
1743
|
+
}
|
|
1744
|
+
function ControlledOutput({
|
|
1745
|
+
fields,
|
|
1746
|
+
operators,
|
|
1747
|
+
groups,
|
|
1748
|
+
onGroupsChange,
|
|
1749
|
+
defaultGroupConfig,
|
|
1750
|
+
className,
|
|
1751
|
+
style
|
|
1752
|
+
}) {
|
|
1753
|
+
const { mutations } = useConditionalOutput({ groups, onGroupsChange });
|
|
1754
|
+
return /* @__PURE__ */ jsx5(
|
|
1755
|
+
OutputView,
|
|
1756
|
+
{
|
|
1757
|
+
groups,
|
|
1758
|
+
mutations,
|
|
1759
|
+
readOnly: false,
|
|
1760
|
+
fields,
|
|
1761
|
+
operators,
|
|
1762
|
+
defaultGroupConfig,
|
|
1763
|
+
className,
|
|
1764
|
+
style
|
|
1765
|
+
}
|
|
1766
|
+
);
|
|
1767
|
+
}
|
|
1768
|
+
function ReadOnlyOutput({
|
|
1769
|
+
fields,
|
|
1770
|
+
operators,
|
|
1771
|
+
groups,
|
|
1772
|
+
defaultGroupConfig,
|
|
1773
|
+
className,
|
|
1774
|
+
style
|
|
1775
|
+
}) {
|
|
1776
|
+
const { mutations } = useConditionalOutput({ groups });
|
|
1777
|
+
return /* @__PURE__ */ jsx5(
|
|
1778
|
+
OutputView,
|
|
1779
|
+
{
|
|
1780
|
+
groups,
|
|
1781
|
+
mutations,
|
|
1782
|
+
readOnly: true,
|
|
1783
|
+
fields,
|
|
1784
|
+
operators,
|
|
1785
|
+
defaultGroupConfig,
|
|
1786
|
+
className,
|
|
1787
|
+
style
|
|
1788
|
+
}
|
|
1789
|
+
);
|
|
1790
|
+
}
|
|
1791
|
+
function OutputView({
|
|
1792
|
+
groups,
|
|
1793
|
+
mutations,
|
|
1794
|
+
readOnly,
|
|
1795
|
+
fields,
|
|
1796
|
+
operators,
|
|
1797
|
+
defaultGroupConfig,
|
|
1798
|
+
className,
|
|
1799
|
+
style
|
|
1800
|
+
}) {
|
|
1660
1801
|
const effectiveDefault = readOnly ? { editable: false, removable: false, ...defaultGroupConfig } : defaultGroupConfig;
|
|
1661
1802
|
const rootClass = ["rcui-output", className].filter(Boolean).join(" ");
|
|
1662
1803
|
const renderOverlay = (entry) => /* @__PURE__ */ jsxs4("div", { className: "rcui-overlay", children: [
|
|
@@ -1690,6 +1831,12 @@ function Output({
|
|
|
1690
1831
|
group.id
|
|
1691
1832
|
)) }) });
|
|
1692
1833
|
}
|
|
1834
|
+
function isManagedOutput(props) {
|
|
1835
|
+
return props.groups === void 0;
|
|
1836
|
+
}
|
|
1837
|
+
function isReadOnlyOutput(props) {
|
|
1838
|
+
return props.groups !== void 0 && props.onGroupsChange === void 0;
|
|
1839
|
+
}
|
|
1693
1840
|
function resolveConfig(groupConfig, defaultConfig) {
|
|
1694
1841
|
return { ...DEFAULT_GROUP_CONFIG, ...defaultConfig, ...groupConfig };
|
|
1695
1842
|
}
|
|
@@ -1759,6 +1906,8 @@ function ConditionalUI({
|
|
|
1759
1906
|
value,
|
|
1760
1907
|
onChange,
|
|
1761
1908
|
onConditionsChange,
|
|
1909
|
+
InputComponent = ManagedInput,
|
|
1910
|
+
OutputComponent = ControlledOutput,
|
|
1762
1911
|
className,
|
|
1763
1912
|
style
|
|
1764
1913
|
}) {
|
|
@@ -1768,7 +1917,7 @@ function ConditionalUI({
|
|
|
1768
1917
|
const rootClass = ["rcui-root", className].filter(Boolean).join(" ");
|
|
1769
1918
|
return /* @__PURE__ */ jsxs5("div", { className: rootClass, style, children: [
|
|
1770
1919
|
/* @__PURE__ */ jsx6(
|
|
1771
|
-
|
|
1920
|
+
InputComponent,
|
|
1772
1921
|
{
|
|
1773
1922
|
fields,
|
|
1774
1923
|
operators,
|
|
@@ -1778,7 +1927,7 @@ function ConditionalUI({
|
|
|
1778
1927
|
}
|
|
1779
1928
|
),
|
|
1780
1929
|
/* @__PURE__ */ jsx6(
|
|
1781
|
-
|
|
1930
|
+
OutputComponent,
|
|
1782
1931
|
{
|
|
1783
1932
|
groups,
|
|
1784
1933
|
fields,
|
|
@@ -1792,9 +1941,14 @@ export {
|
|
|
1792
1941
|
ConditionDataProvider,
|
|
1793
1942
|
ConditionDataProvider as ConditionParser,
|
|
1794
1943
|
ConditionalUI,
|
|
1944
|
+
ControlledInput,
|
|
1945
|
+
ControlledOutput,
|
|
1795
1946
|
DEFAULT_OPERATORS,
|
|
1796
1947
|
Input,
|
|
1948
|
+
ManagedInput,
|
|
1949
|
+
ManagedOutput,
|
|
1797
1950
|
Output,
|
|
1951
|
+
ReadOnlyOutput,
|
|
1798
1952
|
useConditionDataProvider,
|
|
1799
1953
|
useConditionalInput,
|
|
1800
1954
|
useConditionalOutput
|