@sustaina/shared-ui 1.19.0 → 1.21.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 +92 -2
- package/dist/index.d.ts +92 -2
- package/dist/index.js +1009 -25
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +994 -24
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -5
package/dist/index.mjs
CHANGED
|
@@ -3,9 +3,9 @@ import clsx2, { clsx } from 'clsx';
|
|
|
3
3
|
import { twMerge } from 'tailwind-merge';
|
|
4
4
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
5
5
|
import * as React4 from 'react';
|
|
6
|
-
import React4__default, { forwardRef, useRef, useMemo, useCallback, isValidElement, useState, useEffect, createElement } from 'react';
|
|
6
|
+
import React4__default, { forwardRef, useRef, useMemo, useCallback, isValidElement, useState, useEffect, useLayoutEffect, createElement } from 'react';
|
|
7
7
|
import { format, isValid, parseISO, isAfter, compareAsc, parse } from 'date-fns';
|
|
8
|
-
import { CircleX, CircleHelp, Undo, Redo, Bold, Italic, Underline, Strikethrough, Code, Pilcrow, Heading1, Heading2, Heading3, List as List$1, ListOrdered, Quote, CodeSquare, Link, Link2Off, Image as Image$1, AlignLeft, AlignCenter, AlignRight, XIcon, ChevronRight, CheckIcon, Triangle, CalendarIcon, X, Search, ChevronUp, ChevronDown, Plus, ChevronLeft, CircleUserRound, PanelLeftIcon, Bug, GripVertical, Info, CircleMinus, Minus } from 'lucide-react';
|
|
8
|
+
import { CircleX, CircleHelp, Undo, Redo, Bold, Italic, Underline, Strikethrough, Code, Pilcrow, Heading1, Heading2, Heading3, List as List$1, ListOrdered, Quote, CodeSquare, Link, Link2Off, Image as Image$1, AlignLeft, AlignCenter, AlignRight, XIcon, ChevronRight, CheckIcon, Triangle, CalendarIcon, X, Search, ChevronUp, ChevronDown, Minimize2, Maximize2, Plus, ChevronLeft, CircleUserRound, PanelLeftIcon, Bug, GripVertical, Info, CircleMinus, Minus } from 'lucide-react';
|
|
9
9
|
import { createPortal } from 'react-dom';
|
|
10
10
|
import * as SelectPrimitive from '@radix-ui/react-select';
|
|
11
11
|
import { useForm, FormProvider, Controller, useFormContext, useFormState, useFieldArray, useWatch } from 'react-hook-form';
|
|
@@ -20,6 +20,12 @@ import * as SheetPrimitive from '@radix-ui/react-dialog';
|
|
|
20
20
|
import i18n from 'i18next';
|
|
21
21
|
import { initReactI18next, useTranslation } from 'react-i18next';
|
|
22
22
|
import { create } from 'zustand';
|
|
23
|
+
import StarterKit from '@tiptap/starter-kit';
|
|
24
|
+
import { ReactNodeViewRenderer, NodeViewWrapper, useEditor, EditorContent } from '@tiptap/react';
|
|
25
|
+
import ReactDOM from 'react-dom/client';
|
|
26
|
+
import { Node as Node$1, mergeAttributes } from '@tiptap/core';
|
|
27
|
+
import Suggestion from '@tiptap/suggestion';
|
|
28
|
+
import { Plugin, PluginKey } from 'prosemirror-state';
|
|
23
29
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
24
30
|
import { SortableContext, verticalListSortingStrategy, useSortable } from '@dnd-kit/sortable';
|
|
25
31
|
import { CSS } from '@dnd-kit/utilities';
|
|
@@ -5083,7 +5089,8 @@ function DialogAlert({
|
|
|
5083
5089
|
}, [onCancel, onOpenChange]);
|
|
5084
5090
|
const handleConfirm = useCallback(() => {
|
|
5085
5091
|
onConfirm?.();
|
|
5086
|
-
|
|
5092
|
+
onOpenChange(false);
|
|
5093
|
+
}, [onConfirm, onOpenChange]);
|
|
5087
5094
|
return /* @__PURE__ */ jsx(Dialog2, { open, onOpenChange: persistent ? () => {
|
|
5088
5095
|
} : onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent2, { className: "max-w-md", showCloseButton: !persistent, children: [
|
|
5089
5096
|
/* @__PURE__ */ jsxs(DialogHeader2, { children: [
|
|
@@ -5345,6 +5352,989 @@ var getDialogAlertControls = () => ({
|
|
|
5345
5352
|
closeDialogAlert,
|
|
5346
5353
|
openErrorDialogAlert
|
|
5347
5354
|
});
|
|
5355
|
+
function LoadingOverlay({
|
|
5356
|
+
className,
|
|
5357
|
+
fullscreen = true,
|
|
5358
|
+
spinnerClassName,
|
|
5359
|
+
...props
|
|
5360
|
+
}) {
|
|
5361
|
+
return /* @__PURE__ */ jsx(
|
|
5362
|
+
"div",
|
|
5363
|
+
{
|
|
5364
|
+
"data-slot": "loading-overlay",
|
|
5365
|
+
className: cn(
|
|
5366
|
+
fullscreen ? "fixed z-100" : "absolute z-10",
|
|
5367
|
+
"inset-0 flex items-center justify-center transition-opacity duration-300",
|
|
5368
|
+
className
|
|
5369
|
+
),
|
|
5370
|
+
...props,
|
|
5371
|
+
children: /* @__PURE__ */ jsx(Spinner, { className: cn("size-50", spinnerClassName) })
|
|
5372
|
+
}
|
|
5373
|
+
);
|
|
5374
|
+
}
|
|
5375
|
+
|
|
5376
|
+
// src/components/formulaEditor/constants/formulaOperation.ts
|
|
5377
|
+
var defaultOperators = [
|
|
5378
|
+
{ value: "(", label: "(" },
|
|
5379
|
+
{ value: ")", label: ")" },
|
|
5380
|
+
{ value: "%", label: "%" },
|
|
5381
|
+
{ value: "&", label: "&" },
|
|
5382
|
+
{ value: "+", label: "+" },
|
|
5383
|
+
{ value: "-", label: "-" },
|
|
5384
|
+
{ value: "*", label: "\xD7" },
|
|
5385
|
+
{ value: "/", label: "\xF7" },
|
|
5386
|
+
{ value: "=", label: "=" },
|
|
5387
|
+
{ value: "\u2260", label: "\u2260" },
|
|
5388
|
+
{ value: "#NULL", label: "#NULL" }
|
|
5389
|
+
];
|
|
5390
|
+
var defaultOperatorShortcuts = {
|
|
5391
|
+
"(": "(",
|
|
5392
|
+
")": ")",
|
|
5393
|
+
"%": "%",
|
|
5394
|
+
"&": "&",
|
|
5395
|
+
"+": "+",
|
|
5396
|
+
"-": "-",
|
|
5397
|
+
"*": "*",
|
|
5398
|
+
"/": "/",
|
|
5399
|
+
"=": "=",
|
|
5400
|
+
"\u2260": "\u2260",
|
|
5401
|
+
"#NULL": "#NULL"
|
|
5402
|
+
};
|
|
5403
|
+
var DEFAULT_DEBOUNCE = 200;
|
|
5404
|
+
function useKeyboardNavigation(itemsLength, onSelect, isLocked) {
|
|
5405
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
5406
|
+
useEffect(() => {
|
|
5407
|
+
const handler = (event) => {
|
|
5408
|
+
if (event.key === "ArrowDown") {
|
|
5409
|
+
event.preventDefault();
|
|
5410
|
+
setSelectedIndex((prev) => (prev + 1) % Math.max(itemsLength, 1));
|
|
5411
|
+
return;
|
|
5412
|
+
}
|
|
5413
|
+
if (event.key === "ArrowUp") {
|
|
5414
|
+
event.preventDefault();
|
|
5415
|
+
setSelectedIndex((prev) => (prev - 1 + Math.max(itemsLength, 1)) % Math.max(itemsLength, 1));
|
|
5416
|
+
return;
|
|
5417
|
+
}
|
|
5418
|
+
if (event.key === "Enter" || event.key === "Tab") {
|
|
5419
|
+
event.preventDefault();
|
|
5420
|
+
if (!isLocked) onSelect(selectedIndex);
|
|
5421
|
+
}
|
|
5422
|
+
};
|
|
5423
|
+
window.addEventListener("keydown", handler);
|
|
5424
|
+
return () => window.removeEventListener("keydown", handler);
|
|
5425
|
+
}, [itemsLength, onSelect, selectedIndex, isLocked]);
|
|
5426
|
+
return [selectedIndex, setSelectedIndex];
|
|
5427
|
+
}
|
|
5428
|
+
function useDropdownPosition(clientRect, itemsCount) {
|
|
5429
|
+
const [rect, setRect] = useState(null);
|
|
5430
|
+
const [style, setStyle] = useState({});
|
|
5431
|
+
const ref = useRef(null);
|
|
5432
|
+
useEffect(() => {
|
|
5433
|
+
if (!clientRect) return;
|
|
5434
|
+
const update = () => {
|
|
5435
|
+
const nextRect = clientRect();
|
|
5436
|
+
if (nextRect) setRect(nextRect);
|
|
5437
|
+
};
|
|
5438
|
+
update();
|
|
5439
|
+
window.addEventListener("scroll", update, true);
|
|
5440
|
+
window.addEventListener("resize", update);
|
|
5441
|
+
const resizeObserver = new ResizeObserver(update);
|
|
5442
|
+
resizeObserver.observe(document.body);
|
|
5443
|
+
return () => {
|
|
5444
|
+
window.removeEventListener("scroll", update, true);
|
|
5445
|
+
window.removeEventListener("resize", update);
|
|
5446
|
+
resizeObserver.disconnect();
|
|
5447
|
+
};
|
|
5448
|
+
}, [clientRect]);
|
|
5449
|
+
useLayoutEffect(() => {
|
|
5450
|
+
if (!rect || !ref.current) return;
|
|
5451
|
+
const dropdown = ref.current;
|
|
5452
|
+
const dropdownRect = dropdown.getBoundingClientRect();
|
|
5453
|
+
const viewportHeight = window.innerHeight;
|
|
5454
|
+
const viewportWidth = window.innerWidth;
|
|
5455
|
+
let top = rect.bottom + window.scrollY + 6;
|
|
5456
|
+
let left = rect.left + window.scrollX;
|
|
5457
|
+
if (top + dropdownRect.height > viewportHeight + window.scrollY) {
|
|
5458
|
+
top = rect.top + window.scrollY - dropdownRect.height - 6;
|
|
5459
|
+
}
|
|
5460
|
+
if (left + dropdownRect.width > viewportWidth + window.scrollX) {
|
|
5461
|
+
left = Math.max(viewportWidth + window.scrollX - dropdownRect.width - 8, 0);
|
|
5462
|
+
}
|
|
5463
|
+
setStyle({ position: "absolute", zIndex: 50, top, left });
|
|
5464
|
+
}, [rect, itemsCount]);
|
|
5465
|
+
return { ref, style, rect };
|
|
5466
|
+
}
|
|
5467
|
+
var SuggestionList = ({
|
|
5468
|
+
clientRect,
|
|
5469
|
+
command,
|
|
5470
|
+
fetchItems,
|
|
5471
|
+
mapItem,
|
|
5472
|
+
normalizeToken,
|
|
5473
|
+
debounceMs = DEFAULT_DEBOUNCE,
|
|
5474
|
+
query
|
|
5475
|
+
}) => {
|
|
5476
|
+
const [items, setItems] = useState([]);
|
|
5477
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
5478
|
+
const fetchId = useRef(0);
|
|
5479
|
+
const debounceHandle = useRef(null);
|
|
5480
|
+
const itemRefs = useRef([]);
|
|
5481
|
+
const { ref, style, rect } = useDropdownPosition(clientRect, items.length);
|
|
5482
|
+
const normalizedMap = useMemo(() => mapItem, [mapItem]);
|
|
5483
|
+
const normalizeItem = useMemo(() => normalizeToken, [normalizeToken]);
|
|
5484
|
+
useEffect(() => {
|
|
5485
|
+
const runFetch = (input) => {
|
|
5486
|
+
fetchId.current += 1;
|
|
5487
|
+
const currentId = fetchId.current;
|
|
5488
|
+
if (debounceHandle.current) clearTimeout(debounceHandle.current);
|
|
5489
|
+
debounceHandle.current = window.setTimeout(async () => {
|
|
5490
|
+
setIsLoading(true);
|
|
5491
|
+
try {
|
|
5492
|
+
const results = await fetchItems(input);
|
|
5493
|
+
if (fetchId.current !== currentId) return;
|
|
5494
|
+
const mapped = Array.isArray(results) ? results.map((item) => normalizeItem(normalizedMap(item))) : [];
|
|
5495
|
+
setItems(mapped);
|
|
5496
|
+
} catch {
|
|
5497
|
+
if (fetchId.current === currentId) {
|
|
5498
|
+
setItems([]);
|
|
5499
|
+
}
|
|
5500
|
+
} finally {
|
|
5501
|
+
if (fetchId.current === currentId) {
|
|
5502
|
+
setIsLoading(false);
|
|
5503
|
+
}
|
|
5504
|
+
}
|
|
5505
|
+
}, debounceMs);
|
|
5506
|
+
};
|
|
5507
|
+
runFetch(query ?? "");
|
|
5508
|
+
return () => {
|
|
5509
|
+
if (debounceHandle.current) {
|
|
5510
|
+
clearTimeout(debounceHandle.current);
|
|
5511
|
+
}
|
|
5512
|
+
fetchId.current += 1;
|
|
5513
|
+
};
|
|
5514
|
+
}, [query, fetchItems, normalizedMap, normalizeItem, debounceMs]);
|
|
5515
|
+
const handleSelect = useMemo(
|
|
5516
|
+
() => (item) => {
|
|
5517
|
+
if (isLoading) return;
|
|
5518
|
+
command(item);
|
|
5519
|
+
},
|
|
5520
|
+
[command, isLoading]
|
|
5521
|
+
);
|
|
5522
|
+
const [selectedIndex, setSelectedIndex] = useKeyboardNavigation(
|
|
5523
|
+
items.length,
|
|
5524
|
+
(index) => {
|
|
5525
|
+
if (!items.length) return;
|
|
5526
|
+
handleSelect(items[index]);
|
|
5527
|
+
},
|
|
5528
|
+
isLoading
|
|
5529
|
+
);
|
|
5530
|
+
useEffect(() => {
|
|
5531
|
+
setSelectedIndex(0);
|
|
5532
|
+
}, [items, setSelectedIndex]);
|
|
5533
|
+
useEffect(() => {
|
|
5534
|
+
const element = itemRefs.current[selectedIndex];
|
|
5535
|
+
if (element) element.scrollIntoView({ block: "nearest" });
|
|
5536
|
+
}, [selectedIndex]);
|
|
5537
|
+
if (!rect) return null;
|
|
5538
|
+
const showEmptyState = !isLoading && items.length === 0;
|
|
5539
|
+
return /* @__PURE__ */ jsxs(
|
|
5540
|
+
"div",
|
|
5541
|
+
{
|
|
5542
|
+
ref,
|
|
5543
|
+
style,
|
|
5544
|
+
className: "w-80 max-h-60 overflow-y-auto rounded-lg border bg-white p-1 shadow-lg",
|
|
5545
|
+
children: [
|
|
5546
|
+
isLoading && /* @__PURE__ */ jsx("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: "Loading suggestions\u2026" }),
|
|
5547
|
+
showEmptyState && /* @__PURE__ */ jsx("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: "No matches" }),
|
|
5548
|
+
items.map((item, idx) => /* @__PURE__ */ jsxs(
|
|
5549
|
+
"button",
|
|
5550
|
+
{
|
|
5551
|
+
ref: (el) => {
|
|
5552
|
+
itemRefs.current[idx] = el;
|
|
5553
|
+
},
|
|
5554
|
+
type: "button",
|
|
5555
|
+
onClick: () => handleSelect(item),
|
|
5556
|
+
onMouseMove: () => setSelectedIndex(idx),
|
|
5557
|
+
className: cn(
|
|
5558
|
+
"flex w-full items-center justify-between rounded-md px-3 py-2 text-left text-sm",
|
|
5559
|
+
selectedIndex === idx ? "bg-muted" : "hover:bg-muted",
|
|
5560
|
+
isLoading && "pointer-events-none opacity-60"
|
|
5561
|
+
),
|
|
5562
|
+
disabled: isLoading,
|
|
5563
|
+
children: [
|
|
5564
|
+
/* @__PURE__ */ jsx("span", { children: item.label ?? item.code }),
|
|
5565
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
5566
|
+
item.prefix ?? "",
|
|
5567
|
+
item.code ?? ""
|
|
5568
|
+
] })
|
|
5569
|
+
]
|
|
5570
|
+
},
|
|
5571
|
+
item.id
|
|
5572
|
+
))
|
|
5573
|
+
]
|
|
5574
|
+
}
|
|
5575
|
+
);
|
|
5576
|
+
};
|
|
5577
|
+
var DISALLOWED_MARKS = ["bold", "italic", "link"];
|
|
5578
|
+
var SUGGESTION_DEBOUNCE = 200;
|
|
5579
|
+
var DEFAULT_CHIP_CLASS = "outline-1 outline-muted bg-muted/40 text-foreground";
|
|
5580
|
+
var TokenView = ({ node, editor, getPos }) => {
|
|
5581
|
+
const [isFocused, setIsFocused] = React4__default.useState(false);
|
|
5582
|
+
React4__default.useEffect(() => {
|
|
5583
|
+
const handler = () => {
|
|
5584
|
+
const { from, to } = editor.state.selection;
|
|
5585
|
+
const position = getPos();
|
|
5586
|
+
if (position >= from && position + node.nodeSize <= to) {
|
|
5587
|
+
setIsFocused(true);
|
|
5588
|
+
} else {
|
|
5589
|
+
setIsFocused(false);
|
|
5590
|
+
}
|
|
5591
|
+
};
|
|
5592
|
+
editor.on("selectionUpdate", handler);
|
|
5593
|
+
return () => {
|
|
5594
|
+
editor.off("selectionUpdate", handler);
|
|
5595
|
+
};
|
|
5596
|
+
}, [editor, getPos, node.nodeSize]);
|
|
5597
|
+
const remove = () => {
|
|
5598
|
+
const pos = getPos();
|
|
5599
|
+
editor.chain().focus().deleteRange({ from: pos, to: pos + node.nodeSize }).run();
|
|
5600
|
+
};
|
|
5601
|
+
return /* @__PURE__ */ jsxs(
|
|
5602
|
+
NodeViewWrapper,
|
|
5603
|
+
{
|
|
5604
|
+
as: "span",
|
|
5605
|
+
className: cn(
|
|
5606
|
+
"inline-flex items-center gap-1 rounded-sm px-2 py-0.5 mx-2 text-sm font-medium",
|
|
5607
|
+
DEFAULT_CHIP_CLASS,
|
|
5608
|
+
node.attrs.chipClassName,
|
|
5609
|
+
isFocused && "ring-2 ring-offset-1 ring-ring"
|
|
5610
|
+
),
|
|
5611
|
+
children: [
|
|
5612
|
+
/* @__PURE__ */ jsx("span", { children: node.attrs.label ?? node.attrs.code ?? node.attrs.rawValue }),
|
|
5613
|
+
(node.attrs.code || node.attrs.prefix) && /* @__PURE__ */ jsxs("span", { className: "text-xs font-normal opacity-80", children: [
|
|
5614
|
+
"(",
|
|
5615
|
+
node.attrs.prefix,
|
|
5616
|
+
node.attrs.code ?? "",
|
|
5617
|
+
")"
|
|
5618
|
+
] }),
|
|
5619
|
+
/* @__PURE__ */ jsx(
|
|
5620
|
+
"button",
|
|
5621
|
+
{
|
|
5622
|
+
type: "button",
|
|
5623
|
+
onClick: remove,
|
|
5624
|
+
className: "ml-1 cursor-pointer rounded-sm px-1 text-xs leading-none hover:bg-sus-primary-1/30",
|
|
5625
|
+
"aria-label": `Remove ${node.attrs.label ?? node.attrs.code ?? "token"}`,
|
|
5626
|
+
children: "\xD7"
|
|
5627
|
+
}
|
|
5628
|
+
)
|
|
5629
|
+
]
|
|
5630
|
+
}
|
|
5631
|
+
);
|
|
5632
|
+
};
|
|
5633
|
+
function normalizeTokenAttrs(attrs, config) {
|
|
5634
|
+
const idSource = attrs.id ?? attrs.code ?? attrs.label ?? Math.random().toString(36).slice(2);
|
|
5635
|
+
const prefix = attrs.prefix ?? config.prefix;
|
|
5636
|
+
const suffix = attrs.code ?? attrs.label ?? "";
|
|
5637
|
+
const outputType = attrs.outputType ?? config.outputType ?? config.type;
|
|
5638
|
+
return {
|
|
5639
|
+
...attrs,
|
|
5640
|
+
id: String(idSource),
|
|
5641
|
+
type: config.type,
|
|
5642
|
+
prefix,
|
|
5643
|
+
chipClassName: attrs.chipClassName ?? config.chipClassName,
|
|
5644
|
+
rawValue: attrs.rawValue ?? (suffix ? `${prefix}${suffix}` : prefix),
|
|
5645
|
+
outputType
|
|
5646
|
+
};
|
|
5647
|
+
}
|
|
5648
|
+
var isPlainTextContext = (state, range) => {
|
|
5649
|
+
const { doc, schema } = state;
|
|
5650
|
+
const $from = doc.resolve(range.from);
|
|
5651
|
+
if ($from.parent.type.name !== "paragraph") return false;
|
|
5652
|
+
const marks = $from.marks();
|
|
5653
|
+
if (marks.some((mark) => DISALLOWED_MARKS.includes(mark.type.name))) return false;
|
|
5654
|
+
const linkMark = schema.marks.link;
|
|
5655
|
+
if (linkMark && linkMark.isInSet(marks)) return false;
|
|
5656
|
+
const nodeBefore = $from.nodeBefore;
|
|
5657
|
+
if (nodeBefore && nodeBefore.type.name === "token") return false;
|
|
5658
|
+
const nodeAfter = $from.nodeAfter;
|
|
5659
|
+
if (nodeAfter && nodeAfter.type.name === "token") return false;
|
|
5660
|
+
return true;
|
|
5661
|
+
};
|
|
5662
|
+
function suggestionPlugin({ config, onSelect, nodeName, editor, register }) {
|
|
5663
|
+
const pluginKey = new PluginKey(`formula-token-${config.type}-${config.prefix}`);
|
|
5664
|
+
const fetchItems = config.fetchItems ?? (async () => []);
|
|
5665
|
+
const mapItem = config.mapItem ?? ((item) => item);
|
|
5666
|
+
const normalizeForConfig = (attrs) => normalizeTokenAttrs(attrs, config);
|
|
5667
|
+
register({ key: pluginKey, config });
|
|
5668
|
+
return Suggestion({
|
|
5669
|
+
editor,
|
|
5670
|
+
char: config.prefix,
|
|
5671
|
+
pluginKey,
|
|
5672
|
+
allowSpaces: true,
|
|
5673
|
+
startOfLine: false,
|
|
5674
|
+
items: () => [],
|
|
5675
|
+
allow: ({ state, range }) => {
|
|
5676
|
+
if (editor.view.composing) return false;
|
|
5677
|
+
return isPlainTextContext(state, range);
|
|
5678
|
+
},
|
|
5679
|
+
render: () => {
|
|
5680
|
+
let reactRoot = null;
|
|
5681
|
+
let container = null;
|
|
5682
|
+
return {
|
|
5683
|
+
onStart: (props) => {
|
|
5684
|
+
container = document.createElement("div");
|
|
5685
|
+
document.body.appendChild(container);
|
|
5686
|
+
reactRoot = ReactDOM.createRoot(container);
|
|
5687
|
+
reactRoot.render(
|
|
5688
|
+
/* @__PURE__ */ jsx(
|
|
5689
|
+
SuggestionList,
|
|
5690
|
+
{
|
|
5691
|
+
...props,
|
|
5692
|
+
editor,
|
|
5693
|
+
fetchItems,
|
|
5694
|
+
mapItem,
|
|
5695
|
+
normalizeToken: normalizeForConfig,
|
|
5696
|
+
debounceMs: SUGGESTION_DEBOUNCE
|
|
5697
|
+
}
|
|
5698
|
+
)
|
|
5699
|
+
);
|
|
5700
|
+
},
|
|
5701
|
+
onUpdate: (props) => {
|
|
5702
|
+
reactRoot?.render(
|
|
5703
|
+
/* @__PURE__ */ jsx(
|
|
5704
|
+
SuggestionList,
|
|
5705
|
+
{
|
|
5706
|
+
...props,
|
|
5707
|
+
editor,
|
|
5708
|
+
fetchItems,
|
|
5709
|
+
mapItem,
|
|
5710
|
+
normalizeToken: normalizeForConfig,
|
|
5711
|
+
debounceMs: SUGGESTION_DEBOUNCE
|
|
5712
|
+
}
|
|
5713
|
+
)
|
|
5714
|
+
);
|
|
5715
|
+
},
|
|
5716
|
+
onKeyDown: ({ event }) => {
|
|
5717
|
+
if (event.key === "Escape" || event.key === "Enter" || event.key === "Tab") {
|
|
5718
|
+
event.preventDefault();
|
|
5719
|
+
return true;
|
|
5720
|
+
}
|
|
5721
|
+
return false;
|
|
5722
|
+
},
|
|
5723
|
+
onExit: () => {
|
|
5724
|
+
reactRoot?.unmount();
|
|
5725
|
+
reactRoot = null;
|
|
5726
|
+
container?.remove();
|
|
5727
|
+
container = null;
|
|
5728
|
+
}
|
|
5729
|
+
};
|
|
5730
|
+
},
|
|
5731
|
+
command: ({ range, props, editor: editor2 }) => {
|
|
5732
|
+
const attrs = normalizeTokenAttrs(props, config);
|
|
5733
|
+
const nextChar = editor2.state.doc.textBetween(range.to, range.to + 1, "\n", "\n");
|
|
5734
|
+
const shouldInsertSpace = !nextChar || !/\s/.test(nextChar);
|
|
5735
|
+
const content = shouldInsertSpace ? [
|
|
5736
|
+
{ type: nodeName, attrs },
|
|
5737
|
+
{ type: "text", text: " " }
|
|
5738
|
+
] : { type: nodeName, attrs };
|
|
5739
|
+
editor2.chain().focus().insertContentAt(range, content).run();
|
|
5740
|
+
onSelect?.(attrs, config);
|
|
5741
|
+
}
|
|
5742
|
+
});
|
|
5743
|
+
}
|
|
5744
|
+
var createSuggestionMonitorPlugin = (registrations) => new Plugin({
|
|
5745
|
+
key: new PluginKey("formula-token-suggestion-guard"),
|
|
5746
|
+
view(editorView) {
|
|
5747
|
+
const ensureValid = () => {
|
|
5748
|
+
registrations.forEach(({ key, config }) => {
|
|
5749
|
+
const state = key.getState(editorView.state);
|
|
5750
|
+
if (!state?.active) return;
|
|
5751
|
+
const currentText = editorView.state.doc.textBetween(
|
|
5752
|
+
state.range.from,
|
|
5753
|
+
state.range.to,
|
|
5754
|
+
"\n",
|
|
5755
|
+
"\n"
|
|
5756
|
+
);
|
|
5757
|
+
if (!currentText || !currentText.startsWith(config.prefix)) {
|
|
5758
|
+
const tr = editorView.state.tr.setMeta(key, { exit: true });
|
|
5759
|
+
editorView.dispatch(tr);
|
|
5760
|
+
}
|
|
5761
|
+
});
|
|
5762
|
+
};
|
|
5763
|
+
ensureValid();
|
|
5764
|
+
return {
|
|
5765
|
+
update: () => ensureValid()
|
|
5766
|
+
};
|
|
5767
|
+
}
|
|
5768
|
+
});
|
|
5769
|
+
var createTokenSpacingPlugin = (nodeName) => new Plugin({
|
|
5770
|
+
key: new PluginKey(`formula-token-spacing-${nodeName}`),
|
|
5771
|
+
appendTransaction(transactions, oldState, newState) {
|
|
5772
|
+
if (!transactions.some((tr2) => tr2.docChanged)) return null;
|
|
5773
|
+
const insertions = [];
|
|
5774
|
+
newState.doc.descendants((node, pos) => {
|
|
5775
|
+
if (node.type.name !== nodeName) return;
|
|
5776
|
+
const beforePos = pos - 1;
|
|
5777
|
+
if (beforePos >= 0) {
|
|
5778
|
+
const beforeChar = newState.doc.textBetween(beforePos, pos, "\n", "\n");
|
|
5779
|
+
if (beforeChar && !/\s/.test(beforeChar)) {
|
|
5780
|
+
insertions.push({ pos, text: " " });
|
|
5781
|
+
}
|
|
5782
|
+
}
|
|
5783
|
+
const afterPos = pos + node.nodeSize;
|
|
5784
|
+
if (afterPos < newState.doc.content.size) {
|
|
5785
|
+
const afterChar = newState.doc.textBetween(afterPos, afterPos + 1, "\n", "\n");
|
|
5786
|
+
if (afterChar && !/\s/.test(afterChar)) {
|
|
5787
|
+
insertions.push({ pos: afterPos, text: " " });
|
|
5788
|
+
}
|
|
5789
|
+
}
|
|
5790
|
+
});
|
|
5791
|
+
if (!insertions.length) return null;
|
|
5792
|
+
const tr = newState.tr;
|
|
5793
|
+
insertions.sort((a, b) => b.pos - a.pos).forEach(({ pos, text }) => {
|
|
5794
|
+
tr.insertText(text, pos);
|
|
5795
|
+
});
|
|
5796
|
+
return tr;
|
|
5797
|
+
}
|
|
5798
|
+
});
|
|
5799
|
+
var Token = Node$1.create({
|
|
5800
|
+
name: "token",
|
|
5801
|
+
inline: true,
|
|
5802
|
+
group: "inline",
|
|
5803
|
+
atom: true,
|
|
5804
|
+
selectable: false,
|
|
5805
|
+
draggable: false,
|
|
5806
|
+
addAttributes() {
|
|
5807
|
+
return {
|
|
5808
|
+
type: { default: "" },
|
|
5809
|
+
id: { default: "" },
|
|
5810
|
+
code: { default: "" },
|
|
5811
|
+
label: { default: "" },
|
|
5812
|
+
prefix: { default: "" },
|
|
5813
|
+
rawValue: { default: "" },
|
|
5814
|
+
chipClassName: { default: "" },
|
|
5815
|
+
outputType: { default: "" }
|
|
5816
|
+
};
|
|
5817
|
+
},
|
|
5818
|
+
parseHTML() {
|
|
5819
|
+
return [{ tag: "token-chip" }];
|
|
5820
|
+
},
|
|
5821
|
+
renderHTML({ HTMLAttributes }) {
|
|
5822
|
+
return ["token-chip", mergeAttributes(HTMLAttributes), HTMLAttributes.label || ""];
|
|
5823
|
+
},
|
|
5824
|
+
addNodeView() {
|
|
5825
|
+
return ReactNodeViewRenderer(TokenView);
|
|
5826
|
+
},
|
|
5827
|
+
addProseMirrorPlugins() {
|
|
5828
|
+
const configs = this.options.configs ?? [];
|
|
5829
|
+
const registrations = [];
|
|
5830
|
+
const suggestionPlugins = configs.map(
|
|
5831
|
+
(config) => suggestionPlugin({
|
|
5832
|
+
config,
|
|
5833
|
+
onSelect: this.options.onSelect,
|
|
5834
|
+
nodeName: this.name,
|
|
5835
|
+
editor: this.editor,
|
|
5836
|
+
register: (entry) => registrations.push(entry)
|
|
5837
|
+
})
|
|
5838
|
+
);
|
|
5839
|
+
return [
|
|
5840
|
+
...suggestionPlugins,
|
|
5841
|
+
createTokenSpacingPlugin(this.name),
|
|
5842
|
+
createSuggestionMonitorPlugin(registrations)
|
|
5843
|
+
];
|
|
5844
|
+
}
|
|
5845
|
+
});
|
|
5846
|
+
var Operator = Node$1.create({
|
|
5847
|
+
name: "operator",
|
|
5848
|
+
inline: true,
|
|
5849
|
+
group: "inline",
|
|
5850
|
+
atom: true,
|
|
5851
|
+
selectable: false,
|
|
5852
|
+
addAttributes() {
|
|
5853
|
+
return {
|
|
5854
|
+
value: { default: null }
|
|
5855
|
+
};
|
|
5856
|
+
},
|
|
5857
|
+
parseHTML() {
|
|
5858
|
+
return [{ tag: "span[operator]" }];
|
|
5859
|
+
},
|
|
5860
|
+
renderHTML({ HTMLAttributes }) {
|
|
5861
|
+
return ["span", mergeAttributes({ operator: HTMLAttributes.value }), HTMLAttributes.value];
|
|
5862
|
+
},
|
|
5863
|
+
addKeyboardShortcuts() {
|
|
5864
|
+
const shortcuts = this.options.shortcuts ?? {};
|
|
5865
|
+
return Object.fromEntries(
|
|
5866
|
+
Object.entries(shortcuts).map(([key, value]) => [
|
|
5867
|
+
key,
|
|
5868
|
+
({ editor }) => {
|
|
5869
|
+
editor.chain().insertContent({ type: "operator", attrs: { value } }).insertContent(" ").run();
|
|
5870
|
+
return true;
|
|
5871
|
+
}
|
|
5872
|
+
])
|
|
5873
|
+
);
|
|
5874
|
+
}
|
|
5875
|
+
});
|
|
5876
|
+
|
|
5877
|
+
// src/components/formulaEditor/utils/parseFormulaToken.ts
|
|
5878
|
+
var DEFAULT_PARENTHESIS_MAP = {
|
|
5879
|
+
"(": ")",
|
|
5880
|
+
"[": "]",
|
|
5881
|
+
"{": "}"
|
|
5882
|
+
};
|
|
5883
|
+
var createSortedPrefixes = (prefixMap) => Object.keys(prefixMap).sort((a, b) => b.length - a.length);
|
|
5884
|
+
var findPrefixAt = (value, start, prefixes) => prefixes.find((prefix) => value.startsWith(prefix, start));
|
|
5885
|
+
var pushVariableAndInner = (tokens, value, prefixMap, prefixes) => {
|
|
5886
|
+
if (!value) return;
|
|
5887
|
+
tokens.push({ type: "variable", value });
|
|
5888
|
+
for (let cursor = 0; cursor < value.length; ) {
|
|
5889
|
+
const prefix = findPrefixAt(value, cursor, prefixes);
|
|
5890
|
+
if (!prefix) {
|
|
5891
|
+
cursor += 1;
|
|
5892
|
+
continue;
|
|
5893
|
+
}
|
|
5894
|
+
cursor += prefix.length;
|
|
5895
|
+
const rest = value.slice(cursor);
|
|
5896
|
+
const codeMatch = rest.match(/^\w+/);
|
|
5897
|
+
if (codeMatch) {
|
|
5898
|
+
tokens.push({ type: prefixMap[prefix], code: codeMatch[0] });
|
|
5899
|
+
cursor += codeMatch[0].length;
|
|
5900
|
+
}
|
|
5901
|
+
}
|
|
5902
|
+
};
|
|
5903
|
+
var buildPrefixMap = (configs) => {
|
|
5904
|
+
return configs.reduce((acc, config) => {
|
|
5905
|
+
const outputType = config.outputType ?? config.type;
|
|
5906
|
+
if (!acc[config.prefix]) acc[config.prefix] = outputType;
|
|
5907
|
+
return acc;
|
|
5908
|
+
}, {});
|
|
5909
|
+
};
|
|
5910
|
+
var tokenizeFormulaString = (raw, prefixMap) => {
|
|
5911
|
+
const segments = [];
|
|
5912
|
+
if (!raw) return segments;
|
|
5913
|
+
const prefixes = createSortedPrefixes(prefixMap);
|
|
5914
|
+
let buffer = "";
|
|
5915
|
+
const flushBuffer = () => {
|
|
5916
|
+
if (buffer) {
|
|
5917
|
+
segments.push({ kind: "text", value: buffer });
|
|
5918
|
+
buffer = "";
|
|
5919
|
+
}
|
|
5920
|
+
};
|
|
5921
|
+
for (let i = 0; i < raw.length; ) {
|
|
5922
|
+
const prefix = findPrefixAt(raw, i, prefixes);
|
|
5923
|
+
if (prefix) {
|
|
5924
|
+
const rest = raw.slice(i + prefix.length);
|
|
5925
|
+
const codeMatch = rest.match(/^\w+/);
|
|
5926
|
+
if (codeMatch) {
|
|
5927
|
+
flushBuffer();
|
|
5928
|
+
segments.push({ kind: "token", prefix, code: codeMatch[0] });
|
|
5929
|
+
i += prefix.length + codeMatch[0].length;
|
|
5930
|
+
continue;
|
|
5931
|
+
}
|
|
5932
|
+
}
|
|
5933
|
+
buffer += raw[i];
|
|
5934
|
+
i += 1;
|
|
5935
|
+
}
|
|
5936
|
+
flushBuffer();
|
|
5937
|
+
return segments;
|
|
5938
|
+
};
|
|
5939
|
+
var parseFormulaToToken = (text, prefixMap) => {
|
|
5940
|
+
const tokens = [];
|
|
5941
|
+
if (!text) return tokens;
|
|
5942
|
+
const prefixes = createSortedPrefixes(prefixMap);
|
|
5943
|
+
const leadingPrefix = findPrefixAt(text, 0, prefixes);
|
|
5944
|
+
if (!leadingPrefix) {
|
|
5945
|
+
pushVariableAndInner(tokens, text, prefixMap, prefixes);
|
|
5946
|
+
return tokens;
|
|
5947
|
+
}
|
|
5948
|
+
const afterPrefix = text.slice(leadingPrefix.length);
|
|
5949
|
+
const codeMatch = afterPrefix.match(/^\w+/);
|
|
5950
|
+
if (!codeMatch) {
|
|
5951
|
+
pushVariableAndInner(tokens, text, prefixMap, prefixes);
|
|
5952
|
+
return tokens;
|
|
5953
|
+
}
|
|
5954
|
+
tokens.push({ type: prefixMap[leadingPrefix], code: codeMatch[0] });
|
|
5955
|
+
const remaining = afterPrefix.slice(codeMatch[0].length);
|
|
5956
|
+
if (remaining) {
|
|
5957
|
+
let variablePart = remaining;
|
|
5958
|
+
if (remaining.startsWith(":") && remaining.includes("(")) {
|
|
5959
|
+
let depth = 0;
|
|
5960
|
+
let endIndex = -1;
|
|
5961
|
+
for (let i = 0; i < remaining.length; i++) {
|
|
5962
|
+
if (remaining[i] === "(") depth += 1;
|
|
5963
|
+
else if (remaining[i] === ")") depth -= 1;
|
|
5964
|
+
if (depth === 0 && remaining[i] === ")") {
|
|
5965
|
+
endIndex = i;
|
|
5966
|
+
break;
|
|
5967
|
+
}
|
|
5968
|
+
}
|
|
5969
|
+
if (endIndex !== -1) {
|
|
5970
|
+
variablePart = remaining.slice(0, endIndex + 1);
|
|
5971
|
+
}
|
|
5972
|
+
}
|
|
5973
|
+
pushVariableAndInner(tokens, variablePart, prefixMap, prefixes);
|
|
5974
|
+
}
|
|
5975
|
+
return tokens;
|
|
5976
|
+
};
|
|
5977
|
+
var splitOperators = (value, allowedOperators) => {
|
|
5978
|
+
const result = [];
|
|
5979
|
+
if (!value) return result;
|
|
5980
|
+
const sortedOperators = [...allowedOperators].sort((a, b) => b.length - a.length);
|
|
5981
|
+
let buffer = "";
|
|
5982
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
5983
|
+
let matched = false;
|
|
5984
|
+
for (const operator of sortedOperators) {
|
|
5985
|
+
if (operator && value.startsWith(operator, i)) {
|
|
5986
|
+
if (buffer) {
|
|
5987
|
+
result.push({ type: "variable", value: buffer });
|
|
5988
|
+
buffer = "";
|
|
5989
|
+
}
|
|
5990
|
+
result.push({ type: "operator", value: operator });
|
|
5991
|
+
i += operator.length - 1;
|
|
5992
|
+
matched = true;
|
|
5993
|
+
break;
|
|
5994
|
+
}
|
|
5995
|
+
}
|
|
5996
|
+
if (!matched) buffer += value[i];
|
|
5997
|
+
}
|
|
5998
|
+
if (buffer) result.push({ type: "variable", value: buffer });
|
|
5999
|
+
return result;
|
|
6000
|
+
};
|
|
6001
|
+
var parseFormula = (editorJson, prefixMap, allowedOperators) => {
|
|
6002
|
+
const rawParts = [];
|
|
6003
|
+
const tokens = [];
|
|
6004
|
+
const traverse = (nodes2 = []) => {
|
|
6005
|
+
nodes2.forEach((node) => {
|
|
6006
|
+
if (!node) return;
|
|
6007
|
+
switch (node.type) {
|
|
6008
|
+
case "text": {
|
|
6009
|
+
const text = node.text ?? "";
|
|
6010
|
+
if (!text) return;
|
|
6011
|
+
rawParts.push(text);
|
|
6012
|
+
if (text.trim()) {
|
|
6013
|
+
tokens.push({ type: "variable", value: text });
|
|
6014
|
+
}
|
|
6015
|
+
break;
|
|
6016
|
+
}
|
|
6017
|
+
case "operator": {
|
|
6018
|
+
rawParts.push(node.attrs?.value ?? "");
|
|
6019
|
+
tokens.push({ type: "operator", value: node.attrs?.value ?? "" });
|
|
6020
|
+
break;
|
|
6021
|
+
}
|
|
6022
|
+
case "op-library": {
|
|
6023
|
+
rawParts.push(node.attrs?.value ?? "");
|
|
6024
|
+
tokens.push(...splitOperators(node.attrs?.value ?? "", allowedOperators));
|
|
6025
|
+
break;
|
|
6026
|
+
}
|
|
6027
|
+
case "token": {
|
|
6028
|
+
const attrs = node.attrs ?? {};
|
|
6029
|
+
const rawValue = attrs.rawValue ?? `${attrs.prefix ?? ""}${attrs.code ?? ""}`;
|
|
6030
|
+
rawParts.push(rawValue);
|
|
6031
|
+
const outputType = attrs.outputType ?? attrs.type ?? prefixMap[attrs.prefix ?? ""];
|
|
6032
|
+
tokens.push({ ...attrs, type: outputType });
|
|
6033
|
+
break;
|
|
6034
|
+
}
|
|
6035
|
+
default:
|
|
6036
|
+
if (node.content) traverse(node.content);
|
|
6037
|
+
}
|
|
6038
|
+
});
|
|
6039
|
+
};
|
|
6040
|
+
traverse(editorJson?.content ?? []);
|
|
6041
|
+
return { raw: rawParts.join(""), token: tokens };
|
|
6042
|
+
};
|
|
6043
|
+
var validateTokenPrefixes = (rawFormula, prefixMap) => {
|
|
6044
|
+
const prefixes = Object.keys(prefixMap);
|
|
6045
|
+
if (prefixes.length === 0) return { isValid: true };
|
|
6046
|
+
const missingTypes = /* @__PURE__ */ new Set();
|
|
6047
|
+
for (let i = 0; i < rawFormula.length; i += 1) {
|
|
6048
|
+
const prefix = prefixes.find((key) => rawFormula.startsWith(key, i));
|
|
6049
|
+
if (prefix) {
|
|
6050
|
+
const nextChar = rawFormula[i + prefix.length];
|
|
6051
|
+
if (!nextChar || !/\w/.test(nextChar)) {
|
|
6052
|
+
missingTypes.add(prefixMap[prefix]);
|
|
6053
|
+
}
|
|
6054
|
+
i += prefix.length;
|
|
6055
|
+
}
|
|
6056
|
+
}
|
|
6057
|
+
if (missingTypes.size > 0) {
|
|
6058
|
+
return {
|
|
6059
|
+
isValid: false,
|
|
6060
|
+
message: `Invalid token for ${Array.from(missingTypes).join(" and ")}`
|
|
6061
|
+
};
|
|
6062
|
+
}
|
|
6063
|
+
return { isValid: true };
|
|
6064
|
+
};
|
|
6065
|
+
var isValidParentheses = (input) => {
|
|
6066
|
+
const stack = [];
|
|
6067
|
+
const openers = Object.keys(DEFAULT_PARENTHESIS_MAP);
|
|
6068
|
+
const closers = Object.values(DEFAULT_PARENTHESIS_MAP);
|
|
6069
|
+
for (const char of input) {
|
|
6070
|
+
if (openers.includes(char)) {
|
|
6071
|
+
stack.push(char);
|
|
6072
|
+
} else if (closers.includes(char)) {
|
|
6073
|
+
const last = stack.pop();
|
|
6074
|
+
if (!last || DEFAULT_PARENTHESIS_MAP[last] !== char) {
|
|
6075
|
+
return { valid: false };
|
|
6076
|
+
}
|
|
6077
|
+
}
|
|
6078
|
+
}
|
|
6079
|
+
if (stack.length > 0) {
|
|
6080
|
+
return { valid: false };
|
|
6081
|
+
}
|
|
6082
|
+
return { valid: true };
|
|
6083
|
+
};
|
|
6084
|
+
var mapTokensToOutput = (tokens, configs) => {
|
|
6085
|
+
const prefixLookup = /* @__PURE__ */ new Map();
|
|
6086
|
+
const typeLookup = /* @__PURE__ */ new Map();
|
|
6087
|
+
configs.forEach((config) => {
|
|
6088
|
+
prefixLookup.set(config.prefix, config);
|
|
6089
|
+
typeLookup.set(config.outputType ?? config.type, config);
|
|
6090
|
+
});
|
|
6091
|
+
return tokens.map((token) => {
|
|
6092
|
+
if (!token || typeof token !== "object") return token;
|
|
6093
|
+
const tokenPrefix = "prefix" in token ? token.prefix : void 0;
|
|
6094
|
+
const tokenType = "type" in token ? token.type : void 0;
|
|
6095
|
+
const config = (tokenPrefix ? prefixLookup.get(tokenPrefix) : void 0) ?? (tokenType ? typeLookup.get(String(tokenType)) : void 0);
|
|
6096
|
+
if (config?.mapOutput) {
|
|
6097
|
+
return config.mapOutput(token);
|
|
6098
|
+
}
|
|
6099
|
+
return token;
|
|
6100
|
+
});
|
|
6101
|
+
};
|
|
6102
|
+
var DEFAULT_TOKEN_CONFIGS = [
|
|
6103
|
+
{
|
|
6104
|
+
type: "position",
|
|
6105
|
+
prefix: "$",
|
|
6106
|
+
chipClassName: "text-black border border-sus-primary-1 outline-sus-primary-1",
|
|
6107
|
+
fetchItems: async () => []
|
|
6108
|
+
},
|
|
6109
|
+
{
|
|
6110
|
+
type: "impact",
|
|
6111
|
+
prefix: "#",
|
|
6112
|
+
chipClassName: "text-black border border-sus-primary-1 outline-sus-primary-1",
|
|
6113
|
+
fetchItems: async () => []
|
|
6114
|
+
}
|
|
6115
|
+
];
|
|
6116
|
+
var defaultMapItem = (item) => {
|
|
6117
|
+
const id = item?.id ?? item?.code ?? item?.label ?? Math.random().toString(36).slice(2);
|
|
6118
|
+
return {
|
|
6119
|
+
id: String(id),
|
|
6120
|
+
label: item?.label ?? item?.name ?? String(item?.code ?? item?.id ?? ""),
|
|
6121
|
+
code: item?.code ?? String(item?.id ?? "")
|
|
6122
|
+
};
|
|
6123
|
+
};
|
|
6124
|
+
var looksLikeHTML = (value) => /<\/?[a-z][\s\S]*>/i.test(value.trim());
|
|
6125
|
+
var tryParseJSON = (value) => {
|
|
6126
|
+
try {
|
|
6127
|
+
const parsed = JSON.parse(value);
|
|
6128
|
+
if (parsed && typeof parsed === "object") {
|
|
6129
|
+
return parsed;
|
|
6130
|
+
}
|
|
6131
|
+
} catch {
|
|
6132
|
+
}
|
|
6133
|
+
return null;
|
|
6134
|
+
};
|
|
6135
|
+
var buildDocFromRaw = (raw, prefixMap, configLookup) => {
|
|
6136
|
+
const lines = raw.split(/\r?\n/);
|
|
6137
|
+
let tokenCounter = 0;
|
|
6138
|
+
const paragraphs = lines.map((line) => {
|
|
6139
|
+
const segments = tokenizeFormulaString(line, prefixMap);
|
|
6140
|
+
const content = segments.map((segment) => {
|
|
6141
|
+
if (segment.kind === "text") {
|
|
6142
|
+
return segment.value ? { type: "text", text: segment.value } : null;
|
|
6143
|
+
}
|
|
6144
|
+
const config = configLookup.get(segment.prefix);
|
|
6145
|
+
const fallbackType = prefixMap[segment.prefix];
|
|
6146
|
+
const displayType = config?.type ?? fallbackType;
|
|
6147
|
+
const outputType = config?.outputType ?? fallbackType;
|
|
6148
|
+
if (!displayType && !outputType) {
|
|
6149
|
+
return { type: "text", text: `${segment.prefix}${segment.code}` };
|
|
6150
|
+
}
|
|
6151
|
+
const attrs = {
|
|
6152
|
+
id: `token-${tokenCounter++}`,
|
|
6153
|
+
type: displayType ?? outputType ?? "token",
|
|
6154
|
+
code: segment.code,
|
|
6155
|
+
prefix: segment.prefix,
|
|
6156
|
+
rawValue: `${segment.prefix}${segment.code}`,
|
|
6157
|
+
chipClassName: config?.chipClassName,
|
|
6158
|
+
label: segment.code,
|
|
6159
|
+
outputType: outputType ?? displayType
|
|
6160
|
+
};
|
|
6161
|
+
return { type: "token", attrs };
|
|
6162
|
+
}).filter((node) => Boolean(node));
|
|
6163
|
+
return {
|
|
6164
|
+
type: "paragraph",
|
|
6165
|
+
content: content.length > 0 ? content : [{ type: "text", text: "" }]
|
|
6166
|
+
};
|
|
6167
|
+
});
|
|
6168
|
+
return { type: "doc", content: paragraphs };
|
|
6169
|
+
};
|
|
6170
|
+
var FormulaEditor = ({
|
|
6171
|
+
value,
|
|
6172
|
+
disabled,
|
|
6173
|
+
loading = false,
|
|
6174
|
+
className,
|
|
6175
|
+
editorClassName,
|
|
6176
|
+
errorMessage,
|
|
6177
|
+
tokenConfigs,
|
|
6178
|
+
operators = defaultOperators,
|
|
6179
|
+
operatorShortcuts = defaultOperatorShortcuts,
|
|
6180
|
+
onChange,
|
|
6181
|
+
onSelectSuggestion,
|
|
6182
|
+
field,
|
|
6183
|
+
fieldState
|
|
6184
|
+
}) => {
|
|
6185
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
6186
|
+
const lastEmittedValueRef = useRef(null);
|
|
6187
|
+
const ignorePropValueRef = useRef(false);
|
|
6188
|
+
const normalizedConfigs = useMemo(() => {
|
|
6189
|
+
const configsToUse = tokenConfigs?.length ? tokenConfigs : DEFAULT_TOKEN_CONFIGS;
|
|
6190
|
+
return configsToUse.map((config) => ({
|
|
6191
|
+
...config,
|
|
6192
|
+
fetchItems: config.fetchItems ?? (async () => []),
|
|
6193
|
+
mapItem: config.mapItem ?? ((item) => ({ ...item, ...defaultMapItem(item) })),
|
|
6194
|
+
outputType: config.outputType ?? config.type
|
|
6195
|
+
}));
|
|
6196
|
+
}, [tokenConfigs]);
|
|
6197
|
+
const prefixMap = useMemo(() => buildPrefixMap(normalizedConfigs), [normalizedConfigs]);
|
|
6198
|
+
const configLookup = useMemo(() => {
|
|
6199
|
+
const lookup = /* @__PURE__ */ new Map();
|
|
6200
|
+
normalizedConfigs.forEach((config) => {
|
|
6201
|
+
lookup.set(config.prefix, config);
|
|
6202
|
+
});
|
|
6203
|
+
return lookup;
|
|
6204
|
+
}, [normalizedConfigs]);
|
|
6205
|
+
const allowedOperators = useMemo(() => operators.map((operator) => operator.value), [operators]);
|
|
6206
|
+
const displayError = errorMessage ?? fieldState?.error?.message;
|
|
6207
|
+
const hasError = Boolean(displayError);
|
|
6208
|
+
const isInteractionDisabled = Boolean(disabled || loading);
|
|
6209
|
+
const convertValueToContent = useCallback(
|
|
6210
|
+
(input) => {
|
|
6211
|
+
if (!input) return "";
|
|
6212
|
+
const trimmed = input.trim();
|
|
6213
|
+
if (!trimmed) return "";
|
|
6214
|
+
const parsedJSON = tryParseJSON(trimmed);
|
|
6215
|
+
if (parsedJSON && parsedJSON.type === "doc") {
|
|
6216
|
+
return parsedJSON;
|
|
6217
|
+
}
|
|
6218
|
+
if (looksLikeHTML(trimmed)) {
|
|
6219
|
+
return input;
|
|
6220
|
+
}
|
|
6221
|
+
return buildDocFromRaw(input, prefixMap, configLookup);
|
|
6222
|
+
},
|
|
6223
|
+
[configLookup, prefixMap]
|
|
6224
|
+
);
|
|
6225
|
+
const resolvedContent = useMemo(() => convertValueToContent(value), [convertValueToContent, value]);
|
|
6226
|
+
const extensions = useMemo(
|
|
6227
|
+
() => [
|
|
6228
|
+
StarterKit.configure({ bold: false, italic: false }),
|
|
6229
|
+
Token.configure({ configs: normalizedConfigs, onSelect: onSelectSuggestion }),
|
|
6230
|
+
Operator.configure({ shortcuts: operatorShortcuts })
|
|
6231
|
+
],
|
|
6232
|
+
[normalizedConfigs, onSelectSuggestion, operatorShortcuts]
|
|
6233
|
+
);
|
|
6234
|
+
const editor = useEditor({
|
|
6235
|
+
extensions,
|
|
6236
|
+
content: resolvedContent ?? "",
|
|
6237
|
+
onUpdate: ({ editor: nextEditor }) => {
|
|
6238
|
+
const { raw, token } = parseFormula(nextEditor.getJSON(), prefixMap, allowedOperators);
|
|
6239
|
+
const mappedTokens = mapTokensToOutput(token, normalizedConfigs);
|
|
6240
|
+
lastEmittedValueRef.current = raw;
|
|
6241
|
+
ignorePropValueRef.current = true;
|
|
6242
|
+
onChange?.(raw, mappedTokens);
|
|
6243
|
+
},
|
|
6244
|
+
editorProps: {
|
|
6245
|
+
attributes: {
|
|
6246
|
+
class: cn(
|
|
6247
|
+
isExpanded ? "min-h-[300px] max-h-[300px]" : "min-h-[150px] max-h-[150px]",
|
|
6248
|
+
hasError ? "border border-destructive" : "border focus-visible:border-ring",
|
|
6249
|
+
"w-full rounded-lg bg-white px-4 py-3",
|
|
6250
|
+
"overflow-y-auto whitespace-pre-wrap wrap-break-word focus:outline-none",
|
|
6251
|
+
isInteractionDisabled && "pointer-events-none opacity-60",
|
|
6252
|
+
editorClassName
|
|
6253
|
+
),
|
|
6254
|
+
...loading ? { "aria-busy": "true" } : {}
|
|
6255
|
+
}
|
|
6256
|
+
}
|
|
6257
|
+
});
|
|
6258
|
+
useEffect(() => {
|
|
6259
|
+
if (!editor) return;
|
|
6260
|
+
editor.setEditable(!isInteractionDisabled);
|
|
6261
|
+
}, [editor, isInteractionDisabled]);
|
|
6262
|
+
useEffect(() => {
|
|
6263
|
+
if (!editor || resolvedContent === void 0) return;
|
|
6264
|
+
if (ignorePropValueRef.current && typeof value === "string" && value === lastEmittedValueRef.current) {
|
|
6265
|
+
ignorePropValueRef.current = false;
|
|
6266
|
+
return;
|
|
6267
|
+
}
|
|
6268
|
+
ignorePropValueRef.current = false;
|
|
6269
|
+
if (typeof resolvedContent === "string") {
|
|
6270
|
+
const currentHTML = editor.getHTML();
|
|
6271
|
+
if (resolvedContent !== currentHTML) {
|
|
6272
|
+
editor.commands.setContent(resolvedContent, { emitUpdate: false });
|
|
6273
|
+
}
|
|
6274
|
+
return;
|
|
6275
|
+
}
|
|
6276
|
+
const currentJSON = JSON.stringify(editor.getJSON());
|
|
6277
|
+
const nextJSON = JSON.stringify(resolvedContent);
|
|
6278
|
+
if (currentJSON !== nextJSON) {
|
|
6279
|
+
editor.commands.setContent(resolvedContent, { emitUpdate: false });
|
|
6280
|
+
}
|
|
6281
|
+
}, [editor, resolvedContent, value]);
|
|
6282
|
+
const insertOperator = (operator) => {
|
|
6283
|
+
editor?.chain().focus().insertContent({ type: "operator", attrs: { value: operator } }).insertContent(" ").run();
|
|
6284
|
+
};
|
|
6285
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("w-full space-y-2", className), children: [
|
|
6286
|
+
/* @__PURE__ */ jsxs(
|
|
6287
|
+
"div",
|
|
6288
|
+
{
|
|
6289
|
+
ref: field?.ref,
|
|
6290
|
+
onBlur: field?.onBlur,
|
|
6291
|
+
tabIndex: 0,
|
|
6292
|
+
className: "relative",
|
|
6293
|
+
"aria-busy": loading,
|
|
6294
|
+
onFocus: () => {
|
|
6295
|
+
if (editor && !editor.isFocused) {
|
|
6296
|
+
editor.chain().focus().run();
|
|
6297
|
+
}
|
|
6298
|
+
},
|
|
6299
|
+
children: [
|
|
6300
|
+
/* @__PURE__ */ jsx(EditorContent, { editor }),
|
|
6301
|
+
loading && /* @__PURE__ */ jsx(
|
|
6302
|
+
LoadingOverlay,
|
|
6303
|
+
{
|
|
6304
|
+
fullscreen: false,
|
|
6305
|
+
className: "rounded-lg bg-white/80 backdrop-blur-sm",
|
|
6306
|
+
spinnerClassName: "size-6 text-sus-blue-3"
|
|
6307
|
+
}
|
|
6308
|
+
),
|
|
6309
|
+
/* @__PURE__ */ jsx(
|
|
6310
|
+
Button,
|
|
6311
|
+
{
|
|
6312
|
+
type: "button",
|
|
6313
|
+
variant: "ghost",
|
|
6314
|
+
size: "icon",
|
|
6315
|
+
className: "absolute bottom-2 right-4 h-6 w-6 rounded-full bg-white shadow",
|
|
6316
|
+
disabled: isInteractionDisabled,
|
|
6317
|
+
onClick: () => setIsExpanded((prev) => !prev),
|
|
6318
|
+
children: isExpanded ? /* @__PURE__ */ jsx(Minimize2, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(Maximize2, { className: "h-4 w-4" })
|
|
6319
|
+
}
|
|
6320
|
+
)
|
|
6321
|
+
]
|
|
6322
|
+
}
|
|
6323
|
+
),
|
|
6324
|
+
hasError && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", role: "alert", children: displayError }),
|
|
6325
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap justify-end gap-2 py-2", children: operators.map((operator) => /* @__PURE__ */ jsx(
|
|
6326
|
+
Button,
|
|
6327
|
+
{
|
|
6328
|
+
type: "button",
|
|
6329
|
+
onClick: () => insertOperator(operator.value),
|
|
6330
|
+
className: "min-w-10 rounded-sm px-3 bg-sus-blue-3",
|
|
6331
|
+
disabled: isInteractionDisabled,
|
|
6332
|
+
children: operator.label
|
|
6333
|
+
},
|
|
6334
|
+
operator.value
|
|
6335
|
+
)) })
|
|
6336
|
+
] });
|
|
6337
|
+
};
|
|
5348
6338
|
function TooltipProvider({
|
|
5349
6339
|
delayDuration = 0,
|
|
5350
6340
|
...props
|
|
@@ -5744,26 +6734,6 @@ var useGridSettingsStore = create(
|
|
|
5744
6734
|
})
|
|
5745
6735
|
);
|
|
5746
6736
|
var useGridSettingsStore_default = useGridSettingsStore;
|
|
5747
|
-
function LoadingOverlay({
|
|
5748
|
-
className,
|
|
5749
|
-
fullscreen = true,
|
|
5750
|
-
spinnerClassName,
|
|
5751
|
-
...props
|
|
5752
|
-
}) {
|
|
5753
|
-
return /* @__PURE__ */ jsx(
|
|
5754
|
-
"div",
|
|
5755
|
-
{
|
|
5756
|
-
"data-slot": "loading-overlay",
|
|
5757
|
-
className: cn(
|
|
5758
|
-
fullscreen ? "fixed z-100" : "absolute z-10",
|
|
5759
|
-
"inset-0 flex items-center justify-center transition-opacity duration-300",
|
|
5760
|
-
className
|
|
5761
|
-
),
|
|
5762
|
-
...props,
|
|
5763
|
-
children: /* @__PURE__ */ jsx(Spinner, { className: cn("size-50", spinnerClassName) })
|
|
5764
|
-
}
|
|
5765
|
-
);
|
|
5766
|
-
}
|
|
5767
6737
|
var DEVICE_SIZES = [320, 420, 640, 768, 1024, 1280, 1536, 1920];
|
|
5768
6738
|
var IMAGE_SIZES = [16, 32, 48, 64, 96, 128, 256, 384];
|
|
5769
6739
|
var defaultLoader = ({ src }) => src;
|
|
@@ -8746,6 +9716,6 @@ var Truncated = ({ children, className, ellipsis = true, as = "p", style }) => {
|
|
|
8746
9716
|
};
|
|
8747
9717
|
var truncated_default = Truncated;
|
|
8748
9718
|
|
|
8749
|
-
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, AdvanceSearch_default as AdvanceSearch, arrow_default as ArrowIcon, Button, Checkbox, Collapsible, CollapsibleContent2 as CollapsibleContent, CollapsibleTrigger2 as CollapsibleTrigger, CropperModal, CropperModalError, DIALOG_ALERT_I18N_SUBNAMESPACE, DataTable_default as DataTable, DatePicker2 as DatePicker, Dialog, DialogAlert, DialogAlertProvider, DialogContent, DialogDescription, DialogFooter, DialogTitle, DialogTrigger, ErrorCompression, ErrorCreateCanvas, ErrorGeneratingBlob, ErrorInvalidSVG, ErrorSVGExceedSize, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, GridSettingsModal_default as GridSettingsModal, HeaderCell_default as HeaderCell, Image2 as Image, Input, Label2 as Label, List_default as List, container_default as ListContainer, header_default as ListHeader, table_default as ListTable, LookupSelect, MonthPicker2 as MonthPicker, navbar_default as Navbar, not_found_default as NotFoundIcon, Popover, PopoverAnchor, PopoverArrow, PopoverContent, PopoverTrigger, PreventPageLeave_default as PreventPageLeave, RadioGroupItem, RadioGroupRoot, RadioLabel, RichText, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator2 as Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarLayout, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Spinner, calendar_default as SuiCalendarIcon, check_default as SuiCheckIcon, dots_vertical_default as SuiDotsVerticalIcon, empty_data_default as SuiEmptyDataIcon, expand_default as SuiExpandIcon, filter_default as SuiFilterIcon, setting_default as SuiSettingIcon, triangle_down_default as SuiTriangleDownIcon, warning_default as SuiWarningIcon, Switch, Textarea, Tooltip, TooltipArrow, TooltipContent, TooltipProvider, TooltipTrigger, truncated_default as Truncated, ui_exports as UI, booleanToSelectValue, buttonVariants, cn, compareAlphanumeric, debounce, formatISODate, getDialogAlertControls, getDialogTemplates, inputVariants, isDefined, isEmptyObject, selectValueToBoolean, spinnerVariants, stripNullishObject, throttle, useFormField, useGridSettingsStore_default as useGridSettingsStore, useHover_default as useHover, useIntersectionObserver_default as useIntersectionObserver, useMediaQuery_default as useMediaQuery, usePreventPageLeave_default as usePreventPageLeave, usePreventPageLeaveStore_default as usePreventPageLeaveStore, useScreenSize_default as useScreenSize, useSidebar, useTruncated_default as useTruncated };
|
|
9719
|
+
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, AdvanceSearch_default as AdvanceSearch, arrow_default as ArrowIcon, Button, Checkbox, Collapsible, CollapsibleContent2 as CollapsibleContent, CollapsibleTrigger2 as CollapsibleTrigger, CropperModal, CropperModalError, DIALOG_ALERT_I18N_SUBNAMESPACE, DataTable_default as DataTable, DatePicker2 as DatePicker, Dialog, DialogAlert, DialogAlertProvider, DialogContent, DialogDescription, DialogFooter, DialogTitle, DialogTrigger, ErrorCompression, ErrorCreateCanvas, ErrorGeneratingBlob, ErrorInvalidSVG, ErrorSVGExceedSize, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, FormulaEditor, GridSettingsModal_default as GridSettingsModal, HeaderCell_default as HeaderCell, Image2 as Image, Input, Label2 as Label, List_default as List, container_default as ListContainer, header_default as ListHeader, table_default as ListTable, LookupSelect, MonthPicker2 as MonthPicker, navbar_default as Navbar, not_found_default as NotFoundIcon, Popover, PopoverAnchor, PopoverArrow, PopoverContent, PopoverTrigger, PreventPageLeave_default as PreventPageLeave, RadioGroupItem, RadioGroupRoot, RadioLabel, RichText, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator2 as Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarLayout, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Spinner, calendar_default as SuiCalendarIcon, check_default as SuiCheckIcon, dots_vertical_default as SuiDotsVerticalIcon, empty_data_default as SuiEmptyDataIcon, expand_default as SuiExpandIcon, filter_default as SuiFilterIcon, setting_default as SuiSettingIcon, triangle_down_default as SuiTriangleDownIcon, warning_default as SuiWarningIcon, Switch, Textarea, Tooltip, TooltipArrow, TooltipContent, TooltipProvider, TooltipTrigger, truncated_default as Truncated, ui_exports as UI, booleanToSelectValue, buildPrefixMap, buttonVariants, cn, compareAlphanumeric, debounce, defaultOperatorShortcuts, defaultOperators, formatISODate, getDialogAlertControls, getDialogTemplates, inputVariants, isDefined, isEmptyObject, isValidParentheses, mapTokensToOutput, parseFormula, parseFormulaToToken, selectValueToBoolean, spinnerVariants, splitOperators, stripNullishObject, throttle, tokenizeFormulaString, useFormField, useGridSettingsStore_default as useGridSettingsStore, useHover_default as useHover, useIntersectionObserver_default as useIntersectionObserver, useMediaQuery_default as useMediaQuery, usePreventPageLeave_default as usePreventPageLeave, usePreventPageLeaveStore_default as usePreventPageLeaveStore, useScreenSize_default as useScreenSize, useSidebar, useTruncated_default as useTruncated, validateTokenPrefixes };
|
|
8750
9720
|
//# sourceMappingURL=index.mjs.map
|
|
8751
9721
|
//# sourceMappingURL=index.mjs.map
|