twico-ui 1.32.0 → 1.33.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
@@ -2235,6 +2235,24 @@ interface DatatableColumn<T = any> {
2235
2235
  copyType?: string;
2236
2236
  /** Fully custom cell renderer (badges, avatars, etc.). */
2237
2237
  renderCell?: (value: any, row: T) => React.ReactNode;
2238
+ /** #345: per-cell class hook — return a class name applied to the whole cell (`.twc-dt__td`, pinned cells
2239
+ * included), not just its content. Use for value/row-based tinting (e.g. a "changed" cell) via your own
2240
+ * stylesheet, without coupling to the internal class name. Because it participates in the CSS cascade, a
2241
+ * `cellClassName` tint co-exists with the built-in hover/selection/diff backgrounds (a plain class may even
2242
+ * lose to them by specificity — use a more specific selector, or `cellStyle`, to always win). Prefer this
2243
+ * over `cellStyle` when those state cues must stay visible on a tinted cell. Called `(value, row)` to match
2244
+ * `renderCell`/`valueFormatter`. */
2245
+ cellClassName?: (value: any, row: T) => string | undefined;
2246
+ /** #345: per-cell inline-style hook — return a style object applied to the whole cell (`.twc-dt__td`,
2247
+ * pinned cells included). The simplest way to tint an entire cell from the value/row with no stylesheet.
2248
+ * Pin positioning **and** stickiness are always preserved (don't set `position`/`inset-*`). Two caveats,
2249
+ * because an inline style wins over every built-in background: (1) on a **pinned** column the background
2250
+ * must be **opaque** — a translucent value lets the cells scrolling underneath the sticky cell bleed
2251
+ * through; composite over the surface, e.g. `color-mix(in srgb, var(--color-danger) 14%, var(--color-surface))`
2252
+ * rather than `…, transparent`. (2) it also overrides the hover / range-selection / edit-mode / diff
2253
+ * backgrounds on that cell — use `cellClassName` instead if those cues must remain visible. Called
2254
+ * `(value, row)` to match `renderCell`/`valueFormatter`. */
2255
+ cellStyle?: (value: any, row: T) => React.CSSProperties | undefined;
2238
2256
  /** Diff-mode (#239) per-column equality override — return true when the two values are equal.
2239
2257
  * Precedence: column.compare → diff.compare → valueGetter/=== → JSON compare. */
2240
2258
  compare?: (a: any, b: any) => boolean;
package/dist/index.d.ts CHANGED
@@ -2235,6 +2235,24 @@ interface DatatableColumn<T = any> {
2235
2235
  copyType?: string;
2236
2236
  /** Fully custom cell renderer (badges, avatars, etc.). */
2237
2237
  renderCell?: (value: any, row: T) => React.ReactNode;
2238
+ /** #345: per-cell class hook — return a class name applied to the whole cell (`.twc-dt__td`, pinned cells
2239
+ * included), not just its content. Use for value/row-based tinting (e.g. a "changed" cell) via your own
2240
+ * stylesheet, without coupling to the internal class name. Because it participates in the CSS cascade, a
2241
+ * `cellClassName` tint co-exists with the built-in hover/selection/diff backgrounds (a plain class may even
2242
+ * lose to them by specificity — use a more specific selector, or `cellStyle`, to always win). Prefer this
2243
+ * over `cellStyle` when those state cues must stay visible on a tinted cell. Called `(value, row)` to match
2244
+ * `renderCell`/`valueFormatter`. */
2245
+ cellClassName?: (value: any, row: T) => string | undefined;
2246
+ /** #345: per-cell inline-style hook — return a style object applied to the whole cell (`.twc-dt__td`,
2247
+ * pinned cells included). The simplest way to tint an entire cell from the value/row with no stylesheet.
2248
+ * Pin positioning **and** stickiness are always preserved (don't set `position`/`inset-*`). Two caveats,
2249
+ * because an inline style wins over every built-in background: (1) on a **pinned** column the background
2250
+ * must be **opaque** — a translucent value lets the cells scrolling underneath the sticky cell bleed
2251
+ * through; composite over the surface, e.g. `color-mix(in srgb, var(--color-danger) 14%, var(--color-surface))`
2252
+ * rather than `…, transparent`. (2) it also overrides the hover / range-selection / edit-mode / diff
2253
+ * backgrounds on that cell — use `cellClassName` instead if those cues must remain visible. Called
2254
+ * `(value, row)` to match `renderCell`/`valueFormatter`. */
2255
+ cellStyle?: (value: any, row: T) => React.CSSProperties | undefined;
2238
2256
  /** Diff-mode (#239) per-column equality override — return true when the two values are equal.
2239
2257
  * Precedence: column.compare → diff.compare → valueGetter/=== → JSON compare. */
2240
2258
  compare?: (a: any, b: any) => boolean;
package/dist/index.mjs CHANGED
@@ -5867,6 +5867,7 @@ function Combobox({
5867
5867
  setQuery("");
5868
5868
  }
5869
5869
  function commit(v) {
5870
+ if (disabled) return;
5870
5871
  const o = flat.find((x) => x.value === v);
5871
5872
  if (o && o.disabled) return;
5872
5873
  if (value === void 0) setInternal(v);
@@ -6043,7 +6044,7 @@ function Combobox({
6043
6044
  ...rest
6044
6045
  }
6045
6046
  ),
6046
- clearable && selected && !open ? /* @__PURE__ */ jsx("button", { type: "button", className: "twc-cb__clear", "aria-label": "Clear", onClick: (e) => {
6047
+ clearable && selected && !open && !disabled ? /* @__PURE__ */ jsx("button", { type: "button", className: "twc-cb__clear", "aria-label": "Clear", onClick: (e) => {
6047
6048
  e.stopPropagation();
6048
6049
  commit(null);
6049
6050
  }, children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.4", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" }) }) }) : null,
@@ -7333,6 +7334,7 @@ var MULTI_CSS = `
7333
7334
  background: var(--_accent-subtle); color: var(--_accent-subtle-fg);
7334
7335
  border-radius: var(--radius-full); font-size: var(--text-xs); font-weight: var(--font-semibold);
7335
7336
  }
7337
+ .twc-ms__control[data-disabled="true"] .twc-ms__chip { padding-inline-end: 9px; } /* #342: no \u2715 \u2192 symmetric pad */
7336
7338
  .twc-ms__chip-x { display: inline-grid; place-items: center; width: 16px; height: 16px; border: none; padding: 0; background: transparent; color: inherit; cursor: pointer; border-radius: var(--radius-full); opacity: 0.7; }
7337
7339
  .twc-ms__chip-x:hover { opacity: 1; background: color-mix(in srgb, currentColor 18%, transparent); }
7338
7340
  .twc-ms__chip-x svg { width: 12px; height: 12px; }
@@ -7575,6 +7577,7 @@ function MultiSelect({
7575
7577
  setScrollTop(next);
7576
7578
  }, [active, open, virtualized, rows, listH]);
7577
7579
  function commit(next) {
7580
+ if (disabled) return;
7578
7581
  if (value === void 0) setInternal(next);
7579
7582
  onChange?.(next);
7580
7583
  }
@@ -7731,10 +7734,10 @@ function MultiSelect({
7731
7734
  children: [
7732
7735
  (maxTagCount != null ? selectedOpts.slice(0, maxTagCount) : selectedOpts).map((o) => /* @__PURE__ */ jsxs("span", { className: "twc-ms__chip", children: [
7733
7736
  o.label,
7734
- /* @__PURE__ */ jsx("button", { type: "button", tabIndex: -1, className: "twc-ms__chip-x", "aria-label": `Remove ${o.label}`, onClick: (e) => {
7737
+ !disabled ? /* @__PURE__ */ jsx("button", { type: "button", tabIndex: -1, className: "twc-ms__chip-x", "aria-label": `Remove ${o.label}`, onClick: (e) => {
7735
7738
  e.stopPropagation();
7736
7739
  toggle(o.value);
7737
- }, children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.6", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" }) }) })
7740
+ }, children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.6", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" }) }) }) : null
7738
7741
  ] }, o.value)),
7739
7742
  maxTagCount != null && selectedOpts.length > maxTagCount ? /* @__PURE__ */ jsxs("span", { className: "twc-ms__more", title: selectedOpts.slice(maxTagCount).map((o) => o.label).join(", "), children: [
7740
7743
  "+",
@@ -7787,8 +7790,10 @@ function MultiSelect({
7787
7790
  tabIndex: -1,
7788
7791
  onClick: (e) => {
7789
7792
  e.stopPropagation();
7790
- setOpen((o) => !o);
7791
- inputRef.current?.focus();
7793
+ if (!disabled) {
7794
+ setOpen((o) => !o);
7795
+ inputRef.current?.focus();
7796
+ }
7792
7797
  },
7793
7798
  children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" }) })
7794
7799
  }
@@ -10533,6 +10538,7 @@ function Datatable({
10533
10538
  if (el) el.focus();
10534
10539
  }
10535
10540
  function onGridKeyDown(e) {
10541
+ if (e.target.closest("input, textarea, select, [contenteditable='true']")) return;
10536
10542
  if (editing) return;
10537
10543
  const td = e.target.closest(".twc-dt__td[data-r]");
10538
10544
  if (!td) return;
@@ -11492,11 +11498,13 @@ function Datatable({
11492
11498
  const cellId = selectionMode === "cell" ? `${gridId}-${ri}-${ci}` : void 0;
11493
11499
  const display = c.valueFormatter ? c.valueFormatter(val, row) : val;
11494
11500
  const cellTitle = !isActions && !c.renderCell && !isEditing && (typeof display === "string" || typeof display === "number") ? String(display) : void 0;
11501
+ const cellClass = c.cellClassName ? c.cellClassName(val, row) : void 0;
11502
+ const cellSty = c.cellStyle ? c.cellStyle(val, row) : void 0;
11495
11503
  return /* @__PURE__ */ jsxs(
11496
11504
  "td",
11497
11505
  {
11498
11506
  id: cellId,
11499
- className: "twc-dt__td",
11507
+ className: cellClass ? `twc-dt__td ${cellClass}` : "twc-dt__td",
11500
11508
  role: "gridcell",
11501
11509
  "data-r": ri,
11502
11510
  "data-c": ci,
@@ -11513,7 +11521,7 @@ function Datatable({
11513
11521
  "data-wrap": effectiveWrapped.has(c.field) || void 0,
11514
11522
  "data-pin": st.pin,
11515
11523
  "data-pin-edge": st.edge,
11516
- style: { width: widthOf(c), ...st.style },
11524
+ style: { width: widthOf(c), ...cellSty, ...st.style, ...st.pin ? { position: "sticky", zIndex: 2 } : null },
11517
11525
  "data-ovtext": cellTitle,
11518
11526
  onClick: selectionMode === "cell" ? (e) => handleCellClick(e, k, row, c) : void 0,
11519
11527
  onFocus: () => setFocus((f) => f.r === ri && f.c === ci ? f : { r: ri, c: ci }),
@@ -15728,6 +15736,7 @@ function FileUpload({
15728
15736
  } else if (accepted.length) set(accepted.slice(0, 1));
15729
15737
  }
15730
15738
  function remove(i) {
15739
+ if (disabled) return;
15731
15740
  set(files.filter((_, idx) => idx !== i));
15732
15741
  }
15733
15742
  return /* @__PURE__ */ jsxs("div", { className: `twc-upload ${className}`, "data-size": size, ...rest, children: [
@@ -15798,7 +15807,7 @@ function FileUpload({
15798
15807
  /* @__PURE__ */ jsx("span", { className: "twc-upload__file-name", children: f.name }),
15799
15808
  /* @__PURE__ */ jsx("span", { className: "twc-upload__file-size", children: fmtSize(f.size) })
15800
15809
  ] }),
15801
- /* @__PURE__ */ jsx("button", { className: "twc-upload__file-x", "aria-label": `Remove ${f.name}`, onClick: () => remove(i), type: "button", children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.4", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" }) }) })
15810
+ !disabled ? /* @__PURE__ */ jsx("button", { className: "twc-upload__file-x", "aria-label": `Remove ${f.name}`, onClick: () => remove(i), type: "button", children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.4", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" }) }) }) : null
15802
15811
  ] }, fileKey(f))) }) : null,
15803
15812
  error ? /* @__PURE__ */ jsx("span", { id: descId, className: "twc-field__error", children: error }) : null
15804
15813
  ] });