twico-ui 1.23.0 → 1.24.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,7 @@ function Datatable({
9142
9159
  onQuickFilterChange,
9143
9160
  toolbarActions,
9144
9161
  resizableFilters = true,
9162
+ resizablePopovers = false,
9145
9163
  filterFieldMaxWidth,
9146
9164
  rowPinning = false,
9147
9165
  rowReorder = false,
@@ -9231,6 +9249,7 @@ function Datatable({
9231
9249
  });
9232
9250
  const [sort, setSort] = React51__default.useState(null);
9233
9251
  const [filters, setFilters] = React51__default.useState([]);
9252
+ const [filterLogic, setFilterLogic] = React51__default.useState("and");
9234
9253
  const [hidden, setHidden] = React51__default.useState(() => /* @__PURE__ */ new Set());
9235
9254
  const [showRowNum, setShowRowNum] = React51__default.useState(rowNumbers);
9236
9255
  const [wrapped, setWrapped] = React51__default.useState(() => new Set((columns || []).filter((c) => c.wrapText).map((c) => c.field)));
@@ -9296,9 +9315,12 @@ function Datatable({
9296
9315
  const rowFocusRef = React51__default.useRef(null);
9297
9316
  const [fWidths, setFWidths] = React51__default.useState({});
9298
9317
  const [fPanelSize, setFPanelSize] = React51__default.useState(null);
9318
+ const [popSizes, setPopSizes] = React51__default.useState({});
9299
9319
  const serializeState = () => ({
9300
9320
  filters: filters.map(({ id, ...f }) => f),
9301
9321
  // strip the internal row id
9322
+ filterLogic,
9323
+ // #303: "and" (default) | "or"
9302
9324
  sort,
9303
9325
  quickFilter: quick,
9304
9326
  page: pageVal,
@@ -9311,12 +9333,15 @@ function Datatable({
9311
9333
  density,
9312
9334
  // #292: additive, optional — user-resized filter panel + field widths (panel-global, not per column).
9313
9335
  filterPanelSize: fPanelSize || void 0,
9314
- filterFieldWidths: fWidths.col != null || fWidths.op != null || fWidths.val != null ? fWidths : void 0
9336
+ filterFieldWidths: fWidths.col != null || fWidths.op != null || fWidths.val != null ? fWidths : void 0,
9337
+ // #304: additive, optional — user-resized sizes of the other toolbar popovers, keyed by popover id.
9338
+ popoverSizes: Object.keys(popSizes).length ? popSizes : void 0
9315
9339
  });
9316
9340
  const applyState = (s) => {
9317
9341
  if (!s || typeof s !== "object") return;
9318
9342
  const known = new Set(cols.map((c) => c.field).filter(Boolean));
9319
9343
  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 })));
9344
+ if (s.filterLogic === "or" || s.filterLogic === "and") setFilterLogic(s.filterLogic);
9320
9345
  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
9346
  if (typeof s.quickFilter === "string" && quickFilter === void 0) setInternalQuick(s.quickFilter);
9322
9347
  if (typeof s.page === "number" && !pageControlled) setInternalPage(Math.max(0, Math.floor(s.page)));
@@ -9346,6 +9371,14 @@ function Datatable({
9346
9371
  const h = typeof s.filterPanelSize.h === "number" ? clampNum(s.filterPanelSize.h, 120, fMaxH()) : void 0;
9347
9372
  if (w != null || h != null) setFPanelSize({ ...w != null ? { w } : {}, ...h != null ? { h } : {} });
9348
9373
  }
9374
+ if (s.popoverSizes && typeof s.popoverSizes === "object") {
9375
+ const next = {};
9376
+ for (const [id, sz] of Object.entries(s.popoverSizes)) {
9377
+ if (!sz || typeof sz !== "object" || typeof sz.w !== "number" || typeof sz.h !== "number") continue;
9378
+ next[id] = { w: clampNum(sz.w, popMinW(id), fMaxW()), h: clampNum(sz.h, 120, fMaxH()) };
9379
+ }
9380
+ if (Object.keys(next).length) setPopSizes(next);
9381
+ }
9349
9382
  };
9350
9383
  const stateReadyRef = React51__default.useRef(false);
9351
9384
  const stateRestoredRef = React51__default.useRef(false);
@@ -9363,7 +9396,7 @@ function Datatable({
9363
9396
  }
9364
9397
  }
9365
9398
  return void 0;
9366
- }, [filters, sort, quick, pageVal, sizeVal, order, widths, hidden, pins, density, fWidths, fPanelSize, stateKey]);
9399
+ }, [filters, filterLogic, sort, quick, pageVal, sizeVal, order, widths, hidden, pins, density, fWidths, fPanelSize, popSizes, stateKey]);
9367
9400
  const restoreState = () => {
9368
9401
  let saved = null;
9369
9402
  if (stateKey) {
@@ -9630,24 +9663,26 @@ function Datatable({
9630
9663
  const CHK_W = checkboxSelection ? rowReorder ? 68 : 44 : 0;
9631
9664
  const numLeft = CHK_W;
9632
9665
  const leadW = numLeft + (showRowNum ? NUM_W : 0);
9666
+ const visLeft = React51__default.useMemo(() => pins.left.filter((f) => colByField[f] && !hidden.has(f)), [pins.left, colByField, hidden]);
9667
+ const visRight = React51__default.useMemo(() => pins.right.filter((f) => colByField[f] && !hidden.has(f)), [pins.right, colByField, hidden]);
9633
9668
  const stickyOf = (field) => {
9634
- if (pins.left.includes(field)) {
9669
+ if (visLeft.includes(field)) {
9635
9670
  let off = leadW;
9636
- for (const f of pins.left) {
9671
+ for (const f of visLeft) {
9637
9672
  if (f === field) break;
9638
9673
  off += widthOf(colByField[f] || {});
9639
9674
  }
9640
- const isEdge = pins.left[pins.left.length - 1] === field;
9675
+ const isEdge = visLeft[visLeft.length - 1] === field;
9641
9676
  return { style: { insetInlineStart: off }, pin: "left", edge: isEdge ? "left" : void 0 };
9642
9677
  }
9643
- if (pins.right.includes(field)) {
9678
+ if (visRight.includes(field)) {
9644
9679
  let off = 0;
9645
- const rev = [...pins.right].reverse();
9680
+ const rev = [...visRight].reverse();
9646
9681
  for (const f of rev) {
9647
9682
  if (f === field) break;
9648
9683
  off += widthOf(colByField[f] || {});
9649
9684
  }
9650
- const isEdge = pins.right[0] === field;
9685
+ const isEdge = visRight[0] === field;
9651
9686
  return { style: { insetInlineEnd: off }, pin: "right", edge: isEdge ? "right" : void 0 };
9652
9687
  }
9653
9688
  return {};
@@ -9666,10 +9701,13 @@ function Datatable({
9666
9701
  const searchCols = searchFields && searchFields.length ? searchFields.map((f) => colByField[f] || { field: f }) : visibleCols;
9667
9702
  out = out.filter((r2) => searchCols.some((c) => String(getColVal(c, r2) ?? "").toLowerCase().includes(q)));
9668
9703
  }
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)));
9704
+ const activeF = filters.map((f) => [colByField[f.field], f]).filter(([col]) => col);
9705
+ if (activeF.length) {
9706
+ if (filterLogic === "or") {
9707
+ out = out.filter((r2) => activeF.some(([col, f]) => testFilter(getColVal(col, r2), f.op, f.value, filterTypeOf(col))));
9708
+ } else {
9709
+ for (const [col, f] of activeF) out = out.filter((r2) => testFilter(getColVal(col, r2), f.op, f.value, filterTypeOf(col)));
9710
+ }
9673
9711
  }
9674
9712
  if (sort) {
9675
9713
  const col = colByField[sort.field];
@@ -9686,7 +9724,7 @@ function Datatable({
9686
9724
  out = [...out].sort((a, b) => (idx.get(keyOf(a)) ?? 1e9) - (idx.get(keyOf(b)) ?? 1e9));
9687
9725
  }
9688
9726
  return out;
9689
- }, [rows, quick, filters, sort, visibleCols, colByField, serverMode, rowOrder, searchFields]);
9727
+ }, [rows, quick, filters, filterLogic, sort, visibleCols, colByField, serverMode, rowOrder, searchFields]);
9690
9728
  const paginated = pageSize > 0;
9691
9729
  const serverTotal = rowCount == null ? processed.length : rowCount;
9692
9730
  const totalRows = serverMode ? serverTotal : processed.length;
@@ -9961,6 +9999,8 @@ function Datatable({
9961
9999
  pageSize: sizeVal,
9962
10000
  sort,
9963
10001
  filters: filters.map(({ id, ...f }) => f),
10002
+ filterLogic,
10003
+ // #303: "and" (default) | "or" — the server groups its WHERE accordingly
9964
10004
  quickFilter: quick.trim(),
9965
10005
  visibleColumns,
9966
10006
  hiddenColumns
@@ -9968,7 +10008,7 @@ function Datatable({
9968
10008
  });
9969
10009
  }, 250);
9970
10010
  return () => clearTimeout(t);
9971
- }, [serverMode, pageVal, sizeVal, sort, filters, quick, visibleColumns, hiddenColumns]);
10011
+ }, [serverMode, pageVal, sizeVal, sort, filters, filterLogic, quick, visibleColumns, hiddenColumns]);
9972
10012
  const onColVisRef = React51__default.useRef(onColumnVisibilityChange);
