twico-ui 1.33.0 → 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/dist/index.cjs CHANGED
@@ -7963,6 +7963,9 @@ var TOOLTIP_CSS = `
7963
7963
  .twc-tooltip-wrap { display: inline-flex; }
7964
7964
  .twc-tooltip {
7965
7965
  position: fixed; z-index: var(--z-tooltip);
7966
+ /* #348: purely presentational \u2014 the bubble must NEVER intercept the pointer, so neither a hidden
7967
+ (opacity:0) nor a shown bubble can block neighbouring cells/buttons (matches MUI/Radix tooltips). */
7968
+ pointer-events: none;
7966
7969
  padding: 6px 10px; border-radius: var(--radius-md);
7967
7970
  background: var(--color-text); color: var(--color-surface);
7968
7971
  font-family: var(--font-sans); font-size: var(--text-xs); font-weight: var(--font-medium);
@@ -7974,7 +7977,6 @@ var TOOLTIP_CSS = `
7974
7977
  width: max-content; max-width: var(--_tw-maxw, 320px); box-shadow: var(--shadow-md);
7975
7978
  opacity: 0; transform: scale(0.9); transition: opacity var(--duration-fast) var(--ease-out), transform var(--duration-fast) var(--ease-spring);
7976
7979
  }
7977
- .twc-tooltip[data-show="true"] { pointer-events: auto; }
7978
7980
  .twc-tooltip[data-show="true"] { opacity: 1; transform: scale(1); }
7979
7981
  .twc-tooltip[data-place="top"] { transform-origin: bottom center; translate: -50% 0; }
7980
7982
  .twc-tooltip[data-place="bottom"] { transform-origin: top center; translate: -50% 0; }
