twico-ui 1.23.0 → 1.25.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.mjs CHANGED
@@ -8539,6 +8539,13 @@ th.twc-dt__rownum .twc-dt__th-inner { padding-inline: 8px; gap: 2px; justify-con
8539
8539
  .twc-dt__panel-count { font-weight: 600; color: var(--color-text-muted); text-transform: none; letter-spacing: 0; margin-inline-start: 6px; font-variant-numeric: tabular-nums; }
8540
8540
  .twc-dt__link { border: none; background: transparent; color: var(--color-primary); font-family: inherit; font-size: var(--text-xs); font-weight: 600; cursor: pointer; padding: 2px 4px; border-radius: var(--radius-sm); }
8541
8541
  .twc-dt__link:hover { background: var(--color-primary-subtle); }
8542
+ /* #303: AND/OR connective toggle (segmented pill) in the Filters panel head. */
8543
+ .twc-dt__flogic { display: inline-flex; border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-md); overflow: hidden; }
8544
+ .twc-dt__flogic-btn { border: none; background: transparent; color: var(--color-text-muted); font-family: inherit; font-size: 10px; font-weight: 700; letter-spacing: var(--tracking-wide); cursor: pointer; padding: 2px 8px; }
8545
+ .twc-dt__flogic-btn + .twc-dt__flogic-btn { border-inline-start: var(--border-thin) solid var(--color-border); }
8546
+ .twc-dt__flogic-btn:hover { background: var(--color-surface-sunken); }
8547
+ .twc-dt__flogic-btn[data-active] { background: var(--color-primary); color: var(--color-primary-fg); }
8548
+ .twc-dt__flogic-btn[data-active]:hover { background: var(--color-primary); }
8542
8549
 
8543
8550
  /* Filter panel \u2014 user-resizable (#292): panel size + per-field widths resolve through a two-tier CSS var
8544
8551
  cascade \u2014 user drag (-usr) > #289 measured auto-fit (-fit) > 118px fallback \u2014 entirely in clamp(), so
@@ -8569,7 +8576,9 @@ th.twc-dt__rownum .twc-dt__th-inner { padding-inline: 8px; gap: 2px; justify-con
8569
8576
  /* value: the flex shock-absorber by default; the first drag flips it to a fixed width (data-val-fixed). */
8570
8577
  .twc-dt__f-val { position: relative; flex: 1 1 140px; min-width: 140px; }
8571
8578
  .twc-dt__filters[data-val-fixed] .twc-dt__f-val { flex: 0 0 clamp(140px, var(--twc-dt-fval-usr, 240px), 640px); }
8572
- .twc-dt__filters[data-val-fixed] .twc-dt__frm-x { margin-inline-start: auto; }
8579
+ /* #303: each row now has two .twc-dt__frm-x (the add-condition "+" then remove "x"); put the auto margin
8580
+ only on the first so the pair hugs the row end together (two auto margins would split the free space). */
8581
+ .twc-dt__filters[data-val-fixed] .twc-dt__frm-x:first-of-type { margin-inline-start: auto; }
8573
8582
  .twc-dt__frm-x { display: inline-grid; place-items: center; width: 30px; height: 38px; border: none; background: transparent; color: var(--color-text-subtle); border-radius: var(--radius-md); cursor: pointer; flex: none; align-self: flex-start; }
8574
8583
  .twc-dt__frm-x:hover { background: var(--color-danger-subtle); color: var(--color-danger-subtle-fg); }
8575
8584
  .twc-dt__frm-x svg { width: 15px; height: 15px; }
@@ -8589,6 +8598,13 @@ th.twc-dt__rownum .twc-dt__th-inner { padding-inline: 8px; gap: 2px; justify-con
8589
8598
  .twc-dt__f-rz { width: 16px; inset-inline-end: -6px; opacity: 0.4; }
8590
8599
  .twc-dt__pop-grip { width: 20px; height: 20px; opacity: 0.6; }
8591
8600
  }
8601
+ /* #304: generic resizable popovers. When the grip commits a size, the panel carries data-pop-sized +
8602
+ --twc-dt-pop-w/-h; the width override beats each panel default (incl. inline widths) and the inner
8603
+ scroll list drops its max-height and flexes to fill the taller panel. The panel keeps its base
8604
+ position: fixed (the absolute grip anchors to it) \u2014 do NOT set position here (see #294). Filters keep
8605
+ their own data-panel-sized system (untouched). */
8606
+ .twc-dt__pop[data-pop-sized] { display: flex; flex-direction: column; width: var(--twc-dt-pop-w) !important; height: var(--twc-dt-pop-h); max-width: calc(100vw - 32px); max-height: calc(100vh - 32px); }
8607
+ .twc-dt__pop[data-pop-sized] .twc-dt__col-list, .twc-dt__pop[data-pop-sized] .twc-dt__cfg-list, .twc-dt__pop[data-pop-sized] .twc-dt__pivot-body { max-height: none; flex: 1 1 auto; overflow-y: auto; }
8592
8608
  .twc-dt__empty { padding: 30px 12px; text-align: center; color: var(--color-text-subtle); font-size: var(--text-sm); }
8593
8609
 
8594
8610
  /* Action cell */
