twico-ui 1.37.2 → 1.38.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.d.cts CHANGED
@@ -1907,6 +1907,12 @@ interface DatatableProps<T = any> extends Omit<React.HTMLAttributes<HTMLDivEleme
1907
1907
  showDensity?: boolean;
1908
1908
  /** Show the Pivot toolbar button. Also shown automatically when `pivot` or `pivotMode` is set. @default false */
1909
1909
  showPivot?: boolean;
1910
+ /** Show the **Columns** toolbar button (the show/hide picker). Omit for the smart default — shown only when a
1911
+ * column is `hideable` or `rowNumbers` is on (else the picker would be empty). `true`/`false` forces it. */
1912
+ showColumns?: boolean;
1913
+ /** Show the **Filters** toolbar button (the filter builder). Omit for the smart default — shown only when a
1914
+ * column is `filterable` (else the builder would be empty). `true`/`false` forces it. */
1915
+ showFilters?: boolean;
1910
1916
  /** Filename (without extension) for exports (CSV / Excel). @default "export" */
1911
1917
  exportFilename?: string;
1912
1918
  /** Server-mode precomputed aggregation. Per field, either a scalar/node (used as-is) or a per-function map
@@ -2011,6 +2017,16 @@ interface DatatableProps<T = any> extends Omit<React.HTMLAttributes<HTMLDivEleme
2011
2017
  ariaLabel?: string;
2012
2018
  /** Row-grouping fields (collapsible groups with subtotals). Changing the prop re-applies it; users can also group via a column's ⋮ menu. @default [] */
2013
2019
  rowGrouping?: string[];
2020
+ /** Show the "Grouped by …" bar above the header (with the per-field chips + Clear all). Set `false` for a
2021
+ * screen whose layout **is** the grouping — the bar's remove/clear controls would otherwise let the user
2022
+ * dismantle a grouping they can't restore. Grouping still applies; only the bar is hidden. @default true */
2023
+ showGroupBar?: boolean;
2024
+ /** #387: custom content for a group header row (the chevron + collapse/expand toggle are kept). Return the
2025
+ * node to render in place of the default `"<field>: <value> <count>"` — e.g. a section title + a summary.
2026
+ * `rows` is the group's rows; `count` is their number. Return `null` to render an empty label (matching
2027
+ * `renderCell`/`renderHeader`). The content sits **inside** the group's toggle button, so keep it
2028
+ * non-interactive — a nested button/link would be invalid HTML and its clicks would also toggle the group. */
2029
+ renderGroupLabel?: (group: { field: string; value: unknown; count: number; rows: T[] }) => React.ReactNode;
2014
2030
  /** Enable row pinning — adds "Pin to top/bottom" to each row's actions menu; pinned rows stay sticky above/below the scroll body. @default false */
2015
2031
  rowPinning?: boolean;
2016
2032
  /** Enable reorder of rows: the whole row is mouse-draggable, and a focusable drag handle supports
package/dist/index.d.ts CHANGED
@@ -1907,6 +1907,12 @@ interface DatatableProps<T = any> extends Omit<React.HTMLAttributes<HTMLDivEleme
1907
1907
  showDensity?: boolean;
1908
1908
  /** Show the Pivot toolbar button. Also shown automatically when `pivot` or `pivotMode` is set. @default false */
1909
1909
  showPivot?: boolean;
1910
+ /** Show the **Columns** toolbar button (the show/hide picker). Omit for the smart default — shown only when a
1911
+ * column is `hideable` or `rowNumbers` is on (else the picker would be empty). `true`/`false` forces it. */
1912
+ showColumns?: boolean;
1913
+ /** Show the **Filters** toolbar button (the filter builder). Omit for the smart default — shown only when a
1914
+ * column is `filterable` (else the builder would be empty). `true`/`false` forces it. */
1915
+ showFilters?: boolean;
1910
1916
  /** Filename (without extension) for exports (CSV / Excel). @default "export" */
1911
1917
  exportFilename?: string;
1912
1918
  /** Server-mode precomputed aggregation. Per field, either a scalar/node (used as-is) or a per-function map
@@ -2011,6 +2017,16 @@ interface DatatableProps<T = any> extends Omit<React.HTMLAttributes<HTMLDivEleme
2011
2017
  ariaLabel?: string;
2012
2018
  /** Row-grouping fields (collapsible groups with subtotals). Changing the prop re-applies it; users can also group via a column's ⋮ menu. @default [] */
2013
2019
  rowGrouping?: string[];
