@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.cjs
CHANGED
|
@@ -6,7 +6,7 @@ var React36 = require('react');
|
|
|
6
6
|
var reactDom = require('react-dom');
|
|
7
7
|
var jsxRuntime = require('react/jsx-runtime');
|
|
8
8
|
var AvatarPrimitive = require('@radix-ui/react-avatar');
|
|
9
|
-
var
|
|
9
|
+
var DropdownMenu = require('@radix-ui/react-dropdown-menu');
|
|
10
10
|
var Dialog = require('@radix-ui/react-dialog');
|
|
11
11
|
var framerMotion = require('framer-motion');
|
|
12
12
|
var TooltipPrimitive = require('@radix-ui/react-tooltip');
|
|
@@ -43,7 +43,7 @@ function _interopNamespace(e) {
|
|
|
43
43
|
|
|
44
44
|
var React36__default = /*#__PURE__*/_interopDefault(React36);
|
|
45
45
|
var AvatarPrimitive__namespace = /*#__PURE__*/_interopNamespace(AvatarPrimitive);
|
|
46
|
-
var
|
|
46
|
+
var DropdownMenu__namespace = /*#__PURE__*/_interopNamespace(DropdownMenu);
|
|
47
47
|
var Dialog__namespace = /*#__PURE__*/_interopNamespace(Dialog);
|
|
48
48
|
var TooltipPrimitive__namespace = /*#__PURE__*/_interopNamespace(TooltipPrimitive);
|
|
49
49
|
var TabsPrimitive__namespace = /*#__PURE__*/_interopNamespace(TabsPrimitive);
|
|
@@ -667,6 +667,39 @@ var Chevron = () => /* @__PURE__ */ jsxRuntime.jsx(
|
|
|
667
667
|
children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 9l6 6 6-6" })
|
|
668
668
|
}
|
|
669
669
|
);
|
|
670
|
+
var ChevronRight = () => /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) });
|
|
671
|
+
var ITEM_CLASS = [
|
|
672
|
+
"flex items-center gap-2.5 rounded-md px-2.5 py-1.5 text-sm outline-none cursor-pointer select-none transition-colors",
|
|
673
|
+
"data-[disabled]:opacity-50 data-[disabled]:pointer-events-none"
|
|
674
|
+
].join(" ");
|
|
675
|
+
var itemTone = (danger) => danger ? "text-status-error data-[highlighted]:bg-status-error/10" : "text-foreground data-[highlighted]:bg-surface-raised";
|
|
676
|
+
var Label = ({ item }) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
677
|
+
item.icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-4 w-4 flex-shrink-0 items-center justify-center", children: item.icon }),
|
|
678
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 truncate", children: item.label })
|
|
679
|
+
] });
|
|
680
|
+
function renderItem(item) {
|
|
681
|
+
if (item.children && item.children.length > 0) {
|
|
682
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenu__namespace.Sub, { children: [
|
|
683
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DropdownMenu__namespace.SubTrigger, { disabled: item.disabled, className: `${ITEM_CLASS} ${itemTone(item.danger)}`, children: [
|
|
684
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { item }),
|
|
685
|
+
/* @__PURE__ */ jsxRuntime.jsx(ChevronRight, {})
|
|
686
|
+
] }),
|
|
687
|
+
/* @__PURE__ */ jsxRuntime.jsx(DropdownMenu__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
688
|
+
DropdownMenu__namespace.SubContent,
|
|
689
|
+
{
|
|
690
|
+
sideOffset: 4,
|
|
691
|
+
collisionPadding: 8,
|
|
692
|
+
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",
|
|
693
|
+
children: item.children.map((sub) => /* @__PURE__ */ jsxRuntime.jsxs(React36__default.default.Fragment, { children: [
|
|
694
|
+
sub.separatorBefore && /* @__PURE__ */ jsxRuntime.jsx(DropdownMenu__namespace.Separator, { className: "my-1 h-px bg-border" }),
|
|
695
|
+
renderItem(sub)
|
|
696
|
+
] }, sub.key))
|
|
697
|
+
}
|
|
698
|
+
) })
|
|
699
|
+
] });
|
|
700
|
+
}
|
|
701
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DropdownMenu__namespace.Item, { disabled: item.disabled, onSelect: () => item.onSelect?.(), className: `${ITEM_CLASS} ${itemTone(item.danger)}`, children: /* @__PURE__ */ jsxRuntime.jsx(Label, { item }) });
|
|
702
|
+
}
|
|
670
703
|
function MenuButton({
|
|
671
704
|
label,
|
|
672
705
|
items,
|
|
@@ -679,8 +712,8 @@ function MenuButton({
|
|
|
679
712
|
side = "bottom",
|
|
680
713
|
className = ""
|
|
681
714
|
}) {
|
|
682
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
683
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
715
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenu__namespace.Root, { children: [
|
|
716
|
+
/* @__PURE__ */ jsxRuntime.jsx(DropdownMenu__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
684
717
|
Button_default,
|
|
685
718
|
{
|
|
686
719
|
variant,
|
|
@@ -694,8 +727,8 @@ function MenuButton({
|
|
|
694
727
|
] })
|
|
695
728
|
}
|
|
696
729
|
) }),
|
|
697
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
698
|
-
|
|
730
|
+
/* @__PURE__ */ jsxRuntime.jsx(DropdownMenu__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
731
|
+
DropdownMenu__namespace.Content,
|
|
699
732
|
{
|
|
700
733
|
align,
|
|
701
734
|
side,
|
|
@@ -707,23 +740,8 @@ function MenuButton({
|
|
|
707
740
|
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95"
|
|
708
741
|
].join(" "),
|
|
709
742
|
children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(React36__default.default.Fragment, { children: [
|
|
710
|
-
item.separatorBefore && /* @__PURE__ */ jsxRuntime.jsx(
|
|
711
|
-
|
|
712
|
-
DropdownMenu2__namespace.Item,
|
|
713
|
-
{
|
|
714
|
-
disabled: item.disabled,
|
|
715
|
-
onSelect: () => item.onSelect?.(),
|
|
716
|
-
className: [
|
|
717
|
-
"flex items-center gap-2.5 rounded-md px-2.5 py-1.5 text-sm outline-none cursor-pointer select-none transition-colors",
|
|
718
|
-
"data-[disabled]:opacity-50 data-[disabled]:pointer-events-none",
|
|
719
|
-
item.danger ? "text-status-error data-[highlighted]:bg-status-error/10" : "text-foreground data-[highlighted]:bg-surface-raised"
|
|
720
|
-
].join(" "),
|
|
721
|
-
children: [
|
|
722
|
-
item.icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-4 w-4 flex-shrink-0 items-center justify-center", children: item.icon }),
|
|
723
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 truncate", children: item.label })
|
|
724
|
-
]
|
|
725
|
-
}
|
|
726
|
-
)
|
|
743
|
+
item.separatorBefore && /* @__PURE__ */ jsxRuntime.jsx(DropdownMenu__namespace.Separator, { className: "my-1 h-px bg-border" }),
|
|
744
|
+
renderItem(item)
|
|
727
745
|
] }, item.key))
|
|
728
746
|
}
|
|
729
747
|
) })
|
|
@@ -1765,7 +1783,7 @@ function Stepper({
|
|
|
1765
1783
|
const vertical = orientation === "vertical" || forcedVertical;
|
|
1766
1784
|
const s = SIZES[size];
|
|
1767
1785
|
const stateOf = (i) => i < current ? "completed" : i === current ? status : "pending";
|
|
1768
|
-
const
|
|
1786
|
+
const Label3 = ({ step, state, align }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: align === "center" ? "mt-2 text-center" : "pt-0.5", children: [
|
|
1769
1787
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: `font-medium leading-tight ${s.title} ${state === "pending" ? "text-foreground-muted" : "text-foreground"}`, children: step.title }),
|
|
1770
1788
|
step.description && /* @__PURE__ */ jsxRuntime.jsx("div", { className: `mt-0.5 leading-snug text-foreground-muted ${s.desc}`, children: step.description })
|
|
1771
1789
|
] });
|
|
@@ -1780,7 +1798,7 @@ function Stepper({
|
|
|
1780
1798
|
stepButton(i, /* @__PURE__ */ jsxRuntime.jsx(Indicator, { state, index: i, step, sizeKey: size })),
|
|
1781
1799
|
!last && /* @__PURE__ */ jsxRuntime.jsx(VConnector, { filled: i < current, reduced })
|
|
1782
1800
|
] }),
|
|
1783
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: last ? "" : "pb-6", children: stepButton(i, /* @__PURE__ */ jsxRuntime.jsx(
|
|
1801
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: last ? "" : "pb-6", children: stepButton(i, /* @__PURE__ */ jsxRuntime.jsx(Label3, { step, state, align: "left" })) })
|
|
1784
1802
|
] }, step.key);
|
|
1785
1803
|
}) });
|
|
1786
1804
|
}
|
|
@@ -1799,7 +1817,7 @@ function Stepper({
|
|
|
1799
1817
|
) }),
|
|
1800
1818
|
stepButton(i, /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center", children: [
|
|
1801
1819
|
/* @__PURE__ */ jsxRuntime.jsx(Indicator, { state, index: i, step, sizeKey: size }),
|
|
1802
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1820
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label3, { step, state, align: "center" })
|
|
1803
1821
|
] }))
|
|
1804
1822
|
] }, step.key);
|
|
1805
1823
|
}) });
|
|
@@ -4965,7 +4983,7 @@ function ContextMenu({ items, children, className = "" }) {
|
|
|
4965
4983
|
{
|
|
4966
4984
|
className: `${CONTENT_CLASSNAME} ${className}`.trim(),
|
|
4967
4985
|
collisionPadding: 8,
|
|
4968
|
-
children: items.map((item) =>
|
|
4986
|
+
children: items.map((item) => renderItem2(item))
|
|
4969
4987
|
}
|
|
4970
4988
|
) })
|
|
4971
4989
|
] });
|
|
@@ -4987,7 +5005,7 @@ var ITEM_CLASSNAME = [
|
|
|
4987
5005
|
"data-[disabled]:opacity-40 data-[disabled]:cursor-not-allowed data-[disabled]:bg-transparent data-[disabled]:text-foreground-muted",
|
|
4988
5006
|
"focus:outline-none"
|
|
4989
5007
|
].join(" ");
|
|
4990
|
-
function
|
|
5008
|
+
function renderItem2(item) {
|
|
4991
5009
|
if (item.children && item.children.length > 0) {
|
|
4992
5010
|
return /* @__PURE__ */ jsxRuntime.jsxs(ContextMenuPrimitive__namespace.Sub, { children: [
|
|
4993
5011
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -4997,7 +5015,7 @@ function renderItem(item) {
|
|
|
4997
5015
|
className: ITEM_CLASSNAME,
|
|
4998
5016
|
children: [
|
|
4999
5017
|
/* @__PURE__ */ jsxRuntime.jsx(ContextMenuLabel, { icon: item.icon, value: item.value }),
|
|
5000
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5018
|
+
/* @__PURE__ */ jsxRuntime.jsx(ChevronRight2, {})
|
|
5001
5019
|
]
|
|
5002
5020
|
}
|
|
5003
5021
|
),
|
|
@@ -5008,7 +5026,7 @@ function renderItem(item) {
|
|
|
5008
5026
|
sideOffset: 2,
|
|
5009
5027
|
alignOffset: -4,
|
|
5010
5028
|
collisionPadding: 8,
|
|
5011
|
-
children: item.children.map((sub) =>
|
|
5029
|
+
children: item.children.map((sub) => renderItem2(sub))
|
|
5012
5030
|
}
|
|
5013
5031
|
) })
|
|
5014
5032
|
] }, item.key);
|
|
@@ -5030,7 +5048,7 @@ function ContextMenuLabel({ icon, value }) {
|
|
|
5030
5048
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: value })
|
|
5031
5049
|
] });
|
|
5032
5050
|
}
|
|
5033
|
-
function
|
|
5051
|
+
function ChevronRight2() {
|
|
5034
5052
|
return /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) });
|
|
5035
5053
|
}
|
|
5036
5054
|
function readDismissed(key) {
|
|
@@ -5883,7 +5901,7 @@ var useIsoLayoutEffect = typeof window !== "undefined" ? React36.useLayoutEffect
|
|
|
5883
5901
|
function VirtualList({
|
|
5884
5902
|
items,
|
|
5885
5903
|
rowHeight,
|
|
5886
|
-
renderItem:
|
|
5904
|
+
renderItem: renderItem3,
|
|
5887
5905
|
height = 400,
|
|
5888
5906
|
getKey,
|
|
5889
5907
|
overscan = 6,
|
|
@@ -5948,7 +5966,7 @@ function VirtualList({
|
|
|
5948
5966
|
role: "listitem",
|
|
5949
5967
|
style: { height: rowHeight },
|
|
5950
5968
|
className: "box-border",
|
|
5951
|
-
children:
|
|
5969
|
+
children: renderItem3(item, index)
|
|
5952
5970
|
},
|
|
5953
5971
|
getKey ? getKey(item, index) : index
|
|
5954
5972
|
);
|
|
@@ -6489,6 +6507,15 @@ function displayValue(v) {
|
|
|
6489
6507
|
if (v == null) return "";
|
|
6490
6508
|
return String(v);
|
|
6491
6509
|
}
|
|
6510
|
+
function colLetter(i) {
|
|
6511
|
+
let s = "";
|
|
6512
|
+
let n = i;
|
|
6513
|
+
do {
|
|
6514
|
+
s = String.fromCharCode(65 + n % 26) + s;
|
|
6515
|
+
n = Math.floor(n / 26) - 1;
|
|
6516
|
+
} while (n >= 0);
|
|
6517
|
+
return s;
|
|
6518
|
+
}
|
|
6492
6519
|
function DataGrid({
|
|
6493
6520
|
columns,
|
|
6494
6521
|
rows,
|
|
@@ -6504,7 +6531,12 @@ function DataGrid({
|
|
|
6504
6531
|
overscan = 4,
|
|
6505
6532
|
rowNumbers = true,
|
|
6506
6533
|
trailingRows = 0,
|
|
6534
|
+
trailingCols = 0,
|
|
6507
6535
|
onCellEdit,
|
|
6536
|
+
contextMenu = false,
|
|
6537
|
+
onInsertRow,
|
|
6538
|
+
onDeleteRow,
|
|
6539
|
+
onInsertColumn,
|
|
6508
6540
|
className = "",
|
|
6509
6541
|
style,
|
|
6510
6542
|
emptyState = "No data"
|
|
@@ -6512,14 +6544,25 @@ function DataGrid({
|
|
|
6512
6544
|
const scrollRef = React36.useRef(null);
|
|
6513
6545
|
const [scroll, setScroll] = React36.useState({ top: 0, left: 0 });
|
|
6514
6546
|
const [viewport, setViewport] = React36.useState({ w: 0, h: 0 });
|
|
6547
|
+
const [selected, setSelected] = React36.useState(null);
|
|
6548
|
+
const [hoveredRow, setHoveredRow] = React36.useState(null);
|
|
6549
|
+
const [ctxTarget, setCtxTarget] = React36.useState(null);
|
|
6515
6550
|
const [editing, setEditing] = React36.useState(null);
|
|
6516
6551
|
const [draft, setDraft] = React36.useState("");
|
|
6517
6552
|
const [internalSort, setInternalSort] = React36.useState(null);
|
|
6518
6553
|
const sort = sortProp !== void 0 ? sortProp : internalSort;
|
|
6519
6554
|
const gutter = rowNumbers ? GUTTER : 0;
|
|
6520
6555
|
const colSortable = (c) => c.sortable ?? sortable;
|
|
6556
|
+
const cols = React36.useMemo(() => {
|
|
6557
|
+
if (trailingCols <= 0) return columns;
|
|
6558
|
+
const extra = Array.from({ length: trailingCols }, (_, k) => {
|
|
6559
|
+
const idx = columns.length + k;
|
|
6560
|
+
return { key: `__c${idx}`, label: colLetter(idx), editable: false, sortable: false };
|
|
6561
|
+
});
|
|
6562
|
+
return [...columns, ...extra];
|
|
6563
|
+
}, [columns, trailingCols]);
|
|
6521
6564
|
const { widths, offsets, totalWidth } = React36.useMemo(() => {
|
|
6522
|
-
const widths2 =
|
|
6565
|
+
const widths2 = cols.map((c) => resolveWidth(c.width));
|
|
6523
6566
|
const offsets2 = [];
|
|
6524
6567
|
let acc = 0;
|
|
6525
6568
|
for (const w of widths2) {
|
|
@@ -6527,7 +6570,7 @@ function DataGrid({
|
|
|
6527
6570
|
acc += w;
|
|
6528
6571
|
}
|
|
6529
6572
|
return { widths: widths2, offsets: offsets2, totalWidth: acc };
|
|
6530
|
-
}, [
|
|
6573
|
+
}, [cols]);
|
|
6531
6574
|
const order = React36.useMemo(() => {
|
|
6532
6575
|
const idx = rows.map((_, i) => i);
|
|
6533
6576
|
if (!sort) return idx;
|
|
@@ -6556,140 +6599,213 @@ function DataGrid({
|
|
|
6556
6599
|
const rowStart = virtualize ? Math.max(0, Math.floor(scroll.top / rowHeight) - overscan) : 0;
|
|
6557
6600
|
const rowEnd = virtualize ? Math.min(displayRowCount, Math.ceil((scroll.top + bodyH) / rowHeight) + overscan) : displayRowCount;
|
|
6558
6601
|
let colStart = 0;
|
|
6559
|
-
let colEnd =
|
|
6602
|
+
let colEnd = cols.length;
|
|
6560
6603
|
if (virtualize && viewport.w) {
|
|
6561
6604
|
const viewLeft = scroll.left;
|
|
6562
6605
|
const viewRight = scroll.left + (viewport.w - gutter);
|
|
6563
6606
|
colStart = Math.max(0, offsets.findIndex((o, i) => o + widths[i] > viewLeft));
|
|
6564
6607
|
if (colStart < 0) colStart = 0;
|
|
6565
6608
|
colEnd = offsets.findIndex((o) => o > viewRight);
|
|
6566
|
-
colEnd = colEnd === -1 ?
|
|
6609
|
+
colEnd = colEnd === -1 ? cols.length : Math.min(cols.length, colEnd + 1);
|
|
6567
6610
|
colStart = Math.max(0, colStart - overscan);
|
|
6568
|
-
colEnd = Math.min(
|
|
6611
|
+
colEnd = Math.min(cols.length, colEnd + overscan);
|
|
6569
6612
|
}
|
|
6570
6613
|
const visibleRows = Array.from({ length: Math.max(0, rowEnd - rowStart) }, (_, i) => rowStart + i);
|
|
6571
6614
|
const visibleCols = Array.from({ length: Math.max(0, colEnd - colStart) }, (_, i) => colStart + i);
|
|
6572
6615
|
const commit = React36.useCallback(() => {
|
|
6573
6616
|
if (!editing) return;
|
|
6574
|
-
const col =
|
|
6617
|
+
const col = cols[editing.col];
|
|
6575
6618
|
const ri = editing.disp < rows.length ? order[editing.disp] : editing.disp;
|
|
6576
6619
|
onCellEdit?.({ row: ri, column: col.key, value: draft });
|
|
6577
6620
|
setEditing(null);
|
|
6578
|
-
}, [editing,
|
|
6621
|
+
}, [editing, cols, draft, onCellEdit, order, rows.length]);
|
|
6579
6622
|
const startEdit = (disp, col) => {
|
|
6580
|
-
const c =
|
|
6623
|
+
const c = cols[col];
|
|
6581
6624
|
if (!(c.editable ?? editable)) return;
|
|
6582
6625
|
setDraft(displayValue(rows[rowIndexForDisp(disp)]?.[c.key] ?? ""));
|
|
6583
6626
|
setEditing({ disp, col });
|
|
6584
6627
|
};
|
|
6628
|
+
const cellText = (sel) => displayValue(rows[rowIndexForDisp(sel.disp)]?.[cols[sel.col].key] ?? "");
|
|
6629
|
+
const copyCell = React36.useCallback(async () => {
|
|
6630
|
+
if (!selected) return;
|
|
6631
|
+
try {
|
|
6632
|
+
await navigator.clipboard?.writeText(cellText(selected));
|
|
6633
|
+
} catch {
|
|
6634
|
+
}
|
|
6635
|
+
}, [selected, rows, cols, order]);
|
|
6636
|
+
const cutCell = React36.useCallback(async () => {
|
|
6637
|
+
if (!selected) return;
|
|
6638
|
+
await copyCell();
|
|
6639
|
+
onCellEdit?.({ row: rowIndexForDisp(selected.disp), column: cols[selected.col].key, value: "" });
|
|
6640
|
+
}, [selected, copyCell, onCellEdit, cols, order, rows.length]);
|
|
6641
|
+
const pasteCell = React36.useCallback(async () => {
|
|
6642
|
+
if (!selected) return;
|
|
6643
|
+
let text = "";
|
|
6644
|
+
try {
|
|
6645
|
+
text = await navigator.clipboard?.readText() ?? "";
|
|
6646
|
+
} catch {
|
|
6647
|
+
return;
|
|
6648
|
+
}
|
|
6649
|
+
onCellEdit?.({ row: rowIndexForDisp(selected.disp), column: cols[selected.col].key, value: text });
|
|
6650
|
+
}, [selected, onCellEdit, cols, order, rows.length]);
|
|
6651
|
+
const ctxItems = React36.useMemo(() => {
|
|
6652
|
+
if (ctxTarget?.kind === "header") {
|
|
6653
|
+
const ci = ctxTarget.col;
|
|
6654
|
+
return [
|
|
6655
|
+
{ key: "left", value: "Add column to the left", disabled: !onInsertColumn, onClick: () => onInsertColumn?.(ci) },
|
|
6656
|
+
{ key: "right", value: "Add column to the right", disabled: !onInsertColumn, onClick: () => onInsertColumn?.(ci + 1) }
|
|
6657
|
+
];
|
|
6658
|
+
}
|
|
6659
|
+
if (ctxTarget?.kind === "row") {
|
|
6660
|
+
const ri = rowIndexForDisp(ctxTarget.disp);
|
|
6661
|
+
const isData = ctxTarget.disp < rows.length;
|
|
6662
|
+
return [
|
|
6663
|
+
{ key: "above", value: "Add row above", disabled: !onInsertRow, onClick: () => onInsertRow?.(ri) },
|
|
6664
|
+
{ key: "below", value: "Add row below", disabled: !onInsertRow, onClick: () => onInsertRow?.(Math.min(rows.length, ri + 1)) },
|
|
6665
|
+
{ key: "delete", value: "Delete row", disabled: !isData || !onDeleteRow, onClick: () => onDeleteRow?.(ri) }
|
|
6666
|
+
];
|
|
6667
|
+
}
|
|
6668
|
+
return [
|
|
6669
|
+
{ key: "copy", value: "Copy", onClick: () => void copyCell() },
|
|
6670
|
+
{ key: "cut", value: "Cut", disabled: !editable, onClick: () => void cutCell() },
|
|
6671
|
+
{ key: "paste", value: "Paste", disabled: !editable, onClick: () => void pasteCell() }
|
|
6672
|
+
];
|
|
6673
|
+
}, [ctxTarget, rows.length, editable, onInsertRow, onDeleteRow, onInsertColumn, copyCell, cutCell, pasteCell, order]);
|
|
6674
|
+
const rowHighlighted = (disp) => hoveredRow === disp || selected?.disp === disp;
|
|
6675
|
+
const gridInner = /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", width: gutter + totalWidth + END_PAD, height: headerHeight + totalHeight + END_PAD }, children: [
|
|
6676
|
+
rowNumbers && /* @__PURE__ */ jsxRuntime.jsx(
|
|
6677
|
+
"div",
|
|
6678
|
+
{
|
|
6679
|
+
className: "border-b border-r border-border bg-surface",
|
|
6680
|
+
style: { position: "absolute", top: scroll.top, left: scroll.left, width: gutter, height: headerHeight, zIndex: 3 }
|
|
6681
|
+
}
|
|
6682
|
+
),
|
|
6683
|
+
visibleCols.map((ci) => {
|
|
6684
|
+
const c = cols[ci];
|
|
6685
|
+
const sortDir = sort?.key === c.key ? sort.dir : null;
|
|
6686
|
+
const canSort = colSortable(c);
|
|
6687
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6688
|
+
"div",
|
|
6689
|
+
{
|
|
6690
|
+
role: "columnheader",
|
|
6691
|
+
"aria-sort": sortDir ? sortDir === "asc" ? "ascending" : "descending" : void 0,
|
|
6692
|
+
onClick: canSort ? () => toggleSort(c.key) : void 0,
|
|
6693
|
+
onContextMenu: contextMenu ? () => setCtxTarget({ kind: "header", col: ci }) : void 0,
|
|
6694
|
+
className: cx(
|
|
6695
|
+
"flex items-center gap-1 border-b border-r border-border bg-surface px-3 font-medium text-foreground-secondary",
|
|
6696
|
+
canSort && "cursor-pointer select-none hover:text-foreground"
|
|
6697
|
+
),
|
|
6698
|
+
style: {
|
|
6699
|
+
position: "absolute",
|
|
6700
|
+
top: scroll.top,
|
|
6701
|
+
left: gutter + offsets[ci],
|
|
6702
|
+
width: widths[ci],
|
|
6703
|
+
height: headerHeight,
|
|
6704
|
+
zIndex: 2,
|
|
6705
|
+
justifyContent: c.align === "right" ? "flex-end" : c.align === "center" ? "center" : "flex-start"
|
|
6706
|
+
},
|
|
6707
|
+
children: [
|
|
6708
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: c.label ?? c.key }),
|
|
6709
|
+
canSort && /* @__PURE__ */ jsxRuntime.jsx(SortCaret, { dir: sortDir })
|
|
6710
|
+
]
|
|
6711
|
+
},
|
|
6712
|
+
`h-${c.key}`
|
|
6713
|
+
);
|
|
6714
|
+
}),
|
|
6715
|
+
rowNumbers && visibleRows.map((disp) => {
|
|
6716
|
+
const hi = rowHighlighted(disp);
|
|
6717
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
6718
|
+
"div",
|
|
6719
|
+
{
|
|
6720
|
+
onMouseEnter: () => setHoveredRow(disp),
|
|
6721
|
+
onContextMenu: contextMenu ? () => setCtxTarget({ kind: "row", disp }) : void 0,
|
|
6722
|
+
className: cx(
|
|
6723
|
+
"flex items-center justify-center border-b border-r border-border bg-surface text-xs tabular-nums",
|
|
6724
|
+
hi ? "font-medium text-foreground" : "text-foreground-muted"
|
|
6725
|
+
),
|
|
6726
|
+
style: { position: "absolute", left: scroll.left, top: headerHeight + disp * rowHeight, width: gutter, height: rowHeight, zIndex: 1, backgroundColor: hi ? HILITE : void 0 },
|
|
6727
|
+
children: disp + 1
|
|
6728
|
+
},
|
|
6729
|
+
`g-${disp}`
|
|
6730
|
+
);
|
|
6731
|
+
}),
|
|
6732
|
+
visibleRows.map((disp) => {
|
|
6733
|
+
const ri = rowIndexForDisp(disp);
|
|
6734
|
+
const hi = rowHighlighted(disp);
|
|
6735
|
+
return visibleCols.map((ci) => {
|
|
6736
|
+
const c = cols[ci];
|
|
6737
|
+
const isEditing = editing?.disp === disp && editing?.col === ci;
|
|
6738
|
+
const isSelected = selected?.disp === disp && selected?.col === ci;
|
|
6739
|
+
const canEdit = c.editable ?? editable;
|
|
6740
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
6741
|
+
"div",
|
|
6742
|
+
{
|
|
6743
|
+
role: "gridcell",
|
|
6744
|
+
"aria-selected": isSelected || void 0,
|
|
6745
|
+
onClick: () => setSelected({ disp, col: ci }),
|
|
6746
|
+
onDoubleClick: () => startEdit(disp, ci),
|
|
6747
|
+
onMouseEnter: () => setHoveredRow(disp),
|
|
6748
|
+
onContextMenu: contextMenu ? () => {
|
|
6749
|
+
setSelected({ disp, col: ci });
|
|
6750
|
+
setCtxTarget({ kind: "cell", disp });
|
|
6751
|
+
} : void 0,
|
|
6752
|
+
className: cx(
|
|
6753
|
+
"flex items-center border-b border-r border-border px-3",
|
|
6754
|
+
disp % 2 ? "bg-surface-raised" : "bg-surface",
|
|
6755
|
+
canEdit ? "cursor-text" : "cursor-default"
|
|
6756
|
+
),
|
|
6757
|
+
style: {
|
|
6758
|
+
position: "absolute",
|
|
6759
|
+
top: headerHeight + disp * rowHeight,
|
|
6760
|
+
left: gutter + offsets[ci],
|
|
6761
|
+
width: widths[ci],
|
|
6762
|
+
height: rowHeight,
|
|
6763
|
+
zIndex: isSelected ? 1 : void 0,
|
|
6764
|
+
backgroundColor: isSelected ? SELECT_BG : hi ? HILITE : void 0,
|
|
6765
|
+
boxShadow: isSelected ? SELECT_RING : void 0,
|
|
6766
|
+
justifyContent: c.align === "right" ? "flex-end" : c.align === "center" ? "center" : "flex-start"
|
|
6767
|
+
},
|
|
6768
|
+
children: isEditing ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
6769
|
+
"input",
|
|
6770
|
+
{
|
|
6771
|
+
autoFocus: true,
|
|
6772
|
+
value: draft,
|
|
6773
|
+
onChange: (e) => setDraft(e.target.value),
|
|
6774
|
+
onBlur: commit,
|
|
6775
|
+
onKeyDown: (e) => {
|
|
6776
|
+
if (e.key === "Enter") commit();
|
|
6777
|
+
else if (e.key === "Escape") setEditing(null);
|
|
6778
|
+
},
|
|
6779
|
+
className: "h-full w-full bg-transparent text-foreground outline-none"
|
|
6780
|
+
}
|
|
6781
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-foreground", children: displayValue(rows[ri]?.[c.key] ?? "") })
|
|
6782
|
+
},
|
|
6783
|
+
`${disp}-${c.key}`
|
|
6784
|
+
);
|
|
6785
|
+
});
|
|
6786
|
+
})
|
|
6787
|
+
] });
|
|
6585
6788
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6586
6789
|
"div",
|
|
6587
6790
|
{
|
|
6588
6791
|
ref: scrollRef,
|
|
6589
6792
|
onScroll: (e) => setScroll({ top: e.currentTarget.scrollTop, left: e.currentTarget.scrollLeft }),
|
|
6793
|
+
onMouseLeave: () => setHoveredRow(null),
|
|
6590
6794
|
className: cx("relative overflow-auto rounded-lg border border-border bg-surface-raised text-sm", className),
|
|
6591
6795
|
style: { height, width, ...style },
|
|
6592
6796
|
role: "grid",
|
|
6593
6797
|
"aria-rowcount": rows.length,
|
|
6594
6798
|
"aria-colcount": columns.length,
|
|
6595
6799
|
children: [
|
|
6596
|
-
/* @__PURE__ */ jsxRuntime.
|
|
6597
|
-
rowNumbers && /* @__PURE__ */ jsxRuntime.jsx(
|
|
6598
|
-
"div",
|
|
6599
|
-
{
|
|
6600
|
-
className: "border-b border-r border-border bg-surface",
|
|
6601
|
-
style: { position: "absolute", top: scroll.top, left: scroll.left, width: gutter, height: headerHeight, zIndex: 3 }
|
|
6602
|
-
}
|
|
6603
|
-
),
|
|
6604
|
-
visibleCols.map((ci) => {
|
|
6605
|
-
const c = columns[ci];
|
|
6606
|
-
const sortDir = sort?.key === c.key ? sort.dir : null;
|
|
6607
|
-
const canSort = colSortable(c);
|
|
6608
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6609
|
-
"div",
|
|
6610
|
-
{
|
|
6611
|
-
role: "columnheader",
|
|
6612
|
-
"aria-sort": sortDir ? sortDir === "asc" ? "ascending" : "descending" : void 0,
|
|
6613
|
-
onClick: canSort ? () => toggleSort(c.key) : void 0,
|
|
6614
|
-
className: cx(
|
|
6615
|
-
"flex items-center gap-1 border-b border-r border-border bg-surface px-3 font-medium text-foreground-secondary",
|
|
6616
|
-
canSort && "cursor-pointer select-none hover:text-foreground"
|
|
6617
|
-
),
|
|
6618
|
-
style: {
|
|
6619
|
-
position: "absolute",
|
|
6620
|
-
top: scroll.top,
|
|
6621
|
-
left: gutter + offsets[ci],
|
|
6622
|
-
width: widths[ci],
|
|
6623
|
-
height: headerHeight,
|
|
6624
|
-
zIndex: 2,
|
|
6625
|
-
justifyContent: c.align === "right" ? "flex-end" : c.align === "center" ? "center" : "flex-start"
|
|
6626
|
-
},
|
|
6627
|
-
children: [
|
|
6628
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: c.label ?? c.key }),
|
|
6629
|
-
canSort && /* @__PURE__ */ jsxRuntime.jsx(SortCaret, { dir: sortDir })
|
|
6630
|
-
]
|
|
6631
|
-
},
|
|
6632
|
-
`h-${c.key}`
|
|
6633
|
-
);
|
|
6634
|
-
}),
|
|
6635
|
-
rowNumbers && visibleRows.map((disp) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
6636
|
-
"div",
|
|
6637
|
-
{
|
|
6638
|
-
className: "flex items-center justify-center border-b border-r border-border bg-surface text-xs tabular-nums text-foreground-muted",
|
|
6639
|
-
style: { position: "absolute", left: scroll.left, top: headerHeight + disp * rowHeight, width: gutter, height: rowHeight, zIndex: 1 },
|
|
6640
|
-
children: disp + 1
|
|
6641
|
-
},
|
|
6642
|
-
`g-${disp}`
|
|
6643
|
-
)),
|
|
6644
|
-
visibleRows.map((disp) => {
|
|
6645
|
-
const ri = rowIndexForDisp(disp);
|
|
6646
|
-
return visibleCols.map((ci) => {
|
|
6647
|
-
const c = columns[ci];
|
|
6648
|
-
const isEditing = editing?.disp === disp && editing?.col === ci;
|
|
6649
|
-
const canEdit = c.editable ?? editable;
|
|
6650
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
6651
|
-
"div",
|
|
6652
|
-
{
|
|
6653
|
-
role: "gridcell",
|
|
6654
|
-
onDoubleClick: () => startEdit(disp, ci),
|
|
6655
|
-
className: cx(
|
|
6656
|
-
"flex items-center border-b border-r border-border px-3",
|
|
6657
|
-
disp % 2 ? "bg-surface-raised" : "bg-surface",
|
|
6658
|
-
canEdit && "cursor-text"
|
|
6659
|
-
),
|
|
6660
|
-
style: {
|
|
6661
|
-
position: "absolute",
|
|
6662
|
-
top: headerHeight + disp * rowHeight,
|
|
6663
|
-
left: gutter + offsets[ci],
|
|
6664
|
-
width: widths[ci],
|
|
6665
|
-
height: rowHeight,
|
|
6666
|
-
justifyContent: c.align === "right" ? "flex-end" : c.align === "center" ? "center" : "flex-start"
|
|
6667
|
-
},
|
|
6668
|
-
children: isEditing ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
6669
|
-
"input",
|
|
6670
|
-
{
|
|
6671
|
-
autoFocus: true,
|
|
6672
|
-
value: draft,
|
|
6673
|
-
onChange: (e) => setDraft(e.target.value),
|
|
6674
|
-
onBlur: commit,
|
|
6675
|
-
onKeyDown: (e) => {
|
|
6676
|
-
if (e.key === "Enter") commit();
|
|
6677
|
-
else if (e.key === "Escape") setEditing(null);
|
|
6678
|
-
},
|
|
6679
|
-
className: "h-full w-full bg-transparent text-foreground outline-none"
|
|
6680
|
-
}
|
|
6681
|
-
) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-foreground", children: displayValue(rows[ri]?.[c.key] ?? "") })
|
|
6682
|
-
},
|
|
6683
|
-
`${disp}-${c.key}`
|
|
6684
|
-
);
|
|
6685
|
-
});
|
|
6686
|
-
})
|
|
6687
|
-
] }),
|
|
6800
|
+
contextMenu ? /* @__PURE__ */ jsxRuntime.jsx(ContextMenu, { items: ctxItems, children: gridInner }) : gridInner,
|
|
6688
6801
|
displayRowCount === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center text-foreground-muted", style: { top: headerHeight }, children: emptyState })
|
|
6689
6802
|
]
|
|
6690
6803
|
}
|
|
6691
6804
|
);
|
|
6692
6805
|
}
|
|
6806
|
+
var HILITE = "color-mix(in srgb, var(--color-accent) 9%, transparent)";
|
|
6807
|
+
var SELECT_BG = "color-mix(in srgb, var(--color-accent) 16%, transparent)";
|
|
6808
|
+
var SELECT_RING = "inset 0 0 0 2px var(--color-accent)";
|
|
6693
6809
|
function SortCaret({ dir }) {
|
|
6694
6810
|
return /* @__PURE__ */ jsxRuntime.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: [
|
|
6695
6811
|
/* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 6.5 8 3.5l3 3", className: dir === "asc" ? "text-accent" : "text-foreground-muted opacity-50" }),
|
|
@@ -6712,8 +6828,44 @@ function columnLetter(i) {
|
|
|
6712
6828
|
} while (n >= 0);
|
|
6713
6829
|
return s;
|
|
6714
6830
|
}
|
|
6831
|
+
function makeColumns(existing, count) {
|
|
6832
|
+
const used = new Set(existing.map((c) => c.key));
|
|
6833
|
+
const out = [];
|
|
6834
|
+
let n = existing.length;
|
|
6835
|
+
for (let k = 0; k < count; k++) {
|
|
6836
|
+
let key = columnLetter(n);
|
|
6837
|
+
while (used.has(key)) {
|
|
6838
|
+
n++;
|
|
6839
|
+
key = columnLetter(n);
|
|
6840
|
+
}
|
|
6841
|
+
used.add(key);
|
|
6842
|
+
out.push({ key, label: key });
|
|
6843
|
+
n++;
|
|
6844
|
+
}
|
|
6845
|
+
return out;
|
|
6846
|
+
}
|
|
6715
6847
|
function blankSheet(name, cols = 8) {
|
|
6716
|
-
return { name, columns:
|
|
6848
|
+
return { name, columns: makeColumns([], cols), rows: [] };
|
|
6849
|
+
}
|
|
6850
|
+
function parseWorkbook(XLSX, bytes) {
|
|
6851
|
+
const wb = XLSX.read(bytes, { type: "array" });
|
|
6852
|
+
return wb.SheetNames.map((name) => {
|
|
6853
|
+
const ws = wb.Sheets[name];
|
|
6854
|
+
const aoa = XLSX.utils.sheet_to_json(ws, { header: 1, blankrows: false, defval: null });
|
|
6855
|
+
const headerRow = aoa[0] ?? [];
|
|
6856
|
+
const columns = headerRow.map((h, i) => ({
|
|
6857
|
+
key: h != null && String(h).length ? String(h) : `col_${i}`,
|
|
6858
|
+
label: h != null && String(h).length ? String(h) : `Column ${i + 1}`
|
|
6859
|
+
}));
|
|
6860
|
+
const rows = aoa.slice(1).map((r) => {
|
|
6861
|
+
const rec = {};
|
|
6862
|
+
columns.forEach((c, i) => {
|
|
6863
|
+
rec[c.key] = r[i] ?? null;
|
|
6864
|
+
});
|
|
6865
|
+
return rec;
|
|
6866
|
+
});
|
|
6867
|
+
return { name, columns, rows };
|
|
6868
|
+
});
|
|
6717
6869
|
}
|
|
6718
6870
|
function toPlainRows(sheet, columns) {
|
|
6719
6871
|
return sheet.rows.map((row) => {
|
|
@@ -6748,6 +6900,7 @@ function Spreadsheet({
|
|
|
6748
6900
|
virtualize = true,
|
|
6749
6901
|
sortable = true,
|
|
6750
6902
|
emptyRows = 50,
|
|
6903
|
+
emptyCols = 6,
|
|
6751
6904
|
allowAddSheet,
|
|
6752
6905
|
height = 480,
|
|
6753
6906
|
width,
|
|
@@ -6773,24 +6926,7 @@ function Spreadsheet({
|
|
|
6773
6926
|
try {
|
|
6774
6927
|
const bytes = await sourceToBytes(source, remote);
|
|
6775
6928
|
const XLSX = await loadXlsx();
|
|
6776
|
-
const
|
|
6777
|
-
const parsed = wb.SheetNames.map((name) => {
|
|
6778
|
-
const ws = wb.Sheets[name];
|
|
6779
|
-
const aoa = XLSX.utils.sheet_to_json(ws, { header: 1, blankrows: false, defval: null });
|
|
6780
|
-
const headerRow = aoa[0] ?? [];
|
|
6781
|
-
const columns2 = headerRow.map((h, i) => ({
|
|
6782
|
-
key: h != null && String(h).length ? String(h) : `col_${i}`,
|
|
6783
|
-
label: h != null && String(h).length ? String(h) : `Column ${i + 1}`
|
|
6784
|
-
}));
|
|
6785
|
-
const rows = aoa.slice(1).map((r) => {
|
|
6786
|
-
const rec = {};
|
|
6787
|
-
columns2.forEach((c, i) => {
|
|
6788
|
-
rec[c.key] = r[i] ?? null;
|
|
6789
|
-
});
|
|
6790
|
-
return rec;
|
|
6791
|
-
});
|
|
6792
|
-
return { name, columns: columns2, rows };
|
|
6793
|
-
});
|
|
6929
|
+
const parsed = parseWorkbook(XLSX, bytes);
|
|
6794
6930
|
if (cancelled) return;
|
|
6795
6931
|
setSheets(parsed);
|
|
6796
6932
|
setActive(0);
|
|
@@ -6847,6 +6983,83 @@ function Spreadsheet({
|
|
|
6847
6983
|
setActive((a) => Math.max(0, Math.min(index < a ? a - 1 : a, next.length - 1)));
|
|
6848
6984
|
onChange?.(next);
|
|
6849
6985
|
}, [sheets, onChange]);
|
|
6986
|
+
const add100Rows = React36.useCallback(() => {
|
|
6987
|
+
setSheets((prev) => {
|
|
6988
|
+
if (!prev) return prev;
|
|
6989
|
+
const next = prev.map((s, i) => i === active ? { ...s, rows: [...s.rows, ...Array.from({ length: 100 }, () => ({}))] } : s);
|
|
6990
|
+
onChange?.(next);
|
|
6991
|
+
return next;
|
|
6992
|
+
});
|
|
6993
|
+
}, [active, onChange]);
|
|
6994
|
+
const add10Columns = React36.useCallback(() => {
|
|
6995
|
+
setSheets((prev) => {
|
|
6996
|
+
if (!prev) return prev;
|
|
6997
|
+
const next = prev.map((s, i) => {
|
|
6998
|
+
if (i !== active) return s;
|
|
6999
|
+
const cols = toColumns(s.columns);
|
|
7000
|
+
return { ...s, columns: [...cols, ...makeColumns(cols, 10)] };
|
|
7001
|
+
});
|
|
7002
|
+
onChange?.(next);
|
|
7003
|
+
return next;
|
|
7004
|
+
});
|
|
7005
|
+
}, [active, onChange]);
|
|
7006
|
+
const insertColumn = React36.useCallback((index) => {
|
|
7007
|
+
setSheets((prev) => {
|
|
7008
|
+
if (!prev) return prev;
|
|
7009
|
+
const next = prev.map((s, i) => {
|
|
7010
|
+
if (i !== active) return s;
|
|
7011
|
+
const cols = toColumns(s.columns);
|
|
7012
|
+
const at = Math.max(0, Math.min(index, cols.length));
|
|
7013
|
+
const out = [...cols];
|
|
7014
|
+
out.splice(at, 0, makeColumns(cols, 1)[0]);
|
|
7015
|
+
return { ...s, columns: out };
|
|
7016
|
+
});
|
|
7017
|
+
onChange?.(next);
|
|
7018
|
+
return next;
|
|
7019
|
+
});
|
|
7020
|
+
}, [active, onChange]);
|
|
7021
|
+
const insertRow = React36.useCallback((index) => {
|
|
7022
|
+
setSheets((prev) => {
|
|
7023
|
+
if (!prev) return prev;
|
|
7024
|
+
const next = prev.map((s, i) => {
|
|
7025
|
+
if (i !== active) return s;
|
|
7026
|
+
const rows = [...s.rows];
|
|
7027
|
+
rows.splice(Math.max(0, Math.min(index, rows.length)), 0, {});
|
|
7028
|
+
return { ...s, rows };
|
|
7029
|
+
});
|
|
7030
|
+
onChange?.(next);
|
|
7031
|
+
return next;
|
|
7032
|
+
});
|
|
7033
|
+
}, [active, onChange]);
|
|
7034
|
+
const deleteRow = React36.useCallback((index) => {
|
|
7035
|
+
setSheets((prev) => {
|
|
7036
|
+
if (!prev) return prev;
|
|
7037
|
+
const next = prev.map((s, i) => i === active && index < s.rows.length ? { ...s, rows: s.rows.filter((_, r) => r !== index) } : s);
|
|
7038
|
+
onChange?.(next);
|
|
7039
|
+
return next;
|
|
7040
|
+
});
|
|
7041
|
+
}, [active, onChange]);
|
|
7042
|
+
const fileInputRef = React36.useRef(null);
|
|
7043
|
+
const openFile = () => fileInputRef.current?.click();
|
|
7044
|
+
const onFilePicked = React36.useCallback(async (e) => {
|
|
7045
|
+
const file = e.target.files?.[0];
|
|
7046
|
+
e.target.value = "";
|
|
7047
|
+
if (!file) return;
|
|
7048
|
+
setStatus("loading");
|
|
7049
|
+
setError(null);
|
|
7050
|
+
try {
|
|
7051
|
+
const bytes = new Uint8Array(await file.arrayBuffer());
|
|
7052
|
+
const XLSX = await loadXlsx();
|
|
7053
|
+
const parsed = parseWorkbook(XLSX, bytes);
|
|
7054
|
+
setSheets(parsed);
|
|
7055
|
+
setActive(0);
|
|
7056
|
+
setStatus("ready");
|
|
7057
|
+
onChange?.(parsed);
|
|
7058
|
+
} catch (err) {
|
|
7059
|
+
setError(err);
|
|
7060
|
+
setStatus("error");
|
|
7061
|
+
}
|
|
7062
|
+
}, [onChange]);
|
|
6850
7063
|
const baseName = React36.useMemo(
|
|
6851
7064
|
() => (fileName || (typeof source === "object" && "name" in source ? sourceName(source) : null) || "spreadsheet").replace(/\.[^.]+$/, ""),
|
|
6852
7065
|
[fileName, source]
|
|
@@ -6933,22 +7146,38 @@ function Spreadsheet({
|
|
|
6933
7146
|
}
|
|
6934
7147
|
const formats = exportFormats || [];
|
|
6935
7148
|
const exportLabels = {
|
|
6936
|
-
xlsx: "
|
|
6937
|
-
csv: "
|
|
6938
|
-
pdf: "
|
|
7149
|
+
xlsx: "Excel (.xlsx)",
|
|
7150
|
+
csv: "CSV (.csv)",
|
|
7151
|
+
pdf: "PDF (.pdf)"
|
|
6939
7152
|
};
|
|
6940
|
-
const fileItems =
|
|
7153
|
+
const fileItems = [
|
|
7154
|
+
{ key: "open", label: "Open", onSelect: openFile },
|
|
7155
|
+
...formats.length > 0 ? [{ key: "export", label: "Export", separatorBefore: true, children: formats.map((fmt) => ({ key: fmt, label: exportLabels[fmt], onSelect: () => runExport(fmt) })) }] : []
|
|
7156
|
+
];
|
|
6941
7157
|
const sheetItems = [
|
|
6942
|
-
{ key: "
|
|
6943
|
-
{ key: "delete", label: "Delete
|
|
7158
|
+
{ key: "add", label: "Add Sheet", onSelect: addSheet },
|
|
7159
|
+
{ key: "delete", label: "Delete Sheet", danger: true, disabled: sheets.length <= 1, onSelect: () => deleteSheet(active) },
|
|
7160
|
+
{ key: "add100", label: "Add 100 rows", separatorBefore: true, onSelect: add100Rows },
|
|
7161
|
+
{ key: "add10cols", label: "Add 10 columns", onSelect: add10Columns }
|
|
6944
7162
|
];
|
|
6945
|
-
const showMenuBar = fileItems.length > 0 || editable;
|
|
6946
7163
|
const canDeleteSheet = editable && sheets.length > 1;
|
|
6947
7164
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cx("flex flex-col overflow-hidden rounded-lg border border-border bg-surface-raised", className), style: { height, width, ...style }, children: [
|
|
6948
|
-
|
|
6949
|
-
|
|
7165
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-shrink-0 items-center gap-0.5 border-b border-border bg-surface px-1.5 py-1", children: [
|
|
7166
|
+
/* @__PURE__ */ jsxRuntime.jsx(MenuButton, { label: "File", variant: "ghost", size: "sm", hideChevron: true, items: fileItems }),
|
|
6950
7167
|
editable && /* @__PURE__ */ jsxRuntime.jsx(MenuButton, { label: "Sheet", variant: "ghost", size: "sm", hideChevron: true, items: sheetItems })
|
|
6951
7168
|
] }),
|
|
7169
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7170
|
+
"input",
|
|
7171
|
+
{
|
|
7172
|
+
ref: fileInputRef,
|
|
7173
|
+
type: "file",
|
|
7174
|
+
accept: ".xlsx,.xls,.csv,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
7175
|
+
onChange: onFilePicked,
|
|
7176
|
+
className: "hidden",
|
|
7177
|
+
"aria-hidden": "true",
|
|
7178
|
+
tabIndex: -1
|
|
7179
|
+
}
|
|
7180
|
+
),
|
|
6952
7181
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6953
7182
|
DataGrid,
|
|
6954
7183
|
{
|
|
@@ -6958,6 +7187,11 @@ function Spreadsheet({
|
|
|
6958
7187
|
sortable,
|
|
6959
7188
|
virtualize,
|
|
6960
7189
|
trailingRows: emptyRows,
|
|
7190
|
+
trailingCols: emptyCols,
|
|
7191
|
+
contextMenu: true,
|
|
7192
|
+
onInsertRow: editable ? insertRow : void 0,
|
|
7193
|
+
onDeleteRow: editable ? deleteRow : void 0,
|
|
7194
|
+
onInsertColumn: editable ? insertColumn : void 0,
|
|
6961
7195
|
onCellEdit: handleCellEdit,
|
|
6962
7196
|
height: "100%",
|
|
6963
7197
|
className: "!rounded-none !border-0"
|
|
@@ -7121,15 +7355,15 @@ function FlyoutItems({ items }) {
|
|
|
7121
7355
|
child.badge !== void 0 && child.badge > 0 && /* @__PURE__ */ jsxRuntime.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 })
|
|
7122
7356
|
] });
|
|
7123
7357
|
if (child.items?.length) {
|
|
7124
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7125
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
7358
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenu__namespace.Sub, { children: [
|
|
7359
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DropdownMenu__namespace.SubTrigger, { className: cls, children: [
|
|
7126
7360
|
label,
|
|
7127
7361
|
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "m9 6 6 6-6 6" }) })
|
|
7128
7362
|
] }),
|
|
7129
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7363
|
+
/* @__PURE__ */ jsxRuntime.jsx(DropdownMenu__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(DropdownMenu__namespace.SubContent, { sideOffset: 6, collisionPadding: 8, className: FLYOUT_PANEL, children: /* @__PURE__ */ jsxRuntime.jsx(FlyoutItems, { items: child.items }) }) })
|
|
7130
7364
|
] }, child.key);
|
|
7131
7365
|
}
|
|
7132
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7366
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DropdownMenu__namespace.Item, { onSelect: () => child.onClick?.(), className: cls, children: label }, child.key);
|
|
7133
7367
|
}) });
|
|
7134
7368
|
}
|
|
7135
7369
|
function CollapsedFlyout({ item, trigger }) {
|
|
@@ -7144,9 +7378,9 @@ function CollapsedFlyout({ item, trigger }) {
|
|
|
7144
7378
|
closeTimer.current = window.setTimeout(() => setOpen(false), 150);
|
|
7145
7379
|
};
|
|
7146
7380
|
React36__default.default.useEffect(() => () => window.clearTimeout(closeTimer.current), []);
|
|
7147
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7381
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenu__namespace.Root, { open, onOpenChange: setOpen, modal: false, children: [
|
|
7148
7382
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7149
|
-
|
|
7383
|
+
DropdownMenu__namespace.Trigger,
|
|
7150
7384
|
{
|
|
7151
7385
|
asChild: true,
|
|
7152
7386
|
onPointerEnter: openNow,
|
|
@@ -7157,8 +7391,8 @@ function CollapsedFlyout({ item, trigger }) {
|
|
|
7157
7391
|
children: trigger
|
|
7158
7392
|
}
|
|
7159
7393
|
),
|
|
7160
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7161
|
-
|
|
7394
|
+
/* @__PURE__ */ jsxRuntime.jsx(DropdownMenu__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7395
|
+
DropdownMenu__namespace.Content,
|
|
7162
7396
|
{
|
|
7163
7397
|
side: "right",
|
|
7164
7398
|
align: "start",
|
|
@@ -7168,7 +7402,7 @@ function CollapsedFlyout({ item, trigger }) {
|
|
|
7168
7402
|
onPointerEnter: openNow,
|
|
7169
7403
|
onPointerLeave: closeSoon,
|
|
7170
7404
|
children: [
|
|
7171
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7405
|
+
/* @__PURE__ */ jsxRuntime.jsx(DropdownMenu__namespace.Label, { className: "px-2.5 pb-1 pt-1.5 text-xs font-semibold text-foreground-muted", children: item.label }),
|
|
7172
7406
|
/* @__PURE__ */ jsxRuntime.jsx(FlyoutItems, { items: item.items })
|
|
7173
7407
|
]
|
|
7174
7408
|
}
|
|
@@ -9686,7 +9920,7 @@ function DatePicker({
|
|
|
9686
9920
|
},
|
|
9687
9921
|
"aria-label": view === "days" ? "Next month" : view === "months" ? "Next year" : "Next decade",
|
|
9688
9922
|
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",
|
|
9689
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
9923
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(ChevronRight3, {})
|
|
9690
9924
|
}
|
|
9691
9925
|
)
|
|
9692
9926
|
] }),
|
|
@@ -9804,7 +10038,7 @@ function CalendarIcon() {
|
|
|
9804
10038
|
function ChevronLeft() {
|
|
9805
10039
|
return /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "w-4 h-4", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15 19l-7-7 7-7" }) });
|
|
9806
10040
|
}
|
|
9807
|
-
function
|
|
10041
|
+
function ChevronRight3() {
|
|
9808
10042
|
return /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "w-4 h-4", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) });
|
|
9809
10043
|
}
|
|
9810
10044
|
var LINE_HEIGHT_PX = 21;
|