@@ -8818,9 +8834,10 @@ function runDatatableQuery(rows, query, options = {}) {
8818
8834
  let out = Array.isArray(rows) ? rows : [];
8819
8835
  const quick = String(q.quickFilter || "").trim().toLowerCase();
8820
8836
  if (quick) out = out.filter((r2) => searchFields.some((f) => String(gv(f, r2) ?? "").toLowerCase().includes(quick)));
8821
- for (const f of q.filters || []) {
8822
- const fc = cols.find((c) => c.field === f.field);
8823
- out = out.filter((r2) => testFilter(gv(f.field, r2), f.op, f.value, filterTypeOf(fc)));
8837
+ const qF = (q.filters || []).map((f) => [cols.find((c) => c.field === f.field), f]).filter(([fc]) => fc);
8838
+ if (qF.length) {
8839
+ if (q.filterLogic === "or") out = out.filter((r2) => qF.some(([fc, f]) => testFilter(gv(f.field, r2), f.op, f.value, filterTypeOf(fc))));
8840
+ else for (const [fc, f] of qF) out = out.filter((r2) => testFilter(gv(f.field, r2), f.op, f.value, filterTypeOf(fc)));
8824
8841
  }
8825
8842
  if (q.sort && q.sort.field) {
8826
8843
  const { field, dir } = q.sort, numeric = typeOf(field) === "number";
@@ -9142,6 +9159,8 @@ function Datatable({
9142
9159
  onQuickFilterChange,
9143
9160
  toolbarActions,
9144
9161
  resizableFilters = true,
9162
+ resizablePopovers = true,
9163
+ // #314: default-on (matches resizableFilters) — set false to opt a grid out
9145
9164
  filterFieldMaxWidth,
9146
9165
  rowPinning = false,
9147
9166
  rowReorder = false,
@@ -9231,6 +9250,7 @@ function Datatable({
9231
9250
  });
9232
9251
  const [sort, setSort] = React51__default.useState(null);
9233
9252
  const [filters, setFilters] = React51__default.useState([]);
9253
+ const [filterLogic, setFilterLogic] = React51__default.useState("and");
9234
9254
  const [hidden, setHidden] = React51__default.useState(() => /* @__PURE__ */ new Set());
9235
9255
  const [showRowNum, setShowRowNum] = React51__default.useState(rowNumbers);
9236
9256
  const [wrapped, setWrapped] = React51__default.useState(() => new Set((columns || []).filter((c) => c.wrapText).map((c) => c.field)));
@@ -9296,9 +9316,12 @@ function Datatable({
9296
9316
  const rowFocusRef = React51__default.useRef(null);
9297
9317
  const [fWidths, setFWidths] = React51__default.useState({});
9298
9318
  const [fPanelSize, setFPanelSize] = React51__default.useState(null);
9319
+ const [popSizes, setPopSizes] = React51__default.useState({});
9299
9320
  const serializeState = () => ({
9300
9321
  filters: filters.map(({ id, ...f }) => f),
9301
9322
  // strip the internal row id
9323
+ filterLogic,
9324
+ // #303: "and" (default) | "or"
9302
9325
  sort,
9303
9326
  quickFilter: quick,
9304
9327
  page: pageVal,
@@ -9311,12 +9334,15 @@ function Datatable({
9311
9334
  density,
9312
9335
  // #292: additive, optional — user-resized filter panel + field widths (panel-global, not per column).
9313
9336
  filterPanelSize: fPanelSize || void 0,
9314
- filterFieldWidths: fWidths.col != null || fWidths.op != null || fWidths.val != null ? fWidths : void 0
9337
+ filterFieldWidths: fWidths.col != null || fWidths.op != null || fWidths.val != null ? fWidths : void 0,
9338
+ // #304: additive, optional — user-resized sizes of the other toolbar popovers, keyed by popover id.
9339
+ popoverSizes: Object.keys(popSizes).length ? popSizes : void 0
9315
9340
  });
9316
9341
  const applyState = (s) => {
9317
9342
  if (!s || typeof s !== "object") return;
9318
9343
  const known = new Set(cols.map((c) => c.field).filter(Boolean));
9319
9344
  if (Array.isArray(s.filters)) setFilters(s.filters.filter((f) => f && known.has(f.field)).map((f, i) => ({ id: Date.now() + Math.random() + i, field: f.field, op: f.op, value: f.value })));
9345
+ if (s.filterLogic === "or" || s.filterLogic === "and") setFilterLogic(s.filterLogic);
9320
9346
  if ("sort" in s) setSort(s.sort && known.has(s.sort.field) ? { field: s.sort.field, dir: s.sort.dir === "desc" ? "desc" : "asc" } : null);
9321
9347
  if (typeof s.quickFilter === "string" && quickFilter === void 0) setInternalQuick(s.quickFilter);
9322
9348
  if (typeof s.page === "number" && !pageControlled) setInternalPage(Math.max(0, Math.floor(s.page)));
@@ -9346,6 +9372,14 @@ function Datatable({
9346
9372
  const h = typeof s.filterPanelSize.h === "number" ? clampNum(s.filterPanelSize.h, 120, fMaxH()) : void 0;
9347
9373
  if (w != null || h != null) setFPanelSize({ ...w != null ? { w } : {}, ...h != null ? { h } : {} });
9348
9374
  }
9375
+ if (s.popoverSizes && typeof s.popoverSizes === "object") {
9376
+ const next = {};
9377
+ for (const [id, sz] of Object.entries(s.popoverSizes)) {
9378
+ if (!sz || typeof sz !== "object" || typeof sz.w !== "number" || typeof sz.h !== "number") continue;
9379
+ next[id] = { w: clampNum(sz.w, popMinW(id), fMaxW()), h: clampNum(sz.h, 120, fMaxH()) };
9380
+ }
9381
+ if (Object.keys(next).length) setPopSizes(next);
9382
+ }
9349
9383
  };
9350
9384
  const stateReadyRef = React51__default.useRef(false);
9351
9385
  const stateRestoredRef = React51__default.useRef(false);
@@ -9363,7 +9397,7 @@ function Datatable({
9363
9397
  }
9364
9398
  }
9365
9399
  return void 0;
9366
- }, [filters, sort, quick, pageVal, sizeVal, order, widths, hidden, pins, density, fWidths, fPanelSize, stateKey]);
9400
+ }, [filters, filterLogic, sort, quick, pageVal, sizeVal, order, widths, hidden, pins, density, fWidths, fPanelSize, popSizes, stateKey]);
9367
9401
  const restoreState = () => {
9368
9402
  let saved = null;
9369
9403
  if (stateKey) {
@@ -9630,24 +9664,26 @@ function Datatable({
9630
9664
  const CHK_W = checkboxSelection ? rowReorder ? 68 : 44 : 0;
9631
9665
  const numLeft = CHK_W;
9632
9666
  const leadW = numLeft + (showRowNum ? NUM_W : 0);
9667
+ const visLeft = React51__default.useMemo(() => pins.left.filter((f) => colByField[f] && !hidden.has(f)), [pins.left, colByField, hidden]);
9668
+ const visRight = React51__default.useMemo(() => pins.right.filter((f) => colByField[f] && !hidden.has(f)), [pins.right, colByField, hidden]);
9633
9669
  const stickyOf = (field) => {
9634
- if (pins.left.includes(field)) {
9670
+ if (visLeft.includes(field)) {
9635
9671
  let off = leadW;
9636
- for (const f of pins.left) {
9672
+ for (const f of visLeft) {
9637
9673
  if (f === field) break;
9638
9674
  off += widthOf(colByField[f] || {});
9639
9675
  }
9640
- const isEdge = pins.left[pins.left.length - 1] === field;
9676
+ const isEdge = visLeft[visLeft.length - 1] === field;
9641
9677
  return { style: { insetInlineStart: off }, pin: "left", edge: isEdge ? "left" : void 0 };
9642
9678
  }
9643
- if (pins.right.includes(field)) {
9679
+ if (visRight.includes(field)) {
9644
9680
  let off = 0;
9645
- const rev = [...pins.right].reverse();
9681
+ const rev = [...visRight].reverse();
9646
9682
  for (const f of rev) {
9647
9683
  if (f === field) break;
9648
9684
  off += widthOf(colByField[f] || {});
9649
9685
  }
9650
- const isEdge = pins.right[0] === field;
9686
+ const isEdge = visRight[0] === field;
9651
9687
  return { style: { insetInlineEnd: off }, pin: "right", edge: isEdge ? "right" : void 0 };
9652
9688
  }
9653
9689
  return {};
@@ -9666,10 +9702,13 @@ function Datatable({
9666
9702
  const searchCols = searchFields && searchFields.length ? searchFields.map((f) => colByField[f] || { field: f }) : visibleCols;
9667
9703
  out = out.filter((r2) => searchCols.some((c) => String(getColVal(c, r2) ?? "").toLowerCase().includes(q)));
9668
9704
  }
9669
- for (const f of filters) {
9670
- const col = colByField[f.field];
9671
- if (!col) continue;
9672
- out = out.filter((r2) => testFilter(getColVal(col, r2), f.op, f.value, filterTypeOf(col)));
9705
+ const activeF = filters.map((f) => [colByField[f.field], f]).filter(([col]) => col);
9706
+ if (activeF.length) {
9707
+ if (filterLogic === "or") {
9708
+ out = out.filter((r2) => activeF.some(([col, f]) => testFilter(getColVal(col, r2), f.op, f.value, filterTypeOf(col))));
9709
+ } else {
9710
+ for (const [col, f] of activeF) out = out.filter((r2) => testFilter(getColVal(col, r2), f.op, f.value, filterTypeOf(col)));
9711
+ }
9673
9712
  }
9674
9713
  if (sort) {
9675
9714
  const col = colByField[sort.field];
@@ -9686,7 +9725,7 @@ function Datatable({
9686
9725
  out = [...out].sort((a, b) => (idx.get(keyOf(a)) ?? 1e9) - (idx.get(keyOf(b)) ?? 1e9));
9687
9726
  }
9688
9727
  return out;
9689
- }, [rows, quick, filters, sort, visibleCols, colByField, serverMode, rowOrder, searchFields]);
9728
+ }, [rows, quick, filters, filterLogic, sort, visibleCols, colByField, serverMode, rowOrder, searchFields]);
9690
9729
  const paginated = pageSize > 0;
9691
9730
  const serverTotal = rowCount == null ? processed.length : rowCount;
9692
9731
  const totalRows = serverMode ? serverTotal : processed.length;
@@ -9961,6 +10000,8 @@ function Datatable({
9961
10000
  pageSize: sizeVal,
9962
10001
  sort,
9963
10002
  filters: filters.map(({ id, ...f }) => f),
10003
+ filterLogic,
10004
+ // #303: "and" (default) | "or" — the server groups its WHERE accordingly
9964
10005
  quickFilter: quick.trim(),
9965
10006
  visibleColumns,
9966
10007
  hiddenColumns
@@ -9968,7 +10009,7 @@ function Datatable({
9968
10009
  });
9969
10010
  }, 250);
9970
10011
  return () => clearTimeout(t);
9971
- }, [serverMode, pageVal, sizeVal, sort, filters, quick, visibleColumns, hiddenColumns]);
10012
+ }, [serverMode, pageVal, sizeVal, sort, filters, filterLogic, quick, visibleColumns, hiddenColumns]);
9972
10013
  const onColVisRef = React51__default.useRef(onColumnVisibilityChange);
9973
10014
  onColVisRef.current = onColumnVisibilityChange;
9974
10015
  const lastVisibleKey = React51__default.useRef(null);
@@ -10382,7 +10423,9 @@ function Datatable({
10382
10423
  if (!editing) return;
10383
10424
  const onDown = (e) => {
10384
10425
  if (e.target.closest(".twc-dt__editor-wrap") || e.target.closest(".twc-pop")) return;
10385
- setEditing(null);
10426
+ const col = colByField[editing.field];
10427
+ if (col && col.renderEditCell) setEditing(null);
10428
+ else commitEdit();
10386
10429
  };
10387
10430
  document.addEventListener("mousedown", onDown, true);
10388
10431
  return () => document.removeEventListener("mousedown", onDown, true);
@@ -10626,6 +10669,96 @@ function Datatable({
10626
10669
  }
10627
10670
  };
10628
10671
  const hasFilterSizeOverride = fWidths.col != null || fWidths.op != null || fWidths.val != null || fPanelSize != null;
10672
+ const POP_MIN_W = { columns: 240, agg: 240, pivot: 260, batchedit: 260 };
10673
+ const popMinW = (id) => POP_MIN_W[id] || 200;
10674
+ const clearPop = (id, node) => {
10675
+ if (node) {
10676
+ node.style.removeProperty("--twc-dt-pop-w");
10677
+ node.style.removeProperty("--twc-dt-pop-h");
10678
+ node.removeAttribute("data-pop-sized");
10679
+ }
10680
+ setPopSizes((s) => {
10681
+ if (!(id in s)) return s;
10682
+ const n = { ...s };
10683
+ delete n[id];
10684
+ return n;
10685
+ });
10686
+ };
10687
+ const startPopResize = (id) => (e) => {
10688
+ if (e.button != null && e.button !== 0) return;
10689
+ e.preventDefault();
10690
+ e.stopPropagation();
10691
+ const pop = e.currentTarget.closest(".twc-dt__pop");
10692
+ if (!pop) return;
10693
+ const grip = e.currentTarget;
10694
+ const startX = e.clientX, startY = e.clientY;
10695
+ const r2 = pop.getBoundingClientRect();
10696
+ const startW = r2.width, startH = r2.height;
10697
+ const dir = typeof getComputedStyle !== "undefined" && getComputedStyle(pop).direction === "rtl" ? -1 : 1;
10698
+ pop.style.setProperty("--twc-dt-pop-w", `${startW}px`);
10699
+ pop.style.setProperty("--twc-dt-pop-h", `${startH}px`);
10700
+ grip.setAttribute("data-active", "true");
10701
+ pop.setAttribute("data-pop-sized", "");
10702
+ let lw = startW, lh = startH;
10703
+ const onMove = (ev) => {
10704
+ lw = clampNum(startW + (ev.clientX - startX) * dir, popMinW(id), fMaxW());
10705
+ lh = clampNum(startH + (ev.clientY - startY), 120, fMaxH());
10706
+ pop.style.setProperty("--twc-dt-pop-w", `${lw}px`);
10707
+ pop.style.setProperty("--twc-dt-pop-h", `${lh}px`);
10708
+ };
10709
+ const onUp = () => {
10710
+ setPopSizes((s) => ({ ...s, [id]: { w: lw, h: lh } }));
10711
+ grip.removeAttribute("data-active");
10712
+ window.removeEventListener("pointermove", onMove);
10713
+ window.removeEventListener("pointerup", onUp);
10714
+ };
10715
+ window.addEventListener("pointermove", onMove);
10716
+ window.addEventListener("pointerup", onUp);
10717
+ };
10718
+ const onPopGripKey = (id) => (e) => {
10719
+ if (e.key === "Enter" || e.key === " " || e.key === "Backspace") {
10720
+ e.preventDefault();
10721
+ clearPop(id, e.currentTarget.closest(".twc-dt__pop"));
10722
+ return;
10723
+ }
10724
+ if (!["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End"].includes(e.key)) return;
10725
+ e.preventDefault();
10726
+ const pop = e.currentTarget.closest(".twc-dt__pop");
10727
+ if (!pop) return;
10728
+ const r2 = pop.getBoundingClientRect();
10729
+ const step = e.shiftKey ? 4 : 20;
10730
+ const dir = typeof getComputedStyle !== "undefined" && getComputedStyle(pop).direction === "rtl" ? -1 : 1;
10731
+ let w = r2.width, h = r2.height;
10732
+ if (e.key === "ArrowRight") w += step * dir;
10733
+ else if (e.key === "ArrowLeft") w -= step * dir;
10734
+ else if (e.key === "ArrowDown") h += step;
10735
+ else if (e.key === "ArrowUp") h -= step;
10736
+ else if (e.key === "Home") {
10737
+ w = popMinW(id);
10738
+ h = 120;
10739
+ } else if (e.key === "End") {
10740
+ w = fMaxW();
10741
+ h = fMaxH();
10742
+ }
10743
+ setPopSizes((s) => ({ ...s, [id]: { w: clampNum(w, popMinW(id), fMaxW()), h: clampNum(h, 120, fMaxH()) } }));
10744
+ };
10745
+ const popStyle = (id) => popSizes[id] ? { "--twc-dt-pop-w": `${popSizes[id].w}px`, "--twc-dt-pop-h": `${popSizes[id].h}px` } : null;
10746
+ const popGrip = (id) => resizablePopovers ? /* @__PURE__ */ jsx(
10747
+ "span",
10748
+ {
10749
+ className: "twc-dt__pop-grip",
10750
+ role: "slider",
10751
+ tabIndex: 0,
10752
+ "aria-label": "Resize panel (Enter or double-click to reset)",
10753
+ "aria-valuetext": popSizes[id] ? `Width ${Math.round(popSizes[id].w)} pixels, height ${Math.round(popSizes[id].h)} pixels` : "Default size",
10754
+ onPointerDown: startPopResize(id),
10755
+ onKeyDown: onPopGripKey(id),
10756
+ onDoubleClick: (e) => {
10757
+ e.preventDefault();
10758
+ clearPop(id, e.currentTarget.closest(".twc-dt__pop"));
10759
+ }
10760
+ }
10761
+ ) : null;
10629
10762
  const fieldHandle = (field, rowIdx) => resizableFilters && rowIdx === 0 ? /* @__PURE__ */ jsx(
10630
10763
  "span",
10631
10764
  {
@@ -10748,7 +10881,7 @@ function Datatable({
10748
10881
  onDragEnd: reorderable ? () => setRowDrag({ from: null, over: null, after: false }) : void 0,
10749
10882
  onClick: (e) => handleRowClick(e, k, row),
10750
10883
  children: [
10751
- checkboxSelection ? /* @__PURE__ */ jsxs("td", { className: "twc-dt__td", role: "gridcell", "data-pin": "left", "data-pin-edge": pins.left.length || showRowNum ? void 0 : "left", style: { insetInlineStart: 0, width: CHK_W }, children: [
10884
+ checkboxSelection ? /* @__PURE__ */ 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: [
10752
10885
  /* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: 4 }, children: [
10753
10886
  reorderable ? rowHandle(k, midIdx, row) : null,
10754
10887
  /* @__PURE__ */ jsx(
@@ -10773,7 +10906,7 @@ function Datatable({
10773
10906
  ] }),
10774
10907
  rowResize && !pinSide ? /* @__PURE__ */ 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
10775
10908
  ] }) : null,
10776
- showRowNum ? /* @__PURE__ */ jsx("td", { className: "twc-dt__td twc-dt__rownum", role: "gridcell", "aria-hidden": "true", "data-pin": "left", "data-pin-edge": pins.left.length ? void 0 : "left", style: { insetInlineStart: numLeft, width: NUM_W }, children: typeof ri === "number" ? (paginated || serverMode ? pageVal * sizeVal : 0) + ri + 1 : "" }) : null,
10909
+ showRowNum ? /* @__PURE__ */ 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,
10777
10910
  ordered.map((c, ci) => {
10778
10911
  const st = stickyOf(c.field);
10779
10912
  const val = getColVal(c, row);
@@ -11183,7 +11316,7 @@ function Datatable({
11183
11316
  onKeyDown: onGridKeyDown,
11184
11317
  children: [
11185
11318
  /* @__PURE__ */ jsx("thead", { ref: theadRef, children: /* @__PURE__ */ jsxs("tr", { role: "row", "aria-rowindex": 1, children: [
11186
- checkboxSelection ? /* @__PURE__ */ jsx("th", { className: "twc-dt__th", role: "columnheader", "aria-label": "Select", "data-pin": "left", "data-pin-edge": pins.left.length || showRowNum ? void 0 : "left", style: { insetInlineStart: 0, width: CHK_W, minWidth: CHK_W }, children: /* @__PURE__ */ jsx("div", { className: "twc-dt__th-inner", style: { justifyContent: "center", padding: 0 }, children: /* @__PURE__ */ jsx(
11319
+ checkboxSelection ? /* @__PURE__ */ 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__ */ jsx("div", { className: "twc-dt__th-inner", style: { justifyContent: "center", padding: 0 }, children: /* @__PURE__ */ jsx(
11187
11320
  "span",
11188
11321
  {
11189
11322
  className: "twc-dt__check",
@@ -11203,7 +11336,7 @@ function Datatable({
11203
11336
  children: /* @__PURE__ */ jsx(Svg, { d: allSel ? I.check : I.minus })
11204
11337
  }
11205
11338
  ) }) }) : null,
11206
- showRowNum ? /* @__PURE__ */ jsx("th", { className: "twc-dt__th twc-dt__rownum", role: "columnheader", "aria-label": "Row number", "data-pin": "left", "data-pin-edge": pins.left.length ? void 0 : "left", style: { insetInlineStart: numLeft, width: NUM_W, minWidth: NUM_W }, children: /* @__PURE__ */ jsxs("div", { className: "twc-dt__th-inner", children: [
11339
+ showRowNum ? /* @__PURE__ */ jsx("th", { className: "twc-dt__th twc-dt__rownum", role: "columnheader", "aria-label": "Row number", "data-pin": "left", "data-pin-edge": visLeft.length ? void 0 : "left", style: { insetInlineStart: numLeft, width: NUM_W, minWidth: NUM_W }, children: /* @__PURE__ */ jsxs("div", { className: "twc-dt__th-inner", children: [
11207
11340
  /* @__PURE__ */ jsx("span", { className: "twc-dt__th-label", style: { flex: "none", cursor: "default" }, children: "#" }),
11208
11341
  /* @__PURE__ */ jsx(
11209
11342
  "button",
@@ -11381,8 +11514,8 @@ function Datatable({
11381
11514
  pinnedBottomRows.map((row) => renderLeaf(row, keyIndex.get(keyOf(row)), "bottom"))
11382
11515
  ] }) }),
11383
11516
  hasAggregation && aggOn && !loading && paged.length > 0 ? /* @__PURE__ */ jsx("tfoot", { children: /* @__PURE__ */ jsxs("tr", { role: "row", children: [
11384
- checkboxSelection ? /* @__PURE__ */ jsx("td", { role: "gridcell", "data-pin": "left", "data-pin-edge": pins.left.length || showRowNum ? void 0 : "left", style: { insetInlineStart: 0, width: CHK_W } }) : null,
11385
- showRowNum ? /* @__PURE__ */ jsx("td", { className: "twc-dt__rownum", role: "gridcell", "aria-hidden": "true", "data-pin": "left", "data-pin-edge": pins.left.length ? void 0 : "left", style: { insetInlineStart: numLeft, width: NUM_W } }) : null,
11517
+ checkboxSelection ? /* @__PURE__ */ jsx("td", { role: "gridcell", "data-pin": "left", "data-pin-edge": visLeft.length || showRowNum ? void 0 : "left", style: { insetInlineStart: 0, width: CHK_W } }) : null,
11518
+ showRowNum ? /* @__PURE__ */ 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,
11386
11519
  ordered.map((c) => {
11387
11520
  const st = stickyOf(c.field);
11388
11521
  const v = aggregate(c);
@@ -11481,7 +11614,7 @@ function Datatable({
11481
11614
  delete values[f];
11482
11615
  return { ...b, fields, values };
11483
11616
  });
11484
- return /* @__PURE__ */ jsxs("div", { className: "twc-dt__pop twc-dt__cfg", style: { top: batchEditPos.top, left: batchEditPos.left, width: 320 }, children: [
11617
+ return /* @__PURE__ */ jsxs("div", { className: "twc-dt__pop twc-dt__cfg", style: { top: batchEditPos.top, left: batchEditPos.left, width: 320, ...popStyle("batchedit") }, "data-pop-sized": popSizes.batchedit ? "" : void 0, children: [
11485
11618
  /* @__PURE__ */ jsx(Caret, { pos: batchEditPos }),
11486
11619
  /* @__PURE__ */ jsxs("div", { className: "twc-dt__cfg-head", children: [
11487
11620
  "Edit ",
@@ -11543,7 +11676,8 @@ function Datatable({
11543
11676
  "Apply to ",
11544
11677
  selected.size
11545
11678
  ] })
11546
- ] })
11679
+ ] }),
11680
+ popGrip("batchedit")
11547
11681
  ] });
11548
11682
  })() : null,
11549
11683
  colMenu && menuPos ? /* @__PURE__ */ jsxs(
@@ -11716,7 +11850,7 @@ function Datatable({
11716
11850
  ]
11717
11851
  }
11718
11852
  ) : null,
11719
- panel === "columns" && panelPos ? /* @__PURE__ */ jsxs("div", { className: "twc-dt__pop twc-dt__cols", style: { top: panelPos.top, left: panelPos.left }, role: "dialog", "aria-label": "Column settings", children: [
11853
+ panel === "columns" && panelPos ? /* @__PURE__ */ jsxs("div", { className: "twc-dt__pop twc-dt__cols", style: { top: panelPos.top, left: panelPos.left, ...popStyle("columns") }, "data-pop-sized": popSizes.columns ? "" : void 0, role: "dialog", "aria-label": "Column settings", children: [
11720
11854
  /* @__PURE__ */ jsxs("div", { className: "twc-dt__panel-head", children: [
11721
11855
  /* @__PURE__ */ jsxs("span", { className: "twc-dt__panel-title", children: [
11722
11856
  "Columns ",
@@ -11841,9 +11975,10 @@ function Datatable({
11841
11975
  c.field
11842
11976
  );
11843
11977
  })
11844
- ] })
11978
+ ] }),
11979
+ popGrip("columns")
11845
11980
  ] }) : null,
11846
- panel === "agg" && panelPos ? /* @__PURE__ */ jsxs("div", { className: "twc-dt__pop twc-dt__cfg", style: { top: panelPos.top, left: panelPos.left, width: 300 }, role: "dialog", "aria-label": "Aggregation settings", children: [
11981
+ panel === "agg" && panelPos ? /* @__PURE__ */ jsxs("div", { className: "twc-dt__pop twc-dt__cfg", style: { top: panelPos.top, left: panelPos.left, width: 300, ...popStyle("agg") }, "data-pop-sized": popSizes.agg ? "" : void 0, role: "dialog", "aria-label": "Aggregation settings", children: [
11847
11982
  /* @__PURE__ */ jsxs("div", { className: "twc-dt__panel-head", children: [
11848
11983
  /* @__PURE__ */ jsx("span", { className: "twc-dt__panel-title", children: "Aggregation" }),
11849
11984
  /* @__PURE__ */ jsx("button", { type: "button", className: "twc-dt__link", onClick: () => setAggConfig({}), children: "Clear" })
@@ -11892,13 +12027,14 @@ function Datatable({
11892
12027
  }
11893
12028
  ) })
11894
12029
  ] }, c.field);
11895
- }) })
12030
+ }) }),
12031
+ popGrip("agg")
11896
12032
  ] }) : null,
11897
12033
  panel === "pivot" && panelPos ? (() => {
11898
12034
  const fieldOpts = ordered.filter((c) => c.type !== "actions").map((c) => ({ value: c.field, label: c.headerName }));
11899
12035
  const aggOpts = [{ value: "sum", label: "Sum" }, { value: "avg", label: "Avg" }, { value: "min", label: "Min" }, { value: "max", label: "Max" }, { value: "count", label: "Count" }];
11900
12036
  const valueFieldOpts = ordered.filter((c) => c.type !== "actions" && !pivotConfig.values.some((v) => v.field === c.field)).map((c) => ({ value: c.field, label: c.headerName }));
11901
- return /* @__PURE__ */ jsxs("div", { className: "twc-dt__pop twc-dt__cfg", style: { top: panelPos.top, left: panelPos.left, width: 320 }, role: "dialog", "aria-label": "Pivot settings", children: [
12037
+ return /* @__PURE__ */ jsxs("div", { className: "twc-dt__pop twc-dt__cfg", style: { top: panelPos.top, left: panelPos.left, width: 320, ...popStyle("pivot") }, "data-pop-sized": popSizes.pivot ? "" : void 0, role: "dialog", "aria-label": "Pivot settings", children: [
11902
12038
  /* @__PURE__ */ jsxs("div", { className: "twc-dt__panel-head", children: [
11903
12039
  /* @__PURE__ */ jsx("span", { className: "twc-dt__panel-title", children: "Pivot" }),
11904
12040
  /* @__PURE__ */ jsx("button", { type: "button", className: "twc-dt__link", onClick: () => {
@@ -11906,106 +12042,109 @@ function Datatable({
11906
12042
  setPivotOn(false);
11907
12043
  }, children: "Reset" })
11908
12044
  ] }),
11909
- /* @__PURE__ */ jsxs("div", { className: "twc-dt__cfg-toggle", children: [
11910
- /* @__PURE__ */ jsx("span", { children: "Pivot mode" }),
11911
- /* @__PURE__ */ jsx(
11912
- "span",
11913
- {
11914
- className: "twc-dt__sw",
11915
- "data-on": pivotOn || void 0,
11916
- role: "switch",
11917
- "aria-checked": pivotOn,
11918
- tabIndex: 0,
11919
- onClick: () => setPivotOn((v) => !v),
11920
- onKeyDown: (e) => {
11921
- if (e.key === "Enter" || e.key === " ") {
11922
- e.preventDefault();
11923
- setPivotOn((v) => !v);
12045
+ /* @__PURE__ */ jsxs("div", { className: "twc-dt__pivot-body", children: [
12046
+ /* @__PURE__ */ jsxs("div", { className: "twc-dt__cfg-toggle", children: [
12047
+ /* @__PURE__ */ jsx("span", { children: "Pivot mode" }),
12048
+ /* @__PURE__ */ jsx(
12049
+ "span",
12050
+ {
12051
+ className: "twc-dt__sw",
12052
+ "data-on": pivotOn || void 0,
12053
+ role: "switch",
12054
+ "aria-checked": pivotOn,
12055
+ tabIndex: 0,
12056
+ onClick: () => setPivotOn((v) => !v),
12057
+ onKeyDown: (e) => {
12058
+ if (e.key === "Enter" || e.key === " ") {
12059
+ e.preventDefault();
12060
+ setPivotOn((v) => !v);
12061
+ }
11924
12062
  }
11925
12063
  }
11926
- }
11927
- )
11928
- ] }),
11929
- !pivotActive && pivotOn ? /* @__PURE__ */ jsxs("div", { className: "twc-dt__cfg-hint", children: [
11930
- "Add at least one ",
11931
- /* @__PURE__ */ jsx("b", { children: "Row" }),
11932
- " field and one ",
11933
- /* @__PURE__ */ jsx("b", { children: "Value" }),
11934
- " to see the pivot."
11935
- ] }) : null,
11936
- /* @__PURE__ */ jsxs("div", { className: "twc-dt__cfg-section", children: [
11937
- /* @__PURE__ */ jsx("span", { className: "twc-dt__cfg-label", children: "Rows" }),
11938
- /* @__PURE__ */ jsx(
11939
- MultiSelect,
11940
- {
11941
- portal: true,
11942
- placeholder: "Add row fields\u2026",
11943
- value: pivotConfig.rows,
11944
- options: fieldOpts,
11945
- onChange: (vals) => {
11946
- setPivotConfig((p) => ({ ...p, rows: vals }));
11947
- if (vals.length) setPivotOn(true);
11948
- }
11949
- }
11950
- )
11951
- ] }),
11952
- /* @__PURE__ */ jsxs("div", { className: "twc-dt__cfg-section", children: [
11953
- /* @__PURE__ */ jsx("span", { className: "twc-dt__cfg-label", children: "Columns" }),
11954
- /* @__PURE__ */ jsx(
11955
- MultiSelect,
11956
- {
11957
- portal: true,
11958
- placeholder: "Add column fields\u2026",
11959
- value: pivotConfig.columns,
11960
- options: fieldOpts,
11961
- onChange: (vals) => setPivotConfig((p) => ({ ...p, columns: vals }))
11962
- }
11963
- )
11964
- ] }),
11965
- /* @__PURE__ */ jsxs("div", { className: "twc-dt__cfg-section", children: [
11966
- /* @__PURE__ */ jsx("span", { className: "twc-dt__cfg-label", children: "Values" }),
11967
- pivotConfig.values.map((v, i) => /* @__PURE__ */ jsxs("div", { className: "twc-dt__cfg-row", children: [
11968
- /* @__PURE__ */ jsx("span", { className: "twc-dt__cfg-name", children: colByField[v.field]?.headerName || v.field }),
11969
- /* @__PURE__ */ jsx("div", { className: "twc-dt__cfg-ctl", children: /* @__PURE__ */ jsx(
11970
- Select,
12064
+ )
12065
+ ] }),
12066
+ !pivotActive && pivotOn ? /* @__PURE__ */ jsxs("div", { className: "twc-dt__cfg-hint", children: [
12067
+ "Add at least one ",
12068
+ /* @__PURE__ */ jsx("b", { children: "Row" }),
12069
+ " field and one ",
12070
+ /* @__PURE__ */ jsx("b", { children: "Value" }),
12071
+ " to see the pivot."
12072
+ ] }) : null,
12073
+ /* @__PURE__ */ jsxs("div", { className: "twc-dt__cfg-section", children: [
12074
+ /* @__PURE__ */ jsx("span", { className: "twc-dt__cfg-label", children: "Rows" }),
12075
+ /* @__PURE__ */ jsx(
12076
+ MultiSelect,
11971
12077
  {
11972
- size: "sm",
11973
- value: v.agg || "sum",
11974
- options: aggOpts,
11975
12078
  portal: true,
11976
- onChange: (a) => setPivotConfig((p) => ({ ...p, values: p.values.map((x, j) => j === i ? { ...x, agg: a } : x) }))
12079
+ placeholder: "Add row fields\u2026",
12080
+ value: pivotConfig.rows,
12081
+ options: fieldOpts,
12082
+ onChange: (vals) => {
12083
+ setPivotConfig((p) => ({ ...p, rows: vals }));
12084
+ if (vals.length) setPivotOn(true);
12085
+ }
11977
12086
  }
11978
- ) }),
12087
+ )
12088
+ ] }),
12089
+ /* @__PURE__ */ jsxs("div", { className: "twc-dt__cfg-section", children: [
12090
+ /* @__PURE__ */ jsx("span", { className: "twc-dt__cfg-label", children: "Columns" }),
11979
12091
  /* @__PURE__ */ jsx(
11980
- "button",
12092
+ MultiSelect,
11981
12093
  {
11982
- type: "button",
11983
- className: "twc-dt__cfg-x",
11984
- "aria-label": `Remove ${colByField[v.field]?.headerName || v.field}`,
11985
- onClick: () => setPivotConfig((p) => ({ ...p, values: p.values.filter((_, j) => j !== i) })),
11986
- children: /* @__PURE__ */ jsx(Svg, { d: I.x })
12094
+ portal: true,
12095
+ placeholder: "Add column fields\u2026",
12096
+ value: pivotConfig.columns,
12097
+ options: fieldOpts,
12098
+ onChange: (vals) => setPivotConfig((p) => ({ ...p, columns: vals }))
11987
12099
  }
11988
12100
  )
11989
- ] }, v.field + i)),
11990
- valueFieldOpts.length ? /* @__PURE__ */ jsx("div", { className: "twc-dt__cfg-add", children: /* @__PURE__ */ jsx(
11991
- Select,
11992
- {
11993
- size: "sm",
11994
- placeholder: "Add value field\u2026",
11995
- value: "",
11996
- options: valueFieldOpts,
11997
- portal: true,
11998
- searchable: true,
11999
- searchPlaceholder: "Search fields\u2026",
12000
- onChange: (f) => {
12001
- if (f) {
12002
- setPivotConfig((p) => p.values.some((x) => x.field === f) ? p : { ...p, values: [...p.values, { field: f, agg: "sum" }] });
12003
- setPivotOn(true);
12101
+ ] }),
12102
+ /* @__PURE__ */ jsxs("div", { className: "twc-dt__cfg-section", children: [
12103
+ /* @__PURE__ */ jsx("span", { className: "twc-dt__cfg-label", children: "Values" }),
12104
+ pivotConfig.values.map((v, i) => /* @__PURE__ */ jsxs("div", { className: "twc-dt__cfg-row", children: [
12105
+ /* @__PURE__ */ jsx("span", { className: "twc-dt__cfg-name", children: colByField[v.field]?.headerName || v.field }),
12106
+ /* @__PURE__ */ jsx("div", { className: "twc-dt__cfg-ctl", children: /* @__PURE__ */ jsx(
12107
+ Select,
12108
+ {
12109
+ size: "sm",
12110
+ value: v.agg || "sum",
12111
+ options: aggOpts,
12112
+ portal: true,
12113
+ onChange: (a) => setPivotConfig((p) => ({ ...p, values: p.values.map((x, j) => j === i ? { ...x, agg: a } : x) }))
12114
+ }
12115
+ ) }),
12116
+ /* @__PURE__ */ jsx(
12117
+ "button",
12118
+ {
12119
+ type: "button",
12120
+ className: "twc-dt__cfg-x",
12121
+ "aria-label": `Remove ${colByField[v.field]?.headerName || v.field}`,
12122
+ onClick: () => setPivotConfig((p) => ({ ...p, values: p.values.filter((_, j) => j !== i) })),
12123
+ children: /* @__PURE__ */ jsx(Svg, { d: I.x })
12124
+ }
12125
+ )
12126
+ ] }, v.field + i)),
12127
+ valueFieldOpts.length ? /* @__PURE__ */ jsx("div", { className: "twc-dt__cfg-add", children: /* @__PURE__ */ jsx(
12128
+ Select,
12129
+ {
12130
+ size: "sm",
12131
+ placeholder: "Add value field\u2026",
12132
+ value: "",
12133
+ options: valueFieldOpts,
12134
+ portal: true,
12135
+ searchable: true,
12136
+ searchPlaceholder: "Search fields\u2026",
12137
+ onChange: (f) => {
12138
+ if (f) {
12139
+ setPivotConfig((p) => p.values.some((x) => x.field === f) ? p : { ...p, values: [...p.values, { field: f, agg: "sum" }] });
12140
+ setPivotOn(true);
12141
+ }
12004
12142
  }
12005
12143
  }
12006
- }
12007
- ) }) : null
12008
- ] })
12144
+ ) }) : null
12145
+ ] })
12146
+ ] }),
12147
+ popGrip("pivot")
12009
12148
  ] });
12010
12149
  })() : null,
12011
12150
  panel === "filters" && panelPos ? /* @__PURE__ */ jsxs(
@@ -12029,7 +12168,11 @@ function Datatable({
12029
12168
  children: [
12030
12169
  /* @__PURE__ */ jsxs("div", { className: "twc-dt__panel-head", children: [
12031
12170
  /* @__PURE__ */ jsx("span", { className: "twc-dt__panel-title", children: "Filters" }),
12032
- /* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", gap: 2 }, children: [
12171
+ /* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: 6 }, children: [
12172
+ filters.length > 1 ? /* @__PURE__ */ jsxs("span", { className: "twc-dt__flogic", role: "group", "aria-label": "Combine filters with", children: [
12173
+ /* @__PURE__ */ jsx("button", { type: "button", className: "twc-dt__flogic-btn", "data-active": filterLogic === "and" || void 0, "aria-pressed": filterLogic === "and", onClick: () => setFilterLogic("and"), children: "AND" }),
12174
+ /* @__PURE__ */ jsx("button", { type: "button", className: "twc-dt__flogic-btn", "data-active": filterLogic === "or" || void 0, "aria-pressed": filterLogic === "or", onClick: () => setFilterLogic("or"), children: "OR" })
12175
+ ] }) : null,
12033
12176
  resizableFilters && hasFilterSizeOverride ? /* @__PURE__ */ jsx("button", { type: "button", className: "twc-dt__link", onClick: resetFilterSizes, children: "Reset sizes" }) : null,
12034
12177
  filters.length ? /* @__PURE__ */ jsx("button", { type: "button", className: "twc-dt__link", onClick: () => setFilters([]), children: "Clear all" }) : null
12035
12178
  ] })
@@ -12102,6 +12245,7 @@ function Datatable({
12102
12245
  ),
12103
12246
  fieldHandle("val", i)
12104
12247
  ] }),
12248
+ /* @__PURE__ */ jsx("button", { type: "button", className: "twc-dt__frm-x", "aria-label": `Add another condition on ${col.headerName}`, title: "Add another condition on this column", onClick: () => addFilter(f.field), children: /* @__PURE__ */ jsx(Svg, { d: I.plus }) }),
12105
12249
  /* @__PURE__ */ jsx("button", { type: "button", className: "twc-dt__frm-x", "aria-label": "Remove filter", onClick: () => setFilters((arr) => arr.filter((x) => x.id !== f.id)), children: /* @__PURE__ */ jsx(Svg, { d: I.x }) })
12106
12250
  ] }, f.id);
12107
12251
  }) }),