@octaviaflow/core 3.1.0-beta.102 → 3.1.0-beta.103
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ExpressionField/ExpressionField.d.ts +18 -7
- package/dist/components/ExpressionField/ExpressionField.d.ts.map +1 -1
- package/dist/components/ExpressionField/parseExpression.d.ts +35 -0
- package/dist/components/ExpressionField/parseExpression.d.ts.map +1 -0
- package/dist/index.cjs +454 -80
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +497 -122
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -41339,11 +41339,140 @@ ToolCard.displayName = "ToolCard";
|
|
|
41339
41339
|
|
|
41340
41340
|
// src/components/ExpressionField/ExpressionField.tsx
|
|
41341
41341
|
var import_react188 = require("react");
|
|
41342
|
+
|
|
41343
|
+
// src/components/ExpressionField/parseExpression.ts
|
|
41344
|
+
var CALL_NAME_SOURCE = "(?:[A-Z_][A-Z0-9_]*|user\\.[A-Za-z_][A-Za-z0-9_]*)";
|
|
41345
|
+
var CALL_HEAD = new RegExp(`^(${CALL_NAME_SOURCE})\\s*\\(`);
|
|
41346
|
+
function wrappedRegions(value) {
|
|
41347
|
+
const out = [];
|
|
41348
|
+
let i = 0;
|
|
41349
|
+
while (i < value.length) {
|
|
41350
|
+
const open = value.indexOf("{{", i);
|
|
41351
|
+
if (open === -1) break;
|
|
41352
|
+
let depth = 0;
|
|
41353
|
+
let quote = null;
|
|
41354
|
+
let close = -1;
|
|
41355
|
+
for (let j = open; j < value.length; j++) {
|
|
41356
|
+
const ch = value[j];
|
|
41357
|
+
if (quote) {
|
|
41358
|
+
if (ch === quote) quote = null;
|
|
41359
|
+
continue;
|
|
41360
|
+
}
|
|
41361
|
+
if (ch === '"' || ch === "'") {
|
|
41362
|
+
quote = ch;
|
|
41363
|
+
continue;
|
|
41364
|
+
}
|
|
41365
|
+
if (ch === "{" && value[j + 1] === "{") {
|
|
41366
|
+
depth++;
|
|
41367
|
+
j++;
|
|
41368
|
+
continue;
|
|
41369
|
+
}
|
|
41370
|
+
if (ch === "}" && value[j + 1] === "}") {
|
|
41371
|
+
depth--;
|
|
41372
|
+
j++;
|
|
41373
|
+
if (depth === 0) {
|
|
41374
|
+
close = j;
|
|
41375
|
+
break;
|
|
41376
|
+
}
|
|
41377
|
+
}
|
|
41378
|
+
}
|
|
41379
|
+
if (close === -1) break;
|
|
41380
|
+
out.push({ start: open, end: close });
|
|
41381
|
+
i = close + 1;
|
|
41382
|
+
}
|
|
41383
|
+
return out;
|
|
41384
|
+
}
|
|
41385
|
+
function splitArguments(argsText) {
|
|
41386
|
+
const trimmed = argsText.trim();
|
|
41387
|
+
if (!trimmed) return [];
|
|
41388
|
+
const args = [];
|
|
41389
|
+
let current = "";
|
|
41390
|
+
let depth = 0;
|
|
41391
|
+
let braces = 0;
|
|
41392
|
+
let quote = null;
|
|
41393
|
+
for (let i = 0; i < trimmed.length; i++) {
|
|
41394
|
+
const ch = trimmed[i];
|
|
41395
|
+
if (quote) {
|
|
41396
|
+
current += ch;
|
|
41397
|
+
if (ch === quote) quote = null;
|
|
41398
|
+
continue;
|
|
41399
|
+
}
|
|
41400
|
+
if (ch === '"' || ch === "'") {
|
|
41401
|
+
quote = ch;
|
|
41402
|
+
current += ch;
|
|
41403
|
+
continue;
|
|
41404
|
+
}
|
|
41405
|
+
if (ch === "{" && trimmed[i + 1] === "{") {
|
|
41406
|
+
braces++;
|
|
41407
|
+
current += "{{";
|
|
41408
|
+
i++;
|
|
41409
|
+
continue;
|
|
41410
|
+
}
|
|
41411
|
+
if (ch === "}" && trimmed[i + 1] === "}") {
|
|
41412
|
+
braces--;
|
|
41413
|
+
current += "}}";
|
|
41414
|
+
i++;
|
|
41415
|
+
continue;
|
|
41416
|
+
}
|
|
41417
|
+
if (ch === "(") depth++;
|
|
41418
|
+
if (ch === ")") depth--;
|
|
41419
|
+
if (ch === "," && depth === 0 && braces === 0) {
|
|
41420
|
+
args.push(current.trim());
|
|
41421
|
+
current = "";
|
|
41422
|
+
continue;
|
|
41423
|
+
}
|
|
41424
|
+
current += ch;
|
|
41425
|
+
}
|
|
41426
|
+
if (current.trim()) args.push(current.trim());
|
|
41427
|
+
return args;
|
|
41428
|
+
}
|
|
41429
|
+
function parseCall(raw) {
|
|
41430
|
+
const inner = raw.startsWith("{{") && raw.endsWith("}}") ? raw.slice(2, -2).trim() : raw.trim();
|
|
41431
|
+
const head = CALL_HEAD.exec(inner);
|
|
41432
|
+
if (!head) return void 0;
|
|
41433
|
+
const open = inner.indexOf("(");
|
|
41434
|
+
const close = inner.lastIndexOf(")");
|
|
41435
|
+
if (open === -1 || close < open) return void 0;
|
|
41436
|
+
return { name: head[1], args: splitArguments(inner.slice(open + 1, close)) };
|
|
41437
|
+
}
|
|
41438
|
+
function formatCall(name, args) {
|
|
41439
|
+
return `{{${name}(${args.filter((a) => a.trim() !== "").join(", ")})}}`;
|
|
41440
|
+
}
|
|
41441
|
+
function parseExpression(value, pillEveryToken = false) {
|
|
41442
|
+
const segments = [];
|
|
41443
|
+
let cursor = 0;
|
|
41444
|
+
const pushText = (raw) => {
|
|
41445
|
+
if (raw) segments.push({ kind: "text", raw });
|
|
41446
|
+
};
|
|
41447
|
+
for (const { start, end } of wrappedRegions(value)) {
|
|
41448
|
+
pushText(value.slice(cursor, start));
|
|
41449
|
+
const raw = value.slice(start, end + 1);
|
|
41450
|
+
const call = parseCall(raw);
|
|
41451
|
+
if (call) {
|
|
41452
|
+
segments.push({ kind: "call", raw, name: call.name, args: call.args });
|
|
41453
|
+
} else if (raw.startsWith("{{ods://") || pillEveryToken) {
|
|
41454
|
+
segments.push({ kind: "ref", raw });
|
|
41455
|
+
} else {
|
|
41456
|
+
pushText(raw);
|
|
41457
|
+
}
|
|
41458
|
+
cursor = end + 1;
|
|
41459
|
+
}
|
|
41460
|
+
const tail = value.slice(cursor);
|
|
41461
|
+
let last = 0;
|
|
41462
|
+
for (const m of tail.matchAll(/\$\{[^}]+\}/g)) {
|
|
41463
|
+
pushText(tail.slice(last, m.index));
|
|
41464
|
+
segments.push({ kind: "ref", raw: m[0] });
|
|
41465
|
+
last = (m.index ?? 0) + m[0].length;
|
|
41466
|
+
}
|
|
41467
|
+
pushText(tail.slice(last));
|
|
41468
|
+
return segments;
|
|
41469
|
+
}
|
|
41470
|
+
|
|
41471
|
+
// src/components/ExpressionField/ExpressionField.tsx
|
|
41342
41472
|
var import_jsx_runtime174 = require("react/jsx-runtime");
|
|
41343
|
-
var REF_TOKEN = /(\{\{ods:\/\/[^}]+\}\}|\$\{[^}]+\})/g;
|
|
41344
|
-
var ANY_TOKEN = /(\{\{[^}]+\}\}|\$\{[^}]+\})/g;
|
|
41345
|
-
var CALL_TOKEN = /^\{\{\s*(?:[A-Z_][A-Z0-9_]*|user\.[A-Za-z_][A-Za-z0-9_]*)\s*\(/;
|
|
41346
41473
|
var ZERO_WIDTH = "\u200B";
|
|
41474
|
+
var PILL = "ods-expression-field__pill";
|
|
41475
|
+
var CHIP = "ods-expression-field__chip";
|
|
41347
41476
|
function shortReferenceLabel(token) {
|
|
41348
41477
|
if (token.startsWith("{{ods://")) {
|
|
41349
41478
|
const path = token.slice(8, -2);
|
|
@@ -41365,7 +41494,7 @@ function serialize(el) {
|
|
|
41365
41494
|
}
|
|
41366
41495
|
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
|
41367
41496
|
const elem = node;
|
|
41368
|
-
if (elem.classList.contains(
|
|
41497
|
+
if (elem.classList.contains(PILL) || elem.classList.contains(CHIP)) {
|
|
41369
41498
|
out += elem.getAttribute("data-value") || "";
|
|
41370
41499
|
} else if (elem.tagName === "BR") {
|
|
41371
41500
|
out += "\n";
|
|
@@ -41396,6 +41525,13 @@ function insideCallArguments(textBeforeCaret) {
|
|
|
41396
41525
|
}
|
|
41397
41526
|
return depth > 0;
|
|
41398
41527
|
}
|
|
41528
|
+
function argSummary(arg) {
|
|
41529
|
+
const trimmed = arg.trim();
|
|
41530
|
+
if (trimmed.startsWith("{{") || trimmed.startsWith("ods://") || trimmed.startsWith("${")) {
|
|
41531
|
+
return shortReferenceLabel(trimmed.startsWith("ods://") ? `{{${trimmed}}}` : trimmed);
|
|
41532
|
+
}
|
|
41533
|
+
return trimmed;
|
|
41534
|
+
}
|
|
41399
41535
|
function ExpressionField({
|
|
41400
41536
|
value,
|
|
41401
41537
|
onChange,
|
|
@@ -41408,6 +41544,7 @@ function ExpressionField({
|
|
|
41408
41544
|
className,
|
|
41409
41545
|
style,
|
|
41410
41546
|
resolveLabel,
|
|
41547
|
+
resolveCall,
|
|
41411
41548
|
pillEveryToken = false,
|
|
41412
41549
|
"aria-label": ariaLabel
|
|
41413
41550
|
}) {
|
|
@@ -41416,28 +41553,42 @@ function ExpressionField({
|
|
|
41416
41553
|
const generatedId = (0, import_react188.useId)();
|
|
41417
41554
|
const fieldId = id ?? generatedId;
|
|
41418
41555
|
const helperId = `${fieldId}-helper`;
|
|
41419
|
-
const
|
|
41420
|
-
const isPillToken = (0, import_react188.useCallback)(
|
|
41421
|
-
(part) => {
|
|
41422
|
-
if (CALL_TOKEN.test(part)) return false;
|
|
41423
|
-
return pillEveryToken ? /^(\{\{[^}]+\}\}|\$\{[^}]+\})$/.test(part) : /^(\{\{ods:\/\/[^}]+\}\}|\$\{[^}]+\})$/.test(part);
|
|
41424
|
-
},
|
|
41425
|
-
[pillEveryToken]
|
|
41426
|
-
);
|
|
41556
|
+
const [editing, setEditing] = (0, import_react188.useState)(null);
|
|
41427
41557
|
const labelFor = (0, import_react188.useCallback)(
|
|
41428
41558
|
(token) => resolveLabel?.(token) ?? shortReferenceLabel(token),
|
|
41429
41559
|
[resolveLabel]
|
|
41430
41560
|
);
|
|
41431
|
-
const
|
|
41432
|
-
(
|
|
41433
|
-
|
|
41434
|
-
|
|
41435
|
-
|
|
41436
|
-
|
|
41437
|
-
|
|
41438
|
-
return
|
|
41561
|
+
const callLabel = (0, import_react188.useCallback)(
|
|
41562
|
+
(name) => resolveCall?.(name)?.label ?? (name.startsWith("user.") ? name.slice(5) : name),
|
|
41563
|
+
[resolveCall]
|
|
41564
|
+
);
|
|
41565
|
+
const chipText = (0, import_react188.useCallback)(
|
|
41566
|
+
(name, args) => {
|
|
41567
|
+
const summary = args.map(argSummary).filter(Boolean).join(", ");
|
|
41568
|
+
return `${callLabel(name)}(${summary})`;
|
|
41439
41569
|
},
|
|
41440
|
-
[
|
|
41570
|
+
[callLabel]
|
|
41571
|
+
);
|
|
41572
|
+
const makeToken = (0, import_react188.useCallback)(
|
|
41573
|
+
(segment) => {
|
|
41574
|
+
if (segment.kind === "text") return null;
|
|
41575
|
+
const el = document.createElement("span");
|
|
41576
|
+
el.contentEditable = "false";
|
|
41577
|
+
el.setAttribute("data-value", segment.raw);
|
|
41578
|
+
el.tabIndex = -1;
|
|
41579
|
+
if (segment.kind === "ref") {
|
|
41580
|
+
el.className = PILL;
|
|
41581
|
+
el.textContent = labelFor(segment.raw);
|
|
41582
|
+
el.title = segment.raw;
|
|
41583
|
+
} else {
|
|
41584
|
+
el.className = CHIP;
|
|
41585
|
+
el.setAttribute("data-call", segment.name);
|
|
41586
|
+
el.textContent = chipText(segment.name, segment.args);
|
|
41587
|
+
el.title = segment.raw;
|
|
41588
|
+
}
|
|
41589
|
+
return el;
|
|
41590
|
+
},
|
|
41591
|
+
[labelFor, chipText]
|
|
41441
41592
|
);
|
|
41442
41593
|
const render = (0, import_react188.useCallback)(
|
|
41443
41594
|
(raw) => {
|
|
@@ -41445,17 +41596,19 @@ function ExpressionField({
|
|
|
41445
41596
|
if (!el) return;
|
|
41446
41597
|
el.innerHTML = "";
|
|
41447
41598
|
if (!raw) return;
|
|
41448
|
-
|
|
41449
|
-
if (
|
|
41450
|
-
|
|
41451
|
-
|
|
41599
|
+
for (const segment of parseExpression(raw, pillEveryToken)) {
|
|
41600
|
+
if (segment.kind === "text") {
|
|
41601
|
+
el.appendChild(document.createTextNode(segment.raw));
|
|
41602
|
+
continue;
|
|
41603
|
+
}
|
|
41604
|
+
const token = makeToken(segment);
|
|
41605
|
+
if (token) {
|
|
41606
|
+
el.appendChild(token);
|
|
41452
41607
|
el.appendChild(document.createTextNode(ZERO_WIDTH));
|
|
41453
|
-
} else {
|
|
41454
|
-
el.appendChild(document.createTextNode(part));
|
|
41455
41608
|
}
|
|
41456
|
-
}
|
|
41609
|
+
}
|
|
41457
41610
|
},
|
|
41458
|
-
[
|
|
41611
|
+
[pillEveryToken, makeToken]
|
|
41459
41612
|
);
|
|
41460
41613
|
(0, import_react188.useEffect)(() => {
|
|
41461
41614
|
if (fromSelf.current) {
|
|
@@ -41476,9 +41629,8 @@ function ExpressionField({
|
|
|
41476
41629
|
if (!el || !sel || sel.rangeCount === 0 || !el.contains(sel.anchorNode)) return "";
|
|
41477
41630
|
const range = sel.getRangeAt(0).cloneRange();
|
|
41478
41631
|
range.setStart(el, 0);
|
|
41479
|
-
const fragment = range.cloneContents();
|
|
41480
41632
|
const holder = document.createElement("div");
|
|
41481
|
-
holder.appendChild(
|
|
41633
|
+
holder.appendChild(range.cloneContents());
|
|
41482
41634
|
return serialize(holder);
|
|
41483
41635
|
}, []);
|
|
41484
41636
|
const insert = (0, import_react188.useCallback)(
|
|
@@ -41488,9 +41640,19 @@ function ExpressionField({
|
|
|
41488
41640
|
const sel = window.getSelection();
|
|
41489
41641
|
const hasCaret = !!sel && sel.rangeCount > 0 && el.contains(sel.anchorNode);
|
|
41490
41642
|
const asArgument = hasCaret && insideCallArguments(textBeforeCaret());
|
|
41491
|
-
const
|
|
41492
|
-
const
|
|
41493
|
-
const
|
|
41643
|
+
const call = parseCall(raw);
|
|
41644
|
+
const isCall = kind === "function" || !!call;
|
|
41645
|
+
const text = isCall || asArgument ? raw : raw.startsWith("ods://") ? `{{${raw}}}` : raw;
|
|
41646
|
+
let node;
|
|
41647
|
+
if (asArgument) {
|
|
41648
|
+
node = document.createTextNode(text);
|
|
41649
|
+
} else if (isCall && call) {
|
|
41650
|
+
node = makeToken({ kind: "call", raw: text, name: call.name, args: call.args });
|
|
41651
|
+
} else if (text.startsWith("{{") || text.startsWith("${")) {
|
|
41652
|
+
node = makeToken({ kind: "ref", raw: text });
|
|
41653
|
+
} else {
|
|
41654
|
+
node = document.createTextNode(text);
|
|
41655
|
+
}
|
|
41494
41656
|
const range = hasCaret && sel ? sel.getRangeAt(0) : (() => {
|
|
41495
41657
|
const r = document.createRange();
|
|
41496
41658
|
r.selectNodeContents(el);
|
|
@@ -41502,27 +41664,114 @@ function ExpressionField({
|
|
|
41502
41664
|
const after = document.createTextNode(ZERO_WIDTH);
|
|
41503
41665
|
range.setStartAfter(node);
|
|
41504
41666
|
range.insertNode(after);
|
|
41505
|
-
|
|
41506
|
-
|
|
41507
|
-
const close = text.lastIndexOf(")");
|
|
41508
|
-
if (open !== -1 && close === open + 1) {
|
|
41509
|
-
range.setStart(node, open + 1);
|
|
41510
|
-
range.collapse(true);
|
|
41511
|
-
} else {
|
|
41512
|
-
range.setStartAfter(after);
|
|
41513
|
-
range.collapse(true);
|
|
41514
|
-
}
|
|
41515
|
-
} else {
|
|
41516
|
-
range.setStartAfter(after);
|
|
41517
|
-
range.collapse(true);
|
|
41518
|
-
}
|
|
41667
|
+
range.setStartAfter(after);
|
|
41668
|
+
range.collapse(true);
|
|
41519
41669
|
el.focus();
|
|
41520
41670
|
const selection = window.getSelection();
|
|
41521
41671
|
selection?.removeAllRanges();
|
|
41522
41672
|
selection?.addRange(range);
|
|
41523
41673
|
commit();
|
|
41674
|
+
if (isCall && call && node instanceof HTMLElement && call.args.length === 0) {
|
|
41675
|
+
openEditor(node);
|
|
41676
|
+
}
|
|
41677
|
+
},
|
|
41678
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
41679
|
+
[commit, disabled, makeToken, textBeforeCaret]
|
|
41680
|
+
);
|
|
41681
|
+
const selected = (0, import_react188.useRef)(null);
|
|
41682
|
+
const selectToken = (0, import_react188.useCallback)((token) => {
|
|
41683
|
+
selected.current = token;
|
|
41684
|
+
const range = document.createRange();
|
|
41685
|
+
range.selectNode(token);
|
|
41686
|
+
const sel = window.getSelection();
|
|
41687
|
+
sel?.removeAllRanges();
|
|
41688
|
+
sel?.addRange(range);
|
|
41689
|
+
fieldRef.current?.focus();
|
|
41690
|
+
}, []);
|
|
41691
|
+
const openEditor = (0, import_react188.useCallback)((chip) => {
|
|
41692
|
+
const raw = chip.getAttribute("data-value") ?? "";
|
|
41693
|
+
const parsed = parseCall(raw);
|
|
41694
|
+
if (!parsed) return;
|
|
41695
|
+
const box = chip.getBoundingClientRect();
|
|
41696
|
+
const host = fieldRef.current?.getBoundingClientRect();
|
|
41697
|
+
setEditing({
|
|
41698
|
+
el: chip,
|
|
41699
|
+
name: parsed.name,
|
|
41700
|
+
args: parsed.args.length ? parsed.args : [""],
|
|
41701
|
+
rect: {
|
|
41702
|
+
top: box.bottom - (host?.top ?? 0) + 4,
|
|
41703
|
+
left: Math.max(0, box.left - (host?.left ?? 0)),
|
|
41704
|
+
width: Math.max(260, box.width)
|
|
41705
|
+
}
|
|
41706
|
+
});
|
|
41707
|
+
}, []);
|
|
41708
|
+
const applyEditor = (0, import_react188.useCallback)(
|
|
41709
|
+
(args) => {
|
|
41710
|
+
if (!editing) return;
|
|
41711
|
+
const raw = formatCall(editing.name, args);
|
|
41712
|
+
editing.el.setAttribute("data-value", raw);
|
|
41713
|
+
editing.el.textContent = chipText(editing.name, args);
|
|
41714
|
+
editing.el.title = raw;
|
|
41715
|
+
setEditing(null);
|
|
41716
|
+
commit();
|
|
41717
|
+
fieldRef.current?.focus();
|
|
41718
|
+
},
|
|
41719
|
+
[editing, chipText, commit]
|
|
41720
|
+
);
|
|
41721
|
+
const handleClick = (0, import_react188.useCallback)(
|
|
41722
|
+
(e) => {
|
|
41723
|
+
const target = e.target.closest(`.${PILL}, .${CHIP}`);
|
|
41724
|
+
if (!target) return;
|
|
41725
|
+
selectToken(target);
|
|
41726
|
+
if (!disabled && target.classList.contains(CHIP) && e.detail >= 2) {
|
|
41727
|
+
openEditor(target);
|
|
41728
|
+
}
|
|
41524
41729
|
},
|
|
41525
|
-
[
|
|
41730
|
+
[disabled, openEditor, selectToken]
|
|
41731
|
+
);
|
|
41732
|
+
const handleCopy = (0, import_react188.useCallback)(
|
|
41733
|
+
(e, cut = false) => {
|
|
41734
|
+
const sel = window.getSelection();
|
|
41735
|
+
if (!sel || sel.rangeCount === 0) return;
|
|
41736
|
+
const holder = document.createElement("div");
|
|
41737
|
+
holder.appendChild(sel.getRangeAt(0).cloneContents());
|
|
41738
|
+
const token = selected.current;
|
|
41739
|
+
const text = serialize(holder) || (token && fieldRef.current?.contains(token) && sel.containsNode(token, true) ? token.getAttribute("data-value") ?? "" : "");
|
|
41740
|
+
if (!text) return;
|
|
41741
|
+
e.preventDefault();
|
|
41742
|
+
e.clipboardData.setData("text/plain", text);
|
|
41743
|
+
if (cut && !disabled) {
|
|
41744
|
+
if (serialize(holder)) sel.getRangeAt(0).deleteContents();
|
|
41745
|
+
else token?.remove();
|
|
41746
|
+
commit();
|
|
41747
|
+
}
|
|
41748
|
+
},
|
|
41749
|
+
[commit, disabled]
|
|
41750
|
+
);
|
|
41751
|
+
const handlePaste = (0, import_react188.useCallback)(
|
|
41752
|
+
(e) => {
|
|
41753
|
+
e.preventDefault();
|
|
41754
|
+
const text = e.clipboardData.getData("text/plain");
|
|
41755
|
+
if (!text) return;
|
|
41756
|
+
document.execCommand("insertText", false, text);
|
|
41757
|
+
const next = serialize(fieldRef.current);
|
|
41758
|
+
fromSelf.current = false;
|
|
41759
|
+
onChange(next);
|
|
41760
|
+
},
|
|
41761
|
+
[onChange]
|
|
41762
|
+
);
|
|
41763
|
+
const handleKeyDown = (0, import_react188.useCallback)(
|
|
41764
|
+
(e) => {
|
|
41765
|
+
if (e.key !== "Enter" || !e.metaKey) return;
|
|
41766
|
+
const sel = window.getSelection();
|
|
41767
|
+
const node = sel?.anchorNode;
|
|
41768
|
+
const chip = node instanceof HTMLElement ? node.closest(`.${CHIP}`) : node?.parentElement?.closest(`.${CHIP}`) ?? null;
|
|
41769
|
+
if (chip) {
|
|
41770
|
+
e.preventDefault();
|
|
41771
|
+
openEditor(chip);
|
|
41772
|
+
}
|
|
41773
|
+
},
|
|
41774
|
+
[openEditor]
|
|
41526
41775
|
);
|
|
41527
41776
|
const handleDragOver = (0, import_react188.useCallback)(
|
|
41528
41777
|
(e) => {
|
|
@@ -41549,42 +41798,167 @@ function ExpressionField({
|
|
|
41549
41798
|
},
|
|
41550
41799
|
[disabled, insert]
|
|
41551
41800
|
);
|
|
41552
|
-
const
|
|
41553
|
-
|
|
41554
|
-
|
|
41555
|
-
|
|
41556
|
-
|
|
41801
|
+
const handleBlur = (0, import_react188.useCallback)(() => {
|
|
41802
|
+
const el = fieldRef.current;
|
|
41803
|
+
if (!el) return;
|
|
41804
|
+
const next = serialize(el);
|
|
41805
|
+
if (next !== value) {
|
|
41806
|
+
fromSelf.current = true;
|
|
41807
|
+
onChange(next);
|
|
41808
|
+
}
|
|
41809
|
+
if (!editing) render(next);
|
|
41810
|
+
}, [editing, onChange, render, value]);
|
|
41557
41811
|
return /* @__PURE__ */ (0, import_jsx_runtime174.jsxs)("div", { className: cn("ods-expression-field", className), style, children: [
|
|
41558
41812
|
label && /* @__PURE__ */ (0, import_jsx_runtime174.jsx)("label", { className: "ods-expression-field__label", htmlFor: fieldId, children: label }),
|
|
41559
|
-
/* @__PURE__ */ (0, import_jsx_runtime174.
|
|
41560
|
-
|
|
41561
|
-
|
|
41562
|
-
|
|
41563
|
-
|
|
41564
|
-
|
|
41565
|
-
|
|
41566
|
-
|
|
41567
|
-
|
|
41568
|
-
|
|
41569
|
-
|
|
41570
|
-
|
|
41571
|
-
|
|
41572
|
-
|
|
41573
|
-
|
|
41574
|
-
|
|
41575
|
-
|
|
41576
|
-
|
|
41577
|
-
|
|
41578
|
-
|
|
41579
|
-
|
|
41580
|
-
|
|
41581
|
-
|
|
41582
|
-
|
|
41583
|
-
|
|
41584
|
-
|
|
41813
|
+
/* @__PURE__ */ (0, import_jsx_runtime174.jsxs)("div", { className: "ods-expression-field__body", children: [
|
|
41814
|
+
/* @__PURE__ */ (0, import_jsx_runtime174.jsx)(
|
|
41815
|
+
"div",
|
|
41816
|
+
{
|
|
41817
|
+
id: fieldId,
|
|
41818
|
+
ref: fieldRef,
|
|
41819
|
+
role: "textbox",
|
|
41820
|
+
"aria-multiline": "true",
|
|
41821
|
+
"aria-label": ariaLabel ?? (typeof label === "string" ? label : void 0),
|
|
41822
|
+
"aria-describedby": helperText ? helperId : void 0,
|
|
41823
|
+
"aria-disabled": disabled || void 0,
|
|
41824
|
+
contentEditable: !disabled,
|
|
41825
|
+
suppressContentEditableWarning: true,
|
|
41826
|
+
"data-placeholder": placeholder,
|
|
41827
|
+
className: cn(
|
|
41828
|
+
"ods-expression-field__input",
|
|
41829
|
+
disabled && "ods-expression-field__input--disabled"
|
|
41830
|
+
),
|
|
41831
|
+
style: { minHeight: `${Math.max(1, rows) * 1.5 + 1}rem` },
|
|
41832
|
+
onInput: commit,
|
|
41833
|
+
onBlur: handleBlur,
|
|
41834
|
+
onClick: handleClick,
|
|
41835
|
+
onKeyDown: handleKeyDown,
|
|
41836
|
+
onCopy: handleCopy,
|
|
41837
|
+
onCut: (e) => handleCopy(e, true),
|
|
41838
|
+
onPaste: handlePaste,
|
|
41839
|
+
onDragOver: handleDragOver,
|
|
41840
|
+
onDragLeave: handleDragLeave,
|
|
41841
|
+
onDrop: handleDrop
|
|
41842
|
+
}
|
|
41843
|
+
),
|
|
41844
|
+
editing && /* @__PURE__ */ (0, import_jsx_runtime174.jsx)(
|
|
41845
|
+
CallEditor,
|
|
41846
|
+
{
|
|
41847
|
+
name: editing.name,
|
|
41848
|
+
label: callLabel(editing.name),
|
|
41849
|
+
initialArgs: editing.args,
|
|
41850
|
+
signature: resolveCall?.(editing.name),
|
|
41851
|
+
rect: editing.rect,
|
|
41852
|
+
onCancel: () => setEditing(null),
|
|
41853
|
+
onApply: applyEditor
|
|
41854
|
+
}
|
|
41855
|
+
)
|
|
41856
|
+
] }),
|
|
41585
41857
|
helperText && /* @__PURE__ */ (0, import_jsx_runtime174.jsx)("span", { id: helperId, className: "ods-expression-field__helper", children: helperText })
|
|
41586
41858
|
] });
|
|
41587
41859
|
}
|
|
41860
|
+
function CallEditor({
|
|
41861
|
+
name,
|
|
41862
|
+
label,
|
|
41863
|
+
initialArgs,
|
|
41864
|
+
signature,
|
|
41865
|
+
rect,
|
|
41866
|
+
onApply,
|
|
41867
|
+
onCancel
|
|
41868
|
+
}) {
|
|
41869
|
+
const names = signature?.args ?? [];
|
|
41870
|
+
const [args, setArgs] = (0, import_react188.useState)(() => {
|
|
41871
|
+
const seeded = [...initialArgs];
|
|
41872
|
+
while (seeded.length < names.length) seeded.push("");
|
|
41873
|
+
return seeded.length ? seeded : [""];
|
|
41874
|
+
});
|
|
41875
|
+
const [raw, setRaw] = (0, import_react188.useState)(null);
|
|
41876
|
+
const panelRef = (0, import_react188.useRef)(null);
|
|
41877
|
+
(0, import_react188.useEffect)(() => {
|
|
41878
|
+
panelRef.current?.querySelector("input")?.focus();
|
|
41879
|
+
const onKey = (e) => {
|
|
41880
|
+
if (e.key === "Escape") onCancel();
|
|
41881
|
+
};
|
|
41882
|
+
document.addEventListener("keydown", onKey);
|
|
41883
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
41884
|
+
}, [onCancel]);
|
|
41885
|
+
const setArg = (idx, next) => setArgs((prev) => prev.map((a, i) => i === idx ? next : a));
|
|
41886
|
+
const dropInto = (idx) => (e) => {
|
|
41887
|
+
const ref = e.dataTransfer.getData("application/x-fx-insert");
|
|
41888
|
+
if (!ref) return;
|
|
41889
|
+
e.preventDefault();
|
|
41890
|
+
setArg(idx, ref);
|
|
41891
|
+
};
|
|
41892
|
+
return /* @__PURE__ */ (0, import_jsx_runtime174.jsxs)(
|
|
41893
|
+
"div",
|
|
41894
|
+
{
|
|
41895
|
+
ref: panelRef,
|
|
41896
|
+
className: "ods-expression-field__editor",
|
|
41897
|
+
style: { top: rect.top, left: rect.left, width: rect.width },
|
|
41898
|
+
role: "dialog",
|
|
41899
|
+
"aria-label": `Edit ${label}`,
|
|
41900
|
+
children: [
|
|
41901
|
+
/* @__PURE__ */ (0, import_jsx_runtime174.jsxs)("div", { className: "ods-expression-field__editor-head", children: [
|
|
41902
|
+
/* @__PURE__ */ (0, import_jsx_runtime174.jsx)("code", { children: label }),
|
|
41903
|
+
/* @__PURE__ */ (0, import_jsx_runtime174.jsx)("button", { type: "button", className: "ods-expression-field__editor-link", onClick: () => setRaw(raw === null ? formatCall(name, args) : null), children: raw === null ? "Edit as text" : "Back to fields" })
|
|
41904
|
+
] }),
|
|
41905
|
+
raw === null ? /* @__PURE__ */ (0, import_jsx_runtime174.jsxs)("div", { className: "ods-expression-field__editor-rows", children: [
|
|
41906
|
+
args.map((arg, idx) => (
|
|
41907
|
+
// eslint-disable-next-line react/no-array-index-key
|
|
41908
|
+
/* @__PURE__ */ (0, import_jsx_runtime174.jsxs)("label", { className: "ods-expression-field__editor-row", children: [
|
|
41909
|
+
/* @__PURE__ */ (0, import_jsx_runtime174.jsx)("span", { children: names[idx] ?? `argument ${idx + 1}` }),
|
|
41910
|
+
/* @__PURE__ */ (0, import_jsx_runtime174.jsx)(
|
|
41911
|
+
"input",
|
|
41912
|
+
{
|
|
41913
|
+
value: arg,
|
|
41914
|
+
placeholder: "value, or drag one in",
|
|
41915
|
+
onChange: (e) => setArg(idx, e.target.value),
|
|
41916
|
+
onDragOver: (e) => {
|
|
41917
|
+
if (e.dataTransfer.types.includes("application/x-fx-insert")) e.preventDefault();
|
|
41918
|
+
},
|
|
41919
|
+
onDrop: dropInto(idx)
|
|
41920
|
+
}
|
|
41921
|
+
)
|
|
41922
|
+
] }, idx)
|
|
41923
|
+
)),
|
|
41924
|
+
/* @__PURE__ */ (0, import_jsx_runtime174.jsx)(
|
|
41925
|
+
"button",
|
|
41926
|
+
{
|
|
41927
|
+
type: "button",
|
|
41928
|
+
className: "ods-expression-field__editor-link",
|
|
41929
|
+
onClick: () => setArgs((prev) => [...prev, ""]),
|
|
41930
|
+
children: "+ argument"
|
|
41931
|
+
}
|
|
41932
|
+
)
|
|
41933
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime174.jsx)(
|
|
41934
|
+
"textarea",
|
|
41935
|
+
{
|
|
41936
|
+
className: "ods-expression-field__editor-raw",
|
|
41937
|
+
value: raw,
|
|
41938
|
+
rows: 3,
|
|
41939
|
+
onChange: (e) => setRaw(e.target.value)
|
|
41940
|
+
}
|
|
41941
|
+
),
|
|
41942
|
+
/* @__PURE__ */ (0, import_jsx_runtime174.jsxs)("div", { className: "ods-expression-field__editor-foot", children: [
|
|
41943
|
+
/* @__PURE__ */ (0, import_jsx_runtime174.jsx)("button", { type: "button", onClick: onCancel, children: "Cancel" }),
|
|
41944
|
+
/* @__PURE__ */ (0, import_jsx_runtime174.jsx)(
|
|
41945
|
+
"button",
|
|
41946
|
+
{
|
|
41947
|
+
type: "button",
|
|
41948
|
+
className: "ods-expression-field__editor-primary",
|
|
41949
|
+
onClick: () => {
|
|
41950
|
+
if (raw === null) return onApply(args);
|
|
41951
|
+
const parsed = parseCall(raw);
|
|
41952
|
+
onApply(parsed ? parsed.args : splitArguments(raw));
|
|
41953
|
+
},
|
|
41954
|
+
children: "Apply"
|
|
41955
|
+
}
|
|
41956
|
+
)
|
|
41957
|
+
] })
|
|
41958
|
+
]
|
|
41959
|
+
}
|
|
41960
|
+
);
|
|
41961
|
+
}
|
|
41588
41962
|
|
|
41589
41963
|
// src/components/TopBar/TopBar.tsx
|
|
41590
41964
|
var import_jsx_runtime175 = require("react/jsx-runtime");
|