@geomak/ui 7.14.0 → 7.16.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 +221 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -2
- package/dist/index.d.ts +41 -2
- package/dist/index.js +220 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -6537,6 +6537,7 @@ function DataGrid({
|
|
|
6537
6537
|
onInsertRow,
|
|
6538
6538
|
onDeleteRow,
|
|
6539
6539
|
onInsertColumn,
|
|
6540
|
+
onHeaderEdit,
|
|
6540
6541
|
className = "",
|
|
6541
6542
|
style,
|
|
6542
6543
|
emptyState = "No data"
|
|
@@ -6549,6 +6550,8 @@ function DataGrid({
|
|
|
6549
6550
|
const [ctxTarget, setCtxTarget] = React36.useState(null);
|
|
6550
6551
|
const [editing, setEditing] = React36.useState(null);
|
|
6551
6552
|
const [draft, setDraft] = React36.useState("");
|
|
6553
|
+
const [editingHeader, setEditingHeader] = React36.useState(null);
|
|
6554
|
+
const [headerDraft, setHeaderDraft] = React36.useState("");
|
|
6552
6555
|
const [internalSort, setInternalSort] = React36.useState(null);
|
|
6553
6556
|
const sort = sortProp !== void 0 ? sortProp : internalSort;
|
|
6554
6557
|
const gutter = rowNumbers ? GUTTER : 0;
|
|
@@ -6625,6 +6628,17 @@ function DataGrid({
|
|
|
6625
6628
|
setDraft(displayValue(rows[rowIndexForDisp(disp)]?.[c.key] ?? ""));
|
|
6626
6629
|
setEditing({ disp, col });
|
|
6627
6630
|
};
|
|
6631
|
+
const startHeaderEdit = (col) => {
|
|
6632
|
+
if (!onHeaderEdit) return;
|
|
6633
|
+
const c = cols[col];
|
|
6634
|
+
setHeaderDraft(typeof c.label === "string" ? c.label : String(c.key));
|
|
6635
|
+
setEditingHeader(col);
|
|
6636
|
+
};
|
|
6637
|
+
const commitHeader = () => {
|
|
6638
|
+
if (editingHeader == null) return;
|
|
6639
|
+
onHeaderEdit?.(editingHeader, headerDraft);
|
|
6640
|
+
setEditingHeader(null);
|
|
6641
|
+
};
|
|
6628
6642
|
const cellText = (sel) => displayValue(rows[rowIndexForDisp(sel.disp)]?.[cols[sel.col].key] ?? "");
|
|
6629
6643
|
const copyCell = React36.useCallback(async () => {
|
|
6630
6644
|
if (!selected) return;
|
|
@@ -6652,7 +6666,8 @@ function DataGrid({
|
|
|
6652
6666
|
if (ctxTarget?.kind === "header") {
|
|
6653
6667
|
const ci = ctxTarget.col;
|
|
6654
6668
|
return [
|
|
6655
|
-
{ key: "
|
|
6669
|
+
{ key: "rename", value: "Rename column", disabled: !onHeaderEdit, onClick: () => startHeaderEdit(ci) },
|
|
6670
|
+
{ key: "left", value: "Add column to the left", disabled: !onInsertColumn, separatorBefore: true, onClick: () => onInsertColumn?.(ci) },
|
|
6656
6671
|
{ key: "right", value: "Add column to the right", disabled: !onInsertColumn, onClick: () => onInsertColumn?.(ci + 1) }
|
|
6657
6672
|
];
|
|
6658
6673
|
}
|
|
@@ -6670,7 +6685,7 @@ function DataGrid({
|
|
|
6670
6685
|
{ key: "cut", value: "Cut", disabled: !editable, onClick: () => void cutCell() },
|
|
6671
6686
|
{ key: "paste", value: "Paste", disabled: !editable, onClick: () => void pasteCell() }
|
|
6672
6687
|
];
|
|
6673
|
-
}, [ctxTarget, rows.length, editable, onInsertRow, onDeleteRow, onInsertColumn, copyCell, cutCell, pasteCell, order]);
|
|
6688
|
+
}, [ctxTarget, rows.length, editable, onInsertRow, onDeleteRow, onInsertColumn, onHeaderEdit, copyCell, cutCell, pasteCell, order]);
|
|
6674
6689
|
const rowHighlighted = (disp) => hoveredRow === disp || selected?.disp === disp;
|
|
6675
6690
|
const gridInner = /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", width: gutter + totalWidth + END_PAD, height: headerHeight + totalHeight + END_PAD }, children: [
|
|
6676
6691
|
rowNumbers && /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -6684,16 +6699,20 @@ function DataGrid({
|
|
|
6684
6699
|
const c = cols[ci];
|
|
6685
6700
|
const sortDir = sort?.key === c.key ? sort.dir : null;
|
|
6686
6701
|
const canSort = colSortable(c);
|
|
6687
|
-
|
|
6702
|
+
const renamable = !!onHeaderEdit;
|
|
6703
|
+
const isEditingHeader = editingHeader === ci;
|
|
6704
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
6688
6705
|
"div",
|
|
6689
6706
|
{
|
|
6690
6707
|
role: "columnheader",
|
|
6691
6708
|
"aria-sort": sortDir ? sortDir === "asc" ? "ascending" : "descending" : void 0,
|
|
6692
|
-
onClick: canSort ? () => toggleSort(c.key) : void 0,
|
|
6709
|
+
onClick: canSort && !renamable ? () => toggleSort(c.key) : void 0,
|
|
6710
|
+
onDoubleClick: renamable ? () => startHeaderEdit(ci) : void 0,
|
|
6693
6711
|
onContextMenu: contextMenu ? () => setCtxTarget({ kind: "header", col: ci }) : void 0,
|
|
6694
6712
|
className: cx(
|
|
6695
6713
|
"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"
|
|
6714
|
+
canSort && !renamable && "cursor-pointer select-none hover:text-foreground",
|
|
6715
|
+
renamable && "cursor-default select-none"
|
|
6697
6716
|
),
|
|
6698
6717
|
style: {
|
|
6699
6718
|
position: "absolute",
|
|
@@ -6704,10 +6723,27 @@ function DataGrid({
|
|
|
6704
6723
|
zIndex: 2,
|
|
6705
6724
|
justifyContent: c.align === "right" ? "flex-end" : c.align === "center" ? "center" : "flex-start"
|
|
6706
6725
|
},
|
|
6707
|
-
children:
|
|
6726
|
+
children: isEditingHeader ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
6727
|
+
"input",
|
|
6728
|
+
{
|
|
6729
|
+
autoFocus: true,
|
|
6730
|
+
value: headerDraft,
|
|
6731
|
+
onChange: (e) => setHeaderDraft(e.target.value),
|
|
6732
|
+
onBlur: commitHeader,
|
|
6733
|
+
onClick: (e) => e.stopPropagation(),
|
|
6734
|
+
onKeyDown: (e) => {
|
|
6735
|
+
if (e.key === "Enter") commitHeader();
|
|
6736
|
+
else if (e.key === "Escape") setEditingHeader(null);
|
|
6737
|
+
},
|
|
6738
|
+
className: "h-full w-full bg-transparent font-medium text-foreground outline-none"
|
|
6739
|
+
}
|
|
6740
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
6708
6741
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: c.label ?? c.key }),
|
|
6709
|
-
canSort && /* @__PURE__ */ jsxRuntime.jsx(
|
|
6710
|
-
|
|
6742
|
+
canSort && (renamable ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", title: "Sort", onClick: (e) => {
|
|
6743
|
+
e.stopPropagation();
|
|
6744
|
+
toggleSort(c.key);
|
|
6745
|
+
}, className: "flex-shrink-0 rounded hover:text-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(SortCaret, { dir: sortDir }) }) : /* @__PURE__ */ jsxRuntime.jsx(SortCaret, { dir: sortDir }))
|
|
6746
|
+
] })
|
|
6711
6747
|
},
|
|
6712
6748
|
`h-${c.key}`
|
|
6713
6749
|
);
|
|
@@ -6944,12 +6980,21 @@ function Spreadsheet({
|
|
|
6944
6980
|
const sheet = sheets?.[active];
|
|
6945
6981
|
const columns = React36.useMemo(() => sheet ? toColumns(sheet.columns) : [], [sheet]);
|
|
6946
6982
|
const plainRows = React36.useMemo(() => sheet ? toPlainRows(sheet, columns) : [], [sheet, columns]);
|
|
6983
|
+
const displayColumns = React36.useMemo(
|
|
6984
|
+
() => [...columns, ...makeColumns(columns, Math.max(0, emptyCols)).map((c) => ({ ...c, sortable: false }))],
|
|
6985
|
+
[columns, emptyCols]
|
|
6986
|
+
);
|
|
6987
|
+
const isSlackKey = (key) => !columns.some((c) => c.key === key);
|
|
6947
6988
|
const handleCellEdit = React36.useCallback(({ row, column, value }) => {
|
|
6948
6989
|
let coerced = value;
|
|
6949
6990
|
setSheets((prev) => {
|
|
6950
6991
|
if (!prev) return prev;
|
|
6951
|
-
const next = prev.map((s, i) => i === active ? { ...s, rows: s.rows.map((r) => ({ ...r })) } : s);
|
|
6992
|
+
const next = prev.map((s, i) => i === active ? { ...s, columns: toColumns(s.columns), rows: s.rows.map((r) => ({ ...r })) } : s);
|
|
6952
6993
|
const target = next[active];
|
|
6994
|
+
if (isSlackKey(column)) {
|
|
6995
|
+
const j = displayColumns.findIndex((c) => c.key === column);
|
|
6996
|
+
if (j >= 0) target.columns = displayColumns.slice(0, j + 1).map((c) => ({ key: c.key, label: c.label }));
|
|
6997
|
+
}
|
|
6953
6998
|
while (target.rows.length <= row) target.rows.push({});
|
|
6954
6999
|
const existing = target.rows[row]?.[column];
|
|
6955
7000
|
const prevValue = cellValue(existing);
|
|
@@ -6963,7 +7008,22 @@ function Spreadsheet({
|
|
|
6963
7008
|
return next;
|
|
6964
7009
|
});
|
|
6965
7010
|
onCellEdit?.({ sheet: sheets?.[active]?.name ?? "", row, column, value: coerced });
|
|
6966
|
-
}, [active, onCellEdit, onChange, sheets]);
|
|
7011
|
+
}, [active, onCellEdit, onChange, sheets, displayColumns, columns]);
|
|
7012
|
+
const renameColumn = React36.useCallback((index, label) => {
|
|
7013
|
+
setSheets((prev) => {
|
|
7014
|
+
if (!prev) return prev;
|
|
7015
|
+
const next = prev.map((s, i) => i === active ? { ...s, columns: toColumns(s.columns) } : s);
|
|
7016
|
+
const target = next[active];
|
|
7017
|
+
let tcols = target.columns;
|
|
7018
|
+
if (index >= tcols.length) {
|
|
7019
|
+
tcols = displayColumns.slice(0, index + 1).map((c) => ({ key: c.key, label: c.label }));
|
|
7020
|
+
target.columns = tcols;
|
|
7021
|
+
}
|
|
7022
|
+
if (tcols[index]) tcols[index] = { ...tcols[index], label };
|
|
7023
|
+
onChange?.(next);
|
|
7024
|
+
return next;
|
|
7025
|
+
});
|
|
7026
|
+
}, [active, onChange, displayColumns]);
|
|
6967
7027
|
const addSheet = React36.useCallback(() => {
|
|
6968
7028
|
setSheets((prev) => {
|
|
6969
7029
|
const list = prev ?? [];
|
|
@@ -7181,17 +7241,17 @@ function Spreadsheet({
|
|
|
7181
7241
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
7182
7242
|
DataGrid,
|
|
7183
7243
|
{
|
|
7184
|
-
columns,
|
|
7244
|
+
columns: displayColumns,
|
|
7185
7245
|
rows: plainRows,
|
|
7186
7246
|
editable,
|
|
7187
7247
|
sortable,
|
|
7188
7248
|
virtualize,
|
|
7189
7249
|
trailingRows: emptyRows,
|
|
7190
|
-
trailingCols: emptyCols,
|
|
7191
7250
|
contextMenu: true,
|
|
7192
7251
|
onInsertRow: editable ? insertRow : void 0,
|
|
7193
7252
|
onDeleteRow: editable ? deleteRow : void 0,
|
|
7194
7253
|
onInsertColumn: editable ? insertColumn : void 0,
|
|
7254
|
+
onHeaderEdit: editable ? renameColumn : void 0,
|
|
7195
7255
|
onCellEdit: handleCellEdit,
|
|
7196
7256
|
height: "100%",
|
|
7197
7257
|
className: "!rounded-none !border-0"
|
|
@@ -11121,6 +11181,153 @@ function useJwt(token) {
|
|
|
11121
11181
|
const isValid = decoded.payload != null && !isExpired;
|
|
11122
11182
|
return { payload: decoded.payload, header: decoded.header, expiresAt, isExpired, isValid, raw: token ?? null };
|
|
11123
11183
|
}
|
|
11184
|
+
var INVALID_WS_CHARS = /[:\\/?\*[\]]/g;
|
|
11185
|
+
function sanitizeName(name) {
|
|
11186
|
+
return name.replace(INVALID_WS_CHARS, "_").slice(0, 31) || "Sheet";
|
|
11187
|
+
}
|
|
11188
|
+
var xlsxPromise2 = null;
|
|
11189
|
+
var loadXlsx2 = () => xlsxPromise2 ??= import('xlsx');
|
|
11190
|
+
function useExcel() {
|
|
11191
|
+
const [isExporting, setIsExporting] = React36.useState(false);
|
|
11192
|
+
const exportSheets = React36.useCallback(async (sheets, options = {}) => {
|
|
11193
|
+
const { fileName = "export", onSave } = options;
|
|
11194
|
+
setIsExporting(true);
|
|
11195
|
+
try {
|
|
11196
|
+
const XLSX = await loadXlsx2();
|
|
11197
|
+
const wb = XLSX.utils.book_new();
|
|
11198
|
+
for (const sheet of sheets) {
|
|
11199
|
+
let headers;
|
|
11200
|
+
if (sheet.columns) {
|
|
11201
|
+
headers = sheet.columns;
|
|
11202
|
+
} else {
|
|
11203
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11204
|
+
for (const row of sheet.rows) {
|
|
11205
|
+
if (!Array.isArray(row)) {
|
|
11206
|
+
for (const k of Object.keys(row)) seen.add(k);
|
|
11207
|
+
}
|
|
11208
|
+
}
|
|
11209
|
+
headers = Array.from(seen);
|
|
11210
|
+
}
|
|
11211
|
+
const aoa = [];
|
|
11212
|
+
if (headers.length > 0) aoa.push(headers);
|
|
11213
|
+
for (const row of sheet.rows) {
|
|
11214
|
+
if (Array.isArray(row)) {
|
|
11215
|
+
aoa.push(row);
|
|
11216
|
+
} else {
|
|
11217
|
+
aoa.push(headers.map((h) => row[h] ?? null));
|
|
11218
|
+
}
|
|
11219
|
+
}
|
|
11220
|
+
const ws = XLSX.utils.aoa_to_sheet(aoa);
|
|
11221
|
+
if (headers.length > 0) {
|
|
11222
|
+
for (let ci = 0; ci < headers.length; ci++) {
|
|
11223
|
+
const addr = XLSX.utils.encode_cell({ r: 0, c: ci });
|
|
11224
|
+
if (ws[addr]) ws[addr].s = { font: { bold: true } };
|
|
11225
|
+
}
|
|
11226
|
+
}
|
|
11227
|
+
const colWidths = headers.map((h) => Math.min(h.length, 40));
|
|
11228
|
+
for (const row of aoa.slice(1)) {
|
|
11229
|
+
row.forEach((cell, ci) => {
|
|
11230
|
+
const len = cell == null ? 0 : String(cell).length;
|
|
11231
|
+
if (len > (colWidths[ci] ?? 0)) colWidths[ci] = Math.min(len, 40);
|
|
11232
|
+
});
|
|
11233
|
+
}
|
|
11234
|
+
ws["!cols"] = colWidths.map((w) => ({ wch: w + 2 }));
|
|
11235
|
+
XLSX.utils.book_append_sheet(wb, ws, sanitizeName(sheet.name));
|
|
11236
|
+
}
|
|
11237
|
+
const out = XLSX.write(wb, { bookType: "xlsx", type: "array" });
|
|
11238
|
+
const blob = new Blob([out], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
|
|
11239
|
+
const name = `${fileName}.xlsx`;
|
|
11240
|
+
if (onSave) {
|
|
11241
|
+
await onSave(blob, name);
|
|
11242
|
+
} else {
|
|
11243
|
+
downloadBlob(blob, name);
|
|
11244
|
+
}
|
|
11245
|
+
} finally {
|
|
11246
|
+
setIsExporting(false);
|
|
11247
|
+
}
|
|
11248
|
+
}, []);
|
|
11249
|
+
const readWorkbook = React36.useCallback(async (source) => {
|
|
11250
|
+
const XLSX = await loadXlsx2();
|
|
11251
|
+
let bytes;
|
|
11252
|
+
if (source instanceof Uint8Array) {
|
|
11253
|
+
bytes = source;
|
|
11254
|
+
} else if (source instanceof ArrayBuffer) {
|
|
11255
|
+
bytes = new Uint8Array(source);
|
|
11256
|
+
} else {
|
|
11257
|
+
bytes = new Uint8Array(await source.arrayBuffer());
|
|
11258
|
+
}
|
|
11259
|
+
const wb = XLSX.read(bytes, { type: "array" });
|
|
11260
|
+
return wb.SheetNames.map((name) => {
|
|
11261
|
+
const ws = wb.Sheets[name];
|
|
11262
|
+
const aoa = XLSX.utils.sheet_to_json(ws, { header: 1, blankrows: false, defval: null });
|
|
11263
|
+
if (aoa.length === 0) return { name, rows: [] };
|
|
11264
|
+
const headerRow = aoa[0] ?? [];
|
|
11265
|
+
const headers = headerRow.map(
|
|
11266
|
+
(h, i) => h != null && String(h).length ? String(h) : `col_${i}`
|
|
11267
|
+
);
|
|
11268
|
+
const rows = aoa.slice(1).map((row) => {
|
|
11269
|
+
const rec = {};
|
|
11270
|
+
headers.forEach((h, i) => {
|
|
11271
|
+
rec[h] = row[i] ?? null;
|
|
11272
|
+
});
|
|
11273
|
+
return rec;
|
|
11274
|
+
});
|
|
11275
|
+
return { name, rows };
|
|
11276
|
+
});
|
|
11277
|
+
}, []);
|
|
11278
|
+
return { exportSheets, readWorkbook, isExporting };
|
|
11279
|
+
}
|
|
11280
|
+
var jspdfPromise2 = null;
|
|
11281
|
+
var loadJspdf2 = () => jspdfPromise2 ??= import('jspdf');
|
|
11282
|
+
function usePdf() {
|
|
11283
|
+
const [isExporting, setIsExporting] = React36.useState(false);
|
|
11284
|
+
const exportCanvases = React36.useCallback(async (pages, options = {}) => {
|
|
11285
|
+
const { fileName = "report", onSave, orientation = "landscape" } = options;
|
|
11286
|
+
const validPages = pages.filter((p) => p.canvas.width !== 0 && p.canvas.height !== 0);
|
|
11287
|
+
if (validPages.length === 0) return;
|
|
11288
|
+
setIsExporting(true);
|
|
11289
|
+
try {
|
|
11290
|
+
const { jsPDF } = await loadJspdf2();
|
|
11291
|
+
const pdf = new jsPDF({ orientation, unit: "px", compress: true });
|
|
11292
|
+
const pageW = pdf.internal.pageSize.getWidth();
|
|
11293
|
+
const pageH = pdf.internal.pageSize.getHeight();
|
|
11294
|
+
const TITLE_H = 28;
|
|
11295
|
+
for (let i = 0; i < validPages.length; i++) {
|
|
11296
|
+
const { canvas, title } = validPages[i];
|
|
11297
|
+
if (i > 0) pdf.addPage();
|
|
11298
|
+
let contentY = 0;
|
|
11299
|
+
const contentH = title ? pageH - TITLE_H : pageH;
|
|
11300
|
+
if (title) {
|
|
11301
|
+
pdf.setFontSize(12);
|
|
11302
|
+
pdf.setFont("helvetica", "bold");
|
|
11303
|
+
pdf.text(title, pageW / 2, TITLE_H / 2 + 6, { align: "center" });
|
|
11304
|
+
contentY = TITLE_H;
|
|
11305
|
+
}
|
|
11306
|
+
const scale = Math.min(pageW / canvas.width, contentH / canvas.height);
|
|
11307
|
+
const w = canvas.width * scale;
|
|
11308
|
+
const h = canvas.height * scale;
|
|
11309
|
+
const x = (pageW - w) / 2;
|
|
11310
|
+
const y = contentY + (contentH - h) / 2;
|
|
11311
|
+
pdf.addImage(canvas.toDataURL("image/png", 1), "PNG", x, y, w, h);
|
|
11312
|
+
}
|
|
11313
|
+
const blob = pdf.output("blob");
|
|
11314
|
+
const name = `${fileName}.pdf`;
|
|
11315
|
+
if (onSave) {
|
|
11316
|
+
await onSave(blob, name);
|
|
11317
|
+
} else {
|
|
11318
|
+
const url = URL.createObjectURL(blob);
|
|
11319
|
+
const a = document.createElement("a");
|
|
11320
|
+
a.href = url;
|
|
11321
|
+
a.download = name;
|
|
11322
|
+
a.click();
|
|
11323
|
+
URL.revokeObjectURL(url);
|
|
11324
|
+
}
|
|
11325
|
+
} finally {
|
|
11326
|
+
setIsExporting(false);
|
|
11327
|
+
}
|
|
11328
|
+
}, []);
|
|
11329
|
+
return { exportCanvases, isExporting };
|
|
11330
|
+
}
|
|
11124
11331
|
var GRADIENT = "radial-gradient(ellipse 80% 60% at 50% 0%, color-mix(in oklab, var(--color-accent) 12%, transparent), transparent 70%)";
|
|
11125
11332
|
function Jumbotron({
|
|
11126
11333
|
eyebrow,
|
|
@@ -11808,6 +12015,7 @@ exports.runFieldRules = runFieldRules;
|
|
|
11808
12015
|
exports.scorePassword = scorePassword;
|
|
11809
12016
|
exports.useBreakpoint = useBreakpoint;
|
|
11810
12017
|
exports.useCart = useCart;
|
|
12018
|
+
exports.useExcel = useExcel;
|
|
11811
12019
|
exports.useFieldArray = useFieldArray;
|
|
11812
12020
|
exports.useForm = useForm;
|
|
11813
12021
|
exports.useFormField = useFormField;
|
|
@@ -11816,5 +12024,6 @@ exports.useJwt = useJwt;
|
|
|
11816
12024
|
exports.useLocalStorage = useLocalStorage;
|
|
11817
12025
|
exports.useMediaQuery = useMediaQuery;
|
|
11818
12026
|
exports.useNotification = useNotification;
|
|
12027
|
+
exports.usePdf = usePdf;
|
|
11819
12028
|
//# sourceMappingURL=index.cjs.map
|
|
11820
12029
|
//# sourceMappingURL=index.cjs.map
|