twico-ui 1.33.1 → 1.34.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/CHANGELOG.md +13 -0
- package/dist/index.cjs +267 -158
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.mjs +267 -158
- package/dist/index.mjs.map +1 -1
- package/llms.txt +4 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to Twico UI are documented here (newest first). This file is generated automatically on each release by [semantic-release](https://semantic-release.gitbook.io/) from [Conventional Commits](https://www.conventionalcommits.org/) — do not edit it by hand. The same notes appear on each [GitHub Release](https://github.com/EncryptionL/twico-ui/releases).
|
|
4
4
|
|
|
5
|
+
# [1.34.0](https://github.com/EncryptionL/twico-ui/compare/v1.33.1...v1.34.0) (2026-08-14)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **slider:** harden editable inputs (a11y label, mobile minus, form Enter) ([8acc6aa](https://github.com/EncryptionL/twico-ui/commit/8acc6aae0d805fa57da3622595423b5658b61710)), closes [#351](https://github.com/EncryptionL/twico-ui/issues/351)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* **datatable:** expandable/collapsible rows via renderRowDetail (first pass) ([54a4954](https://github.com/EncryptionL/twico-ui/commit/54a495485f3430a37c4ce4191a96653a286d8307))
|
|
16
|
+
* **slider:** editable numeric inputs — type an exact value, synced with thumbs ([b804c65](https://github.com/EncryptionL/twico-ui/commit/b804c650e416a01992a1fc13d67bed2ec5e45250))
|
|
17
|
+
|
|
5
18
|
## [1.33.1](https://github.com/EncryptionL/twico-ui/compare/v1.33.0...v1.33.1) (2026-08-11)
|
|
6
19
|
|
|
7
20
|
|
package/dist/index.cjs
CHANGED
|
@@ -8383,6 +8383,16 @@ var DT_CSS = `
|
|
|
8383
8383
|
.twc-dt__group-sub span { font-variant-numeric: tabular-nums; }
|
|
8384
8384
|
.twc-dt__group-sub span b { color: var(--color-text); }
|
|
8385
8385
|
|
|
8386
|
+
/* #350: expandable row detail (renderRowDetail) \u2014 leading chevron column + full-width detail row */
|
|
8387
|
+
.twc-dt__expand-cell { text-align: center; padding-inline: 0; }
|
|
8388
|
+
.twc-dt__expand-btn { display: inline-grid; place-items: center; width: 24px; height: 24px; border: none; padding: 0; background: transparent; color: var(--color-text-subtle); cursor: pointer; border-radius: var(--radius-sm); }
|
|
8389
|
+
.twc-dt__expand-btn:hover { color: var(--color-text); background: color-mix(in srgb, currentColor 12%, transparent); }
|
|
8390
|
+
.twc-dt__expand-btn:focus-visible { outline: none; box-shadow: var(--ring); }
|
|
8391
|
+
.twc-dt__expand-chev { display: inline-flex; transform: rotate(-90deg); transition: transform var(--duration-base) var(--ease-spring); }
|
|
8392
|
+
.twc-dt__expand-chev[data-open] { transform: rotate(0deg); color: var(--color-primary); }
|
|
8393
|
+
.twc-dt__expand-chev svg { width: 16px; height: 16px; }
|
|
8394
|
+
.twc-dt__detail-cell { box-sizing: border-box; background: var(--color-surface-sunken); border-bottom: var(--border-thin) solid var(--color-divider); padding: 12px 16px; }
|
|
8395
|
+
|
|
8386
8396
|
/* Pivot view */
|
|
8387
8397
|
.twc-dt__pivot { table-layout: auto; width: 100%; }
|
|
8388
8398
|
.twc-dt__pivot th, .twc-dt__pivot td { white-space: nowrap; }
|
|
@@ -9330,6 +9340,13 @@ function Datatable({
|
|
|
9330
9340
|
virtualized = false,
|
|
9331
9341
|
overscan = 8,
|
|
9332
9342
|
rowHeight,
|
|
9343
|
+
// #350 (first pass): expandable/collapsible rows. `renderRowDetail(row)` returns the detail panel for a
|
|
9344
|
+
// row (return null/undefined ⇒ that row isn't expandable). Presence of the fn enables a leading chevron
|
|
9345
|
+
// column + a full-width detail <tr> under each expanded row. Expansion is uncontrolled unless
|
|
9346
|
+
// `expandedRowIds` is supplied; `onExpandedRowsChange` fires the next expanded-key array on every toggle.
|
|
9347
|
+
renderRowDetail,
|
|
9348
|
+
expandedRowIds,
|
|
9349
|
+
onExpandedRowsChange,
|
|
9333
9350
|
className = "",
|
|
9334
9351
|
...rest
|
|
9335
9352
|
}) {
|
|
@@ -9536,6 +9553,12 @@ function Datatable({
|
|
|
9536
9553
|
const gridRef = React51__namespace.default.useRef(null);
|
|
9537
9554
|
const [groupBy, setGroupBy] = React51__namespace.default.useState(rowGrouping || []);
|
|
9538
9555
|
const [collapsed, setCollapsed] = React51__namespace.default.useState(() => /* @__PURE__ */ new Set());
|
|
9556
|
+
const [internalExpanded, setInternalExpanded] = React51__namespace.default.useState(() => /* @__PURE__ */ new Set());
|
|
9557
|
+
const expandControlled = expandedRowIds !== void 0;
|
|
9558
|
+
const expandedSet = React51__namespace.default.useMemo(
|
|
9559
|
+
() => new Set(expandControlled ? expandedRowIds : internalExpanded),
|
|
9560
|
+
[expandControlled, expandedRowIds, internalExpanded]
|
|
9561
|
+
);
|
|
9539
9562
|
const [pinnedRows, setPinnedRows] = React51__namespace.default.useState({ top: [], bottom: [] });
|
|
9540
9563
|
const [headH, setHeadH] = React51__namespace.default.useState(41);
|
|
9541
9564
|
const theadRef = React51__namespace.default.useRef(null);
|
|
@@ -9937,7 +9960,9 @@ function Datatable({
|
|
|
9937
9960
|
}, [pins, effectiveHidden, visibleCols, colByField, order]);
|
|
9938
9961
|
const NUM_W = 56;
|
|
9939
9962
|
const CHK_W = checkboxSelection ? rowReorder ? 68 : 44 : 0;
|
|
9940
|
-
const
|
|
9963
|
+
const hasRowDetail = typeof renderRowDetail === "function";
|
|
9964
|
+
const EXP_W = hasRowDetail ? 44 : 0;
|
|
9965
|
+
const numLeft = EXP_W + CHK_W;
|
|
9941
9966
|
const leadW = numLeft + (showRowNum ? NUM_W : 0);
|
|
9942
9967
|
const visLeft = React51__namespace.default.useMemo(() => pins.left.filter((f) => colByField[f] && !effectiveHidden.has(f)), [pins.left, colByField, effectiveHidden]);
|
|
9943
9968
|
const visRight = React51__namespace.default.useMemo(() => pins.right.filter((f) => colByField[f] && !effectiveHidden.has(f)), [pins.right, colByField, effectiveHidden]);
|
|
@@ -10532,6 +10557,12 @@ function Datatable({
|
|
|
10532
10557
|
return n;
|
|
10533
10558
|
});
|
|
10534
10559
|
}
|
|
10560
|
+
function toggleRowDetail(k) {
|
|
10561
|
+
const n = new Set(expandedSet);
|
|
10562
|
+
n.has(k) ? n.delete(k) : n.add(k);
|
|
10563
|
+
onExpandedRowsChange?.([...n]);
|
|
10564
|
+
if (!expandControlled) setInternalExpanded(n);
|
|
10565
|
+
}
|
|
10535
10566
|
function handleRowClick(e, k, row) {
|
|
10536
10567
|
if (selectionMode === "none") return;
|
|
10537
10568
|
if (e.target.closest("button, a, input, select, .twc-dt__check, .twc-dt__editor-wrap")) return;
|
|
@@ -11378,7 +11409,7 @@ function Datatable({
|
|
|
11378
11409
|
const manageableCols = cols.filter((c) => c.field !== "__pinactions__");
|
|
11379
11410
|
const visibleColCount = manageableCols.filter((c) => !effectiveHidden.has(c.field)).length;
|
|
11380
11411
|
const rppOptions = Array.from(new Set([...pageSizeOptions || [], pageSize].filter((n) => n > 0))).sort((a, b) => a - b).map((n) => ({ value: String(n), label: String(n) }));
|
|
11381
|
-
const totalCols = ordered.length + (checkboxSelection ? 1 : 0) + (showRowNum ? 1 : 0);
|
|
11412
|
+
const totalCols = ordered.length + (checkboxSelection ? 1 : 0) + (showRowNum ? 1 : 0) + (hasRowDetail ? 1 : 0);
|
|
11382
11413
|
function renderGroupRow(item) {
|
|
11383
11414
|
const subs = aggOn ? subtotalText(item.rows) : [];
|
|
11384
11415
|
return /* @__PURE__ */ jsxRuntime.jsx("tr", { className: "twc-dt__group-row", role: "row", children: /* @__PURE__ */ jsxRuntime.jsxs("td", { className: "twc-dt__group-cell", role: "gridcell", colSpan: totalCols, style: { maxWidth: "none" }, children: [
|
|
@@ -11432,6 +11463,9 @@ function Datatable({
|
|
|
11432
11463
|
function renderLeaf(row, ri, pinSide, midIdx) {
|
|
11433
11464
|
const k = keyOf(row, ri);
|
|
11434
11465
|
const sel = selected.has(k);
|
|
11466
|
+
const detail = hasRowDetail && !pinSide ? renderRowDetail(row) : null;
|
|
11467
|
+
const expandable = detail != null;
|
|
11468
|
+
const open = expandable && expandedSet.has(k);
|
|
11435
11469
|
const rowActive = selectionMode === "row" && activeRowVal === k;
|
|
11436
11470
|
const stickyStyle = pinSide === "top" ? { position: "sticky", top: headH, zIndex: 5 } : pinSide === "bottom" ? { position: "sticky", bottom: 0, zIndex: 5 } : void 0;
|
|
11437
11471
|
const h = rowHeights[k];
|
|
@@ -11439,159 +11473,165 @@ function Datatable({
|
|
|
11439
11473
|
const reorderable = canReorderRows && !pinSide;
|
|
11440
11474
|
const grabbed = rowGrab && rowGrab.key === k;
|
|
11441
11475
|
const grabTarget = rowGrab && !grabbed && midIdx != null && rowGrab.index === midIdx ? rowGrab.index >= keyIndexMid.get(rowGrab.key) ? "after" : "before" : void 0;
|
|
11442
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
11443
|
-
|
|
11444
|
-
|
|
11445
|
-
|
|
11446
|
-
|
|
11447
|
-
|
|
11448
|
-
|
|
11449
|
-
|
|
11450
|
-
|
|
11451
|
-
|
|
11452
|
-
|
|
11453
|
-
|
|
11454
|
-
|
|
11455
|
-
|
|
11456
|
-
|
|
11457
|
-
|
|
11458
|
-
|
|
11459
|
-
|
|
11460
|
-
|
|
11461
|
-
|
|
11462
|
-
|
|
11463
|
-
|
|
11464
|
-
|
|
11465
|
-
|
|
11466
|
-
|
|
11467
|
-
|
|
11468
|
-
|
|
11469
|
-
|
|
11470
|
-
|
|
11471
|
-
|
|
11472
|
-
|
|
11473
|
-
|
|
11474
|
-
|
|
11475
|
-
|
|
11476
|
-
|
|
11477
|
-
|
|
11478
|
-
|
|
11479
|
-
|
|
11480
|
-
|
|
11481
|
-
|
|
11482
|
-
/* @__PURE__ */ jsxRuntime.
|
|
11483
|
-
|
|
11484
|
-
|
|
11485
|
-
|
|
11476
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(React51__namespace.default.Fragment, { children: [
|
|
11477
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
11478
|
+
"tr",
|
|
11479
|
+
{
|
|
11480
|
+
className: "twc-dt__row",
|
|
11481
|
+
role: "row",
|
|
11482
|
+
"aria-rowindex": (paginated && !serverMode ? pageVal * sizeVal : 0) + ri + 2,
|
|
11483
|
+
"aria-selected": (checkboxSelection ? sel : rowActive) || void 0,
|
|
11484
|
+
"data-op": row.__diffop || void 0,
|
|
11485
|
+
"data-selected": sel || void 0,
|
|
11486
|
+
"data-active": rowActive || void 0,
|
|
11487
|
+
"data-pinned-row": pinSide || void 0,
|
|
11488
|
+
"data-reorderable": reorderable || void 0,
|
|
11489
|
+
"data-row-dragging": rowDrag.from === k || void 0,
|
|
11490
|
+
"data-row-grabbed": grabbed || void 0,
|
|
11491
|
+
"data-row-grabtarget": grabTarget,
|
|
11492
|
+
"data-row-dropbefore": reorderable && rowDrag.over === k && !rowDrag.after || void 0,
|
|
11493
|
+
"data-row-dropafter": reorderable && rowDrag.over === k && rowDrag.after || void 0,
|
|
11494
|
+
"data-vrow": virtualizing && midIdx != null ? String(k) : void 0,
|
|
11495
|
+
"data-selectable": selectionMode !== "none" || void 0,
|
|
11496
|
+
style: Object.keys(rowStyle).length ? rowStyle : void 0,
|
|
11497
|
+
draggable: reorderable || void 0,
|
|
11498
|
+
onDragStart: reorderable ? (e) => {
|
|
11499
|
+
setRowDrag({ from: k, over: null, after: false });
|
|
11500
|
+
e.dataTransfer.effectAllowed = "move";
|
|
11501
|
+
e.dataTransfer.setData("text/plain", String(k));
|
|
11502
|
+
} : void 0,
|
|
11503
|
+
onDragOver: reorderable && rowDrag.from != null ? (e) => {
|
|
11504
|
+
e.preventDefault();
|
|
11505
|
+
const r2 = e.currentTarget.getBoundingClientRect();
|
|
11506
|
+
const after = e.clientY > r2.top + r2.height / 2;
|
|
11507
|
+
setRowDrag((d) => d.over === k && d.after === after ? d : { ...d, over: k, after });
|
|
11508
|
+
} : void 0,
|
|
11509
|
+
onDrop: reorderable && rowDrag.from != null ? (e) => {
|
|
11510
|
+
e.preventDefault();
|
|
11511
|
+
onRowDrop(k);
|
|
11512
|
+
} : void 0,
|
|
11513
|
+
onDragEnd: reorderable ? () => setRowDrag({ from: null, over: null, after: false }) : void 0,
|
|
11514
|
+
onClick: (e) => handleRowClick(e, k, row),
|
|
11515
|
+
children: [
|
|
11516
|
+
hasRowDetail ? /* @__PURE__ */ jsxRuntime.jsx("td", { className: "twc-dt__td twc-dt__expand-cell", role: "gridcell", "data-pin": "left", "data-pin-edge": checkboxSelection || showRowNum || visLeft.length ? void 0 : "left", style: { insetInlineStart: 0, width: EXP_W }, children: expandable ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dt__expand-btn", "aria-expanded": open, "aria-controls": open ? `${gridId}-detail-${ri}` : void 0, "aria-label": open ? "Collapse row" : "Expand row", onClick: (e) => {
|
|
11517
|
+
e.stopPropagation();
|
|
11518
|
+
toggleRowDetail(k);
|
|
11519
|
+
}, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dt__expand-chev", "data-open": open || void 0, children: /* @__PURE__ */ jsxRuntime.jsx(Svg, { d: I.chevDown }) }) }) : null }) : null,
|
|
11520
|
+
checkboxSelection ? /* @__PURE__ */ jsxRuntime.jsxs("td", { className: "twc-dt__td", role: "gridcell", "data-pin": "left", "data-pin-edge": visLeft.length || showRowNum ? void 0 : "left", style: { insetInlineStart: EXP_W, width: CHK_W }, children: [
|
|
11521
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: 4 }, children: [
|
|
11522
|
+
reorderable ? rowHandle(k, midIdx, row) : null,
|
|
11523
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11524
|
+
"span",
|
|
11525
|
+
{
|
|
11526
|
+
className: "twc-dt__check",
|
|
11527
|
+
"data-checked": sel || void 0,
|
|
11528
|
+
onClick: () => toggleRow(k),
|
|
11529
|
+
role: "checkbox",
|
|
11530
|
+
"aria-checked": sel,
|
|
11531
|
+
"aria-label": "Select row",
|
|
11532
|
+
tabIndex: 0,
|
|
11533
|
+
onKeyDown: (e) => {
|
|
11534
|
+
if (e.key === " " || e.key === "Enter") {
|
|
11535
|
+
e.preventDefault();
|
|
11536
|
+
toggleRow(k);
|
|
11537
|
+
}
|
|
11538
|
+
},
|
|
11539
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Svg, { d: I.check })
|
|
11540
|
+
}
|
|
11541
|
+
)
|
|
11542
|
+
] }),
|
|
11543
|
+
rowResize && !pinSide ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dt__row-resizer", title: "Drag to resize row", onPointerDown: (e) => startRowResize(e, k, e.currentTarget.closest("tr")), onClick: (e) => e.stopPropagation() }) : null
|
|
11544
|
+
] }) : null,
|
|
11545
|
+
showRowNum ? /* @__PURE__ */ jsxRuntime.jsx("td", { className: "twc-dt__td twc-dt__rownum", role: "gridcell", "aria-hidden": "true", "data-pin": "left", "data-pin-edge": visLeft.length ? void 0 : "left", style: { insetInlineStart: numLeft, width: NUM_W }, children: typeof ri === "number" ? (paginated || serverMode ? pageVal * sizeVal : 0) + ri + 1 : "" }) : null,
|
|
11546
|
+
ordered.map((c, ci) => {
|
|
11547
|
+
const st = stickyOf(c.field);
|
|
11548
|
+
const val = getColVal(c, row);
|
|
11549
|
+
const isActions = c.type === "actions";
|
|
11550
|
+
const editable = isColEditable(c);
|
|
11551
|
+
const isEditing = editing && editing.key === k && editing.field === c.field;
|
|
11552
|
+
const cellActive = selectionMode === "cell" && activeCell && activeCell.key === k && activeCell.field === c.field;
|
|
11553
|
+
const cellSelected = selectionMode === "cell" && cellRect && ri >= cellRect.r0 && ri <= cellRect.r1 && ci >= cellRect.c0 && ci <= cellRect.c1;
|
|
11554
|
+
const cellFlash = clipFx && clipFx.rect && ri >= clipFx.rect.r0 && ri <= clipFx.rect.r1 && ci >= clipFx.rect.c0 && ci <= clipFx.rect.c1;
|
|
11555
|
+
const cellId = selectionMode === "cell" ? `${gridId}-${ri}-${ci}` : void 0;
|
|
11556
|
+
const display = c.valueFormatter ? c.valueFormatter(val, row) : val;
|
|
11557
|
+
const cellTitle = !isActions && !c.renderCell && !isEditing && (typeof display === "string" || typeof display === "number") ? String(display) : void 0;
|
|
11558
|
+
const cellClass = c.cellClassName ? c.cellClassName(val, row) : void 0;
|
|
11559
|
+
const cellSty = c.cellStyle ? c.cellStyle(val, row) : void 0;
|
|
11560
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
11561
|
+
"td",
|
|
11486
11562
|
{
|
|
11487
|
-
|
|
11488
|
-
|
|
11489
|
-
|
|
11490
|
-
|
|
11491
|
-
"
|
|
11492
|
-
"aria-
|
|
11493
|
-
tabIndex: 0,
|
|
11494
|
-
|
|
11495
|
-
|
|
11496
|
-
|
|
11497
|
-
|
|
11498
|
-
|
|
11499
|
-
|
|
11500
|
-
|
|
11501
|
-
|
|
11502
|
-
|
|
11503
|
-
|
|
11504
|
-
|
|
11505
|
-
|
|
11506
|
-
|
|
11507
|
-
|
|
11508
|
-
|
|
11509
|
-
|
|
11510
|
-
|
|
11511
|
-
|
|
11512
|
-
|
|
11513
|
-
|
|
11514
|
-
|
|
11515
|
-
|
|
11516
|
-
|
|
11517
|
-
|
|
11518
|
-
|
|
11519
|
-
|
|
11520
|
-
|
|
11521
|
-
|
|
11522
|
-
|
|
11523
|
-
|
|
11524
|
-
|
|
11525
|
-
|
|
11526
|
-
|
|
11527
|
-
|
|
11528
|
-
|
|
11529
|
-
|
|
11530
|
-
|
|
11531
|
-
|
|
11532
|
-
|
|
11533
|
-
|
|
11534
|
-
|
|
11535
|
-
|
|
11536
|
-
|
|
11537
|
-
"data-cell-selected": cellSelected || void 0,
|
|
11538
|
-
"data-copied": cellFlash || void 0,
|
|
11539
|
-
"data-wrap": effectiveWrapped.has(c.field) || void 0,
|
|
11540
|
-
"data-pin": st.pin,
|
|
11541
|
-
"data-pin-edge": st.edge,
|
|
11542
|
-
style: { width: widthOf(c), ...cellSty, ...st.style, ...st.pin ? { position: "sticky", zIndex: 2 } : null },
|
|
11543
|
-
"data-ovtext": cellTitle,
|
|
11544
|
-
onClick: selectionMode === "cell" ? (e) => handleCellClick(e, k, row, c) : void 0,
|
|
11545
|
-
onFocus: () => setFocus((f) => f.r === ri && f.c === ci ? f : { r: ri, c: ci }),
|
|
11546
|
-
onDoubleClick: editable ? () => beginEdit(k, c, row) : void 0,
|
|
11547
|
-
children: [
|
|
11548
|
-
!checkboxSelection && reorderable && ci === 0 && !isEditing ? /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: 6, maxWidth: "100%" }, children: [
|
|
11549
|
-
rowHandle(k, midIdx, row),
|
|
11550
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { overflow: "hidden", textOverflow: "ellipsis" }, children: isActions ? renderActions(c, row) : c.renderCell ? c.renderCell(val, row) : display })
|
|
11551
|
-
] }) : isEditing ? c.renderEditCell ? (
|
|
11552
|
-
// #236: full escape hatch — the host drops in its own editor (searchable/creatable/
|
|
11553
|
-
// async Combobox, …) and drives the lifecycle via commit/cancel. Wrapped in
|
|
11554
|
-
// .twc-dt__editor-wrap so inline clicks (and any .twc-pop portal dropdown) don't
|
|
11555
|
-
// trip the outside-click auto-cancel.
|
|
11556
|
-
// #273: the wrapper handles Escape → cancel so it works for every custom editor without
|
|
11557
|
-
// each having to wire a keydown (they only get commit/cancel). A custom control that
|
|
11558
|
-
// wants Escape itself (e.g. to close its own open dropdown) stops propagation first.
|
|
11559
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11560
|
-
"div",
|
|
11563
|
+
id: cellId,
|
|
11564
|
+
className: cellClass ? `twc-dt__td ${cellClass}` : "twc-dt__td",
|
|
11565
|
+
role: "gridcell",
|
|
11566
|
+
"data-r": ri,
|
|
11567
|
+
"data-c": ci,
|
|
11568
|
+
"aria-colindex": ci + 1 + (checkboxSelection ? 1 : 0) + (hasRowDetail ? 1 : 0),
|
|
11569
|
+
tabIndex: focus.r === ri && focus.c === ci ? 0 : -1,
|
|
11570
|
+
"aria-selected": cellSelected || void 0,
|
|
11571
|
+
"data-num": c.type === "number" || void 0,
|
|
11572
|
+
"data-actions": isActions || void 0,
|
|
11573
|
+
"data-editable": editable && !isEditing || void 0,
|
|
11574
|
+
"data-editing": isEditing || void 0,
|
|
11575
|
+
"data-cell-active": cellActive || void 0,
|
|
11576
|
+
"data-cell-selected": cellSelected || void 0,
|
|
11577
|
+
"data-copied": cellFlash || void 0,
|
|
11578
|
+
"data-wrap": effectiveWrapped.has(c.field) || void 0,
|
|
11579
|
+
"data-pin": st.pin,
|
|
11580
|
+
"data-pin-edge": st.edge,
|
|
11581
|
+
style: { width: widthOf(c), ...cellSty, ...st.style, ...st.pin ? { position: "sticky", zIndex: 2 } : null },
|
|
11582
|
+
"data-ovtext": cellTitle,
|
|
11583
|
+
onClick: selectionMode === "cell" ? (e) => handleCellClick(e, k, row, c) : void 0,
|
|
11584
|
+
onFocus: () => setFocus((f) => f.r === ri && f.c === ci ? f : { r: ri, c: ci }),
|
|
11585
|
+
onDoubleClick: editable ? () => beginEdit(k, c, row) : void 0,
|
|
11586
|
+
children: [
|
|
11587
|
+
!checkboxSelection && reorderable && ci === 0 && !isEditing ? /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: 6, maxWidth: "100%" }, children: [
|
|
11588
|
+
rowHandle(k, midIdx, row),
|
|
11589
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { overflow: "hidden", textOverflow: "ellipsis" }, children: isActions ? renderActions(c, row) : c.renderCell ? c.renderCell(val, row) : display })
|
|
11590
|
+
] }) : isEditing ? c.renderEditCell ? (
|
|
11591
|
+
// #236: full escape hatch — the host drops in its own editor (searchable/creatable/
|
|
11592
|
+
// async Combobox, …) and drives the lifecycle via commit/cancel. Wrapped in
|
|
11593
|
+
// .twc-dt__editor-wrap so inline clicks (and any .twc-pop portal dropdown) don't
|
|
11594
|
+
// trip the outside-click auto-cancel.
|
|
11595
|
+
// #273: the wrapper handles Escape → cancel so it works for every custom editor without
|
|
11596
|
+
// each having to wire a keydown (they only get commit/cancel). A custom control that
|
|
11597
|
+
// wants Escape itself (e.g. to close its own open dropdown) stops propagation first.
|
|
11598
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11599
|
+
"div",
|
|
11600
|
+
{
|
|
11601
|
+
className: "twc-dt__editor-wrap",
|
|
11602
|
+
onKeyDown: (e) => {
|
|
11603
|
+
if (e.key === "Escape") {
|
|
11604
|
+
e.stopPropagation();
|
|
11605
|
+
cancelEdit();
|
|
11606
|
+
}
|
|
11607
|
+
},
|
|
11608
|
+
children: c.renderEditCell({ value: editing.value, row, field: c.field, commit: (v) => commitEdit(v), cancel: cancelEdit })
|
|
11609
|
+
}
|
|
11610
|
+
)
|
|
11611
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
11612
|
+
EditCell,
|
|
11561
11613
|
{
|
|
11562
|
-
|
|
11563
|
-
|
|
11564
|
-
|
|
11565
|
-
|
|
11566
|
-
|
|
11567
|
-
|
|
11568
|
-
|
|
11569
|
-
|
|
11614
|
+
col: c,
|
|
11615
|
+
value: editing.value,
|
|
11616
|
+
options: c.valueOptions ? optionsForField(c.field) : null,
|
|
11617
|
+
onChange: (v) => setEditing((e) => ({ ...e, value: v })),
|
|
11618
|
+
onCommit: () => commitEdit(),
|
|
11619
|
+
onCommitValue: (v) => commitEdit(v),
|
|
11620
|
+
onCancel: cancelEdit,
|
|
11621
|
+
onKeyDown: onEditKey
|
|
11570
11622
|
}
|
|
11571
|
-
)
|
|
11572
|
-
|
|
11573
|
-
|
|
11574
|
-
|
|
11575
|
-
|
|
11576
|
-
|
|
11577
|
-
|
|
11578
|
-
|
|
11579
|
-
|
|
11580
|
-
|
|
11581
|
-
|
|
11582
|
-
|
|
11583
|
-
}
|
|
11584
|
-
) : isActions ? renderActions(c, row) : c.renderCell ? c.renderCell(val, row) : display,
|
|
11585
|
-
editable && !isEditing ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dt__edit-hint", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx(Svg, { d: I.pencil }) }) : null
|
|
11586
|
-
]
|
|
11587
|
-
},
|
|
11588
|
-
c.field
|
|
11589
|
-
);
|
|
11590
|
-
})
|
|
11591
|
-
]
|
|
11592
|
-
},
|
|
11593
|
-
(pinSide ? "p-" : "") + k
|
|
11594
|
-
);
|
|
11623
|
+
) : isActions ? renderActions(c, row) : c.renderCell ? c.renderCell(val, row) : display,
|
|
11624
|
+
editable && !isEditing ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dt__edit-hint", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx(Svg, { d: I.pencil }) }) : null
|
|
11625
|
+
]
|
|
11626
|
+
},
|
|
11627
|
+
c.field
|
|
11628
|
+
);
|
|
11629
|
+
})
|
|
11630
|
+
]
|
|
11631
|
+
}
|
|
11632
|
+
),
|
|
11633
|
+
open ? /* @__PURE__ */ jsxRuntime.jsx("tr", { className: "twc-dt__detail-row", role: "row", children: /* @__PURE__ */ jsxRuntime.jsx("td", { id: `${gridId}-detail-${ri}`, className: "twc-dt__detail-cell", role: "gridcell", colSpan: totalCols, style: { maxWidth: "none" }, children: detail }) }) : null
|
|
11634
|
+
] }, (pinSide ? "p-" : "") + k);
|
|
11595
11635
|
}
|
|
11596
11636
|
function renderPivot() {
|
|
11597
11637
|
const rFields = pivotConfig.rows, cFields = pivotConfig.columns || [], values = pivotConfig.values;
|
|
@@ -11928,14 +11968,15 @@ function Datatable({
|
|
|
11928
11968
|
role: "grid",
|
|
11929
11969
|
"aria-label": ariaLabelAttr || ariaLabel,
|
|
11930
11970
|
"aria-rowcount": totalRows + 1,
|
|
11931
|
-
"aria-colcount": ordered.length + (checkboxSelection ? 1 : 0),
|
|
11971
|
+
"aria-colcount": ordered.length + (checkboxSelection ? 1 : 0) + (hasRowDetail ? 1 : 0),
|
|
11932
11972
|
"aria-multiselectable": selectionMode === "cell" || void 0,
|
|
11933
11973
|
"aria-activedescendant": activeCellId,
|
|
11934
11974
|
"aria-busy": loading || void 0,
|
|
11935
11975
|
onKeyDown: onGridKeyDown,
|
|
11936
11976
|
children: [
|
|
11937
11977
|
/* @__PURE__ */ jsxRuntime.jsx("thead", { ref: theadRef, children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { role: "row", "aria-rowindex": 1, children: [
|
|
11938
|
-
|
|
11978
|
+
hasRowDetail ? /* @__PURE__ */ jsxRuntime.jsx("th", { className: "twc-dt__th twc-dt__expand-cell", role: "columnheader", "aria-label": "Expand", "data-pin": "left", "data-pin-edge": checkboxSelection || showRowNum || visLeft.length ? void 0 : "left", style: { insetInlineStart: 0, width: EXP_W, minWidth: EXP_W } }) : null,
|
|
11979
|
+
checkboxSelection ? /* @__PURE__ */ jsxRuntime.jsx("th", { className: "twc-dt__th", role: "columnheader", "aria-label": "Select", "data-pin": "left", "data-pin-edge": visLeft.length || showRowNum ? void 0 : "left", style: { insetInlineStart: EXP_W, width: CHK_W, minWidth: CHK_W }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dt__th-inner", style: { justifyContent: "center", padding: 0 }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
11939
11980
|
"span",
|
|
11940
11981
|
{
|
|
11941
11982
|
className: "twc-dt__check",
|
|
@@ -12111,7 +12152,8 @@ function Datatable({
|
|
|
12111
12152
|
})
|
|
12112
12153
|
] }) }),
|
|
12113
12154
|
/* @__PURE__ */ jsxRuntime.jsx("tbody", { children: loading ? Array.from({ length: paginated ? Math.min(sizeVal, 8) : 8 }).map((_, ri) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "twc-dt__row", role: "row", children: [
|
|
12114
|
-
|
|
12155
|
+
hasRowDetail ? /* @__PURE__ */ jsxRuntime.jsx("td", { className: "twc-dt__td twc-dt__expand-cell", role: "gridcell", "data-pin": "left", style: { insetInlineStart: 0, width: EXP_W } }) : null,
|
|
12156
|
+
checkboxSelection ? /* @__PURE__ */ jsxRuntime.jsx("td", { className: "twc-dt__td", role: "gridcell", "data-pin": "left", style: { insetInlineStart: EXP_W, width: CHK_W }, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dt__sk", "aria-hidden": "true", style: { "--_w": "18px", height: 18, borderRadius: 4 } }) }) : null,
|
|
12115
12157
|
showRowNum ? /* @__PURE__ */ jsxRuntime.jsx("td", { className: "twc-dt__td twc-dt__rownum", role: "gridcell", "aria-hidden": "true", "data-pin": "left", style: { insetInlineStart: numLeft, width: NUM_W }, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dt__sk", "aria-hidden": "true", style: { "--_w": "16px", height: 14, borderRadius: 4 } }) }) : null,
|
|
12116
12158
|
ordered.map((c, ci) => {
|
|
12117
12159
|
const st = stickyOf(c.field);
|
|
@@ -12133,7 +12175,8 @@ function Datatable({
|
|
|
12133
12175
|
pinnedBottomRows.map((row) => renderLeaf(row, keyIndex.get(keyOf(row)), "bottom"))
|
|
12134
12176
|
] }) }),
|
|
12135
12177
|
hasAggregation && aggOn && !loading && paged.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("tfoot", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { role: "row", children: [
|
|
12136
|
-
|
|
12178
|
+
hasRowDetail ? /* @__PURE__ */ jsxRuntime.jsx("td", { role: "gridcell", "data-pin": "left", "data-pin-edge": checkboxSelection || showRowNum || visLeft.length ? void 0 : "left", style: { insetInlineStart: 0, width: EXP_W } }) : null,
|
|
12179
|
+
checkboxSelection ? /* @__PURE__ */ jsxRuntime.jsx("td", { role: "gridcell", "data-pin": "left", "data-pin-edge": visLeft.length || showRowNum ? void 0 : "left", style: { insetInlineStart: EXP_W, width: CHK_W } }) : null,
|
|
12137
12180
|
showRowNum ? /* @__PURE__ */ jsxRuntime.jsx("td", { className: "twc-dt__rownum", role: "gridcell", "aria-hidden": "true", "data-pin": "left", "data-pin-edge": visLeft.length ? void 0 : "left", style: { insetInlineStart: numLeft, width: NUM_W } }) : null,
|
|
12138
12181
|
ordered.map((c) => {
|
|
12139
12182
|
const st = stickyOf(c.field);
|
|
@@ -17168,6 +17211,18 @@ var SLIDER_CSS = `
|
|
|
17168
17211
|
transition: opacity var(--duration-fast) var(--ease-standard), transform var(--duration-fast) var(--ease-spring);
|
|
17169
17212
|
}
|
|
17170
17213
|
.twc-slider__track[data-show-bubble="true"] .twc-slider__bubble { opacity: 1; transform: translateX(-50%) scale(1); }
|
|
17214
|
+
/* #351: editable numeric inputs (manual entry, synced with the thumbs) */
|
|
17215
|
+
.twc-slider__inputs { display: inline-flex; align-items: center; gap: var(--space-1-5); }
|
|
17216
|
+
.twc-slider__input-sep { color: var(--color-text-muted); font-weight: var(--font-semibold); }
|
|
17217
|
+
.twc-slider__input {
|
|
17218
|
+
min-width: 0; text-align: end; font-family: inherit; font-size: var(--text-sm); font-weight: var(--font-semibold);
|
|
17219
|
+
color: var(--_accent); font-variant-numeric: tabular-nums;
|
|
17220
|
+
padding: 2px 6px; border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-sm); background: var(--color-surface);
|
|
17221
|
+
transition: border-color var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard);
|
|
17222
|
+
}
|
|
17223
|
+
.twc-slider__input:hover:not(:disabled) { border-color: var(--color-border-strong); }
|
|
17224
|
+
.twc-slider__input:focus-visible { outline: none; border-color: var(--_accent); box-shadow: var(--ring); }
|
|
17225
|
+
.twc-slider[data-disabled="true"] .twc-slider__input { cursor: not-allowed; }
|
|
17171
17226
|
`;
|
|
17172
17227
|
function Slider({
|
|
17173
17228
|
label,
|
|
@@ -17186,6 +17241,7 @@ function Slider({
|
|
|
17186
17241
|
range: range2 = false,
|
|
17187
17242
|
showValue = true,
|
|
17188
17243
|
showTicks = false,
|
|
17244
|
+
editable = false,
|
|
17189
17245
|
formatValue,
|
|
17190
17246
|
getAriaValueText,
|
|
17191
17247
|
name,
|
|
@@ -17224,6 +17280,7 @@ function Slider({
|
|
|
17224
17280
|
const indices = isRange ? [0, 1] : [0];
|
|
17225
17281
|
const trackRef = React51__namespace.default.useRef(null);
|
|
17226
17282
|
const [dragging, setDragging] = React51__namespace.default.useState(false);
|
|
17283
|
+
const [typed, setTyped] = React51__namespace.default.useState({});
|
|
17227
17284
|
const fmt = (v) => formatValue ? formatValue(v) : v.toFixed(decimals);
|
|
17228
17285
|
const valueTextFor = (v) => {
|
|
17229
17286
|
if (getAriaValueText) return getAriaValueText(v);
|
|
@@ -17247,6 +17304,27 @@ function Slider({
|
|
|
17247
17304
|
}
|
|
17248
17305
|
commit(next);
|
|
17249
17306
|
};
|
|
17307
|
+
const commitInput = (i) => {
|
|
17308
|
+
const s = typed[i];
|
|
17309
|
+
setTyped((t) => {
|
|
17310
|
+
const n = { ...t };
|
|
17311
|
+
delete n[i];
|
|
17312
|
+
return n;
|
|
17313
|
+
});
|
|
17314
|
+
if (s == null || String(s).trim() === "") return;
|
|
17315
|
+
const num = parseFloat(s);
|
|
17316
|
+
if (Number.isFinite(num)) setThumb(i, num);
|
|
17317
|
+
};
|
|
17318
|
+
const stepInput = (i, dir) => {
|
|
17319
|
+
const base = typed[i] != null && Number.isFinite(parseFloat(typed[i])) ? parseFloat(typed[i]) : clampedVals[i];
|
|
17320
|
+
setTyped((t) => {
|
|
17321
|
+
const n = { ...t };
|
|
17322
|
+
delete n[i];
|
|
17323
|
+
return n;
|
|
17324
|
+
});
|
|
17325
|
+
setThumb(i, base + dir * step);
|
|
17326
|
+
};
|
|
17327
|
+
const inputSize = Math.max(String(min).length, String(max).length, decimals > 0 ? decimals + 2 : 1) + 1;
|
|
17250
17328
|
const fromClientX = (clientX) => {
|
|
17251
17329
|
const node = trackRef.current;
|
|
17252
17330
|
if (!node) return clampedVals[0];
|
|
@@ -17302,9 +17380,40 @@ function Slider({
|
|
|
17302
17380
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-slider ${className}`, "data-tone": tone, "data-size": size, "data-disabled": disabled || void 0, ...rest, children: [
|
|
17303
17381
|
__twcStyles,
|
|
17304
17382
|
name ? clampedVals.map((v, i) => /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: v, disabled: disabled || void 0 }, i)) : null,
|
|
17305
|
-
label || showValue ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-slider__head", children: [
|
|
17383
|
+
label || showValue || editable ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-slider__head", children: [
|
|
17306
17384
|
label ? /* @__PURE__ */ jsxRuntime.jsx("label", { className: "twc-slider__label", id: labelId, children: label }) : /* @__PURE__ */ jsxRuntime.jsx("span", {}),
|
|
17307
|
-
|
|
17385
|
+
editable ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-slider__inputs", children: indices.map((i) => /* @__PURE__ */ jsxRuntime.jsxs(React51__namespace.default.Fragment, { children: [
|
|
17386
|
+
i > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-slider__input-sep", "aria-hidden": "true", children: "\u2013" }) : null,
|
|
17387
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17388
|
+
"input",
|
|
17389
|
+
{
|
|
17390
|
+
className: "twc-slider__input",
|
|
17391
|
+
type: "text",
|
|
17392
|
+
inputMode: min < 0 ? "text" : "decimal",
|
|
17393
|
+
size: inputSize,
|
|
17394
|
+
value: typed[i] != null ? typed[i] : String(clampedVals[i]),
|
|
17395
|
+
disabled: disabled || void 0,
|
|
17396
|
+
"aria-labelledby": isRange ? void 0 : labelId,
|
|
17397
|
+
"aria-label": isRange ? i === 0 ? "Minimum value" : "Maximum value" : labelId ? void 0 : "Value",
|
|
17398
|
+
onChange: (e) => setTyped((t) => ({ ...t, [i]: e.target.value })),
|
|
17399
|
+
onBlur: () => commitInput(i),
|
|
17400
|
+
onKeyDown: (e) => {
|
|
17401
|
+
if (e.key === "Enter") {
|
|
17402
|
+
if (typed[i] != null) {
|
|
17403
|
+
e.preventDefault();
|
|
17404
|
+
commitInput(i);
|
|
17405
|
+
}
|
|
17406
|
+
} else if (e.key === "ArrowUp") {
|
|
17407
|
+
e.preventDefault();
|
|
17408
|
+
stepInput(i, 1);
|
|
17409
|
+
} else if (e.key === "ArrowDown") {
|
|
17410
|
+
e.preventDefault();
|
|
17411
|
+
stepInput(i, -1);
|
|
17412
|
+
}
|
|
17413
|
+
}
|
|
17414
|
+
}
|
|
17415
|
+
)
|
|
17416
|
+
] }, i)) }) : showValue ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-slider__value", children: headValue }) : null
|
|
17308
17417
|
] }) : null,
|
|
17309
17418
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-slider__track", ref: trackRef, "data-show-bubble": dragging || void 0, onPointerDown, children: [
|
|
17310
17419
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-slider__rail" }),
|