9973
10013
  onColVisRef.current = onColumnVisibilityChange;
9974
10014
  const lastVisibleKey = React51__default.useRef(null);
@@ -10626,7 +10666,97 @@ function Datatable({
10626
10666
  }
10627
10667
  };
10628
10668
  const hasFilterSizeOverride = fWidths.col != null || fWidths.op != null || fWidths.val != null || fPanelSize != null;
10629
- const fieldHandle = (field, rowIdx) => resizableFilters && rowIdx === 0 ? /* @__PURE__ */ jsx(
10669
+ const POP_MIN_W = { columns: 240, agg: 240, pivot: 260, batchedit: 260 };
10670
+ const popMinW = (id) => POP_MIN_W[id] || 200;
10671
+ const clearPop = (id, node) => {
10672
+ if (node) {
10673
+ node.style.removeProperty("--twc-dt-pop-w");
10674
+ node.style.removeProperty("--twc-dt-pop-h");
10675
+ node.removeAttribute("data-pop-sized");
10676
+ }
10677
+ setPopSizes((s) => {
10678
+ if (!(id in s)) return s;
10679
+ const n = { ...s };
10680
+ delete n[id];
10681
+ return n;
10682
+ });
10683
+ };
10684
+ const startPopResize = (id) => (e) => {
10685
+ if (e.button != null && e.button !== 0) return;
10686
+ e.preventDefault();
10687
+ e.stopPropagation();
10688
+ const pop = e.currentTarget.closest(".twc-dt__pop");
10689
+ if (!pop) return;
10690
+ const grip = e.currentTarget;
10691
+ const startX = e.clientX, startY = e.clientY;
10692
+ const r2 = pop.getBoundingClientRect();
10693
+ const startW = r2.width, startH = r2.height;
10694
+ const dir = typeof getComputedStyle !== "undefined" && getComputedStyle(pop).direction === "rtl" ? -1 : 1;
10695
+ pop.style.setProperty("--twc-dt-pop-w", `${startW}px`);
10696
+ pop.style.setProperty("--twc-dt-pop-h", `${startH}px`);
10697
+ grip.setAttribute("data-active", "true");
10698
+ pop.setAttribute("data-pop-sized", "");
10699
+ let lw = startW, lh = startH;
10700
+ const onMove = (ev) => {
10701
+ lw = clampNum(startW + (ev.clientX - startX) * dir, popMinW(id), fMaxW());
10702
+ lh = clampNum(startH + (ev.clientY - startY), 120, fMaxH());
10703
+ pop.style.setProperty("--twc-dt-pop-w", `${lw}px`);
10704
+ pop.style.setProperty("--twc-dt-pop-h", `${lh}px`);
10705
+ };
10706
+ const onUp = () => {
10707
+ setPopSizes((s) => ({ ...s, [id]: { w: lw, h: lh } }));
10708
+ grip.removeAttribute("data-active");
10709
+ window.removeEventListener("pointermove", onMove);
10710
+ window.removeEventListener("pointerup", onUp);
10711
+ };
10712
+ window.addEventListener("pointermove", onMove);
10713
+ window.addEventListener("pointerup", onUp);
10714
+ };
10715
+ const onPopGripKey = (id) => (e) => {
10716
+ if (e.key === "Enter" || e.key === " " || e.key === "Backspace") {
10717
+ e.preventDefault();
10718
+ clearPop(id, e.currentTarget.closest(".twc-dt__pop"));
10719
+ return;
10720
+ }
10721
+ if (!["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End"].includes(e.key)) return;
10722
+ e.preventDefault();
10723
+ const pop = e.currentTarget.closest(".twc-dt__pop");
10724
+ if (!pop) return;
10725
+ const r2 = pop.getBoundingClientRect();
10726
+ const step = e.shiftKey ? 4 : 20;
10727
+ const dir = typeof getComputedStyle !== "undefined" && getComputedStyle(pop).direction === "rtl" ? -1 : 1;
10728
+ let w = r2.width, h = r2.height;
10729
+ if (e.key === "ArrowRight") w += step * dir;
10730
+ else if (e.key === "ArrowLeft") w -= step * dir;
10731
+ else if (e.key === "ArrowDown") h += step;
10732
+ else if (e.key === "ArrowUp") h -= step;
10733
+ else if (e.key === "Home") {
10734
+ w = popMinW(id);
10735
+ h = 120;
10736
+ } else if (e.key === "End") {
10737
+ w = fMaxW();
10738
+ h = fMaxH();
10739
+ }
10740
+ setPopSizes((s) => ({ ...s, [id]: { w: clampNum(w, popMinW(id), fMaxW()), h: clampNum(h, 120, fMaxH()) } }));
10741
+ };
10742
+ const popStyle = (id) => popSizes[id] ? { "--twc-dt-pop-w": `${popSizes[id].w}px`, "--twc-dt-pop-h": `${popSizes[id].h}px` } : null;
10743
+ const popGrip = (id) => resizablePopovers ? /* @__PURE__ */ jsx(
10744
+ "span",
10745
+ {
10746
+ className: "twc-dt__pop-grip",
10747
+ role: "slider",
10748
+ tabIndex: 0,
10749
+ "aria-label": "Resize panel (Enter or double-click to reset)",
10750
+ "aria-valuetext": popSizes[id] ? `Width ${Math.round(popSizes[id].w)} pixels, height ${Math.round(popSizes[id].h)} pixels` : "Default size",
10751
+ onPointerDown: startPopResize(id),
10752
+ onKeyDown: onPopGripKey(id),
10753
+ onDoubleClick: (e) => {
10754
+ e.preventDefault();
10755
+ clearPop(id, e.currentTarget.closest(".twc-dt__pop"));
10756
+ }
10757
+ }
10758
+ ) : null;
10759
+ const fieldHandle = (field, rowIdx) => (resizableFilters || resizablePopovers) && rowIdx === 0 ? /* @__PURE__ */ jsx(
10630
10760
  "span",
10631
10761
  {
10632
10762
  className: "twc-dt__f-rz",
@@ -10748,7 +10878,7 @@ function Datatable({
10748
10878
  onDragEnd: reorderable ? () => setRowDrag({ from: null, over: null, after: false }) : void 0,
10749
10879
  onClick: (e) => handleRowClick(e, k, row),
10750
10880
  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: [
10881
+ 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
10882
  /* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: 4 }, children: [
10753
10883
  reorderable ? rowHandle(k, midIdx, row) : null,
10754
10884
  /* @__PURE__ */ jsx(
@@ -10773,7 +10903,7 @@ function Datatable({
10773
10903
  ] }),
10774
10904
  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
10905
  ] }) : 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,
10906
+ 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
10907
  ordered.map((c, ci) => {
10778
10908
  const st = stickyOf(c.field);
10779
10909
  const val = getColVal(c, row);
@@ -11183,7 +11313,7 @@ function Datatable({
11183
11313
  onKeyDown: onGridKeyDown,
11184
11314
  children: [
11185
11315
  /* @__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(
11316
+ 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
11317
  "span",
11188
11318
  {
11189
11319
  className: "twc-dt__check",
@@ -11203,7 +11333,7 @@ function Datatable({
11203
11333
  children: /* @__PURE__ */ jsx(Svg, { d: allSel ? I.check : I.minus })
11204
11334
  }
11205
11335
  ) }) }) : 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: [
11336
+ 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
11337
  /* @__PURE__ */ jsx("span", { className: "twc-dt__th-label", style: { flex: "none", cursor: "default" }, children: "#" }),
11208
11338
  /* @__PURE__ */ jsx(
11209
11339
  "button",
@@ -11381,8 +11511,8 @@ function Datatable({
11381
11511
  pinnedBottomRows.map((row) => renderLeaf(row, keyIndex.get(keyOf(row)), "bottom"))
11382
11512
  ] }) }),
11383
11513
  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,
11514
+ 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,
11515
+ 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
11516
  ordered.map((c) => {
11387
11517
  const st = stickyOf(c.field);
11388
11518
  const v = aggregate(c);
@@ -11481,7 +11611,7 @@ function Datatable({
11481
11611
  delete values[f];
11482
11612
  return { ...b, fields, values };
11483
11613
  });
11484
- return /* @__PURE__ */ jsxs("div", { className: "twc-dt__pop twc-dt__cfg", style: { top: batchEditPos.top, left: batchEditPos.left, width: 320 }, children: [
11614
+ 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
11615
  /* @__PURE__ */ jsx(Caret, { pos: batchEditPos }),
11486
11616
  /* @__PURE__ */ jsxs("div", { className: "twc-dt__cfg-head", children: [
11487
11617
  "Edit ",
@@ -11543,7 +11673,8 @@ function Datatable({
11543
11673
  "Apply to ",
11544
11674
  selected.size
11545
11675
  ] })
11546
- ] })
11676
+ ] }),
11677
+ popGrip("batchedit")
11547
11678
  ] });
