@geomak/ui 7.12.0 → 7.14.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.cjs +406 -172
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -2
- package/dist/index.d.ts +16 -2
- package/dist/index.js +405 -171
- package/dist/index.js.map +1 -1
- package/dist/styles.css +3 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import React36, { useMemo, createContext, useState, useEffect, useId, useCallbac
|
|
|
6
6
|
import { createPortal } from 'react-dom';
|
|
7
7
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
8
|
import * as AvatarPrimitive from '@radix-ui/react-avatar';
|
|
9
|
-
import * as
|
|
9
|
+
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
|
|
10
10
|
import * as Dialog from '@radix-ui/react-dialog';
|
|
11
11
|
import { useReducedMotion, AnimatePresence, motion, useScroll, useTransform } from 'framer-motion';
|
|
12
12
|
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
@@ -631,6 +631,39 @@ var Chevron = () => /* @__PURE__ */ jsx(
|
|
|
631
631
|
children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 9l6 6 6-6" })
|
|
632
632
|
}
|
|
633
633
|
);
|
|
634
|
+
var ChevronRight = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, "aria-hidden": "true", className: "ml-auto h-4 w-4", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) });
|
|
635
|
+
var ITEM_CLASS = [
|
|
636
|
+
"flex items-center gap-2.5 rounded-md px-2.5 py-1.5 text-sm outline-none cursor-pointer select-none transition-colors",
|
|
637
|
+
"data-[disabled]:opacity-50 data-[disabled]:pointer-events-none"
|
|
638
|
+
].join(" ");
|
|
639
|
+
var itemTone = (danger) => danger ? "text-status-error data-[highlighted]:bg-status-error/10" : "text-foreground data-[highlighted]:bg-surface-raised";
|
|
640
|
+
var Label = ({ item }) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
641
|
+
item.icon && /* @__PURE__ */ jsx("span", { className: "flex h-4 w-4 flex-shrink-0 items-center justify-center", children: item.icon }),
|
|
642
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: item.label })
|
|
643
|
+
] });
|
|
644
|
+
function renderItem(item) {
|
|
645
|
+
if (item.children && item.children.length > 0) {
|
|
646
|
+
return /* @__PURE__ */ jsxs(DropdownMenu.Sub, { children: [
|
|
647
|
+
/* @__PURE__ */ jsxs(DropdownMenu.SubTrigger, { disabled: item.disabled, className: `${ITEM_CLASS} ${itemTone(item.danger)}`, children: [
|
|
648
|
+
/* @__PURE__ */ jsx(Label, { item }),
|
|
649
|
+
/* @__PURE__ */ jsx(ChevronRight, {})
|
|
650
|
+
] }),
|
|
651
|
+
/* @__PURE__ */ jsx(DropdownMenu.Portal, { children: /* @__PURE__ */ jsx(
|
|
652
|
+
DropdownMenu.SubContent,
|
|
653
|
+
{
|
|
654
|
+
sideOffset: 4,
|
|
655
|
+
collisionPadding: 8,
|
|
656
|
+
className: "z-[400] min-w-[10rem] rounded-lg border border-border bg-surface p-1 shadow-lg data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
|
657
|
+
children: item.children.map((sub) => /* @__PURE__ */ jsxs(React36.Fragment, { children: [
|
|
658
|
+
sub.separatorBefore && /* @__PURE__ */ jsx(DropdownMenu.Separator, { className: "my-1 h-px bg-border" }),
|
|
659
|
+
renderItem(sub)
|
|
660
|
+
] }, sub.key))
|
|
661
|
+
}
|
|
662
|
+
) })
|
|
663
|
+
] });
|
|
664
|
+
}
|
|
665
|
+
return /* @__PURE__ */ jsx(DropdownMenu.Item, { disabled: item.disabled, onSelect: () => item.onSelect?.(), className: `${ITEM_CLASS} ${itemTone(item.danger)}`, children: /* @__PURE__ */ jsx(Label, { item }) });
|
|
666
|
+
}
|
|
634
667
|
function MenuButton({
|
|
635
668
|
label,
|
|
636
669
|
items,
|
|
@@ -643,8 +676,8 @@ function MenuButton({
|
|
|
643
676
|
side = "bottom",
|
|
644
677
|
className = ""
|
|
645
678
|
}) {
|
|
646
|
-
return /* @__PURE__ */ jsxs(
|
|
647
|
-
/* @__PURE__ */ jsx(
|
|
679
|
+
return /* @__PURE__ */ jsxs(DropdownMenu.Root, { children: [
|
|
680
|
+
/* @__PURE__ */ jsx(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
648
681
|
Button_default,
|
|
649
682
|
{
|
|
650
683
|
variant,
|
|
@@ -658,8 +691,8 @@ function MenuButton({
|
|
|
658
691
|
] })
|
|
659
692
|
}
|
|
660
693
|
) }),
|
|
661
|
-
/* @__PURE__ */ jsx(
|
|
662
|
-
|
|
694
|
+
/* @__PURE__ */ jsx(DropdownMenu.Portal, { children: /* @__PURE__ */ jsx(
|
|
695
|
+
DropdownMenu.Content,
|
|
663
696
|
{
|
|
664
697
|
align,
|
|
665
698
|
side,
|
|
@@ -671,23 +704,8 @@ function MenuButton({
|
|
|
671
704
|
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95"
|
|
672
705
|
].join(" "),
|
|
673
706
|
children: items.map((item) => /* @__PURE__ */ jsxs(React36.Fragment, { children: [
|
|
674
|
-
item.separatorBefore && /* @__PURE__ */ jsx(
|
|
675
|
-
|
|
676
|
-
DropdownMenu2.Item,
|
|
677
|
-
{
|
|
678
|
-
disabled: item.disabled,
|
|
679
|
-
onSelect: () => item.onSelect?.(),
|
|
680
|
-
className: [
|
|
681
|
-
"flex items-center gap-2.5 rounded-md px-2.5 py-1.5 text-sm outline-none cursor-pointer select-none transition-colors",
|
|
682
|
-
"data-[disabled]:opacity-50 data-[disabled]:pointer-events-none",
|
|
683
|
-
item.danger ? "text-status-error data-[highlighted]:bg-status-error/10" : "text-foreground data-[highlighted]:bg-surface-raised"
|
|
684
|
-
].join(" "),
|
|
685
|
-
children: [
|
|
686
|
-
item.icon && /* @__PURE__ */ jsx("span", { className: "flex h-4 w-4 flex-shrink-0 items-center justify-center", children: item.icon }),
|
|
687
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: item.label })
|
|
688
|
-
]
|
|
689
|
-
}
|
|
690
|
-
)
|
|
707
|
+
item.separatorBefore && /* @__PURE__ */ jsx(DropdownMenu.Separator, { className: "my-1 h-px bg-border" }),
|
|
708
|
+
renderItem(item)
|
|
691
709
|
] }, item.key))
|
|
692
710
|
}
|
|
693
711
|
) })
|
|
@@ -1729,7 +1747,7 @@ function Stepper({
|
|
|
1729
1747
|
const vertical = orientation === "vertical" || forcedVertical;
|
|
1730
1748
|
const s = SIZES[size];
|
|
1731
1749
|
const stateOf = (i) => i < current ? "completed" : i === current ? status : "pending";
|
|
1732
|
-
const
|
|
1750
|
+
const Label3 = ({ step, state, align }) => /* @__PURE__ */ jsxs("div", { className: align === "center" ? "mt-2 text-center" : "pt-0.5", children: [
|
|
1733
1751
|
/* @__PURE__ */ jsx("div", { className: `font-medium leading-tight ${s.title} ${state === "pending" ? "text-foreground-muted" : "text-foreground"}`, children: step.title }),
|
|
1734
1752
|
step.description && /* @__PURE__ */ jsx("div", { className: `mt-0.5 leading-snug text-foreground-muted ${s.desc}`, children: step.description })
|
|
1735
1753
|
] });
|
|
@@ -1744,7 +1762,7 @@ function Stepper({
|
|
|
1744
1762
|
stepButton(i, /* @__PURE__ */ jsx(Indicator, { state, index: i, step, sizeKey: size })),
|
|
1745
1763
|
!last && /* @__PURE__ */ jsx(VConnector, { filled: i < current, reduced })
|
|
1746
1764
|
] }),
|
|
1747
|
-
/* @__PURE__ */ jsx("div", { className: last ? "" : "pb-6", children: stepButton(i, /* @__PURE__ */ jsx(
|
|
1765
|
+
/* @__PURE__ */ jsx("div", { className: last ? "" : "pb-6", children: stepButton(i, /* @__PURE__ */ jsx(Label3, { step, state, align: "left" })) })
|
|
1748
1766
|
] }, step.key);
|
|
1749
1767
|
}) });
|
|
1750
1768
|
}
|
|
@@ -1763,7 +1781,7 @@ function Stepper({
|
|
|
1763
1781
|
) }),
|
|
1764
1782
|
stepButton(i, /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center", children: [
|
|
1765
1783
|
/* @__PURE__ */ jsx(Indicator, { state, index: i, step, sizeKey: size }),
|
|
1766
|
-
/* @__PURE__ */ jsx(
|
|
1784
|
+
/* @__PURE__ */ jsx(Label3, { step, state, align: "center" })
|
|
1767
1785
|
] }))
|
|
1768
1786
|
] }, step.key);
|
|
1769
1787
|
}) });
|
|
@@ -4929,7 +4947,7 @@ function ContextMenu({ items, children, className = "" }) {
|
|
|
4929
4947
|
{
|
|
4930
4948
|
className: `${CONTENT_CLASSNAME} ${className}`.trim(),
|
|
4931
4949
|
collisionPadding: 8,
|
|
4932
|
-
children: items.map((item) =>
|
|
4950
|
+
children: items.map((item) => renderItem2(item))
|
|
4933
4951
|
}
|
|
4934
4952
|
) })
|
|
4935
4953
|
] });
|
|
@@ -4951,7 +4969,7 @@ var ITEM_CLASSNAME = [
|
|
|
4951
4969
|
"data-[disabled]:opacity-40 data-[disabled]:cursor-not-allowed data-[disabled]:bg-transparent data-[disabled]:text-foreground-muted",
|
|
4952
4970
|
"focus:outline-none"
|
|
4953
4971
|
].join(" ");
|
|
4954
|
-
function
|
|
4972
|
+
function renderItem2(item) {
|
|
4955
4973
|
if (item.children && item.children.length > 0) {
|
|
4956
4974
|
return /* @__PURE__ */ jsxs(ContextMenuPrimitive.Sub, { children: [
|
|
4957
4975
|
/* @__PURE__ */ jsxs(
|
|
@@ -4961,7 +4979,7 @@ function renderItem(item) {
|
|
|
4961
4979
|
className: ITEM_CLASSNAME,
|
|
4962
4980
|
children: [
|
|
4963
4981
|
/* @__PURE__ */ jsx(ContextMenuLabel, { icon: item.icon, value: item.value }),
|
|
4964
|
-
/* @__PURE__ */ jsx(
|
|
4982
|
+
/* @__PURE__ */ jsx(ChevronRight2, {})
|
|
4965
4983
|
]
|
|
4966
4984
|
}
|
|
4967
4985
|
),
|
|
@@ -4972,7 +4990,7 @@ function renderItem(item) {
|
|
|
4972
4990
|
sideOffset: 2,
|
|
4973
4991
|
alignOffset: -4,
|
|
4974
4992
|
collisionPadding: 8,
|
|
4975
|
-
children: item.children.map((sub) =>
|
|
4993
|
+
children: item.children.map((sub) => renderItem2(sub))
|
|
4976
4994
|
}
|
|
4977
4995
|
) })
|
|
4978
4996
|
] }, item.key);
|
|
@@ -4994,7 +5012,7 @@ function ContextMenuLabel({ icon, value }) {
|
|
|
4994
5012
|
/* @__PURE__ */ jsx("span", { className: "truncate", children: value })
|
|
4995
5013
|
] });
|
|
4996
5014
|
}
|
|
4997
|
-
function
|
|
5015
|
+
function ChevronRight2() {
|
|
4998
5016
|
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4 flex-shrink-0", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) });
|
|
4999
5017
|
}
|
|
5000
5018
|
function readDismissed(key) {
|
|
@@ -5847,7 +5865,7 @@ var useIsoLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEf
|
|
|
5847
5865
|
function VirtualList({
|
|
5848
5866
|
items,
|
|
5849
5867
|
rowHeight,
|
|
5850
|
-
renderItem:
|
|
5868
|
+
renderItem: renderItem3,
|
|
5851
5869
|
height = 400,
|
|
5852
5870
|
getKey,
|
|
5853
5871
|
overscan = 6,
|
|
@@ -5912,7 +5930,7 @@ function VirtualList({
|
|
|
5912
5930
|
role: "listitem",
|
|
5913
5931
|
style: { height: rowHeight },
|
|
5914
5932
|
className: "box-border",
|
|
5915
|
-
children:
|
|
5933
|
+
children: renderItem3(item, index)
|
|
5916
5934
|
},
|
|
5917
5935
|
getKey ? getKey(item, index) : index
|
|
5918
5936
|
);
|
|
@@ -6453,6 +6471,15 @@ function displayValue(v) {
|
|
|
6453
6471
|
if (v == null) return "";
|
|
6454
6472
|
return String(v);
|
|
6455
6473
|
}
|
|
6474
|
+
function colLetter(i) {
|
|
6475
|
+
let s = "";
|
|
6476
|
+
let n = i;
|
|
6477
|
+
do {
|
|
6478
|
+
s = String.fromCharCode(65 + n % 26) + s;
|
|
6479
|
+
n = Math.floor(n / 26) - 1;
|
|
6480
|
+
} while (n >= 0);
|
|
6481
|
+
return s;
|
|
6482
|
+
}
|
|
6456
6483
|
function DataGrid({
|
|
6457
6484
|
columns,
|
|
6458
6485
|
rows,
|
|
@@ -6468,7 +6495,12 @@ function DataGrid({
|
|
|
6468
6495
|
overscan = 4,
|
|
6469
6496
|
rowNumbers = true,
|
|
6470
6497
|
trailingRows = 0,
|
|
6498
|
+
trailingCols = 0,
|
|
6471
6499
|
onCellEdit,
|
|
6500
|
+
contextMenu = false,
|
|
6501
|
+
onInsertRow,
|
|
6502
|
+
onDeleteRow,
|
|
6503
|
+
onInsertColumn,
|
|
6472
6504
|
className = "",
|
|
6473
6505
|
style,
|
|
6474
6506
|
emptyState = "No data"
|
|
@@ -6476,14 +6508,25 @@ function DataGrid({
|
|
|
6476
6508
|
const scrollRef = useRef(null);
|
|
6477
6509
|
const [scroll, setScroll] = useState({ top: 0, left: 0 });
|
|
6478
6510
|
const [viewport, setViewport] = useState({ w: 0, h: 0 });
|
|
6511
|
+
const [selected, setSelected] = useState(null);
|
|
6512
|
+
const [hoveredRow, setHoveredRow] = useState(null);
|
|
6513
|
+
const [ctxTarget, setCtxTarget] = useState(null);
|
|
6479
6514
|
const [editing, setEditing] = useState(null);
|
|
6480
6515
|
const [draft, setDraft] = useState("");
|
|
6481
6516
|
const [internalSort, setInternalSort] = useState(null);
|
|
6482
6517
|
const sort = sortProp !== void 0 ? sortProp : internalSort;
|
|
6483
6518
|
const gutter = rowNumbers ? GUTTER : 0;
|
|
6484
6519
|
const colSortable = (c) => c.sortable ?? sortable;
|
|
6520
|
+
const cols = useMemo(() => {
|
|
6521
|
+
if (trailingCols <= 0) return columns;
|
|
6522
|
+
const extra = Array.from({ length: trailingCols }, (_, k) => {
|
|
6523
|
+
const idx = columns.length + k;
|
|
6524
|
+
return { key: `__c${idx}`, label: colLetter(idx), editable: false, sortable: false };
|
|
6525
|
+
});
|
|
6526
|
+
return [...columns, ...extra];
|
|
6527
|
+
}, [columns, trailingCols]);
|
|
6485
6528
|
const { widths, offsets, totalWidth } = useMemo(() => {
|
|
6486
|
-
const widths2 =
|
|
6529
|
+
const widths2 = cols.map((c) => resolveWidth(c.width));
|
|
6487
6530
|
const offsets2 = [];
|
|
6488
6531
|
let acc = 0;
|
|
6489
6532
|
for (const w of widths2) {
|
|
@@ -6491,7 +6534,7 @@ function DataGrid({
|
|
|
6491
6534
|
acc += w;
|
|
6492
6535
|
}
|
|
6493
6536
|
return { widths: widths2, offsets: offsets2, totalWidth: acc };
|
|
6494
|
-
}, [
|
|
6537
|
+
}, [cols]);
|
|
6495
6538
|
const order = useMemo(() => {
|
|
6496
6539
|
const idx = rows.map((_, i) => i);
|
|
6497
6540
|
if (!sort) return idx;
|
|
@@ -6520,140 +6563,213 @@ function DataGrid({
|
|
|
6520
6563
|
const rowStart = virtualize ? Math.max(0, Math.floor(scroll.top / rowHeight) - overscan) : 0;
|
|
6521
6564
|
const rowEnd = virtualize ? Math.min(displayRowCount, Math.ceil((scroll.top + bodyH) / rowHeight) + overscan) : displayRowCount;
|
|
6522
6565
|
let colStart = 0;
|
|
6523
|
-
let colEnd =
|
|
6566
|
+
let colEnd = cols.length;
|
|
6524
6567
|
if (virtualize && viewport.w) {
|
|
6525
6568
|
const viewLeft = scroll.left;
|
|
6526
6569
|
const viewRight = scroll.left + (viewport.w - gutter);
|
|
6527
6570
|
colStart = Math.max(0, offsets.findIndex((o, i) => o + widths[i] > viewLeft));
|
|
6528
6571
|
if (colStart < 0) colStart = 0;
|
|
6529
6572
|
colEnd = offsets.findIndex((o) => o > viewRight);
|
|
6530
|
-
colEnd = colEnd === -1 ?
|
|
6573
|
+
colEnd = colEnd === -1 ? cols.length : Math.min(cols.length, colEnd + 1);
|
|
6531
6574
|
colStart = Math.max(0, colStart - overscan);
|
|
6532
|
-
colEnd = Math.min(
|
|
6575
|
+
colEnd = Math.min(cols.length, colEnd + overscan);
|
|
6533
6576
|
}
|
|
6534
6577
|
const visibleRows = Array.from({ length: Math.max(0, rowEnd - rowStart) }, (_, i) => rowStart + i);
|
|
6535
6578
|
const visibleCols = Array.from({ length: Math.max(0, colEnd - colStart) }, (_, i) => colStart + i);
|
|
6536
6579
|
const commit = useCallback(() => {
|
|
6537
6580
|
if (!editing) return;
|
|
6538
|
-
const col =
|
|
6581
|
+
const col = cols[editing.col];
|
|
6539
6582
|
const ri = editing.disp < rows.length ? order[editing.disp] : editing.disp;
|
|
6540
6583
|
onCellEdit?.({ row: ri, column: col.key, value: draft });
|
|
6541
6584
|
setEditing(null);
|
|
6542
|
-
}, [editing,
|
|
6585
|
+
}, [editing, cols, draft, onCellEdit, order, rows.length]);
|
|
6543
6586
|
const startEdit = (disp, col) => {
|
|
6544
|
-
const c =
|
|
6587
|
+
const c = cols[col];
|
|
6545
6588
|
if (!(c.editable ?? editable)) return;
|
|
6546
6589
|
setDraft(displayValue(rows[rowIndexForDisp(disp)]?.[c.key] ?? ""));
|
|
6547
6590
|
setEditing({ disp, col });
|
|
6548
6591
|
};
|
|
6592
|
+
const cellText = (sel) => displayValue(rows[rowIndexForDisp(sel.disp)]?.[cols[sel.col].key] ?? "");
|
|
6593
|
+
const copyCell = useCallback(async () => {
|
|
6594
|
+
if (!selected) return;
|
|
6595
|
+
try {
|
|
6596
|
+
await navigator.clipboard?.writeText(cellText(selected));
|
|
6597
|
+
} catch {
|
|
6598
|
+
}
|
|
6599
|
+
}, [selected, rows, cols, order]);
|
|
6600
|
+
const cutCell = useCallback(async () => {
|
|
6601
|
+
if (!selected) return;
|
|
6602
|
+
await copyCell();
|
|
6603
|
+
onCellEdit?.({ row: rowIndexForDisp(selected.disp), column: cols[selected.col].key, value: "" });
|
|
6604
|
+
}, [selected, copyCell, onCellEdit, cols, order, rows.length]);
|
|
6605
|
+
const pasteCell = useCallback(async () => {
|
|
6606
|
+
if (!selected) return;
|
|
6607
|
+
let text = "";
|
|
6608
|
+
try {
|
|
6609
|
+
text = await navigator.clipboard?.readText() ?? "";
|
|
6610
|
+
} catch {
|
|
6611
|
+
return;
|
|
6612
|
+
}
|
|
6613
|
+
onCellEdit?.({ row: rowIndexForDisp(selected.disp), column: cols[selected.col].key, value: text });
|
|
6614
|
+
}, [selected, onCellEdit, cols, order, rows.length]);
|
|
6615
|
+
const ctxItems = useMemo(() => {
|
|
6616
|
+
if (ctxTarget?.kind === "header") {
|
|
6617
|
+
const ci = ctxTarget.col;
|
|
6618
|
+
return [
|
|
6619
|
+
{ key: "left", value: "Add column to the left", disabled: !onInsertColumn, onClick: () => onInsertColumn?.(ci) },
|
|
6620
|
+
{ key: "right", value: "Add column to the right", disabled: !onInsertColumn, onClick: () => onInsertColumn?.(ci + 1) }
|
|
6621
|
+
];
|
|
6622
|
+
}
|
|
6623
|
+
if (ctxTarget?.kind === "row") {
|
|
6624
|
+
const ri = rowIndexForDisp(ctxTarget.disp);
|
|
6625
|
+
const isData = ctxTarget.disp < rows.length;
|
|
6626
|
+
return [
|
|
6627
|
+
{ key: "above", value: "Add row above", disabled: !onInsertRow, onClick: () => onInsertRow?.(ri) },
|
|
6628
|
+
{ key: "below", value: "Add row below", disabled: !onInsertRow, onClick: () => onInsertRow?.(Math.min(rows.length, ri + 1)) },
|
|
6629
|
+
{ key: "delete", value: "Delete row", disabled: !isData || !onDeleteRow, onClick: () => onDeleteRow?.(ri) }
|
|
6630
|
+
];
|
|
6631
|
+
}
|
|
6632
|
+
return [
|
|
6633
|
+
{ key: "copy", value: "Copy", onClick: () => void copyCell() },
|
|
6634
|
+
{ key: "cut", value: "Cut", disabled: !editable, onClick: () => void cutCell() },
|
|
6635
|
+
{ key: "paste", value: "Paste", disabled: !editable, onClick: () => void pasteCell() }
|
|
6636
|
+
];
|
|
6637
|
+
}, [ctxTarget, rows.length, editable, onInsertRow, onDeleteRow, onInsertColumn, copyCell, cutCell, pasteCell, order]);
|
|
6638
|
+
const rowHighlighted = (disp) => hoveredRow === disp || selected?.disp === disp;
|
|
6639
|
+
const gridInner = /* @__PURE__ */ jsxs("div", { style: { position: "relative", width: gutter + totalWidth + END_PAD, height: headerHeight + totalHeight + END_PAD }, children: [
|
|
6640
|
+
rowNumbers && /* @__PURE__ */ jsx(
|
|
6641
|
+
"div",
|
|
6642
|
+
{
|
|
6643
|
+
className: "border-b border-r border-border bg-surface",
|
|
6644
|
+
style: { position: "absolute", top: scroll.top, left: scroll.left, width: gutter, height: headerHeight, zIndex: 3 }
|
|
6645
|
+
}
|
|
6646
|
+
),
|
|
6647
|
+
visibleCols.map((ci) => {
|
|
6648
|
+
const c = cols[ci];
|
|
6649
|
+
const sortDir = sort?.key === c.key ? sort.dir : null;
|
|
6650
|
+
const canSort = colSortable(c);
|
|
6651
|
+
return /* @__PURE__ */ jsxs(
|
|
6652
|
+
"div",
|
|
6653
|
+
{
|
|
6654
|
+
role: "columnheader",
|
|
6655
|
+
"aria-sort": sortDir ? sortDir === "asc" ? "ascending" : "descending" : void 0,
|
|
6656
|
+
onClick: canSort ? () => toggleSort(c.key) : void 0,
|
|
6657
|
+
onContextMenu: contextMenu ? () => setCtxTarget({ kind: "header", col: ci }) : void 0,
|
|
6658
|
+
className: cx(
|
|
6659
|
+
"flex items-center gap-1 border-b border-r border-border bg-surface px-3 font-medium text-foreground-secondary",
|
|
6660
|
+
canSort && "cursor-pointer select-none hover:text-foreground"
|
|
6661
|
+
),
|
|
6662
|
+
style: {
|
|
6663
|
+
position: "absolute",
|
|
6664
|
+
top: scroll.top,
|
|
6665
|
+
left: gutter + offsets[ci],
|
|
6666
|
+
width: widths[ci],
|
|
6667
|
+
height: headerHeight,
|
|
6668
|
+
zIndex: 2,
|
|
6669
|
+
justifyContent: c.align === "right" ? "flex-end" : c.align === "center" ? "center" : "flex-start"
|
|
6670
|
+
},
|
|
6671
|
+
children: [
|
|
6672
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: c.label ?? c.key }),
|
|
6673
|
+
canSort && /* @__PURE__ */ jsx(SortCaret, { dir: sortDir })
|
|
6674
|
+
]
|
|
6675
|
+
},
|
|
6676
|
+
`h-${c.key}`
|
|
6677
|
+
);
|
|
6678
|
+
}),
|
|
6679
|
+
rowNumbers && visibleRows.map((disp) => {
|
|
6680
|
+
const hi = rowHighlighted(disp);
|
|
6681
|
+
return /* @__PURE__ */ jsx(
|
|
6682
|
+
"div",
|
|
6683
|
+
{
|
|
6684
|
+
onMouseEnter: () => setHoveredRow(disp),
|
|
6685
|
+
onContextMenu: contextMenu ? () => setCtxTarget({ kind: "row", disp }) : void 0,
|
|
6686
|
+
className: cx(
|
|
6687
|
+
"flex items-center justify-center border-b border-r border-border bg-surface text-xs tabular-nums",
|
|
6688
|
+
hi ? "font-medium text-foreground" : "text-foreground-muted"
|
|
6689
|
+
),
|
|
6690
|
+
style: { position: "absolute", left: scroll.left, top: headerHeight + disp * rowHeight, width: gutter, height: rowHeight, zIndex: 1, backgroundColor: hi ? HILITE : void 0 },
|
|
6691
|
+
children: disp + 1
|
|
6692
|
+
},
|
|
6693
|
+
`g-${disp}`
|
|
6694
|
+
);
|
|
6695
|
+
}),
|
|
6696
|
+
visibleRows.map((disp) => {
|
|
6697
|
+
const ri = rowIndexForDisp(disp);
|
|
6698
|
+
const hi = rowHighlighted(disp);
|
|
6699
|
+
return visibleCols.map((ci) => {
|
|
6700
|
+
const c = cols[ci];
|
|
6701
|
+
const isEditing = editing?.disp === disp && editing?.col === ci;
|
|
6702
|
+
const isSelected = selected?.disp === disp && selected?.col === ci;
|
|
6703
|
+
const canEdit = c.editable ?? editable;
|
|
6704
|
+
return /* @__PURE__ */ jsx(
|
|
6705
|
+
"div",
|
|
6706
|
+
{
|
|
6707
|
+
role: "gridcell",
|
|
6708
|
+
"aria-selected": isSelected || void 0,
|
|
6709
|
+
onClick: () => setSelected({ disp, col: ci }),
|
|
6710
|
+
onDoubleClick: () => startEdit(disp, ci),
|
|
6711
|
+
onMouseEnter: () => setHoveredRow(disp),
|
|
6712
|
+
onContextMenu: contextMenu ? () => {
|
|
6713
|
+
setSelected({ disp, col: ci });
|
|
6714
|
+
setCtxTarget({ kind: "cell", disp });
|
|
6715
|
+
} : void 0,
|
|
6716
|
+
className: cx(
|
|
6717
|
+
"flex items-center border-b border-r border-border px-3",
|
|
6718
|
+
disp % 2 ? "bg-surface-raised" : "bg-surface",
|
|
6719
|
+
canEdit ? "cursor-text" : "cursor-default"
|
|
6720
|
+
),
|
|
6721
|
+
style: {
|
|
6722
|
+
position: "absolute",
|
|
6723
|
+
top: headerHeight + disp * rowHeight,
|
|
6724
|
+
left: gutter + offsets[ci],
|
|
6725
|
+
width: widths[ci],
|
|
6726
|
+
height: rowHeight,
|
|
6727
|
+
zIndex: isSelected ? 1 : void 0,
|
|
6728
|
+
backgroundColor: isSelected ? SELECT_BG : hi ? HILITE : void 0,
|
|
6729
|
+
boxShadow: isSelected ? SELECT_RING : void 0,
|
|
6730
|
+
justifyContent: c.align === "right" ? "flex-end" : c.align === "center" ? "center" : "flex-start"
|
|
6731
|
+
},
|
|
6732
|
+
children: isEditing ? /* @__PURE__ */ jsx(
|
|
6733
|
+
"input",
|
|
6734
|
+
{
|
|
6735
|
+
autoFocus: true,
|
|
6736
|
+
value: draft,
|
|
6737
|
+
onChange: (e) => setDraft(e.target.value),
|
|
6738
|
+
onBlur: commit,
|
|
6739
|
+
onKeyDown: (e) => {
|
|
6740
|
+
if (e.key === "Enter") commit();
|
|
6741
|
+
else if (e.key === "Escape") setEditing(null);
|
|
6742
|
+
},
|
|
6743
|
+
className: "h-full w-full bg-transparent text-foreground outline-none"
|
|
6744
|
+
}
|
|
6745
|
+
) : /* @__PURE__ */ jsx("span", { className: "truncate text-foreground", children: displayValue(rows[ri]?.[c.key] ?? "") })
|
|
6746
|
+
},
|
|
6747
|
+
`${disp}-${c.key}`
|
|
6748
|
+
);
|
|
6749
|
+
});
|
|
6750
|
+
})
|
|
6751
|
+
] });
|
|
6549
6752
|
return /* @__PURE__ */ jsxs(
|
|
6550
6753
|
"div",
|
|
6551
6754
|
{
|
|
6552
6755
|
ref: scrollRef,
|
|
6553
6756
|
onScroll: (e) => setScroll({ top: e.currentTarget.scrollTop, left: e.currentTarget.scrollLeft }),
|
|
6757
|
+
onMouseLeave: () => setHoveredRow(null),
|
|
6554
6758
|
className: cx("relative overflow-auto rounded-lg border border-border bg-surface-raised text-sm", className),
|
|
6555
6759
|
style: { height, width, ...style },
|
|
6556
6760
|
role: "grid",
|
|
6557
6761
|
"aria-rowcount": rows.length,
|
|
6558
6762
|
"aria-colcount": columns.length,
|
|
6559
6763
|
children: [
|
|
6560
|
-
/* @__PURE__ */
|
|
6561
|
-
rowNumbers && /* @__PURE__ */ jsx(
|
|
6562
|
-
"div",
|
|
6563
|
-
{
|
|
6564
|
-
className: "border-b border-r border-border bg-surface",
|
|
6565
|
-
style: { position: "absolute", top: scroll.top, left: scroll.left, width: gutter, height: headerHeight, zIndex: 3 }
|
|
6566
|
-
}
|
|
6567
|
-
),
|
|
6568
|
-
visibleCols.map((ci) => {
|
|
6569
|
-
const c = columns[ci];
|
|
6570
|
-
const sortDir = sort?.key === c.key ? sort.dir : null;
|
|
6571
|
-
const canSort = colSortable(c);
|
|
6572
|
-
return /* @__PURE__ */ jsxs(
|
|
6573
|
-
"div",
|
|
6574
|
-
{
|
|
6575
|
-
role: "columnheader",
|
|
6576
|
-
"aria-sort": sortDir ? sortDir === "asc" ? "ascending" : "descending" : void 0,
|
|
6577
|
-
onClick: canSort ? () => toggleSort(c.key) : void 0,
|
|
6578
|
-
className: cx(
|
|
6579
|
-
"flex items-center gap-1 border-b border-r border-border bg-surface px-3 font-medium text-foreground-secondary",
|
|
6580
|
-
canSort && "cursor-pointer select-none hover:text-foreground"
|
|
6581
|
-
),
|
|
6582
|
-
style: {
|
|
6583
|
-
position: "absolute",
|
|
6584
|
-
top: scroll.top,
|
|
6585
|
-
left: gutter + offsets[ci],
|
|
6586
|
-
width: widths[ci],
|
|
6587
|
-
height: headerHeight,
|
|
6588
|
-
zIndex: 2,
|
|
6589
|
-
justifyContent: c.align === "right" ? "flex-end" : c.align === "center" ? "center" : "flex-start"
|
|
6590
|
-
},
|
|
6591
|
-
children: [
|
|
6592
|
-
/* @__PURE__ */ jsx("span", { className: "truncate", children: c.label ?? c.key }),
|
|
6593
|
-
canSort && /* @__PURE__ */ jsx(SortCaret, { dir: sortDir })
|
|
6594
|
-
]
|
|
6595
|
-
},
|
|
6596
|
-
`h-${c.key}`
|
|
6597
|
-
);
|
|
6598
|
-
}),
|
|
6599
|
-
rowNumbers && visibleRows.map((disp) => /* @__PURE__ */ jsx(
|
|
6600
|
-
"div",
|
|
6601
|
-
{
|
|
6602
|
-
className: "flex items-center justify-center border-b border-r border-border bg-surface text-xs tabular-nums text-foreground-muted",
|
|
6603
|
-
style: { position: "absolute", left: scroll.left, top: headerHeight + disp * rowHeight, width: gutter, height: rowHeight, zIndex: 1 },
|
|
6604
|
-
children: disp + 1
|
|
6605
|
-
},
|
|
6606
|
-
`g-${disp}`
|
|
6607
|
-
)),
|
|
6608
|
-
visibleRows.map((disp) => {
|
|
6609
|
-
const ri = rowIndexForDisp(disp);
|
|
6610
|
-
return visibleCols.map((ci) => {
|
|
6611
|
-
const c = columns[ci];
|
|
6612
|
-
const isEditing = editing?.disp === disp && editing?.col === ci;
|
|
6613
|
-
const canEdit = c.editable ?? editable;
|
|
6614
|
-
return /* @__PURE__ */ jsx(
|
|
6615
|
-
"div",
|
|
6616
|
-
{
|
|
6617
|
-
role: "gridcell",
|
|
6618
|
-
onDoubleClick: () => startEdit(disp, ci),
|
|
6619
|
-
className: cx(
|
|
6620
|
-
"flex items-center border-b border-r border-border px-3",
|
|
6621
|
-
disp % 2 ? "bg-surface-raised" : "bg-surface",
|
|
6622
|
-
canEdit && "cursor-text"
|
|
6623
|
-
),
|
|
6624
|
-
style: {
|
|
6625
|
-
position: "absolute",
|
|
6626
|
-
top: headerHeight + disp * rowHeight,
|
|
6627
|
-
left: gutter + offsets[ci],
|
|
6628
|
-
width: widths[ci],
|
|
6629
|
-
height: rowHeight,
|
|
6630
|
-
justifyContent: c.align === "right" ? "flex-end" : c.align === "center" ? "center" : "flex-start"
|
|
6631
|
-
},
|
|
6632
|
-
children: isEditing ? /* @__PURE__ */ jsx(
|
|
6633
|
-
"input",
|
|
6634
|
-
{
|
|
6635
|
-
autoFocus: true,
|
|
6636
|
-
value: draft,
|
|
6637
|
-
onChange: (e) => setDraft(e.target.value),
|
|
6638
|
-
onBlur: commit,
|
|
6639
|
-
onKeyDown: (e) => {
|
|
6640
|
-
if (e.key === "Enter") commit();
|
|
6641
|
-
else if (e.key === "Escape") setEditing(null);
|
|
6642
|
-
},
|
|
6643
|
-
className: "h-full w-full bg-transparent text-foreground outline-none"
|
|
6644
|
-
}
|
|
6645
|
-
) : /* @__PURE__ */ jsx("span", { className: "truncate text-foreground", children: displayValue(rows[ri]?.[c.key] ?? "") })
|
|
6646
|
-
},
|
|
6647
|
-
`${disp}-${c.key}`
|
|
6648
|
-
);
|
|
6649
|
-
});
|
|
6650
|
-
})
|
|
6651
|
-
] }),
|
|
6764
|
+
contextMenu ? /* @__PURE__ */ jsx(ContextMenu, { items: ctxItems, children: gridInner }) : gridInner,
|
|
6652
6765
|
displayRowCount === 0 && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center text-foreground-muted", style: { top: headerHeight }, children: emptyState })
|
|
6653
6766
|
]
|
|
6654
6767
|
}
|
|
6655
6768
|
);
|
|
6656
6769
|
}
|
|
6770
|
+
var HILITE = "color-mix(in srgb, var(--color-accent) 9%, transparent)";
|
|
6771
|
+
var SELECT_BG = "color-mix(in srgb, var(--color-accent) 16%, transparent)";
|
|
6772
|
+
var SELECT_RING = "inset 0 0 0 2px var(--color-accent)";
|
|
6657
6773
|
function SortCaret({ dir }) {
|
|
6658
6774
|
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", className: "h-3 w-3 flex-shrink-0", fill: "none", stroke: "currentColor", strokeWidth: 1.75, "aria-hidden": "true", children: [
|
|
6659
6775
|
/* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 6.5 8 3.5l3 3", className: dir === "asc" ? "text-accent" : "text-foreground-muted opacity-50" }),
|
|
@@ -6676,8 +6792,44 @@ function columnLetter(i) {
|
|
|
6676
6792
|
} while (n >= 0);
|
|
6677
6793
|
return s;
|
|
6678
6794
|
}
|
|
6795
|
+
function makeColumns(existing, count) {
|
|
6796
|
+
const used = new Set(existing.map((c) => c.key));
|
|
6797
|
+
const out = [];
|
|
6798
|
+
let n = existing.length;
|
|
6799
|
+
for (let k = 0; k < count; k++) {
|
|
6800
|
+
let key = columnLetter(n);
|
|
6801
|
+
while (used.has(key)) {
|
|
6802
|
+
n++;
|
|
6803
|
+
key = columnLetter(n);
|
|
6804
|
+
}
|
|
6805
|
+
used.add(key);
|
|
6806
|
+
out.push({ key, label: key });
|
|
6807
|
+
n++;
|
|
6808
|
+
}
|
|
6809
|
+
return out;
|
|
6810
|
+
}
|
|
6679
6811
|
function blankSheet(name, cols = 8) {
|
|
6680
|
-
return { name, columns:
|
|
6812
|
+
return { name, columns: makeColumns([], cols), rows: [] };
|
|
6813
|
+
}
|
|
6814
|
+
function parseWorkbook(XLSX, bytes) {
|
|
6815
|
+
const wb = XLSX.read(bytes, { type: "array" });
|
|
6816
|
+
return wb.SheetNames.map((name) => {
|
|
6817
|
+
const ws = wb.Sheets[name];
|
|
6818
|
+
const aoa = XLSX.utils.sheet_to_json(ws, { header: 1, blankrows: false, defval: null });
|
|
6819
|
+
const headerRow = aoa[0] ?? [];
|
|
6820
|
+
const columns = headerRow.map((h, i) => ({
|
|
6821
|
+
key: h != null && String(h).length ? String(h) : `col_${i}`,
|
|
6822
|
+
label: h != null && String(h).length ? String(h) : `Column ${i + 1}`
|
|
6823
|
+
}));
|
|
6824
|
+
const rows = aoa.slice(1).map((r) => {
|
|
6825
|
+
const rec = {};
|
|
6826
|
+
columns.forEach((c, i) => {
|
|
6827
|
+
rec[c.key] = r[i] ?? null;
|
|
6828
|
+
});
|
|
6829
|
+
return rec;
|
|
6830
|
+
});
|
|
6831
|
+
return { name, columns, rows };
|
|
6832
|
+
});
|
|
6681
6833
|
}
|
|
6682
6834
|
function toPlainRows(sheet, columns) {
|
|
6683
6835
|
return sheet.rows.map((row) => {
|
|
@@ -6712,6 +6864,7 @@ function Spreadsheet({
|
|
|
6712
6864
|
virtualize = true,
|
|
6713
6865
|
sortable = true,
|
|
6714
6866
|
emptyRows = 50,
|
|
6867
|
+
emptyCols = 6,
|
|
6715
6868
|
allowAddSheet,
|
|
6716
6869
|
height = 480,
|
|
6717
6870
|
width,
|
|
@@ -6737,24 +6890,7 @@ function Spreadsheet({
|
|
|
6737
6890
|
try {
|
|
6738
6891
|
const bytes = await sourceToBytes(source, remote);
|
|
6739
6892
|
const XLSX = await loadXlsx();
|
|
6740
|
-
const
|
|
6741
|
-
const parsed = wb.SheetNames.map((name) => {
|
|
6742
|
-
const ws = wb.Sheets[name];
|
|
6743
|
-
const aoa = XLSX.utils.sheet_to_json(ws, { header: 1, blankrows: false, defval: null });
|
|
6744
|
-
const headerRow = aoa[0] ?? [];
|
|
6745
|
-
const columns2 = headerRow.map((h, i) => ({
|
|
6746
|
-
key: h != null && String(h).length ? String(h) : `col_${i}`,
|
|
6747
|
-
label: h != null && String(h).length ? String(h) : `Column ${i + 1}`
|
|
6748
|
-
}));
|
|
6749
|
-
const rows = aoa.slice(1).map((r) => {
|
|
6750
|
-
const rec = {};
|
|
6751
|
-
columns2.forEach((c, i) => {
|
|
6752
|
-
rec[c.key] = r[i] ?? null;
|
|
6753
|
-
});
|
|
6754
|
-
return rec;
|
|
6755
|
-
});
|
|
6756
|
-
return { name, columns: columns2, rows };
|
|
6757
|
-
});
|
|
6893
|
+
const parsed = parseWorkbook(XLSX, bytes);
|
|
6758
6894
|
if (cancelled) return;
|
|
6759
6895
|
setSheets(parsed);
|
|
6760
6896
|
setActive(0);
|
|
@@ -6811,6 +6947,83 @@ function Spreadsheet({
|
|
|
6811
6947
|
setActive((a) => Math.max(0, Math.min(index < a ? a - 1 : a, next.length - 1)));
|
|
6812
6948
|
onChange?.(next);
|
|
6813
6949
|
}, [sheets, onChange]);
|
|
6950
|
+
const add100Rows = useCallback(() => {
|
|
6951
|
+
setSheets((prev) => {
|
|
6952
|
+
if (!prev) return prev;
|
|
6953
|
+
const next = prev.map((s, i) => i === active ? { ...s, rows: [...s.rows, ...Array.from({ length: 100 }, () => ({}))] } : s);
|
|
6954
|
+
onChange?.(next);
|
|
6955
|
+
return next;
|
|
6956
|
+
});
|
|
6957
|
+
}, [active, onChange]);
|
|
6958
|
+
const add10Columns = useCallback(() => {
|
|
6959
|
+
setSheets((prev) => {
|
|
6960
|
+
if (!prev) return prev;
|
|
6961
|
+
const next = prev.map((s, i) => {
|
|
6962
|
+
if (i !== active) return s;
|
|
6963
|
+
const cols = toColumns(s.columns);
|
|
6964
|
+
return { ...s, columns: [...cols, ...makeColumns(cols, 10)] };
|
|
6965
|
+
});
|
|
6966
|
+
onChange?.(next);
|
|
6967
|
+
return next;
|
|
6968
|
+
});
|
|
6969
|
+
}, [active, onChange]);
|
|
6970
|
+
const insertColumn = useCallback((index) => {
|
|
6971
|
+
setSheets((prev) => {
|
|
6972
|
+
if (!prev) return prev;
|
|
6973
|
+
const next = prev.map((s, i) => {
|
|
6974
|
+
if (i !== active) return s;
|
|
6975
|
+
const cols = toColumns(s.columns);
|
|
6976
|
+
const at = Math.max(0, Math.min(index, cols.length));
|
|
6977
|
+
const out = [...cols];
|
|
6978
|
+
out.splice(at, 0, makeColumns(cols, 1)[0]);
|
|
6979
|
+
return { ...s, columns: out };
|
|
6980
|
+
});
|
|
6981
|
+
onChange?.(next);
|
|
6982
|
+
return next;
|
|
6983
|
+
});
|
|
6984
|
+
}, [active, onChange]);
|
|
6985
|
+
const insertRow = useCallback((index) => {
|
|
6986
|
+
setSheets((prev) => {
|
|
6987
|
+
if (!prev) return prev;
|
|
6988
|
+
const next = prev.map((s, i) => {
|
|
6989
|
+
if (i !== active) return s;
|
|
6990
|
+
const rows = [...s.rows];
|
|
6991
|
+
rows.splice(Math.max(0, Math.min(index, rows.length)), 0, {});
|
|
6992
|
+
return { ...s, rows };
|
|
6993
|
+
});
|
|
6994
|
+
onChange?.(next);
|
|
6995
|
+
return next;
|
|
6996
|
+
});
|
|
6997
|
+
}, [active, onChange]);
|
|
6998
|
+
const deleteRow = useCallback((index) => {
|
|
6999
|
+
setSheets((prev) => {
|
|
7000
|
+
if (!prev) return prev;
|
|
7001
|
+
const next = prev.map((s, i) => i === active && index < s.rows.length ? { ...s, rows: s.rows.filter((_, r) => r !== index) } : s);
|
|
7002
|
+
onChange?.(next);
|
|
7003
|
+
return next;
|
|
7004
|
+
});
|
|
7005
|
+
}, [active, onChange]);
|
|
7006
|
+
const fileInputRef = useRef(null);
|
|
7007
|
+
const openFile = () => fileInputRef.current?.click();
|
|
7008
|
+
const onFilePicked = useCallback(async (e) => {
|
|
7009
|
+
const file = e.target.files?.[0];
|
|
7010
|
+
e.target.value = "";
|
|
7011
|
+
if (!file) return;
|
|
7012
|
+
setStatus("loading");
|
|
7013
|
+
setError(null);
|
|
7014
|
+
try {
|
|
7015
|
+
const bytes = new Uint8Array(await file.arrayBuffer());
|
|
7016
|
+
const XLSX = await loadXlsx();
|
|
7017
|
+
const parsed = parseWorkbook(XLSX, bytes);
|
|
7018
|
+
setSheets(parsed);
|
|
7019
|
+
setActive(0);
|
|
7020
|
+
setStatus("ready");
|
|
7021
|
+
onChange?.(parsed);
|
|
7022
|
+
} catch (err) {
|
|
7023
|
+
setError(err);
|
|
7024
|
+
setStatus("error");
|
|
7025
|
+
}
|
|
7026
|
+
}, [onChange]);
|
|
6814
7027
|
const baseName = useMemo(
|
|
6815
7028
|
() => (fileName || (typeof source === "object" && "name" in source ? sourceName(source) : null) || "spreadsheet").replace(/\.[^.]+$/, ""),
|
|
6816
7029
|
[fileName, source]
|
|
@@ -6897,22 +7110,38 @@ function Spreadsheet({
|
|
|
6897
7110
|
}
|
|
6898
7111
|
const formats = exportFormats || [];
|
|
6899
7112
|
const exportLabels = {
|
|
6900
|
-
xlsx: "
|
|
6901
|
-
csv: "
|
|
6902
|
-
pdf: "
|
|
7113
|
+
xlsx: "Excel (.xlsx)",
|
|
7114
|
+
csv: "CSV (.csv)",
|
|
7115
|
+
pdf: "PDF (.pdf)"
|
|
6903
7116
|
};
|
|
6904
|
-
const fileItems =
|
|
7117
|
+
const fileItems = [
|
|
7118
|
+
{ key: "open", label: "Open", onSelect: openFile },
|
|
7119
|
+
...formats.length > 0 ? [{ key: "export", label: "Export", separatorBefore: true, children: formats.map((fmt) => ({ key: fmt, label: exportLabels[fmt], onSelect: () => runExport(fmt) })) }] : []
|
|
7120
|
+
];
|
|
6905
7121
|
const sheetItems = [
|
|
6906
|
-
{ key: "
|
|
6907
|
-
{ key: "delete", label: "Delete
|
|
7122
|
+
{ key: "add", label: "Add Sheet", onSelect: addSheet },
|
|
7123
|
+
{ key: "delete", label: "Delete Sheet", danger: true, disabled: sheets.length <= 1, onSelect: () => deleteSheet(active) },
|
|
7124
|
+
{ key: "add100", label: "Add 100 rows", separatorBefore: true, onSelect: add100Rows },
|
|
7125
|
+
{ key: "add10cols", label: "Add 10 columns", onSelect: add10Columns }
|
|
6908
7126
|
];
|
|
6909
|
-
const showMenuBar = fileItems.length > 0 || editable;
|
|
6910
7127
|
const canDeleteSheet = editable && sheets.length > 1;
|
|
6911
7128
|
return /* @__PURE__ */ jsxs("div", { className: cx("flex flex-col overflow-hidden rounded-lg border border-border bg-surface-raised", className), style: { height, width, ...style }, children: [
|
|
6912
|
-
|
|
6913
|
-
|
|
7129
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-shrink-0 items-center gap-0.5 border-b border-border bg-surface px-1.5 py-1", children: [
|
|
7130
|
+
/* @__PURE__ */ jsx(MenuButton, { label: "File", variant: "ghost", size: "sm", hideChevron: true, items: fileItems }),
|
|
6914
7131
|
editable && /* @__PURE__ */ jsx(MenuButton, { label: "Sheet", variant: "ghost", size: "sm", hideChevron: true, items: sheetItems })
|
|
6915
7132
|
] }),
|
|
7133
|
+
/* @__PURE__ */ jsx(
|
|
7134
|
+
"input",
|
|
7135
|
+
{
|
|
7136
|
+
ref: fileInputRef,
|
|
7137
|
+
type: "file",
|
|
7138
|
+
accept: ".xlsx,.xls,.csv,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
7139
|
+
onChange: onFilePicked,
|
|
7140
|
+
className: "hidden",
|
|
7141
|
+
"aria-hidden": "true",
|
|
7142
|
+
tabIndex: -1
|
|
7143
|
+
}
|
|
7144
|
+
),
|
|
6916
7145
|
/* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsx(
|
|
6917
7146
|
DataGrid,
|
|
6918
7147
|
{
|
|
@@ -6922,6 +7151,11 @@ function Spreadsheet({
|
|
|
6922
7151
|
sortable,
|
|
6923
7152
|
virtualize,
|
|
6924
7153
|
trailingRows: emptyRows,
|
|
7154
|
+
trailingCols: emptyCols,
|
|
7155
|
+
contextMenu: true,
|
|
7156
|
+
onInsertRow: editable ? insertRow : void 0,
|
|
7157
|
+
onDeleteRow: editable ? deleteRow : void 0,
|
|
7158
|
+
onInsertColumn: editable ? insertColumn : void 0,
|
|
6925
7159
|
onCellEdit: handleCellEdit,
|
|
6926
7160
|
height: "100%",
|
|
6927
7161
|
className: "!rounded-none !border-0"
|
|
@@ -7085,15 +7319,15 @@ function FlyoutItems({ items }) {
|
|
|
7085
7319
|
child.badge !== void 0 && child.badge > 0 && /* @__PURE__ */ jsx("span", { className: "flex h-4 min-w-4 items-center justify-center rounded-full bg-status-error px-1 text-[9px] font-bold leading-none text-white", children: child.badge > 99 ? "99+" : child.badge })
|
|
7086
7320
|
] });
|
|
7087
7321
|
if (child.items?.length) {
|
|
7088
|
-
return /* @__PURE__ */ jsxs(
|
|
7089
|
-
/* @__PURE__ */ jsxs(
|
|
7322
|
+
return /* @__PURE__ */ jsxs(DropdownMenu.Sub, { children: [
|
|
7323
|
+
/* @__PURE__ */ jsxs(DropdownMenu.SubTrigger, { className: cls, children: [
|
|
7090
7324
|
label,
|
|
7091
7325
|
/* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, "aria-hidden": "true", className: "h-3.5 w-3.5 flex-shrink-0", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "m9 6 6 6-6 6" }) })
|
|
7092
7326
|
] }),
|
|
7093
|
-
/* @__PURE__ */ jsx(
|
|
7327
|
+
/* @__PURE__ */ jsx(DropdownMenu.Portal, { children: /* @__PURE__ */ jsx(DropdownMenu.SubContent, { sideOffset: 6, collisionPadding: 8, className: FLYOUT_PANEL, children: /* @__PURE__ */ jsx(FlyoutItems, { items: child.items }) }) })
|
|
7094
7328
|
] }, child.key);
|
|
7095
7329
|
}
|
|
7096
|
-
return /* @__PURE__ */ jsx(
|
|
7330
|
+
return /* @__PURE__ */ jsx(DropdownMenu.Item, { onSelect: () => child.onClick?.(), className: cls, children: label }, child.key);
|
|
7097
7331
|
}) });
|
|
7098
7332
|
}
|
|
7099
7333
|
function CollapsedFlyout({ item, trigger }) {
|
|
@@ -7108,9 +7342,9 @@ function CollapsedFlyout({ item, trigger }) {
|
|
|
7108
7342
|
closeTimer.current = window.setTimeout(() => setOpen(false), 150);
|
|
7109
7343
|
};
|
|
7110
7344
|
React36.useEffect(() => () => window.clearTimeout(closeTimer.current), []);
|
|
7111
|
-
return /* @__PURE__ */ jsxs(
|
|
7345
|
+
return /* @__PURE__ */ jsxs(DropdownMenu.Root, { open, onOpenChange: setOpen, modal: false, children: [
|
|
7112
7346
|
/* @__PURE__ */ jsx(
|
|
7113
|
-
|
|
7347
|
+
DropdownMenu.Trigger,
|
|
7114
7348
|
{
|
|
7115
7349
|
asChild: true,
|
|
7116
7350
|
onPointerEnter: openNow,
|
|
@@ -7121,8 +7355,8 @@ function CollapsedFlyout({ item, trigger }) {
|
|
|
7121
7355
|
children: trigger
|
|
7122
7356
|
}
|
|
7123
7357
|
),
|
|
7124
|
-
/* @__PURE__ */ jsx(
|
|
7125
|
-
|
|
7358
|
+
/* @__PURE__ */ jsx(DropdownMenu.Portal, { children: /* @__PURE__ */ jsxs(
|
|
7359
|
+
DropdownMenu.Content,
|
|
7126
7360
|
{
|
|
7127
7361
|
side: "right",
|
|
7128
7362
|
align: "start",
|
|
@@ -7132,7 +7366,7 @@ function CollapsedFlyout({ item, trigger }) {
|
|
|
7132
7366
|
onPointerEnter: openNow,
|
|
7133
7367
|
onPointerLeave: closeSoon,
|
|
7134
7368
|
children: [
|
|
7135
|
-
/* @__PURE__ */ jsx(
|
|
7369
|
+
/* @__PURE__ */ jsx(DropdownMenu.Label, { className: "px-2.5 pb-1 pt-1.5 text-xs font-semibold text-foreground-muted", children: item.label }),
|
|
7136
7370
|
/* @__PURE__ */ jsx(FlyoutItems, { items: item.items })
|
|
7137
7371
|
]
|
|
7138
7372
|
}
|
|
@@ -9650,7 +9884,7 @@ function DatePicker({
|
|
|
9650
9884
|
},
|
|
9651
9885
|
"aria-label": view === "days" ? "Next month" : view === "months" ? "Next year" : "Next decade",
|
|
9652
9886
|
className: "w-7 h-7 inline-flex items-center justify-center rounded-md hover:bg-surface-raised focus:outline-none focus-visible:ring-2 focus-visible:ring-accent transition-colors",
|
|
9653
|
-
children: /* @__PURE__ */ jsx(
|
|
9887
|
+
children: /* @__PURE__ */ jsx(ChevronRight3, {})
|
|
9654
9888
|
}
|
|
9655
9889
|
)
|
|
9656
9890
|
] }),
|
|
@@ -9768,7 +10002,7 @@ function CalendarIcon() {
|
|
|
9768
10002
|
function ChevronLeft() {
|
|
9769
10003
|
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "w-4 h-4", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15 19l-7-7 7-7" }) });
|
|
9770
10004
|
}
|
|
9771
|
-
function
|
|
10005
|
+
function ChevronRight3() {
|
|
9772
10006
|
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "w-4 h-4", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) });
|
|
9773
10007
|
}
|
|
9774
10008
|
var LINE_HEIGHT_PX = 21;
|