2020
+ /** Show the "Grouped by …" bar above the header (with the per-field chips + Clear all). Set `false` for a
2021
+ * screen whose layout **is** the grouping — the bar's remove/clear controls would otherwise let the user
2022
+ * dismantle a grouping they can't restore. Grouping still applies; only the bar is hidden. @default true */
2023
+ showGroupBar?: boolean;
2024
+ /** #387: custom content for a group header row (the chevron + collapse/expand toggle are kept). Return the
2025
+ * node to render in place of the default `"<field>: <value> <count>"` — e.g. a section title + a summary.
2026
+ * `rows` is the group's rows; `count` is their number. Return `null` to render an empty label (matching
2027
+ * `renderCell`/`renderHeader`). The content sits **inside** the group's toggle button, so keep it
2028
+ * non-interactive — a nested button/link would be invalid HTML and its clicks would also toggle the group. */
2029
+ renderGroupLabel?: (group: { field: string; value: unknown; count: number; rows: T[] }) => React.ReactNode;
2014
2030
  /** Enable row pinning — adds "Pin to top/bottom" to each row's actions menu; pinned rows stay sticky above/below the scroll body. @default false */
2015
2031
  rowPinning?: boolean;
2016
2032
  /** Enable reorder of rows: the whole row is mouse-draggable, and a focusable drag handle supports
package/dist/index.mjs CHANGED
@@ -7958,7 +7958,11 @@ var TOOLTIP_CSS = `
7958
7958
  (opacity:0) nor a shown bubble can block neighbouring cells/buttons (matches MUI/Radix tooltips). */
7959
7959
  pointer-events: none;
7960
7960
  padding: 6px 10px; border-radius: var(--radius-md);
7961
- background: var(--color-text); color: var(--color-surface);
7961
+ /* #385: capture the ink as the bubble bg via a private var BEFORE the content wrapper re-scopes
7962
+ --color-text, so a token-driven child (Text/Heading) reads the bubble's fg \u2014 not the page ink,
7963
+ which is this very background \u2014 and the re-scope can't feed back into the bg. */
7964
+ --_tt-bg: var(--color-text); --_tt-fg: var(--color-surface);
7965
+ background: var(--_tt-bg); color: var(--_tt-fg);
7962
7966
  font-family: var(--font-sans); font-size: var(--text-xs); font-weight: var(--font-medium);
7963
7967
  line-height: 1.3; white-space: normal;
7964
7968
  /* Grow horizontally to max-width, then wrap. max-content sizes to the content (capped by
@@ -7974,7 +7978,13 @@ var TOOLTIP_CSS = `
7974
7978
  .twc-tooltip[data-place="left"] { transform-origin: right center; translate: 0 -50%; }
7975
7979
  .twc-tooltip[data-place="right"] { transform-origin: left center; translate: 0 -50%; }
7976
7980
  .twc-tooltip[data-show="true"][data-place="top"], .twc-tooltip[data-show="true"][data-place="bottom"] { transform: scale(1); }
7977
- .twc-tooltip__arrow { position: absolute; width: 7px; height: 7px; background: var(--color-text); transform: rotate(45deg); }
7981
+ .twc-tooltip__arrow { position: absolute; width: 7px; height: 7px; background: var(--_tt-bg); transform: rotate(45deg); }
7982
+ /* #385: re-scope token ink for the label subtree so Text/Heading (which set --color-text as an inline
7983
+ style) render in the bubble's fg instead of the page ink. display:contents adds no box, so the label's
7984
+ own flex/block layout is unchanged; custom properties still inherit through it. */
7985
+ .twc-tooltip__content { display: contents; --color-text: var(--_tt-fg);
7986
+ --color-text-muted: color-mix(in srgb, var(--_tt-fg) 72%, var(--_tt-bg));
7987
+ --color-text-subtle: color-mix(in srgb, var(--_tt-fg) 55%, var(--_tt-bg)); }
7978
7988
  /* Arrow tracks the trigger via --_tw-arrow-x/y (px from the tooltip edge) instead of being
7979
7989
  locked to the centre, so it still points at the trigger when the bubble is clamped near a
7980
7990
  viewport edge. Falls back to 50% before measurement / SSR. */
@@ -8099,7 +8109,7 @@ function Tooltip({
8099
8109
  "--_tw-arrow-y": coords.arrowY != null ? `${coords.arrowY}px` : void 0
8100
8110
  },
8101
8111
  children: [
8102
- label,
8112
+ /* @__PURE__ */ jsx("span", { className: "twc-tooltip__content", children: label }),
8103
8113
  /* @__PURE__ */ jsx("span", { className: "twc-tooltip__arrow", "aria-hidden": "true" })
8104
8114
  ]
8105
8115
  }