11548
11679
  })() : null,
11549
11680
  colMenu && menuPos ? /* @__PURE__ */ jsxs(
@@ -11716,7 +11847,7 @@ function Datatable({
11716
11847
  ]
11717
11848
  }
11718
11849
  ) : 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: [
11850
+ 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
11851
  /* @__PURE__ */ jsxs("div", { className: "twc-dt__panel-head", children: [
11721
11852
  /* @__PURE__ */ jsxs("span", { className: "twc-dt__panel-title", children: [
11722
11853
  "Columns ",
@@ -11841,9 +11972,10 @@ function Datatable({
11841
11972
  c.field
11842
11973
  );
11843
11974
  })
11844
- ] })
11975
+ ] }),
11976
+ popGrip("columns")
11845
11977
  ] }) : 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: [
11978
+ 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
11979
  /* @__PURE__ */ jsxs("div", { className: "twc-dt__panel-head", children: [
11848
11980
  /* @__PURE__ */ jsx("span", { className: "twc-dt__panel-title", children: "Aggregation" }),
11849
11981
  /* @__PURE__ */ jsx("button", { type: "button", className: "twc-dt__link", onClick: () => setAggConfig({}), children: "Clear" })
@@ -11892,13 +12024,14 @@ function Datatable({
11892
12024
  }
11893
12025
  ) })
11894
12026
  ] }, c.field);
