@sustaina/shared-ui 1.19.0 → 1.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +91 -2
- package/dist/index.d.ts +91 -2
- package/dist/index.js +974 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +959 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -5
package/dist/index.js
CHANGED
|
@@ -21,11 +21,17 @@ var SheetPrimitive = require('@radix-ui/react-dialog');
|
|
|
21
21
|
var i18n = require('i18next');
|
|
22
22
|
var reactI18next = require('react-i18next');
|
|
23
23
|
var zustand = require('zustand');
|
|
24
|
+
var StarterKit = require('@tiptap/starter-kit');
|
|
25
|
+
var react = require('@tiptap/react');
|
|
26
|
+
var ReactDOM = require('react-dom/client');
|
|
27
|
+
var core = require('@tiptap/core');
|
|
28
|
+
var Suggestion = require('@tiptap/suggestion');
|
|
29
|
+
var prosemirrorState = require('prosemirror-state');
|
|
24
30
|
var zod$1 = require('@hookform/resolvers/zod');
|
|
25
31
|
var sortable = require('@dnd-kit/sortable');
|
|
26
32
|
var utilities = require('@dnd-kit/utilities');
|
|
27
33
|
var TooltipPrimitive = require('@radix-ui/react-tooltip');
|
|
28
|
-
var core = require('@dnd-kit/core');
|
|
34
|
+
var core$1 = require('@dnd-kit/core');
|
|
29
35
|
var modifiers = require('@dnd-kit/modifiers');
|
|
30
36
|
var zod = require('zod');
|
|
31
37
|
var RadioGroupPrimitive = require('@radix-ui/react-radio-group');
|
|
@@ -83,6 +89,9 @@ var CheckboxPrimitive__namespace = /*#__PURE__*/_interopNamespace(CheckboxPrimit
|
|
|
83
89
|
var CollapsiblePrimitive__namespace = /*#__PURE__*/_interopNamespace(CollapsiblePrimitive);
|
|
84
90
|
var SheetPrimitive__namespace = /*#__PURE__*/_interopNamespace(SheetPrimitive);
|
|
85
91
|
var i18n__default = /*#__PURE__*/_interopDefault(i18n);
|
|
92
|
+
var StarterKit__default = /*#__PURE__*/_interopDefault(StarterKit);
|
|
93
|
+
var ReactDOM__default = /*#__PURE__*/_interopDefault(ReactDOM);
|
|
94
|
+
var Suggestion__default = /*#__PURE__*/_interopDefault(Suggestion);
|
|
86
95
|
var TooltipPrimitive__namespace = /*#__PURE__*/_interopNamespace(TooltipPrimitive);
|
|
87
96
|
var RadioGroupPrimitive__namespace = /*#__PURE__*/_interopNamespace(RadioGroupPrimitive);
|
|
88
97
|
var SeparatorPrimitive__namespace = /*#__PURE__*/_interopNamespace(SeparatorPrimitive);
|
|
@@ -5382,6 +5391,956 @@ var getDialogAlertControls = () => ({
|
|
|
5382
5391
|
closeDialogAlert,
|
|
5383
5392
|
openErrorDialogAlert
|
|
5384
5393
|
});
|
|
5394
|
+
|
|
5395
|
+
// src/components/formulaEditor/constants/formulaOperation.ts
|
|
5396
|
+
var defaultOperators = [
|
|
5397
|
+
{ value: "(", label: "(" },
|
|
5398
|
+
{ value: ")", label: ")" },
|
|
5399
|
+
{ value: "%", label: "%" },
|
|
5400
|
+
{ value: "&", label: "&" },
|
|
5401
|
+
{ value: "+", label: "+" },
|
|
5402
|
+
{ value: "-", label: "-" },
|
|
5403
|
+
{ value: "*", label: "\xD7" },
|
|
5404
|
+
{ value: "/", label: "\xF7" },
|
|
5405
|
+
{ value: "=", label: "=" },
|
|
5406
|
+
{ value: "\u2260", label: "\u2260" },
|
|
5407
|
+
{ value: "#NULL", label: "#NULL" }
|
|
5408
|
+
];
|
|
5409
|
+
var defaultOperatorShortcuts = {
|
|
5410
|
+
"(": "(",
|
|
5411
|
+
")": ")",
|
|
5412
|
+
"%": "%",
|
|
5413
|
+
"&": "&",
|
|
5414
|
+
"+": "+",
|
|
5415
|
+
"-": "-",
|
|
5416
|
+
"*": "*",
|
|
5417
|
+
"/": "/",
|
|
5418
|
+
"=": "=",
|
|
5419
|
+
"\u2260": "\u2260",
|
|
5420
|
+
"#NULL": "#NULL"
|
|
5421
|
+
};
|
|
5422
|
+
var DEFAULT_DEBOUNCE = 200;
|
|
5423
|
+
function useKeyboardNavigation(itemsLength, onSelect, isLocked) {
|
|
5424
|
+
const [selectedIndex, setSelectedIndex] = React4.useState(0);
|
|
5425
|
+
React4.useEffect(() => {
|
|
5426
|
+
const handler = (event) => {
|
|
5427
|
+
if (event.key === "ArrowDown") {
|
|
5428
|
+
event.preventDefault();
|
|
5429
|
+
setSelectedIndex((prev) => (prev + 1) % Math.max(itemsLength, 1));
|
|
5430
|
+
return;
|
|
5431
|
+
}
|
|
5432
|
+
if (event.key === "ArrowUp") {
|
|
5433
|
+
event.preventDefault();
|
|
5434
|
+
setSelectedIndex((prev) => (prev - 1 + Math.max(itemsLength, 1)) % Math.max(itemsLength, 1));
|
|
5435
|
+
return;
|
|
5436
|
+
}
|
|
5437
|
+
if (event.key === "Enter" || event.key === "Tab") {
|
|
5438
|
+
event.preventDefault();
|
|
5439
|
+
if (!isLocked) onSelect(selectedIndex);
|
|
5440
|
+
}
|
|
5441
|
+
};
|
|
5442
|
+
window.addEventListener("keydown", handler);
|
|
5443
|
+
return () => window.removeEventListener("keydown", handler);
|
|
5444
|
+
}, [itemsLength, onSelect, selectedIndex, isLocked]);
|
|
5445
|
+
return [selectedIndex, setSelectedIndex];
|
|
5446
|
+
}
|
|
5447
|
+
function useDropdownPosition(clientRect, itemsCount) {
|
|
5448
|
+
const [rect, setRect] = React4.useState(null);
|
|
5449
|
+
const [style, setStyle] = React4.useState({});
|
|
5450
|
+
const ref = React4.useRef(null);
|
|
5451
|
+
React4.useEffect(() => {
|
|
5452
|
+
if (!clientRect) return;
|
|
5453
|
+
const update = () => {
|
|
5454
|
+
const nextRect = clientRect();
|
|
5455
|
+
if (nextRect) setRect(nextRect);
|
|
5456
|
+
};
|
|
5457
|
+
update();
|
|
5458
|
+
window.addEventListener("scroll", update, true);
|
|
5459
|
+
window.addEventListener("resize", update);
|
|
5460
|
+
const resizeObserver = new ResizeObserver(update);
|
|
5461
|
+
resizeObserver.observe(document.body);
|
|
5462
|
+
return () => {
|
|
5463
|
+
window.removeEventListener("scroll", update, true);
|
|
5464
|
+
window.removeEventListener("resize", update);
|
|
5465
|
+
resizeObserver.disconnect();
|
|
5466
|
+
};
|
|
5467
|
+
}, [clientRect]);
|
|
5468
|
+
React4.useLayoutEffect(() => {
|
|
5469
|
+
if (!rect || !ref.current) return;
|
|
5470
|
+
const dropdown = ref.current;
|
|
5471
|
+
const dropdownRect = dropdown.getBoundingClientRect();
|
|
5472
|
+
const viewportHeight = window.innerHeight;
|
|
5473
|
+
const viewportWidth = window.innerWidth;
|
|
5474
|
+
let top = rect.bottom + window.scrollY + 6;
|
|
5475
|
+
let left = rect.left + window.scrollX;
|
|
5476
|
+
if (top + dropdownRect.height > viewportHeight + window.scrollY) {
|
|
5477
|
+
top = rect.top + window.scrollY - dropdownRect.height - 6;
|
|
5478
|
+
}
|
|
5479
|
+
if (left + dropdownRect.width > viewportWidth + window.scrollX) {
|
|
5480
|
+
left = Math.max(viewportWidth + window.scrollX - dropdownRect.width - 8, 0);
|
|
5481
|
+
}
|
|
5482
|
+
setStyle({ position: "absolute", zIndex: 50, top, left });
|
|
5483
|
+
}, [rect, itemsCount]);
|
|
5484
|
+
return { ref, style, rect };
|
|
5485
|
+
}
|
|
5486
|
+
var SuggestionList = ({
|
|
5487
|
+
clientRect,
|
|
5488
|
+
command,
|
|
5489
|
+
fetchItems,
|
|
5490
|
+
mapItem,
|
|
5491
|
+
normalizeToken,
|
|
5492
|
+
debounceMs = DEFAULT_DEBOUNCE,
|
|
5493
|
+
query
|
|
5494
|
+
}) => {
|
|
5495
|
+
const [items, setItems] = React4.useState([]);
|
|
5496
|
+
const [isLoading, setIsLoading] = React4.useState(false);
|
|
5497
|
+
const fetchId = React4.useRef(0);
|
|
5498
|
+
const debounceHandle = React4.useRef(null);
|
|
5499
|
+
const itemRefs = React4.useRef([]);
|
|
5500
|
+
const { ref, style, rect } = useDropdownPosition(clientRect, items.length);
|
|
5501
|
+
const normalizedMap = React4.useMemo(() => mapItem, [mapItem]);
|
|
5502
|
+
const normalizeItem = React4.useMemo(() => normalizeToken, [normalizeToken]);
|
|
5503
|
+
React4.useEffect(() => {
|
|
5504
|
+
const runFetch = (input) => {
|
|
5505
|
+
fetchId.current += 1;
|
|
5506
|
+
const currentId = fetchId.current;
|
|
5507
|
+
if (debounceHandle.current) clearTimeout(debounceHandle.current);
|
|
5508
|
+
debounceHandle.current = window.setTimeout(async () => {
|
|
5509
|
+
setIsLoading(true);
|
|
5510
|
+
try {
|
|
5511
|
+
const results = await fetchItems(input);
|
|
5512
|
+
if (fetchId.current !== currentId) return;
|
|
5513
|
+
const mapped = Array.isArray(results) ? results.map((item) => normalizeItem(normalizedMap(item))) : [];
|
|
5514
|
+
setItems(mapped);
|
|
5515
|
+
} catch {
|
|
5516
|
+
if (fetchId.current === currentId) {
|
|
5517
|
+
setItems([]);
|
|
5518
|
+
}
|
|
5519
|
+
} finally {
|
|
5520
|
+
if (fetchId.current === currentId) {
|
|
5521
|
+
setIsLoading(false);
|
|
5522
|
+
}
|
|
5523
|
+
}
|
|
5524
|
+
}, debounceMs);
|
|
5525
|
+
};
|
|
5526
|
+
runFetch(query ?? "");
|
|
5527
|
+
return () => {
|
|
5528
|
+
if (debounceHandle.current) {
|
|
5529
|
+
clearTimeout(debounceHandle.current);
|
|
5530
|
+
}
|
|
5531
|
+
fetchId.current += 1;
|
|
5532
|
+
};
|
|
5533
|
+
}, [query, fetchItems, normalizedMap, normalizeItem, debounceMs]);
|
|
5534
|
+
const handleSelect = React4.useMemo(
|
|
5535
|
+
() => (item) => {
|
|
5536
|
+
if (isLoading) return;
|
|
5537
|
+
command(item);
|
|
5538
|
+
},
|
|
5539
|
+
[command, isLoading]
|
|
5540
|
+
);
|
|
5541
|
+
const [selectedIndex, setSelectedIndex] = useKeyboardNavigation(
|
|
5542
|
+
items.length,
|
|
5543
|
+
(index) => {
|
|
5544
|
+
if (!items.length) return;
|
|
5545
|
+
handleSelect(items[index]);
|
|
5546
|
+
},
|
|
5547
|
+
isLoading
|
|
5548
|
+
);
|
|
5549
|
+
React4.useEffect(() => {
|
|
5550
|
+
setSelectedIndex(0);
|
|
5551
|
+
}, [items, setSelectedIndex]);
|
|
5552
|
+
React4.useEffect(() => {
|
|
5553
|
+
const element = itemRefs.current[selectedIndex];
|
|
5554
|
+
if (element) element.scrollIntoView({ block: "nearest" });
|
|
5555
|
+
}, [selectedIndex]);
|
|
5556
|
+
if (!rect) return null;
|
|
5557
|
+
const showEmptyState = !isLoading && items.length === 0;
|
|
5558
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5559
|
+
"div",
|
|
5560
|
+
{
|
|
5561
|
+
ref,
|
|
5562
|
+
style,
|
|
5563
|
+
className: "w-80 max-h-60 overflow-y-auto rounded-lg border bg-white p-1 shadow-lg",
|
|
5564
|
+
children: [
|
|
5565
|
+
isLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: "Loading suggestions\u2026" }),
|
|
5566
|
+
showEmptyState && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: "No matches" }),
|
|
5567
|
+
items.map((item, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5568
|
+
"button",
|
|
5569
|
+
{
|
|
5570
|
+
ref: (el) => {
|
|
5571
|
+
itemRefs.current[idx] = el;
|
|
5572
|
+
},
|
|
5573
|
+
type: "button",
|
|
5574
|
+
onClick: () => handleSelect(item),
|
|
5575
|
+
onMouseMove: () => setSelectedIndex(idx),
|
|
5576
|
+
className: cn(
|
|
5577
|
+
"flex w-full items-center justify-between rounded-md px-3 py-2 text-left text-sm",
|
|
5578
|
+
selectedIndex === idx ? "bg-muted" : "hover:bg-muted",
|
|
5579
|
+
isLoading && "pointer-events-none opacity-60"
|
|
5580
|
+
),
|
|
5581
|
+
disabled: isLoading,
|
|
5582
|
+
children: [
|
|
5583
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: item.label ?? item.code }),
|
|
5584
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
5585
|
+
item.prefix ?? "",
|
|
5586
|
+
item.code ?? ""
|
|
5587
|
+
] })
|
|
5588
|
+
]
|
|
5589
|
+
},
|
|
5590
|
+
item.id
|
|
5591
|
+
))
|
|
5592
|
+
]
|
|
5593
|
+
}
|
|
5594
|
+
);
|
|
5595
|
+
};
|
|
5596
|
+
var DISALLOWED_MARKS = ["bold", "italic", "link"];
|
|
5597
|
+
var SUGGESTION_DEBOUNCE = 200;
|
|
5598
|
+
var DEFAULT_CHIP_CLASS = "outline-1 outline-muted bg-muted/40 text-foreground";
|
|
5599
|
+
var TokenView = ({ node, editor, getPos }) => {
|
|
5600
|
+
const [isFocused, setIsFocused] = React4__namespace.default.useState(false);
|
|
5601
|
+
React4__namespace.default.useEffect(() => {
|
|
5602
|
+
const handler = () => {
|
|
5603
|
+
const { from, to } = editor.state.selection;
|
|
5604
|
+
const position = getPos();
|
|
5605
|
+
if (position >= from && position + node.nodeSize <= to) {
|
|
5606
|
+
setIsFocused(true);
|
|
5607
|
+
} else {
|
|
5608
|
+
setIsFocused(false);
|
|
5609
|
+
}
|
|
5610
|
+
};
|
|
5611
|
+
editor.on("selectionUpdate", handler);
|
|
5612
|
+
return () => {
|
|
5613
|
+
editor.off("selectionUpdate", handler);
|
|
5614
|
+
};
|
|
5615
|
+
}, [editor, getPos, node.nodeSize]);
|
|
5616
|
+
const remove = () => {
|
|
5617
|
+
const pos = getPos();
|
|
5618
|
+
editor.chain().focus().deleteRange({ from: pos, to: pos + node.nodeSize }).run();
|
|
5619
|
+
};
|
|
5620
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5621
|
+
react.NodeViewWrapper,
|
|
5622
|
+
{
|
|
5623
|
+
as: "span",
|
|
5624
|
+
className: cn(
|
|
5625
|
+
"inline-flex items-center gap-1 rounded-sm px-2 py-0.5 mx-2 text-sm font-medium",
|
|
5626
|
+
DEFAULT_CHIP_CLASS,
|
|
5627
|
+
node.attrs.chipClassName,
|
|
5628
|
+
isFocused && "ring-2 ring-offset-1 ring-ring"
|
|
5629
|
+
),
|
|
5630
|
+
children: [
|
|
5631
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: node.attrs.label ?? node.attrs.code ?? node.attrs.rawValue }),
|
|
5632
|
+
(node.attrs.code || node.attrs.prefix) && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-normal opacity-80", children: [
|
|
5633
|
+
"(",
|
|
5634
|
+
node.attrs.prefix,
|
|
5635
|
+
node.attrs.code ?? "",
|
|
5636
|
+
")"
|
|
5637
|
+
] }),
|
|
5638
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5639
|
+
"button",
|
|
5640
|
+
{
|
|
5641
|
+
type: "button",
|
|
5642
|
+
onClick: remove,
|
|
5643
|
+
className: "ml-1 cursor-pointer rounded-sm px-1 text-xs leading-none hover:bg-sus-primary-1/30",
|
|
5644
|
+
"aria-label": `Remove ${node.attrs.label ?? node.attrs.code ?? "token"}`,
|
|
5645
|
+
children: "\xD7"
|
|
5646
|
+
}
|
|
5647
|
+
)
|
|
5648
|
+
]
|
|
5649
|
+
}
|
|
5650
|
+
);
|
|
5651
|
+
};
|
|
5652
|
+
function normalizeTokenAttrs(attrs, config) {
|
|
5653
|
+
const idSource = attrs.id ?? attrs.code ?? attrs.label ?? Math.random().toString(36).slice(2);
|
|
5654
|
+
const prefix = attrs.prefix ?? config.prefix;
|
|
5655
|
+
const suffix = attrs.code ?? attrs.label ?? "";
|
|
5656
|
+
const outputType = attrs.outputType ?? config.outputType ?? config.type;
|
|
5657
|
+
return {
|
|
5658
|
+
...attrs,
|
|
5659
|
+
id: String(idSource),
|
|
5660
|
+
type: config.type,
|
|
5661
|
+
prefix,
|
|
5662
|
+
chipClassName: attrs.chipClassName ?? config.chipClassName,
|
|
5663
|
+
rawValue: attrs.rawValue ?? (suffix ? `${prefix}${suffix}` : prefix),
|
|
5664
|
+
outputType
|
|
5665
|
+
};
|
|
5666
|
+
}
|
|
5667
|
+
var isPlainTextContext = (state, range) => {
|
|
5668
|
+
const { doc, schema } = state;
|
|
5669
|
+
const $from = doc.resolve(range.from);
|
|
5670
|
+
if ($from.parent.type.name !== "paragraph") return false;
|
|
5671
|
+
const marks = $from.marks();
|
|
5672
|
+
if (marks.some((mark) => DISALLOWED_MARKS.includes(mark.type.name))) return false;
|
|
5673
|
+
const linkMark = schema.marks.link;
|
|
5674
|
+
if (linkMark && linkMark.isInSet(marks)) return false;
|
|
5675
|
+
const nodeBefore = $from.nodeBefore;
|
|
5676
|
+
if (nodeBefore && nodeBefore.type.name === "token") return false;
|
|
5677
|
+
const nodeAfter = $from.nodeAfter;
|
|
5678
|
+
if (nodeAfter && nodeAfter.type.name === "token") return false;
|
|
5679
|
+
return true;
|
|
5680
|
+
};
|
|
5681
|
+
function suggestionPlugin({ config, onSelect, nodeName, editor, register }) {
|
|
5682
|
+
const pluginKey = new prosemirrorState.PluginKey(`formula-token-${config.type}-${config.prefix}`);
|
|
5683
|
+
const fetchItems = config.fetchItems ?? (async () => []);
|
|
5684
|
+
const mapItem = config.mapItem ?? ((item) => item);
|
|
5685
|
+
const normalizeForConfig = (attrs) => normalizeTokenAttrs(attrs, config);
|
|
5686
|
+
register({ key: pluginKey, config });
|
|
5687
|
+
return Suggestion__default.default({
|
|
5688
|
+
editor,
|
|
5689
|
+
char: config.prefix,
|
|
5690
|
+
pluginKey,
|
|
5691
|
+
allowSpaces: true,
|
|
5692
|
+
startOfLine: false,
|
|
5693
|
+
items: () => [],
|
|
5694
|
+
allow: ({ state, range }) => {
|
|
5695
|
+
if (editor.view.composing) return false;
|
|
5696
|
+
return isPlainTextContext(state, range);
|
|
5697
|
+
},
|
|
5698
|
+
render: () => {
|
|
5699
|
+
let reactRoot = null;
|
|
5700
|
+
let container = null;
|
|
5701
|
+
return {
|
|
5702
|
+
onStart: (props) => {
|
|
5703
|
+
container = document.createElement("div");
|
|
5704
|
+
document.body.appendChild(container);
|
|
5705
|
+
reactRoot = ReactDOM__default.default.createRoot(container);
|
|
5706
|
+
reactRoot.render(
|
|
5707
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5708
|
+
SuggestionList,
|
|
5709
|
+
{
|
|
5710
|
+
...props,
|
|
5711
|
+
editor,
|
|
5712
|
+
fetchItems,
|
|
5713
|
+
mapItem,
|
|
5714
|
+
normalizeToken: normalizeForConfig,
|
|
5715
|
+
debounceMs: SUGGESTION_DEBOUNCE
|
|
5716
|
+
}
|
|
5717
|
+
)
|
|
5718
|
+
);
|
|
5719
|
+
},
|
|
5720
|
+
onUpdate: (props) => {
|
|
5721
|
+
reactRoot?.render(
|
|
5722
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5723
|
+
SuggestionList,
|
|
5724
|
+
{
|
|
5725
|
+
...props,
|
|
5726
|
+
editor,
|
|
5727
|
+
fetchItems,
|
|
5728
|
+
mapItem,
|
|
5729
|
+
normalizeToken: normalizeForConfig,
|
|
5730
|
+
debounceMs: SUGGESTION_DEBOUNCE
|
|
5731
|
+
}
|
|
5732
|
+
)
|
|
5733
|
+
);
|
|
5734
|
+
},
|
|
5735
|
+
onKeyDown: ({ event }) => {
|
|
5736
|
+
if (event.key === "Escape" || event.key === "Enter" || event.key === "Tab") {
|
|
5737
|
+
event.preventDefault();
|
|
5738
|
+
return true;
|
|
5739
|
+
}
|
|
5740
|
+
return false;
|
|
5741
|
+
},
|
|
5742
|
+
onExit: () => {
|
|
5743
|
+
reactRoot?.unmount();
|
|
5744
|
+
reactRoot = null;
|
|
5745
|
+
container?.remove();
|
|
5746
|
+
container = null;
|
|
5747
|
+
}
|
|
5748
|
+
};
|
|
5749
|
+
},
|
|
5750
|
+
command: ({ range, props, editor: editor2 }) => {
|
|
5751
|
+
const attrs = normalizeTokenAttrs(props, config);
|
|
5752
|
+
const nextChar = editor2.state.doc.textBetween(range.to, range.to + 1, "\n", "\n");
|
|
5753
|
+
const shouldInsertSpace = !nextChar || !/\s/.test(nextChar);
|
|
5754
|
+
const content = shouldInsertSpace ? [
|
|
5755
|
+
{ type: nodeName, attrs },
|
|
5756
|
+
{ type: "text", text: " " }
|
|
5757
|
+
] : { type: nodeName, attrs };
|
|
5758
|
+
editor2.chain().focus().insertContentAt(range, content).run();
|
|
5759
|
+
onSelect?.(attrs, config);
|
|
5760
|
+
}
|
|
5761
|
+
});
|
|
5762
|
+
}
|
|
5763
|
+
var createSuggestionMonitorPlugin = (registrations) => new prosemirrorState.Plugin({
|
|
5764
|
+
key: new prosemirrorState.PluginKey("formula-token-suggestion-guard"),
|
|
5765
|
+
view(editorView) {
|
|
5766
|
+
const ensureValid = () => {
|
|
5767
|
+
registrations.forEach(({ key, config }) => {
|
|
5768
|
+
const state = key.getState(editorView.state);
|
|
5769
|
+
if (!state?.active) return;
|
|
5770
|
+
const currentText = editorView.state.doc.textBetween(
|
|
5771
|
+
state.range.from,
|
|
5772
|
+
state.range.to,
|
|
5773
|
+
"\n",
|
|
5774
|
+
"\n"
|
|
5775
|
+
);
|
|
5776
|
+
if (!currentText || !currentText.startsWith(config.prefix)) {
|
|
5777
|
+
const tr = editorView.state.tr.setMeta(key, { exit: true });
|
|
5778
|
+
editorView.dispatch(tr);
|
|
5779
|
+
}
|
|
5780
|
+
});
|
|
5781
|
+
};
|
|
5782
|
+
ensureValid();
|
|
5783
|
+
return {
|
|
5784
|
+
update: () => ensureValid()
|
|
5785
|
+
};
|
|
5786
|
+
}
|
|
5787
|
+
});
|
|
5788
|
+
var createTokenSpacingPlugin = (nodeName) => new prosemirrorState.Plugin({
|
|
5789
|
+
key: new prosemirrorState.PluginKey(`formula-token-spacing-${nodeName}`),
|
|
5790
|
+
appendTransaction(transactions, oldState, newState) {
|
|
5791
|
+
if (!transactions.some((tr2) => tr2.docChanged)) return null;
|
|
5792
|
+
const insertions = [];
|
|
5793
|
+
newState.doc.descendants((node, pos) => {
|
|
5794
|
+
if (node.type.name !== nodeName) return;
|
|
5795
|
+
const beforePos = pos - 1;
|
|
5796
|
+
if (beforePos >= 0) {
|
|
5797
|
+
const beforeChar = newState.doc.textBetween(beforePos, pos, "\n", "\n");
|
|
5798
|
+
if (beforeChar && !/\s/.test(beforeChar)) {
|
|
5799
|
+
insertions.push({ pos, text: " " });
|
|
5800
|
+
}
|
|
5801
|
+
}
|
|
5802
|
+
const afterPos = pos + node.nodeSize;
|
|
5803
|
+
if (afterPos < newState.doc.content.size) {
|
|
5804
|
+
const afterChar = newState.doc.textBetween(afterPos, afterPos + 1, "\n", "\n");
|
|
5805
|
+
if (afterChar && !/\s/.test(afterChar)) {
|
|
5806
|
+
insertions.push({ pos: afterPos, text: " " });
|
|
5807
|
+
}
|
|
5808
|
+
}
|
|
5809
|
+
});
|
|
5810
|
+
if (!insertions.length) return null;
|
|
5811
|
+
const tr = newState.tr;
|
|
5812
|
+
insertions.sort((a, b) => b.pos - a.pos).forEach(({ pos, text }) => {
|
|
5813
|
+
tr.insertText(text, pos);
|
|
5814
|
+
});
|
|
5815
|
+
return tr;
|
|
5816
|
+
}
|
|
5817
|
+
});
|
|
5818
|
+
var Token = core.Node.create({
|
|
5819
|
+
name: "token",
|
|
5820
|
+
inline: true,
|
|
5821
|
+
group: "inline",
|
|
5822
|
+
atom: true,
|
|
5823
|
+
selectable: false,
|
|
5824
|
+
draggable: false,
|
|
5825
|
+
addAttributes() {
|
|
5826
|
+
return {
|
|
5827
|
+
type: { default: "" },
|
|
5828
|
+
id: { default: "" },
|
|
5829
|
+
code: { default: "" },
|
|
5830
|
+
label: { default: "" },
|
|
5831
|
+
prefix: { default: "" },
|
|
5832
|
+
rawValue: { default: "" },
|
|
5833
|
+
chipClassName: { default: "" },
|
|
5834
|
+
outputType: { default: "" }
|
|
5835
|
+
};
|
|
5836
|
+
},
|
|
5837
|
+
parseHTML() {
|
|
5838
|
+
return [{ tag: "token-chip" }];
|
|
5839
|
+
},
|
|
5840
|
+
renderHTML({ HTMLAttributes }) {
|
|
5841
|
+
return ["token-chip", core.mergeAttributes(HTMLAttributes), HTMLAttributes.label || ""];
|
|
5842
|
+
},
|
|
5843
|
+
addNodeView() {
|
|
5844
|
+
return react.ReactNodeViewRenderer(TokenView);
|
|
5845
|
+
},
|
|
5846
|
+
addProseMirrorPlugins() {
|
|
5847
|
+
const configs = this.options.configs ?? [];
|
|
5848
|
+
const registrations = [];
|
|
5849
|
+
const suggestionPlugins = configs.map(
|
|
5850
|
+
(config) => suggestionPlugin({
|
|
5851
|
+
config,
|
|
5852
|
+
onSelect: this.options.onSelect,
|
|
5853
|
+
nodeName: this.name,
|
|
5854
|
+
editor: this.editor,
|
|
5855
|
+
register: (entry) => registrations.push(entry)
|
|
5856
|
+
})
|
|
5857
|
+
);
|
|
5858
|
+
return [
|
|
5859
|
+
...suggestionPlugins,
|
|
5860
|
+
createTokenSpacingPlugin(this.name),
|
|
5861
|
+
createSuggestionMonitorPlugin(registrations)
|
|
5862
|
+
];
|
|
5863
|
+
}
|
|
5864
|
+
});
|
|
5865
|
+
var Operator = core.Node.create({
|
|
5866
|
+
name: "operator",
|
|
5867
|
+
inline: true,
|
|
5868
|
+
group: "inline",
|
|
5869
|
+
atom: true,
|
|
5870
|
+
selectable: false,
|
|
5871
|
+
addAttributes() {
|
|
5872
|
+
return {
|
|
5873
|
+
value: { default: null }
|
|
5874
|
+
};
|
|
5875
|
+
},
|
|
5876
|
+
parseHTML() {
|
|
5877
|
+
return [{ tag: "span[operator]" }];
|
|
5878
|
+
},
|
|
5879
|
+
renderHTML({ HTMLAttributes }) {
|
|
5880
|
+
return ["span", core.mergeAttributes({ operator: HTMLAttributes.value }), HTMLAttributes.value];
|
|
5881
|
+
},
|
|
5882
|
+
addKeyboardShortcuts() {
|
|
5883
|
+
const shortcuts = this.options.shortcuts ?? {};
|
|
5884
|
+
return Object.fromEntries(
|
|
5885
|
+
Object.entries(shortcuts).map(([key, value]) => [
|
|
5886
|
+
key,
|
|
5887
|
+
({ editor }) => {
|
|
5888
|
+
editor.chain().insertContent({ type: "operator", attrs: { value } }).insertContent(" ").run();
|
|
5889
|
+
return true;
|
|
5890
|
+
}
|
|
5891
|
+
])
|
|
5892
|
+
);
|
|
5893
|
+
}
|
|
5894
|
+
});
|
|
5895
|
+
|
|
5896
|
+
// src/components/formulaEditor/utils/parseFormulaToken.ts
|
|
5897
|
+
var DEFAULT_PARENTHESIS_MAP = {
|
|
5898
|
+
"(": ")",
|
|
5899
|
+
"[": "]",
|
|
5900
|
+
"{": "}"
|
|
5901
|
+
};
|
|
5902
|
+
var createSortedPrefixes = (prefixMap) => Object.keys(prefixMap).sort((a, b) => b.length - a.length);
|
|
5903
|
+
var findPrefixAt = (value, start, prefixes) => prefixes.find((prefix) => value.startsWith(prefix, start));
|
|
5904
|
+
var pushVariableAndInner = (tokens, value, prefixMap, prefixes) => {
|
|
5905
|
+
if (!value) return;
|
|
5906
|
+
tokens.push({ type: "variable", value });
|
|
5907
|
+
for (let cursor = 0; cursor < value.length; ) {
|
|
5908
|
+
const prefix = findPrefixAt(value, cursor, prefixes);
|
|
5909
|
+
if (!prefix) {
|
|
5910
|
+
cursor += 1;
|
|
5911
|
+
continue;
|
|
5912
|
+
}
|
|
5913
|
+
cursor += prefix.length;
|
|
5914
|
+
const rest = value.slice(cursor);
|
|
5915
|
+
const codeMatch = rest.match(/^\w+/);
|
|
5916
|
+
if (codeMatch) {
|
|
5917
|
+
tokens.push({ type: prefixMap[prefix], code: codeMatch[0] });
|
|
5918
|
+
cursor += codeMatch[0].length;
|
|
5919
|
+
}
|
|
5920
|
+
}
|
|
5921
|
+
};
|
|
5922
|
+
var buildPrefixMap = (configs) => {
|
|
5923
|
+
return configs.reduce((acc, config) => {
|
|
5924
|
+
const outputType = config.outputType ?? config.type;
|
|
5925
|
+
if (!acc[config.prefix]) acc[config.prefix] = outputType;
|
|
5926
|
+
return acc;
|
|
5927
|
+
}, {});
|
|
5928
|
+
};
|
|
5929
|
+
var tokenizeFormulaString = (raw, prefixMap) => {
|
|
5930
|
+
const segments = [];
|
|
5931
|
+
if (!raw) return segments;
|
|
5932
|
+
const prefixes = createSortedPrefixes(prefixMap);
|
|
5933
|
+
let buffer = "";
|
|
5934
|
+
const flushBuffer = () => {
|
|
5935
|
+
if (buffer) {
|
|
5936
|
+
segments.push({ kind: "text", value: buffer });
|
|
5937
|
+
buffer = "";
|
|
5938
|
+
}
|
|
5939
|
+
};
|
|
5940
|
+
for (let i = 0; i < raw.length; ) {
|
|
5941
|
+
const prefix = findPrefixAt(raw, i, prefixes);
|
|
5942
|
+
if (prefix) {
|
|
5943
|
+
const rest = raw.slice(i + prefix.length);
|
|
5944
|
+
const codeMatch = rest.match(/^\w+/);
|
|
5945
|
+
if (codeMatch) {
|
|
5946
|
+
flushBuffer();
|
|
5947
|
+
segments.push({ kind: "token", prefix, code: codeMatch[0] });
|
|
5948
|
+
i += prefix.length + codeMatch[0].length;
|
|
5949
|
+
continue;
|
|
5950
|
+
}
|
|
5951
|
+
}
|
|
5952
|
+
buffer += raw[i];
|
|
5953
|
+
i += 1;
|
|
5954
|
+
}
|
|
5955
|
+
flushBuffer();
|
|
5956
|
+
return segments;
|
|
5957
|
+
};
|
|
5958
|
+
var parseFormulaToToken = (text, prefixMap) => {
|
|
5959
|
+
const tokens = [];
|
|
5960
|
+
if (!text) return tokens;
|
|
5961
|
+
const prefixes = createSortedPrefixes(prefixMap);
|
|
5962
|
+
const leadingPrefix = findPrefixAt(text, 0, prefixes);
|
|
5963
|
+
if (!leadingPrefix) {
|
|
5964
|
+
pushVariableAndInner(tokens, text, prefixMap, prefixes);
|
|
5965
|
+
return tokens;
|
|
5966
|
+
}
|
|
5967
|
+
const afterPrefix = text.slice(leadingPrefix.length);
|
|
5968
|
+
const codeMatch = afterPrefix.match(/^\w+/);
|
|
5969
|
+
if (!codeMatch) {
|
|
5970
|
+
pushVariableAndInner(tokens, text, prefixMap, prefixes);
|
|
5971
|
+
return tokens;
|
|
5972
|
+
}
|
|
5973
|
+
tokens.push({ type: prefixMap[leadingPrefix], code: codeMatch[0] });
|
|
5974
|
+
const remaining = afterPrefix.slice(codeMatch[0].length);
|
|
5975
|
+
if (remaining) {
|
|
5976
|
+
let variablePart = remaining;
|
|
5977
|
+
if (remaining.startsWith(":") && remaining.includes("(")) {
|
|
5978
|
+
let depth = 0;
|
|
5979
|
+
let endIndex = -1;
|
|
5980
|
+
for (let i = 0; i < remaining.length; i++) {
|
|
5981
|
+
if (remaining[i] === "(") depth += 1;
|
|
5982
|
+
else if (remaining[i] === ")") depth -= 1;
|
|
5983
|
+
if (depth === 0 && remaining[i] === ")") {
|
|
5984
|
+
endIndex = i;
|
|
5985
|
+
break;
|
|
5986
|
+
}
|
|
5987
|
+
}
|
|
5988
|
+
if (endIndex !== -1) {
|
|
5989
|
+
variablePart = remaining.slice(0, endIndex + 1);
|
|
5990
|
+
}
|
|
5991
|
+
}
|
|
5992
|
+
pushVariableAndInner(tokens, variablePart, prefixMap, prefixes);
|
|
5993
|
+
}
|
|
5994
|
+
return tokens;
|
|
5995
|
+
};
|
|
5996
|
+
var splitOperators = (value, allowedOperators) => {
|
|
5997
|
+
const result = [];
|
|
5998
|
+
if (!value) return result;
|
|
5999
|
+
const sortedOperators = [...allowedOperators].sort((a, b) => b.length - a.length);
|
|
6000
|
+
let buffer = "";
|
|
6001
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
6002
|
+
let matched = false;
|
|
6003
|
+
for (const operator of sortedOperators) {
|
|
6004
|
+
if (operator && value.startsWith(operator, i)) {
|
|
6005
|
+
if (buffer) {
|
|
6006
|
+
result.push({ type: "variable", value: buffer });
|
|
6007
|
+
buffer = "";
|
|
6008
|
+
}
|
|
6009
|
+
result.push({ type: "operator", value: operator });
|
|
6010
|
+
i += operator.length - 1;
|
|
6011
|
+
matched = true;
|
|
6012
|
+
break;
|
|
6013
|
+
}
|
|
6014
|
+
}
|
|
6015
|
+
if (!matched) buffer += value[i];
|
|
6016
|
+
}
|
|
6017
|
+
if (buffer) result.push({ type: "variable", value: buffer });
|
|
6018
|
+
return result;
|
|
6019
|
+
};
|
|
6020
|
+
var parseFormula = (editorJson, prefixMap, allowedOperators) => {
|
|
6021
|
+
const rawParts = [];
|
|
6022
|
+
const tokens = [];
|
|
6023
|
+
const traverse = (nodes2 = []) => {
|
|
6024
|
+
nodes2.forEach((node) => {
|
|
6025
|
+
if (!node) return;
|
|
6026
|
+
switch (node.type) {
|
|
6027
|
+
case "text": {
|
|
6028
|
+
const text = node.text ?? "";
|
|
6029
|
+
if (!text) return;
|
|
6030
|
+
rawParts.push(text);
|
|
6031
|
+
if (text.trim()) {
|
|
6032
|
+
tokens.push({ type: "variable", value: text });
|
|
6033
|
+
}
|
|
6034
|
+
break;
|
|
6035
|
+
}
|
|
6036
|
+
case "operator": {
|
|
6037
|
+
rawParts.push(node.attrs?.value ?? "");
|
|
6038
|
+
tokens.push({ type: "operator", value: node.attrs?.value ?? "" });
|
|
6039
|
+
break;
|
|
6040
|
+
}
|
|
6041
|
+
case "op-library": {
|
|
6042
|
+
rawParts.push(node.attrs?.value ?? "");
|
|
6043
|
+
tokens.push(...splitOperators(node.attrs?.value ?? "", allowedOperators));
|
|
6044
|
+
break;
|
|
6045
|
+
}
|
|
6046
|
+
case "token": {
|
|
6047
|
+
const attrs = node.attrs ?? {};
|
|
6048
|
+
const rawValue = attrs.rawValue ?? `${attrs.prefix ?? ""}${attrs.code ?? ""}`;
|
|
6049
|
+
rawParts.push(rawValue);
|
|
6050
|
+
const outputType = attrs.outputType ?? attrs.type ?? prefixMap[attrs.prefix ?? ""];
|
|
6051
|
+
tokens.push({ ...attrs, type: outputType });
|
|
6052
|
+
break;
|
|
6053
|
+
}
|
|
6054
|
+
default:
|
|
6055
|
+
if (node.content) traverse(node.content);
|
|
6056
|
+
}
|
|
6057
|
+
});
|
|
6058
|
+
};
|
|
6059
|
+
traverse(editorJson?.content ?? []);
|
|
6060
|
+
return { raw: rawParts.join(""), token: tokens };
|
|
6061
|
+
};
|
|
6062
|
+
var validateTokenPrefixes = (rawFormula, prefixMap) => {
|
|
6063
|
+
const prefixes = Object.keys(prefixMap);
|
|
6064
|
+
if (prefixes.length === 0) return { isValid: true };
|
|
6065
|
+
const missingTypes = /* @__PURE__ */ new Set();
|
|
6066
|
+
for (let i = 0; i < rawFormula.length; i += 1) {
|
|
6067
|
+
const prefix = prefixes.find((key) => rawFormula.startsWith(key, i));
|
|
6068
|
+
if (prefix) {
|
|
6069
|
+
const nextChar = rawFormula[i + prefix.length];
|
|
6070
|
+
if (!nextChar || !/\w/.test(nextChar)) {
|
|
6071
|
+
missingTypes.add(prefixMap[prefix]);
|
|
6072
|
+
}
|
|
6073
|
+
i += prefix.length;
|
|
6074
|
+
}
|
|
6075
|
+
}
|
|
6076
|
+
if (missingTypes.size > 0) {
|
|
6077
|
+
return {
|
|
6078
|
+
isValid: false,
|
|
6079
|
+
message: `Invalid token for ${Array.from(missingTypes).join(" and ")}`
|
|
6080
|
+
};
|
|
6081
|
+
}
|
|
6082
|
+
return { isValid: true };
|
|
6083
|
+
};
|
|
6084
|
+
var isValidParentheses = (input) => {
|
|
6085
|
+
const stack = [];
|
|
6086
|
+
const openers = Object.keys(DEFAULT_PARENTHESIS_MAP);
|
|
6087
|
+
const closers = Object.values(DEFAULT_PARENTHESIS_MAP);
|
|
6088
|
+
for (const char of input) {
|
|
6089
|
+
if (openers.includes(char)) {
|
|
6090
|
+
stack.push(char);
|
|
6091
|
+
} else if (closers.includes(char)) {
|
|
6092
|
+
const last = stack.pop();
|
|
6093
|
+
if (!last || DEFAULT_PARENTHESIS_MAP[last] !== char) {
|
|
6094
|
+
return { valid: false };
|
|
6095
|
+
}
|
|
6096
|
+
}
|
|
6097
|
+
}
|
|
6098
|
+
if (stack.length > 0) {
|
|
6099
|
+
return { valid: false };
|
|
6100
|
+
}
|
|
6101
|
+
return { valid: true };
|
|
6102
|
+
};
|
|
6103
|
+
var mapTokensToOutput = (tokens, configs) => {
|
|
6104
|
+
const prefixLookup = /* @__PURE__ */ new Map();
|
|
6105
|
+
const typeLookup = /* @__PURE__ */ new Map();
|
|
6106
|
+
configs.forEach((config) => {
|
|
6107
|
+
prefixLookup.set(config.prefix, config);
|
|
6108
|
+
typeLookup.set(config.outputType ?? config.type, config);
|
|
6109
|
+
});
|
|
6110
|
+
return tokens.map((token) => {
|
|
6111
|
+
if (!token || typeof token !== "object") return token;
|
|
6112
|
+
const tokenPrefix = "prefix" in token ? token.prefix : void 0;
|
|
6113
|
+
const tokenType = "type" in token ? token.type : void 0;
|
|
6114
|
+
const config = (tokenPrefix ? prefixLookup.get(tokenPrefix) : void 0) ?? (tokenType ? typeLookup.get(String(tokenType)) : void 0);
|
|
6115
|
+
if (config?.mapOutput) {
|
|
6116
|
+
return config.mapOutput(token);
|
|
6117
|
+
}
|
|
6118
|
+
return token;
|
|
6119
|
+
});
|
|
6120
|
+
};
|
|
6121
|
+
var DEFAULT_TOKEN_CONFIGS = [
|
|
6122
|
+
{
|
|
6123
|
+
type: "position",
|
|
6124
|
+
prefix: "$",
|
|
6125
|
+
chipClassName: "text-black border border-sus-primary-1 outline-sus-primary-1",
|
|
6126
|
+
fetchItems: async () => []
|
|
6127
|
+
},
|
|
6128
|
+
{
|
|
6129
|
+
type: "impact",
|
|
6130
|
+
prefix: "#",
|
|
6131
|
+
chipClassName: "text-black border border-sus-primary-1 outline-sus-primary-1",
|
|
6132
|
+
fetchItems: async () => []
|
|
6133
|
+
}
|
|
6134
|
+
];
|
|
6135
|
+
var defaultMapItem = (item) => {
|
|
6136
|
+
const id = item?.id ?? item?.code ?? item?.label ?? Math.random().toString(36).slice(2);
|
|
6137
|
+
return {
|
|
6138
|
+
id: String(id),
|
|
6139
|
+
label: item?.label ?? item?.name ?? String(item?.code ?? item?.id ?? ""),
|
|
6140
|
+
code: item?.code ?? String(item?.id ?? "")
|
|
6141
|
+
};
|
|
6142
|
+
};
|
|
6143
|
+
var looksLikeHTML = (value) => /<\/?[a-z][\s\S]*>/i.test(value.trim());
|
|
6144
|
+
var tryParseJSON = (value) => {
|
|
6145
|
+
try {
|
|
6146
|
+
const parsed = JSON.parse(value);
|
|
6147
|
+
if (parsed && typeof parsed === "object") {
|
|
6148
|
+
return parsed;
|
|
6149
|
+
}
|
|
6150
|
+
} catch {
|
|
6151
|
+
}
|
|
6152
|
+
return null;
|
|
6153
|
+
};
|
|
6154
|
+
var buildDocFromRaw = (raw, prefixMap, configLookup) => {
|
|
6155
|
+
const lines = raw.split(/\r?\n/);
|
|
6156
|
+
let tokenCounter = 0;
|
|
6157
|
+
const paragraphs = lines.map((line) => {
|
|
6158
|
+
const segments = tokenizeFormulaString(line, prefixMap);
|
|
6159
|
+
const content = segments.map((segment) => {
|
|
6160
|
+
if (segment.kind === "text") {
|
|
6161
|
+
return segment.value ? { type: "text", text: segment.value } : null;
|
|
6162
|
+
}
|
|
6163
|
+
const config = configLookup.get(segment.prefix);
|
|
6164
|
+
const fallbackType = prefixMap[segment.prefix];
|
|
6165
|
+
const displayType = config?.type ?? fallbackType;
|
|
6166
|
+
const outputType = config?.outputType ?? fallbackType;
|
|
6167
|
+
if (!displayType && !outputType) {
|
|
6168
|
+
return { type: "text", text: `${segment.prefix}${segment.code}` };
|
|
6169
|
+
}
|
|
6170
|
+
const attrs = {
|
|
6171
|
+
id: `token-${tokenCounter++}`,
|
|
6172
|
+
type: displayType ?? outputType ?? "token",
|
|
6173
|
+
code: segment.code,
|
|
6174
|
+
prefix: segment.prefix,
|
|
6175
|
+
rawValue: `${segment.prefix}${segment.code}`,
|
|
6176
|
+
chipClassName: config?.chipClassName,
|
|
6177
|
+
label: segment.code,
|
|
6178
|
+
outputType: outputType ?? displayType
|
|
6179
|
+
};
|
|
6180
|
+
return { type: "token", attrs };
|
|
6181
|
+
}).filter((node) => Boolean(node));
|
|
6182
|
+
return {
|
|
6183
|
+
type: "paragraph",
|
|
6184
|
+
content: content.length > 0 ? content : [{ type: "text", text: "" }]
|
|
6185
|
+
};
|
|
6186
|
+
});
|
|
6187
|
+
return { type: "doc", content: paragraphs };
|
|
6188
|
+
};
|
|
6189
|
+
var FormulaEditor = ({
|
|
6190
|
+
value,
|
|
6191
|
+
disabled,
|
|
6192
|
+
className,
|
|
6193
|
+
editorClassName,
|
|
6194
|
+
errorMessage,
|
|
6195
|
+
tokenConfigs,
|
|
6196
|
+
operators = defaultOperators,
|
|
6197
|
+
operatorShortcuts = defaultOperatorShortcuts,
|
|
6198
|
+
onChange,
|
|
6199
|
+
onSelectSuggestion,
|
|
6200
|
+
field,
|
|
6201
|
+
fieldState
|
|
6202
|
+
}) => {
|
|
6203
|
+
const [isExpanded, setIsExpanded] = React4.useState(false);
|
|
6204
|
+
const lastEmittedValueRef = React4.useRef(null);
|
|
6205
|
+
const ignorePropValueRef = React4.useRef(false);
|
|
6206
|
+
const normalizedConfigs = React4.useMemo(() => {
|
|
6207
|
+
const configsToUse = tokenConfigs?.length ? tokenConfigs : DEFAULT_TOKEN_CONFIGS;
|
|
6208
|
+
return configsToUse.map((config) => ({
|
|
6209
|
+
...config,
|
|
6210
|
+
fetchItems: config.fetchItems ?? (async () => []),
|
|
6211
|
+
mapItem: config.mapItem ?? ((item) => ({ ...item, ...defaultMapItem(item) })),
|
|
6212
|
+
outputType: config.outputType ?? config.type
|
|
6213
|
+
}));
|
|
6214
|
+
}, [tokenConfigs]);
|
|
6215
|
+
const prefixMap = React4.useMemo(() => buildPrefixMap(normalizedConfigs), [normalizedConfigs]);
|
|
6216
|
+
const configLookup = React4.useMemo(() => {
|
|
6217
|
+
const lookup = /* @__PURE__ */ new Map();
|
|
6218
|
+
normalizedConfigs.forEach((config) => {
|
|
6219
|
+
lookup.set(config.prefix, config);
|
|
6220
|
+
});
|
|
6221
|
+
return lookup;
|
|
6222
|
+
}, [normalizedConfigs]);
|
|
6223
|
+
const allowedOperators = React4.useMemo(() => operators.map((operator) => operator.value), [operators]);
|
|
6224
|
+
const displayError = errorMessage ?? fieldState?.error?.message;
|
|
6225
|
+
const hasError = Boolean(displayError);
|
|
6226
|
+
const convertValueToContent = React4.useCallback(
|
|
6227
|
+
(input) => {
|
|
6228
|
+
if (!input) return "";
|
|
6229
|
+
const trimmed = input.trim();
|
|
6230
|
+
if (!trimmed) return "";
|
|
6231
|
+
const parsedJSON = tryParseJSON(trimmed);
|
|
6232
|
+
if (parsedJSON && parsedJSON.type === "doc") {
|
|
6233
|
+
return parsedJSON;
|
|
6234
|
+
}
|
|
6235
|
+
if (looksLikeHTML(trimmed)) {
|
|
6236
|
+
return input;
|
|
6237
|
+
}
|
|
6238
|
+
return buildDocFromRaw(input, prefixMap, configLookup);
|
|
6239
|
+
},
|
|
6240
|
+
[configLookup, prefixMap]
|
|
6241
|
+
);
|
|
6242
|
+
const resolvedContent = React4.useMemo(() => convertValueToContent(value), [convertValueToContent, value]);
|
|
6243
|
+
const extensions = React4.useMemo(
|
|
6244
|
+
() => [
|
|
6245
|
+
StarterKit__default.default.configure({ bold: false, italic: false }),
|
|
6246
|
+
Token.configure({ configs: normalizedConfigs, onSelect: onSelectSuggestion }),
|
|
6247
|
+
Operator.configure({ shortcuts: operatorShortcuts })
|
|
6248
|
+
],
|
|
6249
|
+
[normalizedConfigs, onSelectSuggestion, operatorShortcuts]
|
|
6250
|
+
);
|
|
6251
|
+
const editor = react.useEditor({
|
|
6252
|
+
extensions,
|
|
6253
|
+
content: resolvedContent ?? "",
|
|
6254
|
+
onUpdate: ({ editor: nextEditor }) => {
|
|
6255
|
+
const { raw, token } = parseFormula(nextEditor.getJSON(), prefixMap, allowedOperators);
|
|
6256
|
+
const mappedTokens = mapTokensToOutput(token, normalizedConfigs);
|
|
6257
|
+
lastEmittedValueRef.current = raw;
|
|
6258
|
+
ignorePropValueRef.current = true;
|
|
6259
|
+
onChange?.(raw, mappedTokens);
|
|
6260
|
+
},
|
|
6261
|
+
editorProps: {
|
|
6262
|
+
attributes: {
|
|
6263
|
+
class: cn(
|
|
6264
|
+
isExpanded ? "min-h-[300px] max-h-[300px]" : "min-h-[150px] max-h-[150px]",
|
|
6265
|
+
hasError ? "border border-destructive" : "border focus-visible:border-ring",
|
|
6266
|
+
"w-full rounded-lg bg-white px-4 py-3",
|
|
6267
|
+
"overflow-y-auto whitespace-pre-wrap wrap-break-word focus:outline-none",
|
|
6268
|
+
disabled && "pointer-events-none opacity-60",
|
|
6269
|
+
editorClassName
|
|
6270
|
+
)
|
|
6271
|
+
}
|
|
6272
|
+
}
|
|
6273
|
+
});
|
|
6274
|
+
React4.useEffect(() => {
|
|
6275
|
+
if (!editor) return;
|
|
6276
|
+
editor.setEditable(!disabled);
|
|
6277
|
+
}, [disabled, editor]);
|
|
6278
|
+
React4.useEffect(() => {
|
|
6279
|
+
if (!editor || resolvedContent === void 0) return;
|
|
6280
|
+
if (ignorePropValueRef.current && typeof value === "string" && value === lastEmittedValueRef.current) {
|
|
6281
|
+
ignorePropValueRef.current = false;
|
|
6282
|
+
return;
|
|
6283
|
+
}
|
|
6284
|
+
ignorePropValueRef.current = false;
|
|
6285
|
+
if (typeof resolvedContent === "string") {
|
|
6286
|
+
const currentHTML = editor.getHTML();
|
|
6287
|
+
if (resolvedContent !== currentHTML) {
|
|
6288
|
+
editor.commands.setContent(resolvedContent, { emitUpdate: false });
|
|
6289
|
+
}
|
|
6290
|
+
return;
|
|
6291
|
+
}
|
|
6292
|
+
const currentJSON = JSON.stringify(editor.getJSON());
|
|
6293
|
+
const nextJSON = JSON.stringify(resolvedContent);
|
|
6294
|
+
if (currentJSON !== nextJSON) {
|
|
6295
|
+
editor.commands.setContent(resolvedContent, { emitUpdate: false });
|
|
6296
|
+
}
|
|
6297
|
+
}, [editor, resolvedContent, value]);
|
|
6298
|
+
const insertOperator = (operator) => {
|
|
6299
|
+
editor?.chain().focus().insertContent({ type: "operator", attrs: { value: operator } }).insertContent(" ").run();
|
|
6300
|
+
};
|
|
6301
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("w-full space-y-2", className), children: [
|
|
6302
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
6303
|
+
"div",
|
|
6304
|
+
{
|
|
6305
|
+
ref: field?.ref,
|
|
6306
|
+
onBlur: field?.onBlur,
|
|
6307
|
+
tabIndex: 0,
|
|
6308
|
+
className: "relative",
|
|
6309
|
+
onFocus: () => {
|
|
6310
|
+
if (editor && !editor.isFocused) {
|
|
6311
|
+
editor.chain().focus().run();
|
|
6312
|
+
}
|
|
6313
|
+
},
|
|
6314
|
+
children: [
|
|
6315
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.EditorContent, { editor }),
|
|
6316
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6317
|
+
Button,
|
|
6318
|
+
{
|
|
6319
|
+
type: "button",
|
|
6320
|
+
variant: "ghost",
|
|
6321
|
+
size: "icon",
|
|
6322
|
+
className: "absolute bottom-2 right-4 h-6 w-6 rounded-full bg-white shadow",
|
|
6323
|
+
onClick: () => setIsExpanded((prev) => !prev),
|
|
6324
|
+
children: isExpanded ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Minimize2, { className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Maximize2, { className: "h-4 w-4" })
|
|
6325
|
+
}
|
|
6326
|
+
)
|
|
6327
|
+
]
|
|
6328
|
+
}
|
|
6329
|
+
),
|
|
6330
|
+
hasError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-destructive", role: "alert", children: displayError }),
|
|
6331
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap justify-end gap-2 py-2", children: operators.map((operator) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
6332
|
+
Button,
|
|
6333
|
+
{
|
|
6334
|
+
type: "button",
|
|
6335
|
+
onClick: () => insertOperator(operator.value),
|
|
6336
|
+
className: "min-w-10 rounded-sm px-3 bg-sus-blue-3",
|
|
6337
|
+
disabled,
|
|
6338
|
+
children: operator.label
|
|
6339
|
+
},
|
|
6340
|
+
operator.value
|
|
6341
|
+
)) })
|
|
6342
|
+
] });
|
|
6343
|
+
};
|
|
5385
6344
|
function TooltipProvider({
|
|
5386
6345
|
delayDuration = 0,
|
|
5387
6346
|
...props
|
|
@@ -5597,7 +6556,7 @@ var GridSettingsModal = ({
|
|
|
5597
6556
|
onSaveColumns({ ordering, visibility, pinning }, data.columns);
|
|
5598
6557
|
}
|
|
5599
6558
|
};
|
|
5600
|
-
const sensors = core.useSensors(core.useSensor(core.PointerSensor, { activationConstraint: { distance: 5 } }));
|
|
6559
|
+
const sensors = core$1.useSensors(core$1.useSensor(core$1.PointerSensor, { activationConstraint: { distance: 5 } }));
|
|
5601
6560
|
function handleDragEnd(event) {
|
|
5602
6561
|
const { active, over } = event;
|
|
5603
6562
|
if (!over || active.id === over.id) return;
|
|
@@ -5661,10 +6620,10 @@ var GridSettingsModal = ({
|
|
|
5661
6620
|
fields[0]?.fieldId
|
|
5662
6621
|
) }),
|
|
5663
6622
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3 [&_button:not([disabled])]:cursor-pointer", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5664
|
-
core.DndContext,
|
|
6623
|
+
core$1.DndContext,
|
|
5665
6624
|
{
|
|
5666
6625
|
sensors,
|
|
5667
|
-
collisionDetection: core.closestCenter,
|
|
6626
|
+
collisionDetection: core$1.closestCenter,
|
|
5668
6627
|
modifiers: [modifiers.restrictToParentElement, modifiers.restrictToVerticalAxis],
|
|
5669
6628
|
onDragStart: () => setIsDragging(true),
|
|
5670
6629
|
onDragEnd: (event) => {
|
|
@@ -8819,6 +9778,7 @@ exports.FormField = FormField;
|
|
|
8819
9778
|
exports.FormItem = FormItem;
|
|
8820
9779
|
exports.FormLabel = FormLabel;
|
|
8821
9780
|
exports.FormMessage = FormMessage;
|
|
9781
|
+
exports.FormulaEditor = FormulaEditor;
|
|
8822
9782
|
exports.GridSettingsModal = GridSettingsModal_default;
|
|
8823
9783
|
exports.HeaderCell = HeaderCell_default;
|
|
8824
9784
|
exports.Image = Image2;
|
|
@@ -8906,20 +9866,29 @@ exports.TooltipTrigger = TooltipTrigger;
|
|
|
8906
9866
|
exports.Truncated = truncated_default;
|
|
8907
9867
|
exports.UI = ui_exports;
|
|
8908
9868
|
exports.booleanToSelectValue = booleanToSelectValue;
|
|
9869
|
+
exports.buildPrefixMap = buildPrefixMap;
|
|
8909
9870
|
exports.buttonVariants = buttonVariants;
|
|
8910
9871
|
exports.cn = cn;
|
|
8911
9872
|
exports.compareAlphanumeric = compareAlphanumeric;
|
|
8912
9873
|
exports.debounce = debounce;
|
|
9874
|
+
exports.defaultOperatorShortcuts = defaultOperatorShortcuts;
|
|
9875
|
+
exports.defaultOperators = defaultOperators;
|
|
8913
9876
|
exports.formatISODate = formatISODate;
|
|
8914
9877
|
exports.getDialogAlertControls = getDialogAlertControls;
|
|
8915
9878
|
exports.getDialogTemplates = getDialogTemplates;
|
|
8916
9879
|
exports.inputVariants = inputVariants;
|
|
8917
9880
|
exports.isDefined = isDefined;
|
|
8918
9881
|
exports.isEmptyObject = isEmptyObject;
|
|
9882
|
+
exports.isValidParentheses = isValidParentheses;
|
|
9883
|
+
exports.mapTokensToOutput = mapTokensToOutput;
|
|
9884
|
+
exports.parseFormula = parseFormula;
|
|
9885
|
+
exports.parseFormulaToToken = parseFormulaToToken;
|
|
8919
9886
|
exports.selectValueToBoolean = selectValueToBoolean;
|
|
8920
9887
|
exports.spinnerVariants = spinnerVariants;
|
|
9888
|
+
exports.splitOperators = splitOperators;
|
|
8921
9889
|
exports.stripNullishObject = stripNullishObject;
|
|
8922
9890
|
exports.throttle = throttle;
|
|
9891
|
+
exports.tokenizeFormulaString = tokenizeFormulaString;
|
|
8923
9892
|
exports.useFormField = useFormField;
|
|
8924
9893
|
exports.useGridSettingsStore = useGridSettingsStore_default;
|
|
8925
9894
|
exports.useHover = useHover_default;
|
|
@@ -8930,5 +9899,6 @@ exports.usePreventPageLeaveStore = usePreventPageLeaveStore_default;
|
|
|
8930
9899
|
exports.useScreenSize = useScreenSize_default;
|
|
8931
9900
|
exports.useSidebar = useSidebar;
|
|
8932
9901
|
exports.useTruncated = useTruncated_default;
|
|
9902
|
+
exports.validateTokenPrefixes = validateTokenPrefixes;
|
|
8933
9903
|
//# sourceMappingURL=index.js.map
|
|
8934
9904
|
//# sourceMappingURL=index.js.map
|