twico-ui 1.21.1 → 1.22.1
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/CHANGELOG.md +15 -0
- package/dist/index.cjs +398 -81
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.mjs +398 -81
- package/dist/index.mjs.map +1 -1
- package/llms.txt +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to Twico UI are documented here (newest first). This file is generated automatically on each release by [semantic-release](https://semantic-release.gitbook.io/) from [Conventional Commits](https://www.conventionalcommits.org/) — do not edit it by hand. The same notes appear on each [GitHub Release](https://github.com/EncryptionL/twico-ui/releases).
|
|
4
4
|
|
|
5
|
+
## [1.22.1](https://github.com/EncryptionL/twico-ui/compare/v1.22.0...v1.22.1) (2026-07-29)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **datatable:** keep the Filters popover position:fixed (regression from [#292](https://github.com/EncryptionL/twico-ui/issues/292)) ([0d53d05](https://github.com/EncryptionL/twico-ui/commit/0d53d057e861f5327af7b43730b3f143a937205e)), closes [#294](https://github.com/EncryptionL/twico-ui/issues/294)
|
|
11
|
+
* **inputs:** portaled dropdown menu re-tracks its trigger's resize ([b6e6843](https://github.com/EncryptionL/twico-ui/commit/b6e6843f2345f60db0719f7e12bef504393e3189)), closes [#292](https://github.com/EncryptionL/twico-ui/issues/292)
|
|
12
|
+
|
|
13
|
+
# [1.22.0](https://github.com/EncryptionL/twico-ui/compare/v1.21.1...v1.22.0) (2026-07-29)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
* **datatable:** user-resizable filters popover + fields ([4a7aeab](https://github.com/EncryptionL/twico-ui/commit/4a7aeab2e7f08c452b3547f1406df811c9333f03)), closes [#292](https://github.com/EncryptionL/twico-ui/issues/292)
|
|
19
|
+
|
|
5
20
|
## [1.21.1](https://github.com/EncryptionL/twico-ui/compare/v1.21.0...v1.21.1) (2026-07-29)
|
|
6
21
|
|
|
7
22
|
|
package/dist/index.cjs
CHANGED
|
@@ -5345,9 +5345,16 @@ function ColorPicker({
|
|
|
5345
5345
|
const onMove = () => place();
|
|
5346
5346
|
window.addEventListener("scroll", onMove, true);
|
|
5347
5347
|
window.addEventListener("resize", onMove);
|
|
5348
|
+
let ro;
|
|
5349
|
+
const rt = triggerRef.current;
|
|
5350
|
+
if (rt && typeof ResizeObserver !== "undefined") {
|
|
5351
|
+
ro = new ResizeObserver(onMove);
|
|
5352
|
+
ro.observe(rt);
|
|
5353
|
+
}
|
|
5348
5354
|
return () => {
|
|
5349
5355
|
window.removeEventListener("scroll", onMove, true);
|
|
5350
5356
|
window.removeEventListener("resize", onMove);
|
|
5357
|
+
ro?.disconnect();
|
|
5351
5358
|
};
|
|
5352
5359
|
}, [open, place]);
|
|
5353
5360
|
const labelId = React51__namespace.default.useId();
|
|
@@ -5807,9 +5814,16 @@ function Combobox({
|
|
|
5807
5814
|
const onMove = () => place();
|
|
5808
5815
|
window.addEventListener("scroll", onMove, true);
|
|
5809
5816
|
window.addEventListener("resize", onMove);
|
|
5817
|
+
let ro;
|
|
5818
|
+
const rt = controlRef.current;
|
|
5819
|
+
if (rt && typeof ResizeObserver !== "undefined") {
|
|
5820
|
+
ro = new ResizeObserver(onMove);
|
|
5821
|
+
ro.observe(rt);
|
|
5822
|
+
}
|
|
5810
5823
|
return () => {
|
|
5811
5824
|
window.removeEventListener("scroll", onMove, true);
|
|
5812
5825
|
window.removeEventListener("resize", onMove);
|
|
5826
|
+
ro?.disconnect();
|
|
5813
5827
|
};
|
|
5814
5828
|
}, [open, portal, place]);
|
|
5815
5829
|
React51__namespace.default.useEffect(() => {
|
|
@@ -6634,9 +6648,16 @@ function Select({
|
|
|
6634
6648
|
const onMove = () => place();
|
|
6635
6649
|
window.addEventListener("scroll", onMove, true);
|
|
6636
6650
|
window.addEventListener("resize", onMove);
|
|
6651
|
+
let ro;
|
|
6652
|
+
const rt = triggerRef.current;
|
|
6653
|
+
if (rt && typeof ResizeObserver !== "undefined") {
|
|
6654
|
+
ro = new ResizeObserver(onMove);
|
|
6655
|
+
ro.observe(rt);
|
|
6656
|
+
}
|
|
6637
6657
|
return () => {
|
|
6638
6658
|
window.removeEventListener("scroll", onMove, true);
|
|
6639
6659
|
window.removeEventListener("resize", onMove);
|
|
6660
|
+
ro?.disconnect();
|
|
6640
6661
|
};
|
|
6641
6662
|
}, [open, portal, place]);
|
|
6642
6663
|
React51__namespace.default.useEffect(() => {
|
|
@@ -7463,9 +7484,16 @@ function MultiSelect({
|
|
|
7463
7484
|
const onMove = () => place();
|
|
7464
7485
|
window.addEventListener("scroll", onMove, true);
|
|
7465
7486
|
window.addEventListener("resize", onMove);
|
|
7487
|
+
let ro;
|
|
7488
|
+
const rt = controlRef.current;
|
|
7489
|
+
if (rt && typeof ResizeObserver !== "undefined") {
|
|
7490
|
+
ro = new ResizeObserver(onMove);
|
|
7491
|
+
ro.observe(rt);
|
|
7492
|
+
}
|
|
7466
7493
|
return () => {
|
|
7467
7494
|
window.removeEventListener("scroll", onMove, true);
|
|
7468
7495
|
window.removeEventListener("resize", onMove);
|
|
7496
|
+
ro?.disconnect();
|
|
7469
7497
|
};
|
|
7470
7498
|
}, [open, portal, place]);
|
|
7471
7499
|
React51__namespace.default.useEffect(() => {
|
|
@@ -8525,20 +8553,55 @@ th.twc-dt__rownum .twc-dt__th-inner { padding-inline: 8px; gap: 2px; justify-con
|
|
|
8525
8553
|
.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); }
|
|
8526
8554
|
.twc-dt__link:hover { background: var(--color-primary-subtle); }
|
|
8527
8555
|
|
|
8528
|
-
/* Filter panel
|
|
8529
|
-
|
|
8556
|
+
/* Filter panel \u2014 user-resizable (#292): panel size + per-field widths resolve through a two-tier CSS var
|
|
8557
|
+
cascade \u2014 user drag (-usr) > #289 measured auto-fit (-fit) > 118px fallback \u2014 entirely in clamp(), so
|
|
8558
|
+
neither writer reads the other (reset = remove the -usr var and auto-fit instantly re-wins). */
|
|
8559
|
+
.twc-dt__filters {
|
|
8560
|
+
/* #294: do NOT set 'position' here \u2014 this element also carries .twc-dt__pop (position: fixed), and an
|
|
8561
|
+
equal-specificity 'position: relative' defined later would clobber it, dropping the popover into normal
|
|
8562
|
+
flow below the table. The panel stays position: fixed, which already anchors the absolutely-positioned
|
|
8563
|
+
.twc-dt__pop-grip; per-field handles anchor to their own position: relative .twc-dt__f-* fields. */
|
|
8564
|
+
width: var(--twc-dt-panel-w, 580px); max-width: calc(100vw - 32px);
|
|
8565
|
+
height: var(--twc-dt-panel-h, auto); max-height: calc(100vh - 32px);
|
|
8566
|
+
display: flex; flex-direction: column;
|
|
8567
|
+
}
|
|
8568
|
+
/* Rows list scrolls only once the user pins a height; Add-filter stays sticky. Safe because the filter
|
|
8569
|
+
Selects portal to <body> \u2014 an inline (non-portaled) menu would be clipped by this scroll box. */
|
|
8570
|
+
.twc-dt__frows { min-height: 0; }
|
|
8571
|
+
.twc-dt__filters[data-panel-sized] .twc-dt__frows { flex: 1 1 auto; overflow-y: auto; }
|
|
8572
|
+
.twc-dt__filters[data-panel-sized] .twc-dt__f-add { position: sticky; bottom: 0; background: var(--color-surface-raised); padding-inline-end: 20px; }
|
|
8530
8573
|
/* #289: wrap is a safety net \u2014 a beyond-cap label or the narrow responsive panel drops the value input
|
|
8531
8574
|
to its own line instead of clipping the remove button; no effect in the normal (capped) case. */
|
|
8532
8575
|
.twc-dt__frow { display: flex; align-items: center; gap: 6px; padding: 5px 4px; flex-wrap: wrap; }
|
|
8533
|
-
/*
|
|
8534
|
-
|
|
8535
|
-
|
|
8536
|
-
|
|
8537
|
-
.twc-dt__f-op {
|
|
8538
|
-
|
|
8576
|
+
/* col/op: user > measured (-fit, written by the #289 measurer) > 118px, clamped to a drag cap that
|
|
8577
|
+
filterFieldMaxWidth can raise. Position:relative hosts the resize handle. */
|
|
8578
|
+
.twc-dt__f-col { position: relative; flex: none; min-width: 118px;
|
|
8579
|
+
width: clamp(118px, var(--twc-dt-fcol-usr, var(--twc-dt-fcol-fit, 118px)), var(--twc-dt-fcol-cap, 360px)); }
|
|
8580
|
+
.twc-dt__f-op { position: relative; flex: none; min-width: 118px;
|
|
8581
|
+
width: clamp(118px, var(--twc-dt-fop-usr, var(--twc-dt-fop-fit, 118px)), var(--twc-dt-fop-cap, 260px)); }
|
|
8582
|
+
/* value: the flex shock-absorber by default; the first drag flips it to a fixed width (data-val-fixed). */
|
|
8583
|
+
.twc-dt__f-val { position: relative; flex: 1 1 140px; min-width: 140px; }
|
|
8584
|
+
.twc-dt__filters[data-val-fixed] .twc-dt__f-val { flex: 0 0 clamp(140px, var(--twc-dt-fval-usr, 240px), 640px); }
|
|
8585
|
+
.twc-dt__filters[data-val-fixed] .twc-dt__frm-x { margin-inline-start: auto; }
|
|
8539
8586
|
.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; }
|
|
8540
8587
|
.twc-dt__frm-x:hover { background: var(--color-danger-subtle); color: var(--color-danger-subtle-fg); }
|
|
8541
8588
|
.twc-dt__frm-x svg { width: 15px; height: 15px; }
|
|
8589
|
+
/* per-field resize handle \u2014 cloned from the grid's .twc-dt__resizer; always-on baseline opacity so it's
|
|
8590
|
+
discoverable/hittable on touch (no hover there), with a non-color-only focus ring. */
|
|
8591
|
+
.twc-dt__f-rz { position: absolute; top: 0; inset-inline-end: -3px; width: 8px; height: 100%; cursor: col-resize; touch-action: none; z-index: 6; opacity: 0.28; transition: opacity var(--duration-fast); }
|
|
8592
|
+
.twc-dt__f-rz::after { content: ""; position: absolute; top: 22%; inset-inline-end: 2px; width: 2px; height: 56%; background: var(--color-border-strong); border-radius: 2px; transition: width var(--duration-fast), background-color var(--duration-fast); }
|
|
8593
|
+
.twc-dt__frow:hover .twc-dt__f-rz, .twc-dt__f-rz:focus-visible, .twc-dt__f-rz[data-active="true"] { opacity: 1; }
|
|
8594
|
+
.twc-dt__f-rz:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--color-primary); border-radius: 2px; }
|
|
8595
|
+
.twc-dt__f-rz:focus-visible::after, .twc-dt__f-rz[data-active="true"]::after { width: 3px; background: var(--color-primary); }
|
|
8596
|
+
/* panel corner grip (2-axis) at the bottom-inline-end. */
|
|
8597
|
+
.twc-dt__pop-grip { position: absolute; inset-block-end: 2px; inset-inline-end: 2px; width: 14px; height: 14px; cursor: nwse-resize; touch-action: none; z-index: 8; opacity: 0.5; border-radius: 3px; background: linear-gradient(135deg, transparent 0 55%, var(--color-border-strong) 55% 64%, transparent 64% 73%, var(--color-border-strong) 73% 82%, transparent 82%); transition: opacity var(--duration-fast); }
|
|
8598
|
+
.twc-dt__pop-grip:hover, .twc-dt__pop-grip:focus-visible, .twc-dt__pop-grip[data-active="true"] { opacity: 1; }
|
|
8599
|
+
.twc-dt__pop-grip:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 1px; }
|
|
8600
|
+
[dir="rtl"] .twc-dt__pop-grip { cursor: nesw-resize; }
|
|
8601
|
+
@media (pointer: coarse) {
|
|
8602
|
+
.twc-dt__f-rz { width: 16px; inset-inline-end: -6px; opacity: 0.4; }
|
|
8603
|
+
.twc-dt__pop-grip { width: 20px; height: 20px; opacity: 0.6; }
|
|
8604
|
+
}
|
|
8542
8605
|
.twc-dt__empty { padding: 30px 12px; text-align: center; color: var(--color-text-subtle); font-size: var(--text-sm); }
|
|
8543
8606
|
|
|
8544
8607
|
/* Action cell */
|
|
@@ -9091,6 +9154,8 @@ function Datatable({
|
|
|
9091
9154
|
quickFilter,
|
|
9092
9155
|
onQuickFilterChange,
|
|
9093
9156
|
toolbarActions,
|
|
9157
|
+
resizableFilters = true,
|
|
9158
|
+
filterFieldMaxWidth,
|
|
9094
9159
|
rowPinning = false,
|
|
9095
9160
|
rowReorder = false,
|
|
9096
9161
|
rowResize = false,
|
|
@@ -9239,6 +9304,8 @@ function Datatable({
|
|
|
9239
9304
|
const [rowGrab, setRowGrab] = React51__namespace.default.useState(null);
|
|
9240
9305
|
const [reorderMsg, setReorderMsg] = React51__namespace.default.useState("");
|
|
9241
9306
|
const rowFocusRef = React51__namespace.default.useRef(null);
|
|
9307
|
+
const [fWidths, setFWidths] = React51__namespace.default.useState({});
|
|
9308
|
+
const [fPanelSize, setFPanelSize] = React51__namespace.default.useState(null);
|
|
9242
9309
|
const serializeState = () => ({
|
|
9243
9310
|
filters: filters.map(({ id, ...f }) => f),
|
|
9244
9311
|
// strip the internal row id
|
|
@@ -9251,7 +9318,10 @@ function Datatable({
|
|
|
9251
9318
|
columnVisibility: Object.fromEntries([...hidden].map((f) => [f, false])),
|
|
9252
9319
|
// absent = visible
|
|
9253
9320
|
columnPinning: Object.fromEntries([...pins.left.map((f) => [f, "left"]), ...pins.right.map((f) => [f, "right"])]),
|
|
9254
|
-
density
|
|
9321
|
+
density,
|
|
9322
|
+
// #292: additive, optional — user-resized filter panel + field widths (panel-global, not per column).
|
|
9323
|
+
filterPanelSize: fPanelSize || void 0,
|
|
9324
|
+
filterFieldWidths: fWidths.col != null || fWidths.op != null || fWidths.val != null ? fWidths : void 0
|
|
9255
9325
|
});
|
|
9256
9326
|
const applyState = (s) => {
|
|
9257
9327
|
if (!s || typeof s !== "object") return;
|
|
@@ -9276,6 +9346,16 @@ function Datatable({
|
|
|
9276
9346
|
setPins({ left: l, right: r2 });
|
|
9277
9347
|
}
|
|
9278
9348
|
if (s.density === "compact" || s.density === "standard" || s.density === "comfortable") setDensity(s.density);
|
|
9349
|
+
if (s.filterFieldWidths && typeof s.filterFieldWidths === "object") {
|
|
9350
|
+
const fw = {};
|
|
9351
|
+
for (const k of ["col", "op", "val"]) if (typeof s.filterFieldWidths[k] === "number") fw[k] = s.filterFieldWidths[k];
|
|
9352
|
+
if (Object.keys(fw).length) setFWidths(fw);
|
|
9353
|
+
}
|
|
9354
|
+
if (s.filterPanelSize && typeof s.filterPanelSize === "object") {
|
|
9355
|
+
const w = typeof s.filterPanelSize.w === "number" ? clampNum(s.filterPanelSize.w, 220, fMaxW()) : void 0;
|
|
9356
|
+
const h = typeof s.filterPanelSize.h === "number" ? clampNum(s.filterPanelSize.h, 120, fMaxH()) : void 0;
|
|
9357
|
+
if (w != null || h != null) setFPanelSize({ ...w != null ? { w } : {}, ...h != null ? { h } : {} });
|
|
9358
|
+
}
|
|
9279
9359
|
};
|
|
9280
9360
|
const stateReadyRef = React51__namespace.default.useRef(false);
|
|
9281
9361
|
const onStateChangeRef = React51__namespace.default.useRef(onStateChange);
|
|
@@ -9292,7 +9372,7 @@ function Datatable({
|
|
|
9292
9372
|
}
|
|
9293
9373
|
}
|
|
9294
9374
|
return void 0;
|
|
9295
|
-
}, [filters, sort, quick, pageVal, sizeVal, order, widths, hidden, pins, density, stateKey]);
|
|
9375
|
+
}, [filters, sort, quick, pageVal, sizeVal, order, widths, hidden, pins, density, fWidths, fPanelSize, stateKey]);
|
|
9296
9376
|
React51__namespace.default.useEffect(() => {
|
|
9297
9377
|
let saved = null;
|
|
9298
9378
|
if (stateKey) {
|
|
@@ -10374,8 +10454,8 @@ function Datatable({
|
|
|
10374
10454
|
const colCap = Math.max(118, Math.min(210, el.clientWidth - 118 - 30 - 140 - 3 * 6 - 2 * 4));
|
|
10375
10455
|
const colW = Math.min(colCap, Math.max(118, Math.ceil(widest(filterableCols.map((c) => c.headerName ?? c.field)) + chrome)));
|
|
10376
10456
|
const opW = Math.min(170, Math.max(118, Math.ceil(widest(ALL_OP_LABELS) + chrome)));
|
|
10377
|
-
if (Number.isFinite(colW)) el.style.setProperty("--twc-dt-fcol-
|
|
10378
|
-
if (Number.isFinite(opW)) el.style.setProperty("--twc-dt-fop-
|
|
10457
|
+
if (Number.isFinite(colW)) el.style.setProperty("--twc-dt-fcol-fit", `${colW}px`);
|
|
10458
|
+
if (Number.isFinite(opW)) el.style.setProperty("--twc-dt-fop-fit", `${opW}px`);
|
|
10379
10459
|
};
|
|
10380
10460
|
measure();
|
|
10381
10461
|
document.fonts?.ready?.then?.(measure).catch?.(() => {
|
|
@@ -10387,6 +10467,174 @@ function Datatable({
|
|
|
10387
10467
|
}
|
|
10388
10468
|
return () => ro?.disconnect();
|
|
10389
10469
|
}, [panel, filterableCols, filters.length]);
|
|
10470
|
+
const F_MIN = { col: 118, op: 118, val: 140 };
|
|
10471
|
+
const F_VAR = { col: "--twc-dt-fcol-usr", op: "--twc-dt-fop-usr", val: "--twc-dt-fval-usr" };
|
|
10472
|
+
const F_SEL = { col: ".twc-dt__f-col", op: ".twc-dt__f-op", val: ".twc-dt__f-val" };
|
|
10473
|
+
const F_LABEL = { col: "column", op: "operator", val: "value" };
|
|
10474
|
+
const fMaxW = () => typeof window === "undefined" ? 9999 : window.innerWidth - 32;
|
|
10475
|
+
const fMaxH = () => typeof window === "undefined" ? 9999 : window.innerHeight - 32;
|
|
10476
|
+
const clampNum = (v, lo, hi) => Math.max(lo, Math.min(hi, Math.round(v)));
|
|
10477
|
+
const fLiveW = (field) => {
|
|
10478
|
+
const p = filterPanelRef.current, el = p && p.querySelector(F_SEL[field]);
|
|
10479
|
+
return el ? el.getBoundingClientRect().width : F_MIN[field];
|
|
10480
|
+
};
|
|
10481
|
+
const fCap = (field) => {
|
|
10482
|
+
if (field === "col") return filterFieldMaxWidth || 360;
|
|
10483
|
+
if (field === "op") return filterFieldMaxWidth || 260;
|
|
10484
|
+
const p = filterPanelRef.current;
|
|
10485
|
+
if (!p) return 640;
|
|
10486
|
+
return clampNum(p.clientWidth - fLiveW("col") - fLiveW("op") - 30 - 24, F_MIN.val, 640);
|
|
10487
|
+
};
|
|
10488
|
+
const isRtl = () => {
|
|
10489
|
+
const p = filterPanelRef.current;
|
|
10490
|
+
return p ? getComputedStyle(p).direction === "rtl" : false;
|
|
10491
|
+
};
|
|
10492
|
+
const startFieldResize = (e, field) => {
|
|
10493
|
+
if (e.button != null && e.button !== 0) return;
|
|
10494
|
+
e.preventDefault();
|
|
10495
|
+
e.stopPropagation();
|
|
10496
|
+
const panel2 = filterPanelRef.current;
|
|
10497
|
+
if (!panel2) return;
|
|
10498
|
+
const handle = e.currentTarget;
|
|
10499
|
+
const startX = e.clientX;
|
|
10500
|
+
const startW = fLiveW(field);
|
|
10501
|
+
const cap = fCap(field);
|
|
10502
|
+
const dir = isRtl() ? -1 : 1;
|
|
10503
|
+
handle.setAttribute("data-active", "true");
|
|
10504
|
+
if (field === "val") panel2.setAttribute("data-val-fixed", "");
|
|
10505
|
+
let last = startW;
|
|
10506
|
+
const onMove = (ev) => {
|
|
10507
|
+
last = clampNum(startW + (ev.clientX - startX) * dir, F_MIN[field], cap);
|
|
10508
|
+
panel2.style.setProperty(F_VAR[field], `${last}px`);
|
|
10509
|
+
};
|
|
10510
|
+
const onUp = () => {
|
|
10511
|
+
setFWidths((m) => ({ ...m, [field]: last }));
|
|
10512
|
+
handle.removeAttribute("data-active");
|
|
10513
|
+
window.removeEventListener("pointermove", onMove);
|
|
10514
|
+
window.removeEventListener("pointerup", onUp);
|
|
10515
|
+
};
|
|
10516
|
+
window.addEventListener("pointermove", onMove);
|
|
10517
|
+
window.addEventListener("pointerup", onUp);
|
|
10518
|
+
};
|
|
10519
|
+
const onFieldResizeKey = (e, field) => {
|
|
10520
|
+
if (e.key === "Enter" || e.key === " " || e.key === "Backspace") {
|
|
10521
|
+
e.preventDefault();
|
|
10522
|
+
e.stopPropagation();
|
|
10523
|
+
resetFWidth(field);
|
|
10524
|
+
return;
|
|
10525
|
+
}
|
|
10526
|
+
if (!["ArrowLeft", "ArrowRight", "Home", "End"].includes(e.key)) return;
|
|
10527
|
+
e.preventDefault();
|
|
10528
|
+
e.stopPropagation();
|
|
10529
|
+
const cur = fLiveW(field), cap = fCap(field), step = e.shiftKey ? 2 : 10, dir = isRtl() ? -1 : 1;
|
|
10530
|
+
let next = cur;
|
|
10531
|
+
if (e.key === "ArrowRight") next = cur + step * dir;
|
|
10532
|
+
else if (e.key === "ArrowLeft") next = cur - step * dir;
|
|
10533
|
+
else if (e.key === "Home") next = F_MIN[field];
|
|
10534
|
+
else if (e.key === "End") next = cap;
|
|
10535
|
+
setFWidths((m) => ({ ...m, [field]: clampNum(next, F_MIN[field], cap) }));
|
|
10536
|
+
};
|
|
10537
|
+
const resetFWidth = (field) => {
|
|
10538
|
+
setFWidths((m) => {
|
|
10539
|
+
const n = { ...m };
|
|
10540
|
+
delete n[field];
|
|
10541
|
+
return n;
|
|
10542
|
+
});
|
|
10543
|
+
const p = filterPanelRef.current;
|
|
10544
|
+
if (p) {
|
|
10545
|
+
p.style.removeProperty(F_VAR[field]);
|
|
10546
|
+
if (field === "val") p.removeAttribute("data-val-fixed");
|
|
10547
|
+
}
|
|
10548
|
+
};
|
|
10549
|
+
const startPanelResize = (e) => {
|
|
10550
|
+
if (e.button != null && e.button !== 0) return;
|
|
10551
|
+
e.preventDefault();
|
|
10552
|
+
e.stopPropagation();
|
|
10553
|
+
const panel2 = filterPanelRef.current;
|
|
10554
|
+
if (!panel2) return;
|
|
10555
|
+
const grip = e.currentTarget;
|
|
10556
|
+
const startX = e.clientX, startY = e.clientY;
|
|
10557
|
+
const r2 = panel2.getBoundingClientRect();
|
|
10558
|
+
const startW = r2.width, startH = r2.height;
|
|
10559
|
+
const dir = isRtl() ? -1 : 1;
|
|
10560
|
+
grip.setAttribute("data-active", "true");
|
|
10561
|
+
panel2.setAttribute("data-panel-sized", "");
|
|
10562
|
+
let lw = startW, lh = startH;
|
|
10563
|
+
const onMove = (ev) => {
|
|
10564
|
+
lw = clampNum(startW + (ev.clientX - startX) * dir, 220, fMaxW());
|
|
10565
|
+
lh = clampNum(startH + (ev.clientY - startY), 120, fMaxH());
|
|
10566
|
+
panel2.style.setProperty("--twc-dt-panel-w", `${lw}px`);
|
|
10567
|
+
panel2.style.setProperty("--twc-dt-panel-h", `${lh}px`);
|
|
10568
|
+
};
|
|
10569
|
+
const onUp = () => {
|
|
10570
|
+
setFPanelSize({ w: lw, h: lh });
|
|
10571
|
+
grip.removeAttribute("data-active");
|
|
10572
|
+
window.removeEventListener("pointermove", onMove);
|
|
10573
|
+
window.removeEventListener("pointerup", onUp);
|
|
10574
|
+
};
|
|
10575
|
+
window.addEventListener("pointermove", onMove);
|
|
10576
|
+
window.addEventListener("pointerup", onUp);
|
|
10577
|
+
};
|
|
10578
|
+
const onPanelGripKey = (e) => {
|
|
10579
|
+
if (e.key === "Enter" || e.key === " " || e.key === "Backspace") {
|
|
10580
|
+
e.preventDefault();
|
|
10581
|
+
resetPanelSize();
|
|
10582
|
+
return;
|
|
10583
|
+
}
|
|
10584
|
+
if (!["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End"].includes(e.key)) return;
|
|
10585
|
+
e.preventDefault();
|
|
10586
|
+
const panel2 = filterPanelRef.current;
|
|
10587
|
+
if (!panel2) return;
|
|
10588
|
+
const r2 = panel2.getBoundingClientRect();
|
|
10589
|
+
const step = e.shiftKey ? 4 : 20;
|
|
10590
|
+
const dir = isRtl() ? -1 : 1;
|
|
10591
|
+
let w = r2.width, h = r2.height;
|
|
10592
|
+
if (e.key === "ArrowRight") w += step * dir;
|
|
10593
|
+
else if (e.key === "ArrowLeft") w -= step * dir;
|
|
10594
|
+
else if (e.key === "ArrowDown") h += step;
|
|
10595
|
+
else if (e.key === "ArrowUp") h -= step;
|
|
10596
|
+
else if (e.key === "Home") {
|
|
10597
|
+
w = 220;
|
|
10598
|
+
h = 120;
|
|
10599
|
+
} else if (e.key === "End") {
|
|
10600
|
+
w = fMaxW();
|
|
10601
|
+
h = fMaxH();
|
|
10602
|
+
}
|
|
10603
|
+
setFPanelSize({ w: clampNum(w, 220, fMaxW()), h: clampNum(h, 120, fMaxH()) });
|
|
10604
|
+
};
|
|
10605
|
+
const resetPanelSize = () => {
|
|
10606
|
+
setFPanelSize(null);
|
|
10607
|
+
const p = filterPanelRef.current;
|
|
10608
|
+
if (p) {
|
|
10609
|
+
p.style.removeProperty("--twc-dt-panel-w");
|
|
10610
|
+
p.style.removeProperty("--twc-dt-panel-h");
|
|
10611
|
+
p.removeAttribute("data-panel-sized");
|
|
10612
|
+
}
|
|
10613
|
+
};
|
|
10614
|
+
const resetFilterSizes = () => {
|
|
10615
|
+
setFWidths({});
|
|
10616
|
+
setFPanelSize(null);
|
|
10617
|
+
const p = filterPanelRef.current;
|
|
10618
|
+
if (p) {
|
|
10619
|
+
["--twc-dt-fcol-usr", "--twc-dt-fop-usr", "--twc-dt-fval-usr", "--twc-dt-panel-w", "--twc-dt-panel-h"].forEach((v) => p.style.removeProperty(v));
|
|
10620
|
+
p.removeAttribute("data-val-fixed");
|
|
10621
|
+
p.removeAttribute("data-panel-sized");
|
|
10622
|
+
}
|
|
10623
|
+
};
|
|
10624
|
+
const hasFilterSizeOverride = fWidths.col != null || fWidths.op != null || fWidths.val != null || fPanelSize != null;
|
|
10625
|
+
const fieldHandle = (field, rowIdx) => resizableFilters && rowIdx === 0 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
10626
|
+
"span",
|
|
10627
|
+
{
|
|
10628
|
+
className: "twc-dt__f-rz",
|
|
10629
|
+
role: "separator",
|
|
10630
|
+
"aria-orientation": "vertical",
|
|
10631
|
+
tabIndex: 0,
|
|
10632
|
+
"aria-label": `Resize ${F_LABEL[field]} field (Enter to reset)`,
|
|
10633
|
+
onPointerDown: (e) => startFieldResize(e, field),
|
|
10634
|
+
onClick: (e) => e.stopPropagation(),
|
|
10635
|
+
onKeyDown: (e) => onFieldResizeKey(e, field)
|
|
10636
|
+
}
|
|
10637
|
+
) : null;
|
|
10390
10638
|
const orderIdxOf = (f) => {
|
|
10391
10639
|
const i = order.indexOf(f);
|
|
10392
10640
|
return i === -1 ? 9999 : i;
|
|
@@ -11756,78 +12004,126 @@ function Datatable({
|
|
|
11756
12004
|
] })
|
|
11757
12005
|
] });
|
|
11758
12006
|
})() : null,
|
|
11759
|
-
panel === "filters" && panelPos ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
11760
|
-
|
|
11761
|
-
|
|
11762
|
-
|
|
11763
|
-
|
|
11764
|
-
|
|
11765
|
-
|
|
11766
|
-
|
|
11767
|
-
|
|
11768
|
-
|
|
11769
|
-
|
|
11770
|
-
|
|
12007
|
+
panel === "filters" && panelPos ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
12008
|
+
"div",
|
|
12009
|
+
{
|
|
12010
|
+
ref: filterPanelRef,
|
|
12011
|
+
className: "twc-dt__pop twc-dt__filters",
|
|
12012
|
+
style: {
|
|
12013
|
+
top: panelPos.top,
|
|
12014
|
+
left: panelPos.left,
|
|
12015
|
+
"--twc-dt-fcol-usr": fWidths.col != null ? `${fWidths.col}px` : void 0,
|
|
12016
|
+
"--twc-dt-fop-usr": fWidths.op != null ? `${fWidths.op}px` : void 0,
|
|
12017
|
+
"--twc-dt-fval-usr": fWidths.val != null ? `${fWidths.val}px` : void 0,
|
|
12018
|
+
"--twc-dt-panel-w": fPanelSize?.w != null ? `${fPanelSize.w}px` : void 0,
|
|
12019
|
+
"--twc-dt-panel-h": fPanelSize?.h != null ? `${fPanelSize.h}px` : void 0,
|
|
12020
|
+
"--twc-dt-fcol-cap": filterFieldMaxWidth ? `${filterFieldMaxWidth}px` : void 0,
|
|
12021
|
+
"--twc-dt-fop-cap": filterFieldMaxWidth ? `${filterFieldMaxWidth}px` : void 0
|
|
12022
|
+
},
|
|
12023
|
+
"data-val-fixed": fWidths.val != null ? "" : void 0,
|
|
12024
|
+
"data-panel-sized": fPanelSize?.h != null ? "" : void 0,
|
|
12025
|
+
children: [
|
|
12026
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__panel-head", children: [
|
|
12027
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dt__panel-title", children: "Filters" }),
|
|
12028
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "inline-flex", gap: 2 }, children: [
|
|
12029
|
+
resizableFilters && hasFilterSizeOverride ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dt__link", onClick: resetFilterSizes, children: "Reset sizes" }) : null,
|
|
12030
|
+
filters.length ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dt__link", onClick: () => setFilters([]), children: "Clear all" }) : null
|
|
12031
|
+
] })
|
|
12032
|
+
] }),
|
|
12033
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dt__frows", children: filters.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dt__empty", style: { padding: "16px 12px" }, children: "No filters applied" }) : filters.map((f, i) => {
|
|
12034
|
+
const col = colByField[f.field] || cols[0];
|
|
12035
|
+
const ops = opsFor(filterTypeOf(col));
|
|
12036
|
+
const op = ops.find((o) => o.value === f.op) || ops[0];
|
|
12037
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__frow", children: [
|
|
12038
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__f-col", children: [
|
|
12039
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
12040
|
+
Select,
|
|
12041
|
+
{
|
|
12042
|
+
size: "sm",
|
|
12043
|
+
portal: true,
|
|
12044
|
+
value: f.field,
|
|
12045
|
+
options: filterableCols.map((c) => ({ value: c.field, label: c.headerName })),
|
|
12046
|
+
onChange: (v) => {
|
|
12047
|
+
const nc = colByField[v];
|
|
12048
|
+
setFilters((arr) => arr.map((x) => x.id === f.id ? { ...x, field: v, op: opsFor(filterTypeOf(nc))[0].value, value: "" } : x));
|
|
12049
|
+
}
|
|
12050
|
+
}
|
|
12051
|
+
),
|
|
12052
|
+
fieldHandle("col", i)
|
|
12053
|
+
] }),
|
|
12054
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__f-op", children: [
|
|
12055
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
12056
|
+
Select,
|
|
12057
|
+
{
|
|
12058
|
+
size: "sm",
|
|
12059
|
+
portal: true,
|
|
12060
|
+
value: f.op,
|
|
12061
|
+
options: ops.map((o) => ({ value: o.value, label: o.label })),
|
|
12062
|
+
onChange: (v) => setFilters((arr) => arr.map((x) => {
|
|
12063
|
+
if (x.id !== f.id) return x;
|
|
12064
|
+
const nextMulti = isMultiOp(v);
|
|
12065
|
+
const wasArr = Array.isArray(x.value);
|
|
12066
|
+
return { ...x, op: v, value: nextMulti ? wasArr ? x.value : [] : wasArr ? "" : x.value };
|
|
12067
|
+
}))
|
|
12068
|
+
}
|
|
12069
|
+
),
|
|
12070
|
+
fieldHandle("op", i)
|
|
12071
|
+
] }),
|
|
12072
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__f-val", children: [
|
|
12073
|
+
op.noInput ? null : op.multi ? col.loadValueOptions ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
12074
|
+
AsyncFilterValue,
|
|
12075
|
+
{
|
|
12076
|
+
col,
|
|
12077
|
+
value: f.value,
|
|
12078
|
+
onChange: (vals) => setFilters((arr) => arr.map((x) => x.id === f.id ? { ...x, value: vals } : x))
|
|
12079
|
+
}
|
|
12080
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
12081
|
+
MultiSelect,
|
|
12082
|
+
{
|
|
12083
|
+
portal: true,
|
|
12084
|
+
placeholder: "Any of\u2026",
|
|
12085
|
+
value: Array.isArray(f.value) ? f.value : [],
|
|
12086
|
+
options: optionsForField(f.field),
|
|
12087
|
+
onChange: (vals) => setFilters((arr) => arr.map((x) => x.id === f.id ? { ...x, value: vals } : x))
|
|
12088
|
+
}
|
|
12089
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
12090
|
+
Input,
|
|
12091
|
+
{
|
|
12092
|
+
size: "sm",
|
|
12093
|
+
type: filterTypeOf(col) === "number" ? "number" : "text",
|
|
12094
|
+
placeholder: "Value",
|
|
12095
|
+
value: f.value,
|
|
12096
|
+
onChange: (e) => setFilters((arr) => arr.map((x) => x.id === f.id ? { ...x, value: e.target.value } : x))
|
|
12097
|
+
}
|
|
12098
|
+
),
|
|
12099
|
+
fieldHandle("val", i)
|
|
12100
|
+
] }),
|
|
12101
|
+
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx(Svg, { d: I.x }) })
|
|
12102
|
+
] }, f.id);
|
|
12103
|
+
}) }),
|
|
12104
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dt__f-add", style: { padding: "6px 4px 2px" }, children: /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: "twc-dt__mi", style: { color: "var(--color-primary)" }, onClick: () => addFilter(cols[0].field), children: [
|
|
12105
|
+
/* @__PURE__ */ jsxRuntime.jsx(Svg, { d: I.plus, style: { color: "var(--color-primary)" } }),
|
|
12106
|
+
" Add filter"
|
|
12107
|
+
] }) }),
|
|
12108
|
+
resizableFilters ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
12109
|
+
"span",
|
|
11771
12110
|
{
|
|
11772
|
-
|
|
11773
|
-
|
|
11774
|
-
|
|
11775
|
-
|
|
11776
|
-
|
|
11777
|
-
|
|
11778
|
-
|
|
12111
|
+
className: "twc-dt__pop-grip",
|
|
12112
|
+
role: "slider",
|
|
12113
|
+
tabIndex: 0,
|
|
12114
|
+
"aria-label": "Resize filters panel (Enter to reset)",
|
|
12115
|
+
"aria-valuetext": `Width ${Math.round(fPanelSize?.w ?? 580)} pixels, height ${fPanelSize?.h != null ? Math.round(fPanelSize.h) + " pixels" : "auto"}`,
|
|
12116
|
+
onPointerDown: startPanelResize,
|
|
12117
|
+
onKeyDown: onPanelGripKey,
|
|
12118
|
+
onDoubleClick: (e) => {
|
|
12119
|
+
e.preventDefault();
|
|
12120
|
+
resetPanelSize();
|
|
11779
12121
|
}
|
|
11780
12122
|
}
|
|
11781
|
-
)
|
|
11782
|
-
|
|
11783
|
-
|
|
11784
|
-
|
|
11785
|
-
size: "sm",
|
|
11786
|
-
portal: true,
|
|
11787
|
-
value: f.op,
|
|
11788
|
-
options: ops.map((o) => ({ value: o.value, label: o.label })),
|
|
11789
|
-
onChange: (v) => setFilters((arr) => arr.map((x) => {
|
|
11790
|
-
if (x.id !== f.id) return x;
|
|
11791
|
-
const nextMulti = isMultiOp(v);
|
|
11792
|
-
const wasArr = Array.isArray(x.value);
|
|
11793
|
-
return { ...x, op: v, value: nextMulti ? wasArr ? x.value : [] : wasArr ? "" : x.value };
|
|
11794
|
-
}))
|
|
11795
|
-
}
|
|
11796
|
-
) }),
|
|
11797
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dt__f-val", children: op.noInput ? null : op.multi ? col.loadValueOptions ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
11798
|
-
AsyncFilterValue,
|
|
11799
|
-
{
|
|
11800
|
-
col,
|
|
11801
|
-
value: f.value,
|
|
11802
|
-
onChange: (vals) => setFilters((arr) => arr.map((x) => x.id === f.id ? { ...x, value: vals } : x))
|
|
11803
|
-
}
|
|
11804
|
-
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
11805
|
-
MultiSelect,
|
|
11806
|
-
{
|
|
11807
|
-
portal: true,
|
|
11808
|
-
placeholder: "Any of\u2026",
|
|
11809
|
-
value: Array.isArray(f.value) ? f.value : [],
|
|
11810
|
-
options: optionsForField(f.field),
|
|
11811
|
-
onChange: (vals) => setFilters((arr) => arr.map((x) => x.id === f.id ? { ...x, value: vals } : x))
|
|
11812
|
-
}
|
|
11813
|
-
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
11814
|
-
Input,
|
|
11815
|
-
{
|
|
11816
|
-
size: "sm",
|
|
11817
|
-
type: filterTypeOf(col) === "number" ? "number" : "text",
|
|
11818
|
-
placeholder: "Value",
|
|
11819
|
-
value: f.value,
|
|
11820
|
-
onChange: (e) => setFilters((arr) => arr.map((x) => x.id === f.id ? { ...x, value: e.target.value } : x))
|
|
11821
|
-
}
|
|
11822
|
-
) }),
|
|
11823
|
-
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx(Svg, { d: I.x }) })
|
|
11824
|
-
] }, f.id);
|
|
11825
|
-
}),
|
|
11826
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "6px 4px 2px" }, children: /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: "twc-dt__mi", style: { color: "var(--color-primary)" }, onClick: () => addFilter(cols[0].field), children: [
|
|
11827
|
-
/* @__PURE__ */ jsxRuntime.jsx(Svg, { d: I.plus, style: { color: "var(--color-primary)" } }),
|
|
11828
|
-
" Add filter"
|
|
11829
|
-
] }) })
|
|
11830
|
-
] }) : null
|
|
12123
|
+
) : null
|
|
12124
|
+
]
|
|
12125
|
+
}
|
|
12126
|
+
) : null
|
|
11831
12127
|
] });
|
|
11832
12128
|
}
|
|
11833
12129
|
function space(v) {
|
|
@@ -12341,9 +12637,16 @@ function DatePicker({
|
|
|
12341
12637
|
const onMove = () => place();
|
|
12342
12638
|
window.addEventListener("scroll", onMove, true);
|
|
12343
12639
|
window.addEventListener("resize", onMove);
|
|
12640
|
+
let ro;
|
|
12641
|
+
const rt = triggerRef.current;
|
|
12642
|
+
if (rt && typeof ResizeObserver !== "undefined") {
|
|
12643
|
+
ro = new ResizeObserver(onMove);
|
|
12644
|
+
ro.observe(rt);
|
|
12645
|
+
}
|
|
12344
12646
|
return () => {
|
|
12345
12647
|
window.removeEventListener("scroll", onMove, true);
|
|
12346
12648
|
window.removeEventListener("resize", onMove);
|
|
12649
|
+
ro?.disconnect();
|
|
12347
12650
|
};
|
|
12348
12651
|
}, [open, place]);
|
|
12349
12652
|
React51__namespace.default.useEffect(() => {
|
|
@@ -12885,9 +13188,16 @@ function DateRangePicker({
|
|
|
12885
13188
|
const onMove = () => place();
|
|
12886
13189
|
window.addEventListener("scroll", onMove, true);
|
|
12887
13190
|
window.addEventListener("resize", onMove);
|
|
13191
|
+
let ro;
|
|
13192
|
+
const rt = triggerRef.current;
|
|
13193
|
+
if (rt && typeof ResizeObserver !== "undefined") {
|
|
13194
|
+
ro = new ResizeObserver(onMove);
|
|
13195
|
+
ro.observe(rt);
|
|
13196
|
+
}
|
|
12888
13197
|
return () => {
|
|
12889
13198
|
window.removeEventListener("scroll", onMove, true);
|
|
12890
13199
|
window.removeEventListener("resize", onMove);
|
|
13200
|
+
ro?.disconnect();
|
|
12891
13201
|
};
|
|
12892
13202
|
}, [open, place]);
|
|
12893
13203
|
React51__namespace.default.useEffect(() => {
|
|
@@ -13438,9 +13748,16 @@ function TimePicker({
|
|
|
13438
13748
|
const onMove = () => place();
|
|
13439
13749
|
window.addEventListener("scroll", onMove, true);
|
|
13440
13750
|
window.addEventListener("resize", onMove);
|
|
13751
|
+
let ro;
|
|
13752
|
+
const rt = triggerRef.current;
|
|
13753
|
+
if (rt && typeof ResizeObserver !== "undefined") {
|
|
13754
|
+
ro = new ResizeObserver(onMove);
|
|
13755
|
+
ro.observe(rt);
|
|
13756
|
+
}
|
|
13441
13757
|
return () => {
|
|
13442
13758
|
window.removeEventListener("scroll", onMove, true);
|
|
13443
13759
|
window.removeEventListener("resize", onMove);
|
|
13760
|
+
ro?.disconnect();
|
|
13444
13761
|
};
|
|
13445
13762
|
}, [open, place]);
|
|
13446
13763
|
React51__namespace.default.useEffect(() => {
|