11895
- }) })
12027
+ }) }),
12028
+ popGrip("agg")
11896
12029
  ] }) : null,
11897
12030
  panel === "pivot" && panelPos ? (() => {
11898
12031
  const fieldOpts = ordered.filter((c) => c.type !== "actions").map((c) => ({ value: c.field, label: c.headerName }));
11899
12032
  const aggOpts = [{ value: "sum", label: "Sum" }, { value: "avg", label: "Avg" }, { value: "min", label: "Min" }, { value: "max", label: "Max" }, { value: "count", label: "Count" }];
11900
12033
  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: [
12034
+ 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
12035
  /* @__PURE__ */ jsxs("div", { className: "twc-dt__panel-head", children: [
11903
12036
  /* @__PURE__ */ jsx("span", { className: "twc-dt__panel-title", children: "Pivot" }),
11904
12037
  /* @__PURE__ */ jsx("button", { type: "button", className: "twc-dt__link", onClick: () => {
@@ -11906,106 +12039,109 @@ function Datatable({
11906
12039
  setPivotOn(false);
11907
12040
  }, children: "Reset" })
11908
12041
  ] }),
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);
12042
+ /* @__PURE__ */ jsxs("div", { className: "twc-dt__pivot-body", children: [
12043
+ /* @__PURE__ */ jsxs("div", { className: "twc-dt__cfg-toggle", children: [
12044
+ /* @__PURE__ */ jsx("span", { children: "Pivot mode" }),
12045
+ /* @__PURE__ */ jsx(
12046
+ "span",
12047
+ {
12048
+ className: "twc-dt__sw",
12049
+ "data-on": pivotOn || void 0,
12050
+ role: "switch",
12051
+ "aria-checked": pivotOn,
12052
+ tabIndex: 0,
12053
+ onClick: () => setPivotOn((v) => !v),
12054
+ onKeyDown: (e) => {
12055
+ if (e.key === "Enter" || e.key === " ") {
12056
+ e.preventDefault();
12057
+ setPivotOn((v) => !v);
12058
+ }
11924
12059
  }
11925
12060
  }
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,
12061
+ )
12062
+ ] }),
12063
+ !pivotActive && pivotOn ? /* @__PURE__ */ jsxs("div", { className: "twc-dt__cfg-hint", children: [
12064
+ "Add at least one ",
12065
+ /* @__PURE__ */ jsx("b", { children: "Row" }),
12066
+ " field and one ",
12067
+ /* @__PURE__ */ jsx("b", { children: "Value" }),
12068
+ " to see the pivot."
12069
+ ] }) : null,
12070
+ /* @__PURE__ */ jsxs("div", { className: "twc-dt__cfg-section", children: [
12071
+ /* @__PURE__ */ jsx("span", { className: "twc-dt__cfg-label", children: "Rows" }),
12072
+ /* @__PURE__ */ jsx(
12073
+ MultiSelect,
11971
12074
  {
11972
- size: "sm",
11973
- value: v.agg || "sum",
11974
- options: aggOpts,
11975
12075
  portal: true,
11976
- onChange: (a) => setPivotConfig((p) => ({ ...p, values: p.values.map((x, j) => j === i ? { ...x, agg: a } : x) }))
12076
+ placeholder: "Add row fields\u2026",
12077
+ value: pivotConfig.rows,
12078
+ options: fieldOpts,
12079
+ onChange: (vals) => {
12080
+ setPivotConfig((p) => ({ ...p, rows: vals }));
12081
+ if (vals.length) setPivotOn(true);
12082
+ }
11977
12083
  }
11978
- ) }),
12084
+ )
12085
+ ] }),
12086
+ /* @__PURE__ */ jsxs("div", { className: "twc-dt__cfg-section", children: [
12087
+ /* @__PURE__ */ jsx("span", { className: "twc-dt__cfg-label", children: "Columns" }),
11979
12088
  /* @__PURE__ */ jsx(
11980
- "button",
12089
+ MultiSelect,
11981
12090
  {
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 })
12091
+ portal: true,
12092
+ placeholder: "Add column fields\u2026",
12093
+ value: pivotConfig.columns,
12094
+ options: fieldOpts,
12095
+ onChange: (vals) => setPivotConfig((p) => ({ ...p, columns: vals }))
11987
12096
  }
11988
12097
  )
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);
12098
+ ] }),
12099
+ /* @__PURE__ */ jsxs("div", { className: "twc-dt__cfg-section", children: [
12100
+ /* @__PURE__ */ jsx("span", { className: "twc-dt__cfg-label", children: "Values" }),
12101
+ pivotConfig.values.map((v, i) => /* @__PURE__ */ jsxs("div", { className: "twc-dt__cfg-row", children: [
12102
+ /* @__PURE__ */ jsx("span", { className: "twc-dt__cfg-name", children: colByField[v.field]?.headerName || v.field }),
12103
+ /* @__PURE__ */ jsx("div", { className: "twc-dt__cfg-ctl", children: /* @__PURE__ */ jsx(
12104
+ Select,
12105
+ {
12106
+ size: "sm",
12107
+ value: v.agg || "sum",
12108
+ options: aggOpts,
12109
+ portal: true,
12110
+ onChange: (a) => setPivotConfig((p) => ({ ...p, values: p.values.map((x, j) => j === i ? { ...x, agg: a } : x) }))
12111
+ }
12112
+ ) }),
12113
+ /* @__PURE__ */ jsx(
12114
+ "button",
12115
+ {
12116
+ type: "button",
12117
+ className: "twc-dt__cfg-x",
12118
+ "aria-label": `Remove ${colByField[v.field]?.headerName || v.field}`,
12119
+ onClick: () => setPivotConfig((p) => ({ ...p, values: p.values.filter((_, j) => j !== i) })),
12120
+ children: /* @__PURE__ */ jsx(Svg, { d: I.x })
12121
+ }
12122
+ )
12123
+ ] }, v.field + i)),
12124
+ valueFieldOpts.length ? /* @__PURE__ */ jsx("div", { className: "twc-dt__cfg-add", children: /* @__PURE__ */ jsx(
12125
+ Select,
12126
+ {
12127
+ size: "sm",
12128
+ placeholder: "Add value field\u2026",
12129
+ value: "",
12130
+ options: valueFieldOpts,
12131
+ portal: true,
12132
+ searchable: true,
12133
+ searchPlaceholder: "Search fields\u2026",
12134
+ onChange: (f) => {
12135
+ if (f) {
12136
+ setPivotConfig((p) => p.values.some((x) => x.field === f) ? p : { ...p, values: [...p.values, { field: f, agg: "sum" }] });
12137
+ setPivotOn(true);
12138
+ }
12004
12139
  }
12005
12140
  }
12006
- }
12007
- ) }) : null
12008
- ] })
12141
+ ) }) : null
12142
+ ] })
12143
+ ] }),
12144
+ popGrip("pivot")
12009
12145
  ] });
