hazo_ui 6.15.0 → 6.18.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/CHANGE_LOG.md +91 -0
- package/README.md +13 -0
- package/dist/index.cjs +536 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -2
- package/dist/index.d.ts +33 -2
- package/dist/index.js +539 -7
- package/dist/index.js.map +1 -1
- package/dist/index.locale-picker.cjs +7 -1
- package/dist/index.locale-picker.cjs.map +1 -1
- package/dist/index.locale-picker.js +7 -1
- package/dist/index.locale-picker.js.map +1 -1
- package/package.json +6 -4
- package/tailwind-preset.cjs +120 -0
package/dist/index.cjs
CHANGED
|
@@ -877,6 +877,8 @@ var en_default = {
|
|
|
877
877
|
"locale_picker.change_language": "Change language",
|
|
878
878
|
"app_logo.alt_fallback": "Logo",
|
|
879
879
|
"app_header.aria_label": "Application header",
|
|
880
|
+
"app_header.menu_label": "Menu",
|
|
881
|
+
"app_header.nav_title": "Navigation",
|
|
880
882
|
"skeleton.loading_content": "Loading content",
|
|
881
883
|
"spinner.loading": "Loading",
|
|
882
884
|
"spinner.loading_text": "Loading...",
|
|
@@ -934,7 +936,11 @@ var en_default = {
|
|
|
934
936
|
"charts.pct_of_first": "% of first: {{pct}}%",
|
|
935
937
|
"charts.pct_of_prev": "% of prev: {{pct}}%",
|
|
936
938
|
"charts.dropoff": "Drop-off: \u2212{{value}}",
|
|
937
|
-
"toast.missing_prefix": "Missing: {{items}}"
|
|
939
|
+
"toast.missing_prefix": "Missing: {{items}}",
|
|
940
|
+
"editable_data_table.add": "Add",
|
|
941
|
+
"editable_data_table.delete": "Delete row",
|
|
942
|
+
"editable_data_table.empty": "No rows yet. Click Add to start.",
|
|
943
|
+
"editable_data_table.drag_to_reorder": "Drag to reorder"
|
|
938
944
|
};
|
|
939
945
|
var default_t = i18n.createPackageT("hazo_ui", en_default);
|
|
940
946
|
var defaultHazoUiT = default_t;
|
|
@@ -12527,6 +12533,7 @@ function Logo({
|
|
|
12527
12533
|
}
|
|
12528
12534
|
function AppHeader({ identity, homeHref, navItems, actions, className }) {
|
|
12529
12535
|
const t = useHazoUiT();
|
|
12536
|
+
const [drawerOpen, setDrawerOpen] = React27__namespace.useState(false);
|
|
12530
12537
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
12531
12538
|
"header",
|
|
12532
12539
|
{
|
|
@@ -12546,9 +12553,34 @@ function AppHeader({ identity, homeHref, navItems, actions, className }) {
|
|
|
12546
12553
|
href: homeHref
|
|
12547
12554
|
}
|
|
12548
12555
|
),
|
|
12549
|
-
navItems
|
|
12556
|
+
navItems && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden md:flex items-center", children: navItems })
|
|
12550
12557
|
] }),
|
|
12551
|
-
/* @__PURE__ */ jsxRuntime.
|
|
12558
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
12559
|
+
actions,
|
|
12560
|
+
navItems && /* @__PURE__ */ jsxRuntime.jsxs(Drawer, { open: drawerOpen, onOpenChange: setDrawerOpen, children: [
|
|
12561
|
+
/* @__PURE__ */ jsxRuntime.jsx(DrawerTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
12562
|
+
"button",
|
|
12563
|
+
{
|
|
12564
|
+
className: "md:hidden rounded-md p-1.5 hover:bg-navbar-active/10",
|
|
12565
|
+
"aria-label": t("app_header.menu_label"),
|
|
12566
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Menu, { className: "h-5 w-5" })
|
|
12567
|
+
}
|
|
12568
|
+
) }),
|
|
12569
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DrawerContent, { children: [
|
|
12570
|
+
/* @__PURE__ */ jsxRuntime.jsx(DrawerTitle, { className: "sr-only", children: t("app_header.nav_title") }),
|
|
12571
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
12572
|
+
"div",
|
|
12573
|
+
{
|
|
12574
|
+
className: "p-4 [&_nav]:flex-col [&_nav]:gap-2 [&_nav]:items-stretch",
|
|
12575
|
+
onClick: (e) => {
|
|
12576
|
+
if (e.target.closest("a")) setDrawerOpen(false);
|
|
12577
|
+
},
|
|
12578
|
+
children: navItems
|
|
12579
|
+
}
|
|
12580
|
+
)
|
|
12581
|
+
] })
|
|
12582
|
+
] })
|
|
12583
|
+
] })
|
|
12552
12584
|
]
|
|
12553
12585
|
}
|
|
12554
12586
|
);
|
|
@@ -13196,6 +13228,506 @@ function useHazoEvent(pkg, event, handler) {
|
|
|
13196
13228
|
return () => window.removeEventListener(event_name, listener);
|
|
13197
13229
|
}, [pkg, event]);
|
|
13198
13230
|
}
|
|
13231
|
+
function EditableCell({
|
|
13232
|
+
value,
|
|
13233
|
+
type,
|
|
13234
|
+
options,
|
|
13235
|
+
defaultColorValue,
|
|
13236
|
+
clearable,
|
|
13237
|
+
onCommit,
|
|
13238
|
+
editable,
|
|
13239
|
+
forceEdit,
|
|
13240
|
+
onTabOut,
|
|
13241
|
+
onEnterDown,
|
|
13242
|
+
onPaste,
|
|
13243
|
+
onActivated
|
|
13244
|
+
}) {
|
|
13245
|
+
const [editing, setEditing] = React27.useState(false);
|
|
13246
|
+
const [draft, setDraft] = React27.useState(String(value ?? ""));
|
|
13247
|
+
const inputRef = React27.useRef(null);
|
|
13248
|
+
React27.useEffect(() => {
|
|
13249
|
+
if (forceEdit) {
|
|
13250
|
+
setDraft(String(value ?? ""));
|
|
13251
|
+
setEditing(true);
|
|
13252
|
+
}
|
|
13253
|
+
}, [forceEdit]);
|
|
13254
|
+
React27.useEffect(() => {
|
|
13255
|
+
if (editing && inputRef.current) {
|
|
13256
|
+
inputRef.current.focus();
|
|
13257
|
+
inputRef.current.select();
|
|
13258
|
+
if (forceEdit) {
|
|
13259
|
+
onActivated?.();
|
|
13260
|
+
}
|
|
13261
|
+
}
|
|
13262
|
+
}, [editing]);
|
|
13263
|
+
const commit = React27.useCallback(() => {
|
|
13264
|
+
setEditing(false);
|
|
13265
|
+
if (type === "number") {
|
|
13266
|
+
const n = Number(draft);
|
|
13267
|
+
onCommit(Number.isNaN(n) || draft.trim() === "" ? 0 : n);
|
|
13268
|
+
} else {
|
|
13269
|
+
onCommit(draft);
|
|
13270
|
+
}
|
|
13271
|
+
}, [draft, onCommit, type]);
|
|
13272
|
+
const cancel = React27.useCallback(() => {
|
|
13273
|
+
setEditing(false);
|
|
13274
|
+
setDraft(String(value ?? ""));
|
|
13275
|
+
}, [value]);
|
|
13276
|
+
const handleKeyDown = React27.useCallback(
|
|
13277
|
+
(e) => {
|
|
13278
|
+
if (e.key === "Enter") {
|
|
13279
|
+
e.preventDefault();
|
|
13280
|
+
onEnterDown?.();
|
|
13281
|
+
commit();
|
|
13282
|
+
} else if (e.key === "Escape") {
|
|
13283
|
+
e.preventDefault();
|
|
13284
|
+
cancel();
|
|
13285
|
+
} else if (e.key === "Tab") {
|
|
13286
|
+
const direction = e.shiftKey ? -1 : 1;
|
|
13287
|
+
const handled = onTabOut?.(direction) ?? false;
|
|
13288
|
+
if (handled) {
|
|
13289
|
+
e.preventDefault();
|
|
13290
|
+
commit();
|
|
13291
|
+
}
|
|
13292
|
+
}
|
|
13293
|
+
},
|
|
13294
|
+
[commit, cancel, onTabOut, onEnterDown]
|
|
13295
|
+
);
|
|
13296
|
+
if (!editable) {
|
|
13297
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs truncate block px-1 py-1.5", children: String(value ?? "") });
|
|
13298
|
+
}
|
|
13299
|
+
if (type === "color") {
|
|
13300
|
+
const colorValue = String(
|
|
13301
|
+
value ?? defaultColorValue ?? "#94a3b8"
|
|
13302
|
+
);
|
|
13303
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5", children: [
|
|
13304
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
13305
|
+
"input",
|
|
13306
|
+
{
|
|
13307
|
+
type: "color",
|
|
13308
|
+
className: "h-7 w-8 cursor-pointer border border-border rounded p-0.5",
|
|
13309
|
+
value: colorValue,
|
|
13310
|
+
onChange: (e) => onCommit(e.target.value)
|
|
13311
|
+
}
|
|
13312
|
+
),
|
|
13313
|
+
clearable && value != null && /* @__PURE__ */ jsxRuntime.jsx(
|
|
13314
|
+
"button",
|
|
13315
|
+
{
|
|
13316
|
+
type: "button",
|
|
13317
|
+
className: "text-xs leading-none text-muted-foreground hover:text-foreground px-0.5",
|
|
13318
|
+
onClick: () => onCommit(void 0),
|
|
13319
|
+
children: "\xD7"
|
|
13320
|
+
}
|
|
13321
|
+
)
|
|
13322
|
+
] });
|
|
13323
|
+
}
|
|
13324
|
+
if (type === "select") {
|
|
13325
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
13326
|
+
Select,
|
|
13327
|
+
{
|
|
13328
|
+
value: String(value ?? ""),
|
|
13329
|
+
onValueChange: (v) => onCommit(v),
|
|
13330
|
+
children: [
|
|
13331
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { className: "h-7 text-xs", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, {}) }),
|
|
13332
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectContent, { children: (options ?? []).map((opt) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: opt, children: opt }, opt)) })
|
|
13333
|
+
]
|
|
13334
|
+
}
|
|
13335
|
+
);
|
|
13336
|
+
}
|
|
13337
|
+
if (!editing) {
|
|
13338
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
13339
|
+
"span",
|
|
13340
|
+
{
|
|
13341
|
+
className: "text-xs truncate block px-1 py-1.5 cursor-text hover:bg-muted/50 rounded",
|
|
13342
|
+
onClick: () => {
|
|
13343
|
+
setDraft(String(value ?? ""));
|
|
13344
|
+
setEditing(true);
|
|
13345
|
+
},
|
|
13346
|
+
role: "button",
|
|
13347
|
+
tabIndex: 0,
|
|
13348
|
+
onKeyDown: (e) => {
|
|
13349
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
13350
|
+
setDraft(String(value ?? ""));
|
|
13351
|
+
setEditing(true);
|
|
13352
|
+
}
|
|
13353
|
+
},
|
|
13354
|
+
children: String(value ?? "")
|
|
13355
|
+
}
|
|
13356
|
+
);
|
|
13357
|
+
}
|
|
13358
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
13359
|
+
"input",
|
|
13360
|
+
{
|
|
13361
|
+
ref: inputRef,
|
|
13362
|
+
type: type === "number" ? "number" : "text",
|
|
13363
|
+
className: "h-7 text-xs border border-input rounded-md px-2 bg-background focus:outline-none focus:ring-1 focus:ring-ring w-full",
|
|
13364
|
+
value: draft,
|
|
13365
|
+
onChange: (e) => setDraft(e.target.value),
|
|
13366
|
+
onKeyDown: handleKeyDown,
|
|
13367
|
+
onPaste: (e) => {
|
|
13368
|
+
const text = e.clipboardData.getData("text/plain") || e.clipboardData.getData("text");
|
|
13369
|
+
if (!text) return;
|
|
13370
|
+
const handled = onPaste?.(text) ?? false;
|
|
13371
|
+
if (handled) {
|
|
13372
|
+
e.preventDefault();
|
|
13373
|
+
setEditing(false);
|
|
13374
|
+
}
|
|
13375
|
+
},
|
|
13376
|
+
onBlur: commit
|
|
13377
|
+
}
|
|
13378
|
+
);
|
|
13379
|
+
}
|
|
13380
|
+
var SortableRow = React27__namespace.default.memo(function SortableRow2({
|
|
13381
|
+
id,
|
|
13382
|
+
highlighted,
|
|
13383
|
+
children
|
|
13384
|
+
}) {
|
|
13385
|
+
const t = useHazoUiT();
|
|
13386
|
+
const {
|
|
13387
|
+
attributes,
|
|
13388
|
+
listeners,
|
|
13389
|
+
setNodeRef,
|
|
13390
|
+
setActivatorNodeRef,
|
|
13391
|
+
transform,
|
|
13392
|
+
transition,
|
|
13393
|
+
isDragging
|
|
13394
|
+
} = sortable.useSortable({ id });
|
|
13395
|
+
const style = {
|
|
13396
|
+
transform: utilities.CSS.Transform.toString(transform),
|
|
13397
|
+
transition,
|
|
13398
|
+
opacity: isDragging ? 0.5 : 1
|
|
13399
|
+
};
|
|
13400
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
13401
|
+
TableRow2,
|
|
13402
|
+
{
|
|
13403
|
+
ref: setNodeRef,
|
|
13404
|
+
style,
|
|
13405
|
+
"data-sortable-id": id,
|
|
13406
|
+
className: cn(highlighted && "bg-blue-50 ring-1 ring-blue-300"),
|
|
13407
|
+
children: [
|
|
13408
|
+
/* @__PURE__ */ jsxRuntime.jsx(TableCell2, { className: "px-0 py-0.5 w-6", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
13409
|
+
"div",
|
|
13410
|
+
{
|
|
13411
|
+
ref: setActivatorNodeRef,
|
|
13412
|
+
className: "cursor-grab active:cursor-grabbing text-muted-foreground hover:text-foreground flex items-center justify-center",
|
|
13413
|
+
title: t("editable_data_table.drag_to_reorder"),
|
|
13414
|
+
...attributes,
|
|
13415
|
+
...listeners,
|
|
13416
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GripVertical, { className: "h-3.5 w-3.5" })
|
|
13417
|
+
}
|
|
13418
|
+
) }),
|
|
13419
|
+
children
|
|
13420
|
+
]
|
|
13421
|
+
}
|
|
13422
|
+
);
|
|
13423
|
+
});
|
|
13424
|
+
SortableRow.displayName = "SortableRow";
|
|
13425
|
+
var EditableTableRow = React27__namespace.default.memo(function EditableTableRow2({
|
|
13426
|
+
row,
|
|
13427
|
+
rowIndex,
|
|
13428
|
+
rowKey,
|
|
13429
|
+
isHighlighted,
|
|
13430
|
+
columns,
|
|
13431
|
+
activeEditColKey,
|
|
13432
|
+
deleteLabel,
|
|
13433
|
+
onCellCommit,
|
|
13434
|
+
onDeleteRow,
|
|
13435
|
+
onTabOutCell,
|
|
13436
|
+
onEnterDownCell,
|
|
13437
|
+
onPasteCell,
|
|
13438
|
+
onActivated
|
|
13439
|
+
}) {
|
|
13440
|
+
const renderCountRef = React27.useRef(0);
|
|
13441
|
+
renderCountRef.current += 1;
|
|
13442
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(SortableRow, { id: rowKey, highlighted: isHighlighted, children: [
|
|
13443
|
+
columns.map((col, colIndex) => {
|
|
13444
|
+
const resolvedOptions = typeof col.options === "function" ? col.options(row) : col.options;
|
|
13445
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
13446
|
+
TableCell2,
|
|
13447
|
+
{
|
|
13448
|
+
className: cn("px-1 py-0.5", col.width),
|
|
13449
|
+
"data-row-render-count": colIndex === 0 ? renderCountRef.current : void 0,
|
|
13450
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
13451
|
+
EditableCell,
|
|
13452
|
+
{
|
|
13453
|
+
value: row[col.key],
|
|
13454
|
+
type: col.type ?? "text",
|
|
13455
|
+
options: resolvedOptions,
|
|
13456
|
+
defaultColorValue: col.defaultValue?.(rowIndex),
|
|
13457
|
+
clearable: col.clearable,
|
|
13458
|
+
editable: col.editable !== false,
|
|
13459
|
+
forceEdit: activeEditColKey === col.key,
|
|
13460
|
+
onTabOut: (direction) => onTabOutCell(rowIndex, col.key, direction),
|
|
13461
|
+
onEnterDown: () => onEnterDownCell(rowIndex, col.key),
|
|
13462
|
+
onPaste: (text) => onPasteCell(rowIndex, col.key, text),
|
|
13463
|
+
onActivated,
|
|
13464
|
+
onCommit: (v) => onCellCommit(rowIndex, col.key, v)
|
|
13465
|
+
}
|
|
13466
|
+
)
|
|
13467
|
+
},
|
|
13468
|
+
col.key
|
|
13469
|
+
);
|
|
13470
|
+
}),
|
|
13471
|
+
/* @__PURE__ */ jsxRuntime.jsx(TableCell2, { className: "px-0 py-0.5 w-8", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
13472
|
+
Button,
|
|
13473
|
+
{
|
|
13474
|
+
variant: "ghost",
|
|
13475
|
+
size: "icon",
|
|
13476
|
+
className: "h-7 w-7 text-muted-foreground hover:text-destructive",
|
|
13477
|
+
onClick: () => onDeleteRow(rowIndex),
|
|
13478
|
+
"aria-label": deleteLabel,
|
|
13479
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3 w-3" })
|
|
13480
|
+
}
|
|
13481
|
+
) })
|
|
13482
|
+
] });
|
|
13483
|
+
});
|
|
13484
|
+
EditableTableRow.displayName = "EditableTableRow";
|
|
13485
|
+
function EditableDataTable({
|
|
13486
|
+
columns,
|
|
13487
|
+
rows,
|
|
13488
|
+
onChange,
|
|
13489
|
+
getRowKey,
|
|
13490
|
+
createRow,
|
|
13491
|
+
highlightedKey,
|
|
13492
|
+
className,
|
|
13493
|
+
label
|
|
13494
|
+
}) {
|
|
13495
|
+
const t = useHazoUiT();
|
|
13496
|
+
const editableColumns = React27.useMemo(
|
|
13497
|
+
() => columns.filter(
|
|
13498
|
+
(col) => col.editable !== false && (col.type ?? "text") !== "select" && (col.type ?? "text") !== "color"
|
|
13499
|
+
).map((col) => col.key),
|
|
13500
|
+
[columns]
|
|
13501
|
+
);
|
|
13502
|
+
const [activeEditCell, setActiveEditCell] = React27.useState(null);
|
|
13503
|
+
const rowsRef = React27.useRef(rows);
|
|
13504
|
+
rowsRef.current = rows;
|
|
13505
|
+
const onChangeRef = React27.useRef(onChange);
|
|
13506
|
+
onChangeRef.current = onChange;
|
|
13507
|
+
const handleTabOut = React27.useCallback(
|
|
13508
|
+
(rowIndex, colKey, direction) => {
|
|
13509
|
+
const idx = editableColumns.indexOf(colKey);
|
|
13510
|
+
if (idx === -1) return false;
|
|
13511
|
+
if (direction === 1) {
|
|
13512
|
+
if (idx < editableColumns.length - 1) {
|
|
13513
|
+
setActiveEditCell({ row: rowIndex, col: editableColumns[idx + 1] });
|
|
13514
|
+
return true;
|
|
13515
|
+
}
|
|
13516
|
+
if (rowIndex + 1 < rows.length) {
|
|
13517
|
+
setActiveEditCell({ row: rowIndex + 1, col: editableColumns[0] });
|
|
13518
|
+
return true;
|
|
13519
|
+
}
|
|
13520
|
+
return false;
|
|
13521
|
+
}
|
|
13522
|
+
if (idx > 0) {
|
|
13523
|
+
setActiveEditCell({ row: rowIndex, col: editableColumns[idx - 1] });
|
|
13524
|
+
return true;
|
|
13525
|
+
}
|
|
13526
|
+
if (rowIndex - 1 >= 0) {
|
|
13527
|
+
setActiveEditCell({
|
|
13528
|
+
row: rowIndex - 1,
|
|
13529
|
+
col: editableColumns[editableColumns.length - 1]
|
|
13530
|
+
});
|
|
13531
|
+
return true;
|
|
13532
|
+
}
|
|
13533
|
+
return false;
|
|
13534
|
+
},
|
|
13535
|
+
[editableColumns, rows.length]
|
|
13536
|
+
);
|
|
13537
|
+
const handleEnterDown = React27.useCallback(
|
|
13538
|
+
(rowIndex, colKey) => {
|
|
13539
|
+
if (rowIndex + 1 < rows.length) {
|
|
13540
|
+
setActiveEditCell({ row: rowIndex + 1, col: colKey });
|
|
13541
|
+
return true;
|
|
13542
|
+
}
|
|
13543
|
+
return false;
|
|
13544
|
+
},
|
|
13545
|
+
[rows.length]
|
|
13546
|
+
);
|
|
13547
|
+
const handlePaste = React27.useCallback(
|
|
13548
|
+
(rowIndex, colKey, text) => {
|
|
13549
|
+
const startColIdx = editableColumns.indexOf(
|
|
13550
|
+
colKey
|
|
13551
|
+
);
|
|
13552
|
+
if (startColIdx === -1) return false;
|
|
13553
|
+
const normalized = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
13554
|
+
const lines = normalized.split("\n");
|
|
13555
|
+
if (lines.length > 1 && lines[lines.length - 1] === "") {
|
|
13556
|
+
lines.pop();
|
|
13557
|
+
}
|
|
13558
|
+
const hasTab = normalized.includes(" ");
|
|
13559
|
+
const delimiter = hasTab ? " " : ",";
|
|
13560
|
+
const grid = lines.map((line) => line.split(delimiter));
|
|
13561
|
+
const isSingleCell = grid.length === 1 && grid[0].length === 1;
|
|
13562
|
+
if (isSingleCell) return false;
|
|
13563
|
+
const currentRows = rowsRef.current;
|
|
13564
|
+
const nextRows = currentRows.slice();
|
|
13565
|
+
grid.forEach((lineCells, rowOffset) => {
|
|
13566
|
+
const targetRowIndex = rowIndex + rowOffset;
|
|
13567
|
+
while (targetRowIndex >= nextRows.length) {
|
|
13568
|
+
nextRows.push(createRow());
|
|
13569
|
+
}
|
|
13570
|
+
const targetRow = {
|
|
13571
|
+
...nextRows[targetRowIndex]
|
|
13572
|
+
};
|
|
13573
|
+
lineCells.forEach((cellValue, colOffset) => {
|
|
13574
|
+
const targetColIdx = startColIdx + colOffset;
|
|
13575
|
+
if (targetColIdx >= editableColumns.length) return;
|
|
13576
|
+
const targetColKey = editableColumns[targetColIdx];
|
|
13577
|
+
const col = columns.find((c) => c.key === targetColKey);
|
|
13578
|
+
if (col?.type === "number") {
|
|
13579
|
+
const n = Number(cellValue);
|
|
13580
|
+
targetRow[targetColKey] = Number.isNaN(n) || cellValue.trim() === "" ? 0 : n;
|
|
13581
|
+
} else {
|
|
13582
|
+
targetRow[targetColKey] = cellValue;
|
|
13583
|
+
}
|
|
13584
|
+
});
|
|
13585
|
+
nextRows[targetRowIndex] = targetRow;
|
|
13586
|
+
});
|
|
13587
|
+
onChangeRef.current(nextRows);
|
|
13588
|
+
return true;
|
|
13589
|
+
},
|
|
13590
|
+
[editableColumns, columns, createRow]
|
|
13591
|
+
);
|
|
13592
|
+
const handleCellChange = React27.useCallback(
|
|
13593
|
+
(rowIndex, colKey, value) => {
|
|
13594
|
+
const currentRows = rowsRef.current;
|
|
13595
|
+
const next = currentRows.map(
|
|
13596
|
+
(row, i) => i === rowIndex ? { ...row, [colKey]: value } : row
|
|
13597
|
+
);
|
|
13598
|
+
onChangeRef.current(next);
|
|
13599
|
+
},
|
|
13600
|
+
[]
|
|
13601
|
+
);
|
|
13602
|
+
const handleDelete = React27.useCallback((rowIndex) => {
|
|
13603
|
+
const currentRows = rowsRef.current;
|
|
13604
|
+
onChangeRef.current(currentRows.filter((_, i) => i !== rowIndex));
|
|
13605
|
+
}, []);
|
|
13606
|
+
const onCellActivated = React27.useCallback(() => setActiveEditCell(null), []);
|
|
13607
|
+
const handleAdd = () => {
|
|
13608
|
+
onChange([...rows, createRow()]);
|
|
13609
|
+
};
|
|
13610
|
+
const sensors = core$1.useSensors(
|
|
13611
|
+
core$1.useSensor(core$1.PointerSensor),
|
|
13612
|
+
core$1.useSensor(core$1.KeyboardSensor, {
|
|
13613
|
+
coordinateGetter: sortable.sortableKeyboardCoordinates
|
|
13614
|
+
})
|
|
13615
|
+
);
|
|
13616
|
+
const rowKeys = React27.useMemo(
|
|
13617
|
+
() => rows.map((row, i) => getRowKey(row, i)),
|
|
13618
|
+
[rows, getRowKey]
|
|
13619
|
+
);
|
|
13620
|
+
const handleDragEnd = React27.useCallback(
|
|
13621
|
+
(event) => {
|
|
13622
|
+
const { active, over } = event;
|
|
13623
|
+
if (!over || active.id === over.id) return;
|
|
13624
|
+
const oldIndex = rows.findIndex(
|
|
13625
|
+
(row, i) => getRowKey(row, i) === active.id
|
|
13626
|
+
);
|
|
13627
|
+
const newIndex = rows.findIndex(
|
|
13628
|
+
(row, i) => getRowKey(row, i) === over.id
|
|
13629
|
+
);
|
|
13630
|
+
if (oldIndex === -1 || newIndex === -1) return;
|
|
13631
|
+
onChange(sortable.arrayMove(rows, oldIndex, newIndex));
|
|
13632
|
+
},
|
|
13633
|
+
[rows, getRowKey, onChange]
|
|
13634
|
+
);
|
|
13635
|
+
return (
|
|
13636
|
+
// DndContext wraps the whole component rather than just <TableBody> —
|
|
13637
|
+
// it renders its own hidden accessibility helpers (RestoreFocus,
|
|
13638
|
+
// live-region announcer) as DOM siblings of its children. Nesting it
|
|
13639
|
+
// directly inside <tbody> would make those helpers invalid children of
|
|
13640
|
+
// <tbody> (only <tr> is valid there); wrapping the outer <div> instead
|
|
13641
|
+
// gives them a valid home. <SortableContext> is a plain context
|
|
13642
|
+
// provider with no DOM output of its own, so it can stay right inside
|
|
13643
|
+
// <TableBody>, wrapping only the actual <tr> rows.
|
|
13644
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
13645
|
+
core$1.DndContext,
|
|
13646
|
+
{
|
|
13647
|
+
sensors,
|
|
13648
|
+
collisionDetection: core$1.closestCenter,
|
|
13649
|
+
onDragEnd: handleDragEnd,
|
|
13650
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-editable-data-table": true, className: cn("flex flex-col gap-1", className), children: [
|
|
13651
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-1", children: [
|
|
13652
|
+
label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: label }),
|
|
13653
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
13654
|
+
Button,
|
|
13655
|
+
{
|
|
13656
|
+
variant: "ghost",
|
|
13657
|
+
size: "sm",
|
|
13658
|
+
className: "h-6 px-2 text-xs ml-auto",
|
|
13659
|
+
onClick: handleAdd,
|
|
13660
|
+
"aria-label": t("editable_data_table.add"),
|
|
13661
|
+
children: [
|
|
13662
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-3 w-3 mr-1" }),
|
|
13663
|
+
t("editable_data_table.add")
|
|
13664
|
+
]
|
|
13665
|
+
}
|
|
13666
|
+
)
|
|
13667
|
+
] }),
|
|
13668
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Table2, { children: [
|
|
13669
|
+
/* @__PURE__ */ jsxRuntime.jsx(TableHeader2, { children: /* @__PURE__ */ jsxRuntime.jsxs(TableRow2, { className: "bg-muted/60 hover:bg-muted/60 border-b-2 border-border", children: [
|
|
13670
|
+
/* @__PURE__ */ jsxRuntime.jsx(TableHead, { className: "w-6 px-0" }),
|
|
13671
|
+
columns.map((col) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
13672
|
+
TableHead,
|
|
13673
|
+
{
|
|
13674
|
+
className: cn(
|
|
13675
|
+
"text-[11px] font-semibold uppercase tracking-wide text-foreground px-1 py-1 h-8",
|
|
13676
|
+
col.width
|
|
13677
|
+
),
|
|
13678
|
+
children: col.label
|
|
13679
|
+
},
|
|
13680
|
+
col.key
|
|
13681
|
+
)),
|
|
13682
|
+
/* @__PURE__ */ jsxRuntime.jsx(TableHead, { className: "w-8 px-0" })
|
|
13683
|
+
] }) }),
|
|
13684
|
+
/* @__PURE__ */ jsxRuntime.jsxs(TableBody, { children: [
|
|
13685
|
+
rows.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(TableRow2, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
13686
|
+
TableCell2,
|
|
13687
|
+
{
|
|
13688
|
+
colSpan: columns.length + 2,
|
|
13689
|
+
className: "text-center text-xs text-muted-foreground italic py-4",
|
|
13690
|
+
children: t("editable_data_table.empty")
|
|
13691
|
+
}
|
|
13692
|
+
) }),
|
|
13693
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
13694
|
+
sortable.SortableContext,
|
|
13695
|
+
{
|
|
13696
|
+
items: rowKeys,
|
|
13697
|
+
strategy: sortable.verticalListSortingStrategy,
|
|
13698
|
+
children: rows.map((row, rowIndex) => {
|
|
13699
|
+
const key = rowKeys[rowIndex];
|
|
13700
|
+
const isHighlighted = highlightedKey != null && key === highlightedKey;
|
|
13701
|
+
const activeEditColKey = activeEditCell?.row === rowIndex ? activeEditCell.col : null;
|
|
13702
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
13703
|
+
EditableTableRow,
|
|
13704
|
+
{
|
|
13705
|
+
row,
|
|
13706
|
+
rowIndex,
|
|
13707
|
+
rowKey: key,
|
|
13708
|
+
isHighlighted,
|
|
13709
|
+
columns,
|
|
13710
|
+
activeEditColKey,
|
|
13711
|
+
deleteLabel: t("editable_data_table.delete"),
|
|
13712
|
+
onCellCommit: handleCellChange,
|
|
13713
|
+
onDeleteRow: handleDelete,
|
|
13714
|
+
onTabOutCell: handleTabOut,
|
|
13715
|
+
onEnterDownCell: handleEnterDown,
|
|
13716
|
+
onPasteCell: handlePaste,
|
|
13717
|
+
onActivated: onCellActivated
|
|
13718
|
+
},
|
|
13719
|
+
key
|
|
13720
|
+
);
|
|
13721
|
+
})
|
|
13722
|
+
}
|
|
13723
|
+
)
|
|
13724
|
+
] })
|
|
13725
|
+
] })
|
|
13726
|
+
] })
|
|
13727
|
+
}
|
|
13728
|
+
)
|
|
13729
|
+
);
|
|
13730
|
+
}
|
|
13199
13731
|
|
|
13200
13732
|
Object.defineProperty(exports, "rawToast", {
|
|
13201
13733
|
enumerable: true,
|
|
@@ -13288,6 +13820,7 @@ exports.DropdownMenuSub = DropdownMenuSub;
|
|
|
13288
13820
|
exports.DropdownMenuSubContent = DropdownMenuSubContent;
|
|
13289
13821
|
exports.DropdownMenuSubTrigger = DropdownMenuSubTrigger;
|
|
13290
13822
|
exports.DropdownMenuTrigger = DropdownMenuTrigger;
|
|
13823
|
+
exports.EditableDataTable = EditableDataTable;
|
|
13291
13824
|
exports.EmptyState = EmptyState;
|
|
13292
13825
|
exports.ErrorBanner = ErrorBanner;
|
|
13293
13826
|
exports.ErrorPage = ErrorPage;
|