@octaviaflow/core 3.1.0-beta.103 → 3.1.0-beta.105
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.map +1 -1
- package/dist/components/ExpressionField/parseExpression.d.ts +17 -0
- package/dist/components/ExpressionField/parseExpression.d.ts.map +1 -1
- package/dist/index.cjs +199 -71
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +199 -71
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -34556,6 +34556,39 @@ function parseExpression(value, pillEveryToken = false) {
|
|
|
34556
34556
|
pushText(tail.slice(last));
|
|
34557
34557
|
return segments;
|
|
34558
34558
|
}
|
|
34559
|
+
function callAt(raw, path) {
|
|
34560
|
+
let current = parseCall(raw);
|
|
34561
|
+
for (const index of path) {
|
|
34562
|
+
if (!current) return void 0;
|
|
34563
|
+
const arg = current.args[index];
|
|
34564
|
+
if (arg === void 0) return void 0;
|
|
34565
|
+
current = parseCall(arg);
|
|
34566
|
+
}
|
|
34567
|
+
return current;
|
|
34568
|
+
}
|
|
34569
|
+
function formatNestedCall(name, args) {
|
|
34570
|
+
return `${name}(${args.filter((a) => a.trim() !== "").join(", ")})`;
|
|
34571
|
+
}
|
|
34572
|
+
function replaceArgument(raw, path, next) {
|
|
34573
|
+
if (path.length === 0) return next;
|
|
34574
|
+
const [index, ...rest] = path;
|
|
34575
|
+
const call = parseCall(raw);
|
|
34576
|
+
if (!call || index === void 0 || index > call.args.length) return raw;
|
|
34577
|
+
const args = [...call.args];
|
|
34578
|
+
if (args[index] === void 0) args[index] = "";
|
|
34579
|
+
args[index] = rest.length === 0 ? next : replaceArgument(args[index], rest, next);
|
|
34580
|
+
const wrapped = raw.trim().startsWith("{{");
|
|
34581
|
+
return wrapped ? formatCall(call.name, args) : formatNestedCall(call.name, args);
|
|
34582
|
+
}
|
|
34583
|
+
function argumentKind(arg) {
|
|
34584
|
+
const trimmed = arg.trim();
|
|
34585
|
+
if (!trimmed) return "literal";
|
|
34586
|
+
if (parseCall(trimmed)) return "call";
|
|
34587
|
+
if (trimmed.startsWith("{{") || trimmed.startsWith("ods://") || trimmed.startsWith("${")) {
|
|
34588
|
+
return "reference";
|
|
34589
|
+
}
|
|
34590
|
+
return "literal";
|
|
34591
|
+
}
|
|
34559
34592
|
|
|
34560
34593
|
// src/components/ExpressionField/ExpressionField.tsx
|
|
34561
34594
|
import { jsx as jsx147, jsxs as jsxs141 } from "react/jsx-runtime";
|
|
@@ -34614,8 +34647,14 @@ function insideCallArguments(textBeforeCaret) {
|
|
|
34614
34647
|
}
|
|
34615
34648
|
return depth > 0;
|
|
34616
34649
|
}
|
|
34617
|
-
function argSummary(arg) {
|
|
34650
|
+
function argSummary(arg, nameOf, depth = 2) {
|
|
34618
34651
|
const trimmed = arg.trim();
|
|
34652
|
+
const call = parseCall(trimmed);
|
|
34653
|
+
if (call) {
|
|
34654
|
+
if (depth <= 0) return `${nameOf(call.name)}(\u2026)`;
|
|
34655
|
+
const inner = call.args.map((a) => argSummary(a, nameOf, depth - 1)).filter(Boolean).join(", ");
|
|
34656
|
+
return `${nameOf(call.name)}(${inner})`;
|
|
34657
|
+
}
|
|
34619
34658
|
if (trimmed.startsWith("{{") || trimmed.startsWith("ods://") || trimmed.startsWith("${")) {
|
|
34620
34659
|
return shortReferenceLabel(trimmed.startsWith("ods://") ? `{{${trimmed}}}` : trimmed);
|
|
34621
34660
|
}
|
|
@@ -34653,7 +34692,7 @@ function ExpressionField({
|
|
|
34653
34692
|
);
|
|
34654
34693
|
const chipText = useCallback50(
|
|
34655
34694
|
(name, args) => {
|
|
34656
|
-
const summary = args.map(argSummary).filter(Boolean).join(", ");
|
|
34695
|
+
const summary = args.map((a) => argSummary(a, callLabel)).filter(Boolean).join(", ");
|
|
34657
34696
|
return `${callLabel(name)}(${summary})`;
|
|
34658
34697
|
},
|
|
34659
34698
|
[callLabel]
|
|
@@ -34672,8 +34711,18 @@ function ExpressionField({
|
|
|
34672
34711
|
} else {
|
|
34673
34712
|
el.className = CHIP;
|
|
34674
34713
|
el.setAttribute("data-call", segment.name);
|
|
34675
|
-
el.textContent = chipText(segment.name, segment.args);
|
|
34676
34714
|
el.title = segment.raw;
|
|
34715
|
+
const text = document.createElement("span");
|
|
34716
|
+
text.className = "ods-expression-field__chip-text";
|
|
34717
|
+
text.textContent = chipText(segment.name, segment.args);
|
|
34718
|
+
el.appendChild(text);
|
|
34719
|
+
const open = document.createElement("span");
|
|
34720
|
+
open.className = "ods-expression-field__chip-open";
|
|
34721
|
+
open.setAttribute("data-open", "true");
|
|
34722
|
+
open.setAttribute("role", "button");
|
|
34723
|
+
open.setAttribute("aria-label", "Edit arguments");
|
|
34724
|
+
open.textContent = "\u2304";
|
|
34725
|
+
el.appendChild(open);
|
|
34677
34726
|
}
|
|
34678
34727
|
return el;
|
|
34679
34728
|
},
|
|
@@ -34779,27 +34828,28 @@ function ExpressionField({
|
|
|
34779
34828
|
}, []);
|
|
34780
34829
|
const openEditor = useCallback50((chip) => {
|
|
34781
34830
|
const raw = chip.getAttribute("data-value") ?? "";
|
|
34782
|
-
|
|
34783
|
-
if (!parsed) return;
|
|
34831
|
+
if (!parseCall(raw)) return;
|
|
34784
34832
|
const box = chip.getBoundingClientRect();
|
|
34785
34833
|
const host = fieldRef.current?.getBoundingClientRect();
|
|
34786
34834
|
setEditing({
|
|
34787
34835
|
el: chip,
|
|
34788
|
-
|
|
34789
|
-
args: parsed.args.length ? parsed.args : [""],
|
|
34836
|
+
raw,
|
|
34790
34837
|
rect: {
|
|
34791
34838
|
top: box.bottom - (host?.top ?? 0) + 4,
|
|
34792
34839
|
left: Math.max(0, box.left - (host?.left ?? 0)),
|
|
34793
|
-
width: Math.max(
|
|
34840
|
+
width: Math.max(300, box.width)
|
|
34794
34841
|
}
|
|
34795
34842
|
});
|
|
34796
34843
|
}, []);
|
|
34797
34844
|
const applyEditor = useCallback50(
|
|
34798
|
-
(
|
|
34845
|
+
(raw) => {
|
|
34799
34846
|
if (!editing) return;
|
|
34800
|
-
const
|
|
34847
|
+
const call = parseCall(raw);
|
|
34848
|
+
if (!call) return;
|
|
34801
34849
|
editing.el.setAttribute("data-value", raw);
|
|
34802
|
-
editing.el.
|
|
34850
|
+
const text = editing.el.querySelector(".ods-expression-field__chip-text");
|
|
34851
|
+
if (text) text.textContent = chipText(call.name, call.args);
|
|
34852
|
+
else editing.el.textContent = chipText(call.name, call.args);
|
|
34803
34853
|
editing.el.title = raw;
|
|
34804
34854
|
setEditing(null);
|
|
34805
34855
|
commit();
|
|
@@ -34809,12 +34859,13 @@ function ExpressionField({
|
|
|
34809
34859
|
);
|
|
34810
34860
|
const handleClick = useCallback50(
|
|
34811
34861
|
(e) => {
|
|
34812
|
-
const
|
|
34862
|
+
const node = e.target;
|
|
34863
|
+
const target = node.closest(`.${PILL}, .${CHIP}`);
|
|
34813
34864
|
if (!target) return;
|
|
34814
|
-
|
|
34815
|
-
|
|
34816
|
-
|
|
34817
|
-
|
|
34865
|
+
const chipEl = target;
|
|
34866
|
+
const wantsEditor = chipEl.classList.contains(CHIP) && (!!node.closest("[data-open]") || e.detail >= 2);
|
|
34867
|
+
selectToken(chipEl);
|
|
34868
|
+
if (!disabled && wantsEditor) openEditor(chipEl);
|
|
34818
34869
|
},
|
|
34819
34870
|
[disabled, openEditor, selectToken]
|
|
34820
34871
|
);
|
|
@@ -34933,11 +34984,11 @@ function ExpressionField({
|
|
|
34933
34984
|
editing && /* @__PURE__ */ jsx147(
|
|
34934
34985
|
CallEditor,
|
|
34935
34986
|
{
|
|
34936
|
-
|
|
34937
|
-
label: callLabel(editing.name),
|
|
34938
|
-
initialArgs: editing.args,
|
|
34939
|
-
signature: resolveCall?.(editing.name),
|
|
34987
|
+
raw: editing.raw,
|
|
34940
34988
|
rect: editing.rect,
|
|
34989
|
+
nameOf: callLabel,
|
|
34990
|
+
labelFor,
|
|
34991
|
+
resolveCall,
|
|
34941
34992
|
onCancel: () => setEditing(null),
|
|
34942
34993
|
onApply: applyEditor
|
|
34943
34994
|
}
|
|
@@ -34947,36 +34998,44 @@ function ExpressionField({
|
|
|
34947
34998
|
] });
|
|
34948
34999
|
}
|
|
34949
35000
|
function CallEditor({
|
|
34950
|
-
|
|
34951
|
-
label,
|
|
34952
|
-
initialArgs,
|
|
34953
|
-
signature,
|
|
35001
|
+
raw,
|
|
34954
35002
|
rect,
|
|
35003
|
+
nameOf,
|
|
35004
|
+
labelFor,
|
|
35005
|
+
resolveCall,
|
|
34955
35006
|
onApply,
|
|
34956
35007
|
onCancel
|
|
34957
35008
|
}) {
|
|
34958
|
-
const
|
|
34959
|
-
const [
|
|
34960
|
-
|
|
34961
|
-
while (seeded.length < names.length) seeded.push("");
|
|
34962
|
-
return seeded.length ? seeded : [""];
|
|
34963
|
-
});
|
|
34964
|
-
const [raw, setRaw] = useState71(null);
|
|
35009
|
+
const [draft, setDraft] = useState71(raw);
|
|
35010
|
+
const [path, setPath] = useState71([]);
|
|
35011
|
+
const [rawMode, setRawMode] = useState71(false);
|
|
34965
35012
|
const panelRef = useRef50(null);
|
|
34966
35013
|
useEffect56(() => {
|
|
34967
|
-
panelRef.current?.querySelector("input")?.focus();
|
|
34968
35014
|
const onKey = (e) => {
|
|
34969
35015
|
if (e.key === "Escape") onCancel();
|
|
34970
35016
|
};
|
|
34971
35017
|
document.addEventListener("keydown", onKey);
|
|
34972
35018
|
return () => document.removeEventListener("keydown", onKey);
|
|
34973
35019
|
}, [onCancel]);
|
|
34974
|
-
const
|
|
34975
|
-
const
|
|
34976
|
-
|
|
34977
|
-
|
|
35020
|
+
const current = callAt(draft, path);
|
|
35021
|
+
const signature = current ? resolveCall?.(current.name) : void 0;
|
|
35022
|
+
const names = signature?.args ?? [];
|
|
35023
|
+
const args = current ? current.args.length ? current.args : [""] : [];
|
|
35024
|
+
const crumbs = [];
|
|
35025
|
+
for (let i = 0; i <= path.length; i++) {
|
|
35026
|
+
const at = callAt(draft, path.slice(0, i));
|
|
35027
|
+
if (at) crumbs.push({ label: nameOf(at.name), path: path.slice(0, i) });
|
|
35028
|
+
}
|
|
35029
|
+
const setArg = (index, next) => setDraft((d) => replaceArgument(d, [...path, index], next));
|
|
35030
|
+
const dropInto = (index) => (e) => {
|
|
35031
|
+
const dropped = e.dataTransfer.getData("application/x-fx-insert");
|
|
35032
|
+
if (!dropped) return;
|
|
34978
35033
|
e.preventDefault();
|
|
34979
|
-
|
|
35034
|
+
e.stopPropagation();
|
|
35035
|
+
const kind = e.dataTransfer.getData("application/x-fx-type");
|
|
35036
|
+
const bare = dropped.startsWith("{{") && dropped.endsWith("}}") ? dropped.slice(2, -2).trim() : dropped;
|
|
35037
|
+
setDraft((d) => replaceArgument(d, [...path, index], bare));
|
|
35038
|
+
if (kind === "function" || parseCall(bare)) setPath((pth) => [...pth, index]);
|
|
34980
35039
|
};
|
|
34981
35040
|
return /* @__PURE__ */ jsxs141(
|
|
34982
35041
|
"div",
|
|
@@ -34985,49 +35044,122 @@ function CallEditor({
|
|
|
34985
35044
|
className: "ods-expression-field__editor",
|
|
34986
35045
|
style: { top: rect.top, left: rect.left, width: rect.width },
|
|
34987
35046
|
role: "dialog",
|
|
34988
|
-
"aria-label": `Edit ${label}`,
|
|
35047
|
+
"aria-label": `Edit ${crumbs[0]?.label ?? "function"}`,
|
|
34989
35048
|
children: [
|
|
34990
35049
|
/* @__PURE__ */ jsxs141("div", { className: "ods-expression-field__editor-head", children: [
|
|
34991
|
-
/* @__PURE__ */ jsx147("
|
|
34992
|
-
|
|
34993
|
-
|
|
34994
|
-
|
|
34995
|
-
|
|
34996
|
-
|
|
34997
|
-
|
|
34998
|
-
|
|
34999
|
-
|
|
35000
|
-
|
|
35001
|
-
|
|
35002
|
-
|
|
35003
|
-
placeholder: "value, or drag one in",
|
|
35004
|
-
onChange: (e) => setArg(idx, e.target.value),
|
|
35005
|
-
onDragOver: (e) => {
|
|
35006
|
-
if (e.dataTransfer.types.includes("application/x-fx-insert")) e.preventDefault();
|
|
35007
|
-
},
|
|
35008
|
-
onDrop: dropInto(idx)
|
|
35009
|
-
}
|
|
35010
|
-
)
|
|
35011
|
-
] }, idx)
|
|
35012
|
-
)),
|
|
35050
|
+
/* @__PURE__ */ jsx147("nav", { className: "ods-expression-field__editor-crumbs", "aria-label": "Nested functions", children: crumbs.map((crumb, i) => /* @__PURE__ */ jsxs141("span", { children: [
|
|
35051
|
+
i > 0 && /* @__PURE__ */ jsx147("span", { "aria-hidden": "true", children: " \u203A " }),
|
|
35052
|
+
/* @__PURE__ */ jsx147(
|
|
35053
|
+
"button",
|
|
35054
|
+
{
|
|
35055
|
+
type: "button",
|
|
35056
|
+
disabled: i === crumbs.length - 1,
|
|
35057
|
+
onClick: () => setPath(crumb.path),
|
|
35058
|
+
children: crumb.label
|
|
35059
|
+
}
|
|
35060
|
+
)
|
|
35061
|
+
] }, crumb.path.join(".") || "root")) }),
|
|
35013
35062
|
/* @__PURE__ */ jsx147(
|
|
35014
35063
|
"button",
|
|
35015
35064
|
{
|
|
35016
35065
|
type: "button",
|
|
35017
35066
|
className: "ods-expression-field__editor-link",
|
|
35018
|
-
onClick: () =>
|
|
35019
|
-
children: "
|
|
35067
|
+
onClick: () => setRawMode((r) => !r),
|
|
35068
|
+
children: rawMode ? "Back to fields" : "Edit as text"
|
|
35020
35069
|
}
|
|
35021
35070
|
)
|
|
35022
|
-
] })
|
|
35071
|
+
] }),
|
|
35072
|
+
rawMode ? /* @__PURE__ */ jsx147(
|
|
35023
35073
|
"textarea",
|
|
35024
35074
|
{
|
|
35025
35075
|
className: "ods-expression-field__editor-raw",
|
|
35026
|
-
value:
|
|
35076
|
+
value: draft,
|
|
35027
35077
|
rows: 3,
|
|
35028
|
-
|
|
35078
|
+
"aria-label": "Expression",
|
|
35079
|
+
onChange: (e) => setDraft(e.target.value)
|
|
35029
35080
|
}
|
|
35030
|
-
),
|
|
35081
|
+
) : /* @__PURE__ */ jsxs141("div", { className: "ods-expression-field__editor-rows", children: [
|
|
35082
|
+
args.map((arg, index) => {
|
|
35083
|
+
const kind = argumentKind(arg);
|
|
35084
|
+
const rowLabel = names[index] ?? `argument ${index + 1}`;
|
|
35085
|
+
const nested = kind === "call" ? parseCall(arg) : void 0;
|
|
35086
|
+
return (
|
|
35087
|
+
// eslint-disable-next-line react/no-array-index-key
|
|
35088
|
+
/* @__PURE__ */ jsxs141("div", { className: "ods-expression-field__editor-row", children: [
|
|
35089
|
+
/* @__PURE__ */ jsx147("span", { id: `arg-${index}-label`, children: rowLabel }),
|
|
35090
|
+
nested ? /* @__PURE__ */ jsxs141(
|
|
35091
|
+
"div",
|
|
35092
|
+
{
|
|
35093
|
+
className: "ods-expression-field__editor-nested",
|
|
35094
|
+
onDragOver: (e) => {
|
|
35095
|
+
if (e.dataTransfer.types.includes("application/x-fx-insert")) e.preventDefault();
|
|
35096
|
+
},
|
|
35097
|
+
onDrop: dropInto(index),
|
|
35098
|
+
children: [
|
|
35099
|
+
/* @__PURE__ */ jsx147("code", { children: argSummary(arg, nameOf) }),
|
|
35100
|
+
/* @__PURE__ */ jsxs141("span", { children: [
|
|
35101
|
+
/* @__PURE__ */ jsx147("button", { type: "button", onClick: () => setPath([...path, index]), children: "Open" }),
|
|
35102
|
+
/* @__PURE__ */ jsx147("button", { type: "button", onClick: () => setArg(index, ""), "aria-label": `Clear ${rowLabel}`, children: "Clear" })
|
|
35103
|
+
] })
|
|
35104
|
+
]
|
|
35105
|
+
}
|
|
35106
|
+
) : kind === "reference" ? (
|
|
35107
|
+
// A reference is a value from somewhere else — named, never
|
|
35108
|
+
// its raw `ods://…` path, here as much as in the field.
|
|
35109
|
+
/* @__PURE__ */ jsxs141(
|
|
35110
|
+
"div",
|
|
35111
|
+
{
|
|
35112
|
+
className: "ods-expression-field__editor-ref",
|
|
35113
|
+
onDragOver: (e) => {
|
|
35114
|
+
if (e.dataTransfer.types.includes("application/x-fx-insert")) e.preventDefault();
|
|
35115
|
+
},
|
|
35116
|
+
onDrop: dropInto(index),
|
|
35117
|
+
children: [
|
|
35118
|
+
/* @__PURE__ */ jsx147(
|
|
35119
|
+
"span",
|
|
35120
|
+
{
|
|
35121
|
+
className: "ods-expression-field__pill",
|
|
35122
|
+
title: arg,
|
|
35123
|
+
"aria-labelledby": `arg-${index}-label`,
|
|
35124
|
+
children: labelFor(arg.startsWith("ods://") ? `{{${arg}}}` : arg)
|
|
35125
|
+
}
|
|
35126
|
+
),
|
|
35127
|
+
/* @__PURE__ */ jsx147("button", { type: "button", onClick: () => setArg(index, ""), "aria-label": `Clear ${rowLabel}`, children: "Clear" })
|
|
35128
|
+
]
|
|
35129
|
+
}
|
|
35130
|
+
)
|
|
35131
|
+
) : /* @__PURE__ */ jsx147(
|
|
35132
|
+
"input",
|
|
35133
|
+
{
|
|
35134
|
+
"aria-labelledby": `arg-${index}-label`,
|
|
35135
|
+
value: arg,
|
|
35136
|
+
placeholder: 'a value, "text", or drag one in',
|
|
35137
|
+
"data-kind": kind,
|
|
35138
|
+
onChange: (e) => setArg(index, e.target.value),
|
|
35139
|
+
onDragOver: (e) => {
|
|
35140
|
+
if (e.dataTransfer.types.includes("application/x-fx-insert")) e.preventDefault();
|
|
35141
|
+
},
|
|
35142
|
+
onDrop: dropInto(index)
|
|
35143
|
+
}
|
|
35144
|
+
)
|
|
35145
|
+
] }, index)
|
|
35146
|
+
);
|
|
35147
|
+
}),
|
|
35148
|
+
/* @__PURE__ */ jsx147(
|
|
35149
|
+
"button",
|
|
35150
|
+
{
|
|
35151
|
+
type: "button",
|
|
35152
|
+
className: "ods-expression-field__editor-link",
|
|
35153
|
+
onClick: () => setDraft((d) => {
|
|
35154
|
+
const at = callAt(d, path);
|
|
35155
|
+
if (!at) return d;
|
|
35156
|
+
const next = [...at.args, '""'];
|
|
35157
|
+
return path.length === 0 ? formatCall(at.name, next) : replaceArgument(d, path, `${at.name}(${next.join(", ")})`);
|
|
35158
|
+
}),
|
|
35159
|
+
children: "+ argument"
|
|
35160
|
+
}
|
|
35161
|
+
)
|
|
35162
|
+
] }),
|
|
35031
35163
|
/* @__PURE__ */ jsxs141("div", { className: "ods-expression-field__editor-foot", children: [
|
|
35032
35164
|
/* @__PURE__ */ jsx147("button", { type: "button", onClick: onCancel, children: "Cancel" }),
|
|
35033
35165
|
/* @__PURE__ */ jsx147(
|
|
@@ -35035,11 +35167,7 @@ function CallEditor({
|
|
|
35035
35167
|
{
|
|
35036
35168
|
type: "button",
|
|
35037
35169
|
className: "ods-expression-field__editor-primary",
|
|
35038
|
-
onClick: () =>
|
|
35039
|
-
if (raw === null) return onApply(args);
|
|
35040
|
-
const parsed = parseCall(raw);
|
|
35041
|
-
onApply(parsed ? parsed.args : splitArguments(raw));
|
|
35042
|
-
},
|
|
35170
|
+
onClick: () => onApply(draft),
|
|
35043
35171
|
children: "Apply"
|
|
35044
35172
|
}
|
|
35045
35173
|
)
|