12010
12146
  })() : null,
12011
12147
  panel === "filters" && panelPos ? /* @__PURE__ */ jsxs(
@@ -12029,8 +12165,12 @@ function Datatable({
12029
12165
  children: [
12030
12166
  /* @__PURE__ */ jsxs("div", { className: "twc-dt__panel-head", children: [
12031
12167
  /* @__PURE__ */ jsx("span", { className: "twc-dt__panel-title", children: "Filters" }),
12032
- /* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", gap: 2 }, children: [
12033
- resizableFilters && hasFilterSizeOverride ? /* @__PURE__ */ jsx("button", { type: "button", className: "twc-dt__link", onClick: resetFilterSizes, children: "Reset sizes" }) : null,
12168
+ /* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: 6 }, children: [
12169
+ filters.length > 1 ? /* @__PURE__ */ jsxs("span", { className: "twc-dt__flogic", role: "group", "aria-label": "Combine filters with", children: [
12170
+ /* @__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" }),
12171
+ /* @__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" })
12172
+ ] }) : null,
12173
+ (resizableFilters || resizablePopovers) && hasFilterSizeOverride ? /* @__PURE__ */ jsx("button", { type: "button", className: "twc-dt__link", onClick: resetFilterSizes, children: "Reset sizes" }) : null,
12034
12174
  filters.length ? /* @__PURE__ */ jsx("button", { type: "button", className: "twc-dt__link", onClick: () => setFilters([]), children: "Clear all" }) : null
12035
12175
  ] })
12036
12176
  ] }),
@@ -12102,6 +12242,7 @@ function Datatable({
12102
12242
  ),
12103
12243
  fieldHandle("val", i)
12104
12244
  ] }),
12245
+ /* @__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
12246
  /* @__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
12247
  ] }, f.id);
12107
12248
  }) }),
@@ -12109,7 +12250,7 @@ function Datatable({
12109
12250
  /* @__PURE__ */ jsx(Svg, { d: I.plus, style: { color: "var(--color-primary)" } }),
12110
12251
  " Add filter"
12111
12252
  ] }) }),
12112
- resizableFilters ? /* @__PURE__ */ jsx(
12253
+ resizableFilters || resizablePopovers ? /* @__PURE__ */ jsx(
12113
12254
  "span",
12114
12255
  {
12115
12256
  className: "twc-dt__pop-grip",