@@ -8021,9 +8023,6 @@ function Tooltip({
8021
8023
  clearTimeout(closeTimer.current);
8022
8024
  closeTimer.current = setTimeout(() => setShowU(false), 120);
8023
8025
  };
8024
- const cancelClose = () => {
8025
- clearTimeout(closeTimer.current);
8026
- };
8027
8026
  React51__namespace.default.useEffect(() => () => {
8028
8027
  clearTimeout(timer.current);
8029
8028
  clearTimeout(closeTimer.current);
@@ -8099,8 +8098,6 @@ function Tooltip({
8099
8098
  "data-show": show || void 0,
8100
8099
  role: "tooltip",
8101
8100
  "aria-hidden": show ? void 0 : "true",
8102
- onMouseEnter: anchored ? void 0 : cancelClose,
8103
- onMouseLeave: anchored ? void 0 : close,
8104
8101
  style: {
8105
8102
  position: "fixed",
8106
8103
  left: coords.left,
@@ -8386,6 +8383,16 @@ var DT_CSS = `
8386
8383
  .twc-dt__group-sub span { font-variant-numeric: tabular-nums; }
8387
8384
  .twc-dt__group-sub span b { color: var(--color-text); }
8388
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
+
8389
8396
  /* Pivot view */
8390
8397
  .twc-dt__pivot { table-layout: auto; width: 100%; }
8391
8398
  .twc-dt__pivot th, .twc-dt__pivot td { white-space: nowrap; }
@@ -9333,6 +9340,13 @@ function Datatable({
9333
9340
  virtualized = false,
9334
9341
  overscan = 8,
9335
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,
9336
9350
  className = "",
9337
9351
  ...rest
9338
9352
  }) {
@@ -9539,6 +9553,12 @@ function Datatable({
9539
9553
  const gridRef = React51__namespace.default.useRef(null);
9540
9554
  const [groupBy, setGroupBy] = React51__namespace.default.useState(rowGrouping || []);
9541
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
+ );
9542
9562
  const [pinnedRows, setPinnedRows] = React51__namespace.default.useState({ top: [], bottom: [] });
9543
9563
  const [headH, setHeadH] = React51__namespace.default.useState(41);
9544
9564
  const theadRef = React51__namespace.default.useRef(null);
@@ -9940,7 +9960,9 @@ function Datatable({
9940
9960
  }, [pins, effectiveHidden, visibleCols, colByField, order]);
9941
9961
  const NUM_W = 56;
9942
9962
  const CHK_W = checkboxSelection ? rowReorder ? 68 : 44 : 0;
9943
- const numLeft = CHK_W;
9963
+ const hasRowDetail = typeof renderRowDetail === "function";
9964
+ const EXP_W = hasRowDetail ? 44 : 0;
9965
+ const numLeft = EXP_W + CHK_W;
9944
9966
  const leadW = numLeft + (showRowNum ? NUM_W : 0);
9945
9967
  const visLeft = React51__namespace.default.useMemo(() => pins.left.filter((f) => colByField[f] && !effectiveHidden.has(f)), [pins.left, colByField, effectiveHidden]);
9946
9968
  const visRight = React51__namespace.default.useMemo(() => pins.right.filter((f) => colByField[f] && !effectiveHidden.has(f)), [pins.right, colByField, effectiveHidden]);
@@ -10535,6 +10557,12 @@ function Datatable({
10535
10557
  return n;
10536
10558
  });
10537
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
+ }
10538
10566
  function handleRowClick(e, k, row) {
10539
10567
  if (selectionMode === "none") return;
10540
10568
  if (e.target.closest("button, a, input, select, .twc-dt__check, .twc-dt__editor-wrap")) return;
@@ -11381,7 +11409,7 @@ function Datatable({
11381
11409
  const manageableCols = cols.filter((c) => c.field !== "__pinactions__");
11382
11410
  const visibleColCount = manageableCols.filter((c) => !effectiveHidden.has(c.field)).length;
11383
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) }));
11384
- const totalCols = ordered.length + (checkboxSelection ? 1 : 0) + (showRowNum ? 1 : 0);
11412
+ const totalCols = ordered.length + (checkboxSelection ? 1 : 0) + (showRowNum ? 1 : 0) + (hasRowDetail ? 1 : 0);
11385
11413
  function renderGroupRow(item) {
11386
11414
  const subs = aggOn ? subtotalText(item.rows) : [];
11387
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: [
@@ -11435,6 +11463,9 @@ function Datatable({
11435
11463
  function renderLeaf(row, ri, pinSide, midIdx) {
11436
11464
  const k = keyOf(row, ri);
11437
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);
11438
11469
  const rowActive = selectionMode === "row" && activeRowVal === k;
11439
11470
  const stickyStyle = pinSide === "top" ? { position: "sticky", top: headH, zIndex: 5 } : pinSide === "bottom" ? { position: "sticky", bottom: 0, zIndex: 5 } : void 0;
11440
11471
  const h = rowHeights[k];
@@ -11442,159 +11473,165 @@ function Datatable({
11442
11473
  const reorderable = canReorderRows && !pinSide;
11443
11474
  const grabbed = rowGrab && rowGrab.key === k;
11444
11475
  const grabTarget = rowGrab && !grabbed && midIdx != null && rowGrab.index === midIdx ? rowGrab.index >= keyIndexMid.get(rowGrab.key) ? "after" : "before" : void 0;
11445
- return /* @__PURE__ */ jsxRuntime.jsxs(
11446
- "tr",
11447
- {
11448
- className: "twc-dt__row",
11449
- role: "row",
11450
- "aria-rowindex": (paginated && !serverMode ? pageVal * sizeVal : 0) + ri + 2,
11451
- "aria-selected": (checkboxSelection ? sel : rowActive) || void 0,
11452
- "data-op": row.__diffop || void 0,
11453
- "data-selected": sel || void 0,
11454
- "data-active": rowActive || void 0,
11455
- "data-pinned-row": pinSide || void 0,
11456
- "data-reorderable": reorderable || void 0,
11457
- "data-row-dragging": rowDrag.from === k || void 0,
11458
- "data-row-grabbed": grabbed || void 0,
11459
- "data-row-grabtarget": grabTarget,
11460
- "data-row-dropbefore": reorderable && rowDrag.over === k && !rowDrag.after || void 0,
11461
- "data-row-dropafter": reorderable && rowDrag.over === k && rowDrag.after || void 0,
11462
- "data-vrow": virtualizing && midIdx != null ? String(k) : void 0,
11463
- "data-selectable": selectionMode !== "none" || void 0,
11464
- style: Object.keys(rowStyle).length ? rowStyle : void 0,
11465
- draggable: reorderable || void 0,
11466
- onDragStart: reorderable ? (e) => {
11467
- setRowDrag({ from: k, over: null, after: false });
11468
- e.dataTransfer.effectAllowed = "move";
11469
- e.dataTransfer.setData("text/plain", String(k));
11470
- } : void 0,
11471
- onDragOver: reorderable && rowDrag.from != null ? (e) => {
11472
- e.preventDefault();
11473
- const r2 = e.currentTarget.getBoundingClientRect();
11474
- const after = e.clientY > r2.top + r2.height / 2;
11475
- setRowDrag((d) => d.over === k && d.after === after ? d : { ...d, over: k, after });
11476
- } : void 0,
11477
- onDrop: reorderable && rowDrag.from != null ? (e) => {
11478
- e.preventDefault();
11479
- onRowDrop(k);
11480
- } : void 0,
11481
- onDragEnd: reorderable ? () => setRowDrag({ from: null, over: null, after: false }) : void 0,
11482
- onClick: (e) => handleRowClick(e, k, row),
11483
- children: [
11484
- 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: 0, width: CHK_W }, children: [
11485
- /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: 4 }, children: [
11486
- reorderable ? rowHandle(k, midIdx, row) : null,
11487
- /* @__PURE__ */ jsxRuntime.jsx(
11488
- "span",
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",
11489
11562
  {
11490
- className: "twc-dt__check",
11491
- "data-checked": sel || void 0,
11492
- onClick: () => toggleRow(k),
11493
- role: "checkbox",
11494
- "aria-checked": sel,
11495
- "aria-label": "Select row",
11496
- tabIndex: 0,
11497
- onKeyDown: (e) => {
11498
- if (e.key === " " || e.key === "Enter") {
11499
- e.preventDefault();
11500
- toggleRow(k);
11501
- }
11502
- },
11503
- children: /* @__PURE__ */ jsxRuntime.jsx(Svg, { d: I.check })
11504
- }
11505
- )
11506
- ] }),
11507
- 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
11508
- ] }) : null,
11509
- 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,
11510
- ordered.map((c, ci) => {
11511
- const st = stickyOf(c.field);
11512
- const val = getColVal(c, row);
11513
- const isActions = c.type === "actions";
11514
- const editable = isColEditable(c);
11515
- const isEditing = editing && editing.key === k && editing.field === c.field;
11516
- const cellActive = selectionMode === "cell" && activeCell && activeCell.key === k && activeCell.field === c.field;
11517
- const cellSelected = selectionMode === "cell" && cellRect && ri >= cellRect.r0 && ri <= cellRect.r1 && ci >= cellRect.c0 && ci <= cellRect.c1;
11518
- const cellFlash = clipFx && clipFx.rect && ri >= clipFx.rect.r0 && ri <= clipFx.rect.r1 && ci >= clipFx.rect.c0 && ci <= clipFx.rect.c1;
11519
- const cellId = selectionMode === "cell" ? `${gridId}-${ri}-${ci}` : void 0;
11520
- const display = c.valueFormatter ? c.valueFormatter(val, row) : val;
11521
- const cellTitle = !isActions && !c.renderCell && !isEditing && (typeof display === "string" || typeof display === "number") ? String(display) : void 0;
11522
- const cellClass = c.cellClassName ? c.cellClassName(val, row) : void 0;
11523
- const cellSty = c.cellStyle ? c.cellStyle(val, row) : void 0;
11524
- return /* @__PURE__ */ jsxRuntime.jsxs(
11525
- "td",
11526
- {
11527
- id: cellId,
11528
- className: cellClass ? `twc-dt__td ${cellClass}` : "twc-dt__td",
11529
- role: "gridcell",
11530
- "data-r": ri,
11531
- "data-c": ci,
11532
- "aria-colindex": ci + 1 + (checkboxSelection ? 1 : 0),
11533
- tabIndex: focus.r === ri && focus.c === ci ? 0 : -1,
11534
- "aria-selected": cellSelected || void 0,
11535
- "data-num": c.type === "number" || void 0,
11536
- "data-actions": isActions || void 0,
11537
- "data-editable": editable && !isEditing || void 0,
11538
- "data-editing": isEditing || void 0,
11539
- "data-cell-active": cellActive || void 0,
11540
- "data-cell-selected": cellSelected || void 0,
11541
- "data-copied": cellFlash || void 0,
11542
- "data-wrap": effectiveWrapped.has(c.field) || void 0,
11543
- "data-pin": st.pin,
11544
- "data-pin-edge": st.edge,
11545
- style: { width: widthOf(c), ...cellSty, ...st.style, ...st.pin ? { position: "sticky", zIndex: 2 } : null },
11546
- "data-ovtext": cellTitle,
11547
- onClick: selectionMode === "cell" ? (e) => handleCellClick(e, k, row, c) : void 0,
11548
- onFocus: () => setFocus((f) => f.r === ri && f.c === ci ? f : { r: ri, c: ci }),
11549
- onDoubleClick: editable ? () => beginEdit(k, c, row) : void 0,
11550
- children: [
11551
- !checkboxSelection && reorderable && ci === 0 && !isEditing ? /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: 6, maxWidth: "100%" }, children: [
11552
- rowHandle(k, midIdx, row),
11553
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { overflow: "hidden", textOverflow: "ellipsis" }, children: isActions ? renderActions(c, row) : c.renderCell ? c.renderCell(val, row) : display })
11554
- ] }) : isEditing ? c.renderEditCell ? (
11555
- // #236: full escape hatch — the host drops in its own editor (searchable/creatable/
11556
- // async Combobox, …) and drives the lifecycle via commit/cancel. Wrapped in
11557
- // .twc-dt__editor-wrap so inline clicks (and any .twc-pop portal dropdown) don't
11558
- // trip the outside-click auto-cancel.
11559
- // #273: the wrapper handles Escape → cancel so it works for every custom editor without
11560
- // each having to wire a keydown (they only get commit/cancel). A custom control that
11561
- // wants Escape itself (e.g. to close its own open dropdown) stops propagation first.
11562
- /* @__PURE__ */ jsxRuntime.jsx(
11563
- "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,
11564
11613
  {
11565
- className: "twc-dt__editor-wrap",
11566
- onKeyDown: (e) => {
11567
- if (e.key === "Escape") {
11568
- e.stopPropagation();
11569
- cancelEdit();
11570
- }
11571
- },
11572
- children: c.renderEditCell({ value: editing.value, row, field: c.field, commit: (v) => commitEdit(v), cancel: cancelEdit })
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
11573
11622
  }
11574
- )
11575
- ) : /* @__PURE__ */ jsxRuntime.jsx(
11576
- EditCell,
11577
- {
11578
- col: c,
11579
- value: editing.value,
11580
- options: c.valueOptions ? optionsForField(c.field) : null,
11581
- onChange: (v) => setEditing((e) => ({ ...e, value: v })),
11582
- onCommit: () => commitEdit(),
11583
- onCommitValue: (v) => commitEdit(v),
11584
- onCancel: cancelEdit,
11585
- onKeyDown: onEditKey
11586
- }
11587
- ) : isActions ? renderActions(c, row) : c.renderCell ? c.renderCell(val, row) : display,
11588
- editable && !isEditing ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dt__edit-hint", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx(Svg, { d: I.pencil }) }) : null
11589
- ]
11590
- },
11591
- c.field
11592
- );
11593
- })
11594
- ]
11595
- },
11596
- (pinSide ? "p-" : "") + k
11597
- );
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);
11598
11635
  }
11599
11636
  function renderPivot() {
11600
11637
  const rFields = pivotConfig.rows, cFields = pivotConfig.columns || [], values = pivotConfig.values;
@@ -11931,14 +11968,15 @@ function Datatable({
11931
11968
  role: "grid",
11932
11969
  "aria-label": ariaLabelAttr || ariaLabel,
11933
11970
  "aria-rowcount": totalRows + 1,
11934
- "aria-colcount": ordered.length + (checkboxSelection ? 1 : 0),
11971
+ "aria-colcount": ordered.length + (checkboxSelection ? 1 : 0) + (hasRowDetail ? 1 : 0),
11935
11972
  "aria-multiselectable": selectionMode === "cell" || void 0,
11936
11973
  "aria-activedescendant": activeCellId,
11937
11974
  "aria-busy": loading || void 0,
11938
11975
  onKeyDown: onGridKeyDown,
11939
11976
  children: [
11940
11977
  /* @__PURE__ */ jsxRuntime.jsx("thead", { ref: theadRef, children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { role: "row", "aria-rowindex": 1, children: [
11941
- 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: 0, 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(
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(
11942
11980
  "span",
11943
11981
  {
11944
11982
  className: "twc-dt__check",
@@ -12114,7 +12152,8 @@ function Datatable({
12114
12152
  })
12115
12153
  ] }) }),
12116
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: [
12117
- checkboxSelection ? /* @__PURE__ */ jsxRuntime.jsx("td", { className: "twc-dt__td", role: "gridcell", "data-pin": "left", style: { insetInlineStart: 0, width: CHK_W }, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dt__sk", "aria-hidden": "true", style: { "--_w": "18px", height: 18, borderRadius: 4 } }) }) : null,
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,
12118
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,
12119
12158
  ordered.map((c, ci) => {
12120
12159
  const st = stickyOf(c.field);
@@ -12136,7 +12175,8 @@ function Datatable({
12136
12175
  pinnedBottomRows.map((row) => renderLeaf(row, keyIndex.get(keyOf(row)), "bottom"))
12137
12176
  ] }) }),
12138
12177
  hasAggregation && aggOn && !loading && paged.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("tfoot", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { role: "row", children: [
12139
- checkboxSelection ? /* @__PURE__ */ jsxRuntime.jsx("td", { role: "gridcell", "data-pin": "left", "data-pin-edge": visLeft.length || showRowNum ? void 0 : "left", style: { insetInlineStart: 0, width: CHK_W } }) : null,
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,
12140
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,
12141
12181
  ordered.map((c) => {
12142
12182
  const st = stickyOf(c.field);
@@ -17171,6 +17211,18 @@ var SLIDER_CSS = `
17171
17211
  transition: opacity var(--duration-fast) var(--ease-standard), transform var(--duration-fast) var(--ease-spring);
17172
17212
  }
17173
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; }
17174
17226
  `;
17175
17227
  function Slider({
17176
17228
  label,
@@ -17189,6 +17241,7 @@ function Slider({
17189
17241
  range: range2 = false,
17190
17242
  showValue = true,
17191
17243
  showTicks = false,
17244
+ editable = false,
17192
17245
  formatValue,
17193
17246
  getAriaValueText,
17194
17247
  name,
@@ -17227,6 +17280,7 @@ function Slider({
17227
17280
  const indices = isRange ? [0, 1] : [0];
17228
17281
  const trackRef = React51__namespace.default.useRef(null);
17229
17282
  const [dragging, setDragging] = React51__namespace.default.useState(false);
17283
+ const [typed, setTyped] = React51__namespace.default.useState({});
17230
17284
  const fmt = (v) => formatValue ? formatValue(v) : v.toFixed(decimals);
17231
17285
  const valueTextFor = (v) => {
17232
17286
  if (getAriaValueText) return getAriaValueText(v);
@@ -17250,6 +17304,27 @@ function Slider({
17250
17304
  }
17251
17305
  commit(next);
17252
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;
17253
17328
  const fromClientX = (clientX) => {
17254
17329
  const node = trackRef.current;
17255
17330
  if (!node) return clampedVals[0];
@@ -17305,9 +17380,40 @@ function Slider({
17305
17380
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-slider ${className}`, "data-tone": tone, "data-size": size, "data-disabled": disabled || void 0, ...rest, children: [
17306
17381
  __twcStyles,
17307
17382
  name ? clampedVals.map((v, i) => /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: v, disabled: disabled || void 0 }, i)) : null,
17308
- label || showValue ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-slider__head", children: [
17383
+ label || showValue || editable ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-slider__head", children: [
17309
17384
  label ? /* @__PURE__ */ jsxRuntime.jsx("label", { className: "twc-slider__label", id: labelId, children: label }) : /* @__PURE__ */ jsxRuntime.jsx("span", {}),
17310
- showValue ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-slider__value", children: headValue }) : null
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
17311
17417
  ] }) : null,
17312
17418
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-slider__track", ref: trackRef, "data-show-bubble": dragging || void 0, onPointerDown, children: [
17313
17419
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-slider__rail" }),