@geomak/ui 7.13.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 +93 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -3
- package/dist/index.d.ts +9 -3
- package/dist/index.js +93 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -6507,6 +6507,15 @@ function displayValue(v) {
|
|
|
6507
6507
|
if (v == null) return "";
|
|
6508
6508
|
return String(v);
|
|
6509
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
|
+
}
|
|
6510
6519
|
function DataGrid({
|
|
6511
6520
|
columns,
|
|
6512
6521
|
rows,
|
|
@@ -6522,10 +6531,12 @@ function DataGrid({
|
|
|
6522
6531
|
overscan = 4,
|
|
6523
6532
|
rowNumbers = true,
|
|
6524
6533
|
trailingRows = 0,
|
|
6534
|
+
trailingCols = 0,
|
|
6525
6535
|
onCellEdit,
|
|
6526
6536
|
contextMenu = false,
|
|
6527
6537
|
onInsertRow,
|
|
6528
6538
|
onDeleteRow,
|
|
6539
|
+
onInsertColumn,
|
|
6529
6540
|
className = "",
|
|
6530
6541
|
style,
|
|
6531
6542
|
emptyState = "No data"
|
|
@@ -6542,8 +6553,16 @@ function DataGrid({
|
|
|
6542
6553
|
const sort = sortProp !== void 0 ? sortProp : internalSort;
|
|
6543
6554
|
const gutter = rowNumbers ? GUTTER : 0;
|
|
6544
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]);
|
|
6545
6564
|
const { widths, offsets, totalWidth } = React36.useMemo(() => {
|
|
6546
|
-
const widths2 =
|
|
6565
|
+
const widths2 = cols.map((c) => resolveWidth(c.width));
|
|
6547
6566
|
const offsets2 = [];
|
|
6548
6567
|
let acc = 0;
|
|
6549
6568
|
for (const w of widths2) {
|
|
@@ -6551,7 +6570,7 @@ function DataGrid({
|
|
|
6551
6570
|
acc += w;
|
|
6552
6571
|
}
|
|
6553
6572
|
return { widths: widths2, offsets: offsets2, totalWidth: acc };
|
|
6554
|
-
}, [
|
|
6573
|
+
}, [cols]);
|
|
6555
6574
|
const order = React36.useMemo(() => {
|
|
6556
6575
|
const idx = rows.map((_, i) => i);
|
|
6557
6576
|
if (!sort) return idx;
|
|
@@ -6580,45 +6599,45 @@ function DataGrid({
|
|
|
6580
6599
|
const rowStart = virtualize ? Math.max(0, Math.floor(scroll.top / rowHeight) - overscan) : 0;
|
|
6581
6600
|
const rowEnd = virtualize ? Math.min(displayRowCount, Math.ceil((scroll.top + bodyH) / rowHeight) + overscan) : displayRowCount;
|
|
6582
6601
|
let colStart = 0;
|
|
6583
|
-
let colEnd =
|
|
6602
|
+
let colEnd = cols.length;
|
|
6584
6603
|
if (virtualize && viewport.w) {
|
|
6585
6604
|
const viewLeft = scroll.left;
|
|
6586
6605
|
const viewRight = scroll.left + (viewport.w - gutter);
|
|
6587
6606
|
colStart = Math.max(0, offsets.findIndex((o, i) => o + widths[i] > viewLeft));
|
|
6588
6607
|
if (colStart < 0) colStart = 0;
|
|
6589
6608
|
colEnd = offsets.findIndex((o) => o > viewRight);
|
|
6590
|
-
colEnd = colEnd === -1 ?
|
|
6609
|
+
colEnd = colEnd === -1 ? cols.length : Math.min(cols.length, colEnd + 1);
|
|
6591
6610
|
colStart = Math.max(0, colStart - overscan);
|
|
6592
|
-
colEnd = Math.min(
|
|
6611
|
+
colEnd = Math.min(cols.length, colEnd + overscan);
|
|
6593
6612
|
}
|
|
6594
6613
|
const visibleRows = Array.from({ length: Math.max(0, rowEnd - rowStart) }, (_, i) => rowStart + i);
|
|
6595
6614
|
const visibleCols = Array.from({ length: Math.max(0, colEnd - colStart) }, (_, i) => colStart + i);
|
|
6596
6615
|
const commit = React36.useCallback(() => {
|
|
6597
6616
|
if (!editing) return;
|
|
6598
|
-
const col =
|
|
6617
|
+
const col = cols[editing.col];
|
|
6599
6618
|
const ri = editing.disp < rows.length ? order[editing.disp] : editing.disp;
|
|
6600
6619
|
onCellEdit?.({ row: ri, column: col.key, value: draft });
|
|
6601
6620
|
setEditing(null);
|
|
6602
|
-
}, [editing,
|
|
6621
|
+
}, [editing, cols, draft, onCellEdit, order, rows.length]);
|
|
6603
6622
|
const startEdit = (disp, col) => {
|
|
6604
|
-
const c =
|
|
6623
|
+
const c = cols[col];
|
|
6605
6624
|
if (!(c.editable ?? editable)) return;
|
|
6606
6625
|
setDraft(displayValue(rows[rowIndexForDisp(disp)]?.[c.key] ?? ""));
|
|
6607
6626
|
setEditing({ disp, col });
|
|
6608
6627
|
};
|
|
6609
|
-
const cellText = (sel) => displayValue(rows[rowIndexForDisp(sel.disp)]?.[
|
|
6628
|
+
const cellText = (sel) => displayValue(rows[rowIndexForDisp(sel.disp)]?.[cols[sel.col].key] ?? "");
|
|
6610
6629
|
const copyCell = React36.useCallback(async () => {
|
|
6611
6630
|
if (!selected) return;
|
|
6612
6631
|
try {
|
|
6613
6632
|
await navigator.clipboard?.writeText(cellText(selected));
|
|
6614
6633
|
} catch {
|
|
6615
6634
|
}
|
|
6616
|
-
}, [selected, rows,
|
|
6635
|
+
}, [selected, rows, cols, order]);
|
|
6617
6636
|
const cutCell = React36.useCallback(async () => {
|
|
6618
6637
|
if (!selected) return;
|
|
6619
6638
|
await copyCell();
|
|
6620
|
-
onCellEdit?.({ row: rowIndexForDisp(selected.disp), column:
|
|
6621
|
-
}, [selected, copyCell, onCellEdit,
|
|
6639
|
+
onCellEdit?.({ row: rowIndexForDisp(selected.disp), column: cols[selected.col].key, value: "" });
|
|
6640
|
+
}, [selected, copyCell, onCellEdit, cols, order, rows.length]);
|
|
6622
6641
|
const pasteCell = React36.useCallback(async () => {
|
|
6623
6642
|
if (!selected) return;
|
|
6624
6643
|
let text = "";
|
|
@@ -6627,9 +6646,16 @@ function DataGrid({
|
|
|
6627
6646
|
} catch {
|
|
6628
6647
|
return;
|
|
6629
6648
|
}
|
|
6630
|
-
onCellEdit?.({ row: rowIndexForDisp(selected.disp), column:
|
|
6631
|
-
}, [selected, onCellEdit,
|
|
6649
|
+
onCellEdit?.({ row: rowIndexForDisp(selected.disp), column: cols[selected.col].key, value: text });
|
|
6650
|
+
}, [selected, onCellEdit, cols, order, rows.length]);
|
|
6632
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
|
+
}
|
|
6633
6659
|
if (ctxTarget?.kind === "row") {
|
|
6634
6660
|
const ri = rowIndexForDisp(ctxTarget.disp);
|
|
6635
6661
|
const isData = ctxTarget.disp < rows.length;
|
|
@@ -6644,7 +6670,7 @@ function DataGrid({
|
|
|
6644
6670
|
{ key: "cut", value: "Cut", disabled: !editable, onClick: () => void cutCell() },
|
|
6645
6671
|
{ key: "paste", value: "Paste", disabled: !editable, onClick: () => void pasteCell() }
|
|
6646
6672
|
];
|
|
6647
|
-
}, [ctxTarget, rows.length, editable, onInsertRow, onDeleteRow, copyCell, cutCell, pasteCell, order]);
|
|
6673
|
+
}, [ctxTarget, rows.length, editable, onInsertRow, onDeleteRow, onInsertColumn, copyCell, cutCell, pasteCell, order]);
|
|
6648
6674
|
const rowHighlighted = (disp) => hoveredRow === disp || selected?.disp === disp;
|
|
6649
6675
|
const gridInner = /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", width: gutter + totalWidth + END_PAD, height: headerHeight + totalHeight + END_PAD }, children: [
|
|
6650
6676
|
rowNumbers && /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -6655,7 +6681,7 @@ function DataGrid({
|
|
|
6655
6681
|
}
|
|
6656
6682
|
),
|
|
6657
6683
|
visibleCols.map((ci) => {
|
|
6658
|
-
const c =
|
|
6684
|
+
const c = cols[ci];
|
|
6659
6685
|
const sortDir = sort?.key === c.key ? sort.dir : null;
|
|
6660
6686
|
const canSort = colSortable(c);
|
|
6661
6687
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -6664,6 +6690,7 @@ function DataGrid({
|
|
|
6664
6690
|
role: "columnheader",
|
|
6665
6691
|
"aria-sort": sortDir ? sortDir === "asc" ? "ascending" : "descending" : void 0,
|
|
6666
6692
|
onClick: canSort ? () => toggleSort(c.key) : void 0,
|
|
6693
|
+
onContextMenu: contextMenu ? () => setCtxTarget({ kind: "header", col: ci }) : void 0,
|
|
6667
6694
|
className: cx(
|
|
6668
6695
|
"flex items-center gap-1 border-b border-r border-border bg-surface px-3 font-medium text-foreground-secondary",
|
|
6669
6696
|
canSort && "cursor-pointer select-none hover:text-foreground"
|
|
@@ -6706,7 +6733,7 @@ function DataGrid({
|
|
|
6706
6733
|
const ri = rowIndexForDisp(disp);
|
|
6707
6734
|
const hi = rowHighlighted(disp);
|
|
6708
6735
|
return visibleCols.map((ci) => {
|
|
6709
|
-
const c =
|
|
6736
|
+
const c = cols[ci];
|
|
6710
6737
|
const isEditing = editing?.disp === disp && editing?.col === ci;
|
|
6711
6738
|
const isSelected = selected?.disp === disp && selected?.col === ci;
|
|
6712
6739
|
const canEdit = c.editable ?? editable;
|
|
@@ -6801,8 +6828,24 @@ function columnLetter(i) {
|
|
|
6801
6828
|
} while (n >= 0);
|
|
6802
6829
|
return s;
|
|
6803
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
|
+
}
|
|
6804
6847
|
function blankSheet(name, cols = 8) {
|
|
6805
|
-
return { name, columns:
|
|
6848
|
+
return { name, columns: makeColumns([], cols), rows: [] };
|
|
6806
6849
|
}
|
|
6807
6850
|
function parseWorkbook(XLSX, bytes) {
|
|
6808
6851
|
const wb = XLSX.read(bytes, { type: "array" });
|
|
@@ -6857,6 +6900,7 @@ function Spreadsheet({
|
|
|
6857
6900
|
virtualize = true,
|
|
6858
6901
|
sortable = true,
|
|
6859
6902
|
emptyRows = 50,
|
|
6903
|
+
emptyCols = 6,
|
|
6860
6904
|
allowAddSheet,
|
|
6861
6905
|
height = 480,
|
|
6862
6906
|
width,
|
|
@@ -6947,6 +6991,33 @@ function Spreadsheet({
|
|
|
6947
6991
|
return next;
|
|
6948
6992
|
});
|
|
6949
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]);
|
|
6950
7021
|
const insertRow = React36.useCallback((index) => {
|
|
6951
7022
|
setSheets((prev) => {
|
|
6952
7023
|
if (!prev) return prev;
|
|
@@ -7086,7 +7157,8 @@ function Spreadsheet({
|
|
|
7086
7157
|
const sheetItems = [
|
|
7087
7158
|
{ key: "add", label: "Add Sheet", onSelect: addSheet },
|
|
7088
7159
|
{ key: "delete", label: "Delete Sheet", danger: true, disabled: sheets.length <= 1, onSelect: () => deleteSheet(active) },
|
|
7089
|
-
{ key: "add100", label: "Add 100 rows", separatorBefore: true, onSelect: add100Rows }
|
|
7160
|
+
{ key: "add100", label: "Add 100 rows", separatorBefore: true, onSelect: add100Rows },
|
|
7161
|
+
{ key: "add10cols", label: "Add 10 columns", onSelect: add10Columns }
|
|
7090
7162
|
];
|
|
7091
7163
|
const canDeleteSheet = editable && sheets.length > 1;
|
|
7092
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: [
|
|
@@ -7115,9 +7187,11 @@ function Spreadsheet({
|
|
|
7115
7187
|
sortable,
|
|
7116
7188
|
virtualize,
|
|
7117
7189
|
trailingRows: emptyRows,
|
|
7190
|
+
trailingCols: emptyCols,
|
|
7118
7191
|
contextMenu: true,
|
|
7119
7192
|
onInsertRow: editable ? insertRow : void 0,
|
|
7120
7193
|
onDeleteRow: editable ? deleteRow : void 0,
|
|
7194
|
+
onInsertColumn: editable ? insertColumn : void 0,
|
|
7121
7195
|
onCellEdit: handleCellEdit,
|
|
7122
7196
|
height: "100%",
|
|
7123
7197
|
className: "!rounded-none !border-0"
|