@@ -9279,6 +9289,8 @@ function Datatable({
9279
9289
  showExport = false,
9280
9290
  showDensity = false,
9281
9291
  showPivot = false,
9292
+ showColumns,
9293
+ showFilters,
9282
9294
  exportFilename = "export",
9283
9295
  aggregationValues = null,
9284
9296
  disableColumnReorder = false,
@@ -9310,6 +9322,8 @@ function Datatable({
9310
9322
  ariaLabel = "Data table",
9311
9323
  "aria-label": ariaLabelAttr,
9312
9324
  rowGrouping = [],
9325
+ showGroupBar = true,
9326
+ renderGroupLabel,
9313
9327
  rowNumbers = false,
9314
9328
  searchFields = null,
9315
9329
  searchable = true,
@@ -11089,6 +11103,8 @@ function Datatable({
11089
11103
  }
11090
11104
  const tableMinWidth = leadW + ordered.reduce((a, c) => a + widthOf(c), 0);
11091
11105
  const filterableCols = React51__default.useMemo(() => cols.filter((c) => c.filterable), [cols]);
11106
+ const columnsShown = showColumns !== void 0 ? showColumns : cols.some((c) => c.hideable) || rowNumbers;
11107
+ const filtersShown = showFilters !== void 0 ? showFilters : filterableCols.length > 0;
11092
11108
  React51__default.useEffect(() => {
11093
11109
  const el = filterPanelRef.current;
11094
11110
  if (panel !== "filters" || !el || typeof document === "undefined") return void 0;
@@ -11475,6 +11491,7 @@ function Datatable({
11475
11491
  const totalCols = ordered.length + (checkboxSelection ? 1 : 0) + (showRowNum ? 1 : 0) + (hasExpandCol ? 1 : 0);
11476
11492
  function renderGroupRow(item) {
11477
11493
  const subs = aggOn ? subtotalText(item.rows) : [];
11494
+ const custom = renderGroupLabel ? renderGroupLabel({ field: item.field, value: item.value, count: item.count, rows: item.rows }) : null;
11478
11495
  return /* @__PURE__ */ jsx("tr", { className: "twc-dt__group-row", role: "row", children: /* @__PURE__ */ jsxs("td", { className: "twc-dt__group-cell", role: "gridcell", colSpan: totalCols, style: { maxWidth: "none" }, children: [
11479
11496
  /* @__PURE__ */ jsxs(
11480
11497
  "button",
@@ -11486,12 +11503,14 @@ function Datatable({
11486
11503
  onClick: () => toggleGroup(item.key),
11487
11504
  children: [
11488
11505
  /* @__PURE__ */ jsx("span", { className: "twc-dt__group-chev", "data-open": !item.collapsed || void 0, children: /* @__PURE__ */ jsx(Svg, { d: I.chevDown }) }),
11489
- /* @__PURE__ */ jsxs("span", { className: "twc-dt__group-name", children: [
11490
- fieldLabel(item.field),
11491
- ":"
11492
- ] }),
11493
- /* @__PURE__ */ jsx("span", { className: "twc-dt__group-val", children: String(item.value) }),
11494
- /* @__PURE__ */ jsx("span", { className: "twc-dt__group-count", children: item.count })
11506
+ renderGroupLabel ? custom : /* @__PURE__ */ jsxs(Fragment, { children: [
11507
+ /* @__PURE__ */ jsxs("span", { className: "twc-dt__group-name", children: [
11508
+ fieldLabel(item.field),
11509
+ ":"
11510
+ ] }),
11511
+ /* @__PURE__ */ jsx("span", { className: "twc-dt__group-val", children: String(item.value) }),
11512
+ /* @__PURE__ */ jsx("span", { className: "twc-dt__group-count", children: item.count })
11513
+ ] })
11495
11514
  ]
11496
11515
  }
11497
11516
  ),
@@ -11856,11 +11875,12 @@ function Datatable({
11856
11875
  /* @__PURE__ */ jsx("input", { type: "checkbox", checked: diffOnly, onChange: (e) => setDiffOnly(e.target.checked) }),
11857
11876
  diff.toggleLabel != null ? diff.toggleLabel : "Only changed"
11858
11877
  ] }) : null,
11859
- /* @__PURE__ */ jsxs(
11878
+ columnsShown ? /* @__PURE__ */ jsxs(
11860
11879
  "button",
11861
11880
  {
11862
11881
  type: "button",
11863
11882
  className: "twc-dt__tbtn",
11883
+ "data-tbtn": "columns",
11864
11884
  "data-active": panel === "columns" || void 0,
11865
11885
  "data-tip": "Show or hide columns",
11866
11886
  onClick: (e) => {
@@ -11880,12 +11900,13 @@ function Datatable({
11880
11900
  hidden.size ? /* @__PURE__ */ jsx("span", { className: "twc-dt__tbadge", children: cols.length - hidden.size }) : null
11881
11901
  ]
11882
11902
  }
11883
- ),
11884
- /* @__PURE__ */ jsxs(
11903
+ ) : null,
11904
+ filtersShown ? /* @__PURE__ */ jsxs(
11885
11905
  "button",
11886
11906
  {
11887
11907
  type: "button",
11888
11908
  className: "twc-dt__tbtn",
11909
+ "data-tbtn": "filters",
11889
11910
  "data-active": panel === "filters" || void 0,
11890
11911
  "data-tip": "Filter rows",
11891
11912
  onClick: (e) => {
@@ -11904,7 +11925,7 @@ function Datatable({
11904
11925
  filters.length ? /* @__PURE__ */ jsx("span", { className: "twc-dt__tbadge", children: filters.length }) : null
11905
11926
  ]
11906
11927
  }
11907
- ),
11928
+ ) : null,
11908
11929
  showDensity ? /* @__PURE__ */ jsxs("button", { type: "button", className: "twc-dt__tbtn", "data-tip": "Change row density", onClick: () => setDensity((d) => d === "compact" ? "standard" : d === "standard" ? "comfortable" : "compact"), children: [
11909
11930
  /* @__PURE__ */ jsx(Svg, { d: I.density }),
11910
11931
  /* @__PURE__ */ jsx("span", { className: "twc-dt__tlabel", children: density[0].toUpperCase() + density.slice(1) })
@@ -12001,7 +12022,7 @@ function Datatable({
12001
12022
  }, children: /* @__PURE__ */ jsx(Svg, { d: I.x }) }) : null
12002
12023
  ] }) : null
12003
12024
  ] }),
12004
- !pivotActive && activeGroupBy.length ? /* @__PURE__ */ jsxs("div", { className: "twc-dt__groupbar", children: [
12025
+ !pivotActive && activeGroupBy.length && showGroupBar ? /* @__PURE__ */ jsxs("div", { className: "twc-dt__groupbar", children: [
12005
12026
  /* @__PURE__ */ jsx(Svg, { d: I.group }),
12006
12027
  /* @__PURE__ */ jsx("span", { className: "twc-dt__groupbar-label", children: "Grouped by" }),
12007
12028
  activeGroupBy.map((f) => /* @__PURE__ */ jsxs("span", { className: "twc-dt__groupchip", children: [
@@ -12476,13 +12497,13 @@ function Datatable({
12476
12497
  " Sort descending"
12477
12498
  ] })
12478
12499
  ] }) : null,
12479
- c.filterable ? /* @__PURE__ */ jsxs("button", { type: "button", role: "menuitem", className: "twc-dt__mi", onClick: (e) => {
12500
+ c.filterable && filtersShown ? /* @__PURE__ */ jsxs("button", { type: "button", role: "menuitem", className: "twc-dt__mi", onClick: (e) => {
12480
12501
  addFilter(c.field);
12481
12502
  setColMenu(null);
12482
12503
  closeMenu();
12483
12504
  restoreTriggerFocus();
12484
12505
  setPanel("filters");
12485
- openPanel(document.querySelector(".twc-dt__toolbar .twc-dt__tbtn:nth-child(2)"), "left", DT_FILTER_PANEL_W);
12506
+ openPanel(document.querySelector('.twc-dt__toolbar .twc-dt__tbtn[data-tbtn="filters"]'), "left", DT_FILTER_PANEL_W);
12486
12507
  }, children: [
12487
12508
  /* @__PURE__ */ jsx(Svg, { d: I.filter }),
12488
12509
  " Filter"
@@ -12555,7 +12576,7 @@ function Datatable({
12555
12576
  " ",
12556
12577
  wrapped.has(c.field) ? "Unwrap text" : "Wrap text"
12557
12578
  ] }) : null,
12558
- c.hideable ? /* @__PURE__ */ jsxs("button", { type: "button", role: "menuitem", className: "twc-dt__mi", onClick: () => {
12579
+ c.hideable && columnsShown ? /* @__PURE__ */ jsxs("button", { type: "button", role: "menuitem", className: "twc-dt__mi", onClick: () => {
12559
12580
  setHidden((h) => new Set(h).add(c.field));
12560
12581
  close();
12561
12582
  }, children: [