twico-ui 1.29.0 → 1.31.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/CHANGELOG.md +19 -0
- package/dist/index.cjs +181 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +32 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.mjs +181 -66
- package/dist/index.mjs.map +1 -1
- package/llms.txt +8 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
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.31.0](https://github.com/EncryptionL/twico-ui/compare/v1.30.0...v1.31.0) (2026-08-04)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **datatable:** keep DT_CSS a plain literal so single-component bundles tree-shake ([9574c2b](https://github.com/EncryptionL/twico-ui/commit/9574c2b52be39bdfcd27241a381b463eb7002c11)), closes [#330](https://github.com/EncryptionL/twico-ui/issues/330)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* **datatable:** MUI DataGrid-style per-row And/Or filter connector + controllable filterLogic ([3d79a1f](https://github.com/EncryptionL/twico-ui/commit/3d79a1f1c2e8f55d439e1bfeb37c0258b37cf5bc)), closes [#303](https://github.com/EncryptionL/twico-ui/issues/303) [#330](https://github.com/EncryptionL/twico-ui/issues/330)
|
|
16
|
+
|
|
17
|
+
# [1.30.0](https://github.com/EncryptionL/twico-ui/compare/v1.29.0...v1.30.0) (2026-08-04)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Features
|
|
21
|
+
|
|
22
|
+
* **inputs,datatable:** clearable inputs, richer select options, and datatable input clears ([e5eadbc](https://github.com/EncryptionL/twico-ui/commit/e5eadbce72db3ed96487bc7097d49c7c7caa6dfb)), closes [#328](https://github.com/EncryptionL/twico-ui/issues/328) [#326](https://github.com/EncryptionL/twico-ui/issues/326) [#327](https://github.com/EncryptionL/twico-ui/issues/327) [#326](https://github.com/EncryptionL/twico-ui/issues/326) [#327](https://github.com/EncryptionL/twico-ui/issues/327) [#328](https://github.com/EncryptionL/twico-ui/issues/328)
|
|
23
|
+
|
|
5
24
|
# [1.29.0](https://github.com/EncryptionL/twico-ui/compare/v1.28.0...v1.29.0) (2026-08-03)
|
|
6
25
|
|
|
7
26
|
|
package/dist/index.cjs
CHANGED
|
@@ -5671,6 +5671,10 @@ var COMBO_CSS = `
|
|
|
5671
5671
|
.twc-opt__main { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
|
|
5672
5672
|
.twc-opt__label { line-height: 1.3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
5673
5673
|
.twc-opt__desc { font-size: var(--text-xs); color: var(--color-text-muted); line-height: 1.3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
5674
|
+
/* #326: optional leading icon/swatch + trailing (right-aligned) hint on an option. */
|
|
5675
|
+
.twc-opt__icon { flex: none; display: inline-flex; align-items: center; color: var(--color-text-muted); }
|
|
5676
|
+
.twc-opt__icon svg { width: 16px; height: 16px; }
|
|
5677
|
+
.twc-opt__hint { flex: none; margin-inline-start: auto; padding-inline-start: var(--space-2); color: var(--color-text-muted); font-size: var(--text-xs); white-space: nowrap; }
|
|
5674
5678
|
/* #300: wrapOptions \u2014 let long labels/descriptions wrap onto multiple lines instead of truncating.
|
|
5675
5679
|
overflow-wrap: anywhere also breaks a single very long token so it can't overflow the popover edge. */
|
|
5676
5680
|
.twc-opt[data-wrap="true"] { align-items: flex-start; }
|
|
@@ -5738,6 +5742,7 @@ function Combobox({
|
|
|
5738
5742
|
virtualized: virtualizedProp = false,
|
|
5739
5743
|
overscan = 8,
|
|
5740
5744
|
wrapOptions = false,
|
|
5745
|
+
renderOption,
|
|
5741
5746
|
id,
|
|
5742
5747
|
className = "",
|
|
5743
5748
|
onFocus,
|
|
@@ -5745,8 +5750,8 @@ function Combobox({
|
|
|
5745
5750
|
...rest
|
|
5746
5751
|
}) {
|
|
5747
5752
|
const __twcStyles = useScopedStyles("twc-combo-styles", COMBO_CSS);
|
|
5748
|
-
if (virtualizedProp && wrapOptions) warnOnce("combobox-
|
|
5749
|
-
const virtualized = virtualizedProp && !wrapOptions;
|
|
5753
|
+
if (virtualizedProp && (wrapOptions || renderOption)) warnOnce("combobox-var-height-virtualized", "twico-ui Combobox: `wrapOptions`/`renderOption` disable `virtualized` \u2014 those rows aren't a fixed height.");
|
|
5754
|
+
const virtualized = virtualizedProp && !wrapOptions && !renderOption;
|
|
5750
5755
|
const groups = React51__namespace.default.useMemo(() => normalizeGroups(options), [options]);
|
|
5751
5756
|
const flat = React51__namespace.default.useMemo(() => groups.flatMap((g) => g.options), [groups]);
|
|
5752
5757
|
const autoId = React51__namespace.default.useId();
|
|
@@ -5917,7 +5922,7 @@ function Combobox({
|
|
|
5917
5922
|
const activeId = open && visible[active] ? optionId(active) : void 0;
|
|
5918
5923
|
const descId = `${fieldId}-desc`;
|
|
5919
5924
|
const describedBy = error || hint ? descId : void 0;
|
|
5920
|
-
const
|
|
5925
|
+
const renderRow = (o, idx) => {
|
|
5921
5926
|
const isSel = o.value === current;
|
|
5922
5927
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5923
5928
|
"button",
|
|
@@ -5940,9 +5945,13 @@ function Combobox({
|
|
|
5940
5945
|
onMouseDown: (e) => e.preventDefault(),
|
|
5941
5946
|
onClick: () => commit(o.value),
|
|
5942
5947
|
children: [
|
|
5943
|
-
/* @__PURE__ */ jsxRuntime.
|
|
5944
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-
|
|
5945
|
-
|
|
5948
|
+
renderOption ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-opt__main", children: renderOption(o, { selected: isSel, active: idx === active }) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5949
|
+
o.icon != null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-opt__icon", "aria-hidden": "true", children: o.icon }) : null,
|
|
5950
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-opt__main", children: [
|
|
5951
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-opt__label", children: o.label }),
|
|
5952
|
+
o.description ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-opt__desc", children: o.description }) : null
|
|
5953
|
+
] }),
|
|
5954
|
+
o.hint != null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-opt__hint", children: o.hint }) : null
|
|
5946
5955
|
] }),
|
|
5947
5956
|
isSel ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-opt__check", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.6", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20 6 9 17l-5-5" }) }) }) : null
|
|
5948
5957
|
]
|
|
@@ -5964,14 +5973,14 @@ function Combobox({
|
|
|
5964
5973
|
"Loading\u2026"
|
|
5965
5974
|
] }) : visible.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__empty", children: emptyText }) : virtualized ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5966
5975
|
vTop > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": "true", style: { height: vTop } }) : null,
|
|
5967
|
-
vShown.map((r2) => r2.kind === "group" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__group", children: r2.label }, `g${r2.gi}`) :
|
|
5976
|
+
vShown.map((r2) => r2.kind === "group" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__group", children: r2.label }, `g${r2.gi}`) : renderRow(r2.o, r2.idx)),
|
|
5968
5977
|
vBottom > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": "true", style: { height: vBottom } }) : null
|
|
5969
5978
|
] }) : fGroups.map((g, gi) => /* @__PURE__ */ jsxRuntime.jsxs(React51__namespace.default.Fragment, { children: [
|
|
5970
5979
|
g.group ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__group", children: g.group }) : null,
|
|
5971
5980
|
g.options.map((o) => {
|
|
5972
5981
|
counter += 1;
|
|
5973
5982
|
const idx = counter;
|
|
5974
|
-
return
|
|
5983
|
+
return renderRow(o, idx);
|
|
5975
5984
|
})
|
|
5976
5985
|
] }, gi)) });
|
|
5977
5986
|
const RD = { createPortal: typeof reactDom.createPortal === "function" ? reactDom.createPortal : typeof window !== "undefined" && window.ReactDOM && window.ReactDOM.createPortal };
|
|
@@ -6508,6 +6517,10 @@ var SELECT_CSS = `
|
|
|
6508
6517
|
.twc-opt__main { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
|
|
6509
6518
|
.twc-opt__label { line-height: 1.3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
6510
6519
|
.twc-opt__desc { font-size: var(--text-xs); color: var(--color-text-muted); line-height: 1.3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
6520
|
+
/* #326: optional leading icon/swatch + trailing (right-aligned) hint on an option. */
|
|
6521
|
+
.twc-opt__icon { flex: none; display: inline-flex; align-items: center; color: var(--color-text-muted); }
|
|
6522
|
+
.twc-opt__icon svg { width: 16px; height: 16px; }
|
|
6523
|
+
.twc-opt__hint { flex: none; margin-inline-start: auto; padding-inline-start: var(--space-2); color: var(--color-text-muted); font-size: var(--text-xs); white-space: nowrap; }
|
|
6511
6524
|
.twc-opt__check { flex: none; color: var(--color-primary); display: inline-flex; }
|
|
6512
6525
|
.twc-opt__check svg { width: 16px; height: 16px; }
|
|
6513
6526
|
.twc-pop__empty { padding: 14px 12px; text-align: center; font-size: var(--text-sm); color: var(--color-text-subtle); }
|
|
@@ -6568,8 +6581,9 @@ function Select({
|
|
|
6568
6581
|
loading = false,
|
|
6569
6582
|
emptyText = "No results found",
|
|
6570
6583
|
name,
|
|
6571
|
-
virtualized = false,
|
|
6584
|
+
virtualized: virtualizedProp = false,
|
|
6572
6585
|
overscan = 8,
|
|
6586
|
+
renderOption,
|
|
6573
6587
|
id,
|
|
6574
6588
|
className = "",
|
|
6575
6589
|
onClick,
|
|
@@ -6577,6 +6591,8 @@ function Select({
|
|
|
6577
6591
|
...rest
|
|
6578
6592
|
}) {
|
|
6579
6593
|
const __twcStyles = useScopedStyles("twc-select-styles", SELECT_CSS);
|
|
6594
|
+
if (virtualizedProp && renderOption) warnOnce("select-renderoption-virtualized", "twico-ui Select: `renderOption` disables `virtualized` \u2014 custom rows aren't a fixed height.");
|
|
6595
|
+
const virtualized = virtualizedProp && !renderOption;
|
|
6580
6596
|
const bufferRef = React51__namespace.default.useRef("");
|
|
6581
6597
|
const bufferTimerRef = React51__namespace.default.useRef(null);
|
|
6582
6598
|
const groups = React51__namespace.default.useMemo(() => normalizeGroups2(options), [options]);
|
|
@@ -6782,7 +6798,7 @@ function Select({
|
|
|
6782
6798
|
const activeId = open && visible[active] ? optionId(active) : void 0;
|
|
6783
6799
|
const descId = `${fieldId}-desc`;
|
|
6784
6800
|
const describedBy = error || hint ? descId : void 0;
|
|
6785
|
-
const
|
|
6801
|
+
const renderRow = (o, idx) => {
|
|
6786
6802
|
const isSel = o.value === current;
|
|
6787
6803
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6788
6804
|
"button",
|
|
@@ -6804,9 +6820,13 @@ function Select({
|
|
|
6804
6820
|
},
|
|
6805
6821
|
onClick: () => commit(o.value),
|
|
6806
6822
|
children: [
|
|
6807
|
-
/* @__PURE__ */ jsxRuntime.
|
|
6808
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-
|
|
6809
|
-
|
|
6823
|
+
renderOption ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-opt__main", children: renderOption(o, { selected: isSel, active: idx === active }) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
6824
|
+
o.icon != null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-opt__icon", "aria-hidden": "true", children: o.icon }) : null,
|
|
6825
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-opt__main", children: [
|
|
6826
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-opt__label", children: o.label }),
|
|
6827
|
+
o.description ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-opt__desc", children: o.description }) : null
|
|
6828
|
+
] }),
|
|
6829
|
+
o.hint != null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-opt__hint", children: o.hint }) : null
|
|
6810
6830
|
] }),
|
|
6811
6831
|
isSel ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-opt__check", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.6", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20 6 9 17l-5-5" }) }) }) : null
|
|
6812
6832
|
]
|
|
@@ -6854,14 +6874,14 @@ function Select({
|
|
|
6854
6874
|
"Loading\u2026"
|
|
6855
6875
|
] }) : visible.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__empty", children: emptyText }) : virtualized ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
6856
6876
|
vTop > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": "true", style: { height: vTop } }) : null,
|
|
6857
|
-
vShown.map((r2) => r2.kind === "group" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__group", children: r2.label }, `g${r2.gi}`) :
|
|
6877
|
+
vShown.map((r2) => r2.kind === "group" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__group", children: r2.label }, `g${r2.gi}`) : renderRow(r2.o, r2.idx)),
|
|
6858
6878
|
vBottom > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": "true", style: { height: vBottom } }) : null
|
|
6859
6879
|
] }) : fGroups.map((g, gi) => /* @__PURE__ */ jsxRuntime.jsxs(React51__namespace.default.Fragment, { children: [
|
|
6860
6880
|
g.group ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__group", children: g.group }) : null,
|
|
6861
6881
|
g.options.map((o) => {
|
|
6862
6882
|
counter += 1;
|
|
6863
6883
|
const idx = counter;
|
|
6864
|
-
return
|
|
6884
|
+
return renderRow(o, idx);
|
|
6865
6885
|
})
|
|
6866
6886
|
] }, gi)) }),
|
|
6867
6887
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-sel__sr", role: "status", "aria-live": "polite", children: loading ? "Loading\u2026" : visible.length === 0 ? emptyText : `${visible.length} result${visible.length === 1 ? "" : "s"}` })
|
|
@@ -7165,6 +7185,11 @@ var INPUT_EXTRA_CSS = `
|
|
|
7165
7185
|
.twc-input__reveal:hover { background: var(--color-surface-sunken); color: var(--color-text); }
|
|
7166
7186
|
.twc-input__reveal:focus-visible { outline: none; box-shadow: var(--ring); }
|
|
7167
7187
|
.twc-input__reveal svg { width: 17px; height: 17px; }
|
|
7188
|
+
/* #328: trailing clear (X) button \u2014 mirrors the reveal toggle; sits alongside rightIcon / the password eye. */
|
|
7189
|
+
.twc-input__clear { display: inline-grid; place-items: center; width: 24px; height: 24px; padding: 0; border: none; background: transparent; color: var(--color-text-subtle); cursor: pointer; border-radius: var(--radius-sm); flex: none; transition: background-color var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard); }
|
|
7190
|
+
.twc-input__clear:hover { background: var(--color-surface-sunken); color: var(--color-text); }
|
|
7191
|
+
.twc-input__clear:focus-visible { outline: none; box-shadow: var(--ring); }
|
|
7192
|
+
.twc-input__clear svg { width: 15px; height: 15px; }
|
|
7168
7193
|
.twc-input__addon { align-self: stretch; display: inline-flex; align-items: center; padding-inline: var(--space-3);
|
|
7169
7194
|
background: var(--color-surface-sunken); color: var(--color-text-muted); font-size: var(--text-sm);
|
|
7170
7195
|
font-weight: var(--font-semibold); white-space: nowrap; flex: none; }
|
|
@@ -7186,6 +7211,7 @@ function Input({
|
|
|
7186
7211
|
leftAddon,
|
|
7187
7212
|
rightAddon,
|
|
7188
7213
|
showCount = false,
|
|
7214
|
+
clearable = false,
|
|
7189
7215
|
type = "text",
|
|
7190
7216
|
disabled = false,
|
|
7191
7217
|
id,
|
|
@@ -7207,8 +7233,25 @@ function Input({
|
|
|
7207
7233
|
const near = showCounter && count >= Math.floor(max * 0.9);
|
|
7208
7234
|
const handleChange = (e) => {
|
|
7209
7235
|
rest.onChange?.(e);
|
|
7210
|
-
if (showCount && !countControlled) setLen(e.target.value.length);
|
|
7236
|
+
if ((showCount || clearable) && !countControlled) setLen(e.target.value.length);
|
|
7211
7237
|
};
|
|
7238
|
+
const inputRef = React51__namespace.default.useRef(null);
|
|
7239
|
+
const userRef = rest.ref;
|
|
7240
|
+
const setRef = React51__namespace.default.useCallback((node) => {
|
|
7241
|
+
inputRef.current = node;
|
|
7242
|
+
if (typeof userRef === "function") userRef(node);
|
|
7243
|
+
else if (userRef && typeof userRef === "object") userRef.current = node;
|
|
7244
|
+
}, [userRef]);
|
|
7245
|
+
const clearField = () => {
|
|
7246
|
+
const el = inputRef.current;
|
|
7247
|
+
if (!el) return;
|
|
7248
|
+
const setter = typeof HTMLInputElement !== "undefined" && Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set;
|
|
7249
|
+
if (setter) setter.call(el, "");
|
|
7250
|
+
else el.value = "";
|
|
7251
|
+
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
7252
|
+
el.focus();
|
|
7253
|
+
};
|
|
7254
|
+
const showClear = clearable && count > 0 && !disabled && !rest.readOnly;
|
|
7212
7255
|
const ariaInvalid = invalid ? true : rest["aria-invalid"] ?? void 0;
|
|
7213
7256
|
const describedBy = [error || hint ? descId : null, showCounter ? countId : null, rest["aria-describedby"]].filter(Boolean).join(" ") || void 0;
|
|
7214
7257
|
const isPassword = type === "password";
|
|
@@ -7255,11 +7298,16 @@ function Input({
|
|
|
7255
7298
|
required: required || void 0,
|
|
7256
7299
|
"aria-required": required || void 0,
|
|
7257
7300
|
...rest,
|
|
7301
|
+
ref: setRef,
|
|
7258
7302
|
onChange: handleChange,
|
|
7259
7303
|
"aria-invalid": ariaInvalid,
|
|
7260
7304
|
"aria-describedby": describedBy
|
|
7261
7305
|
}
|
|
7262
7306
|
),
|
|
7307
|
+
showClear ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-input__clear", "aria-label": "Clear", tabIndex: 0, onMouseDown: (e) => e.preventDefault(), onClick: clearField, children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
7308
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6 6 18" }),
|
|
7309
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "m6 6 12 12" })
|
|
7310
|
+
] }) }) : null,
|
|
7263
7311
|
suffix,
|
|
7264
7312
|
rightAddon != null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-input__addon twc-input__addon--end", children: rightAddon }) : null
|
|
7265
7313
|
] }),
|
|
@@ -7347,6 +7395,10 @@ var MULTI_CSS = `
|
|
|
7347
7395
|
.twc-opt__main { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
|
|
7348
7396
|
.twc-opt__label { line-height: 1.3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
7349
7397
|
.twc-opt__desc { font-size: var(--text-xs); color: var(--color-text-muted); line-height: 1.3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
7398
|
+
/* #326: optional leading icon/swatch + trailing (right-aligned) hint on an option. */
|
|
7399
|
+
.twc-opt__icon { flex: none; display: inline-flex; align-items: center; color: var(--color-text-muted); }
|
|
7400
|
+
.twc-opt__icon svg { width: 16px; height: 16px; }
|
|
7401
|
+
.twc-opt__hint { flex: none; margin-inline-start: auto; padding-inline-start: var(--space-2); color: var(--color-text-muted); font-size: var(--text-xs); white-space: nowrap; }
|
|
7350
7402
|
.twc-pop__empty { padding: 14px 12px; text-align: center; font-size: var(--text-sm); color: var(--color-text-subtle); }
|
|
7351
7403
|
.twc-opt[data-disabled="true"] { opacity: 0.45; cursor: not-allowed; pointer-events: none; }
|
|
7352
7404
|
.twc-pop__loading { display: flex; align-items: center; justify-content: center; gap: var(--space-2); padding: 14px 12px; font-size: var(--text-sm); color: var(--color-text-muted); }
|
|
@@ -7408,8 +7460,9 @@ function MultiSelect({
|
|
|
7408
7460
|
maxTagCount,
|
|
7409
7461
|
filter,
|
|
7410
7462
|
onInputChange,
|
|
7411
|
-
virtualized = false,
|
|
7463
|
+
virtualized: virtualizedProp = false,
|
|
7412
7464
|
overscan = 8,
|
|
7465
|
+
renderOption,
|
|
7413
7466
|
id,
|
|
7414
7467
|
className = "",
|
|
7415
7468
|
onFocus,
|
|
@@ -7417,6 +7470,8 @@ function MultiSelect({
|
|
|
7417
7470
|
...rest
|
|
7418
7471
|
}) {
|
|
7419
7472
|
const __twcStyles = useScopedStyles("twc-ms-styles", MULTI_CSS);
|
|
7473
|
+
if (virtualizedProp && renderOption) warnOnce("multiselect-renderoption-virtualized", "twico-ui MultiSelect: `renderOption` disables `virtualized` \u2014 custom rows aren't a fixed height.");
|
|
7474
|
+
const virtualized = virtualizedProp && !renderOption;
|
|
7420
7475
|
const groups = React51__namespace.default.useMemo(() => normalizeGroups3(options), [options]);
|
|
7421
7476
|
const flat = React51__namespace.default.useMemo(() => groups.flatMap((g) => g.options), [groups]);
|
|
7422
7477
|
const autoId = React51__namespace.default.useId();
|
|
@@ -7576,7 +7631,7 @@ function MultiSelect({
|
|
|
7576
7631
|
const activeId = open && visible[active] ? optionId(active) : void 0;
|
|
7577
7632
|
const descId = `${fieldId}-desc`;
|
|
7578
7633
|
const describedBy = error || hint ? descId : void 0;
|
|
7579
|
-
const
|
|
7634
|
+
const renderRow = (o, idx) => {
|
|
7580
7635
|
const isSel = selected.includes(o.value);
|
|
7581
7636
|
const optDisabled = isOptDisabled(o);
|
|
7582
7637
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -7600,9 +7655,13 @@ function MultiSelect({
|
|
|
7600
7655
|
onClick: () => toggle(o.value),
|
|
7601
7656
|
children: [
|
|
7602
7657
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-opt__box", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20 6 9 17l-5-5" }) }) }),
|
|
7603
|
-
/* @__PURE__ */ jsxRuntime.
|
|
7604
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-
|
|
7605
|
-
|
|
7658
|
+
renderOption ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-opt__main", children: renderOption(o, { selected: isSel, active: idx === active }) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
7659
|
+
o.icon != null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-opt__icon", "aria-hidden": "true", children: o.icon }) : null,
|
|
7660
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-opt__main", children: [
|
|
7661
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-opt__label", children: o.label }),
|
|
7662
|
+
o.description ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-opt__desc", children: o.description }) : null
|
|
7663
|
+
] }),
|
|
7664
|
+
o.hint != null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-opt__hint", children: o.hint }) : null
|
|
7606
7665
|
] })
|
|
7607
7666
|
]
|
|
7608
7667
|
},
|
|
@@ -7632,14 +7691,14 @@ function MultiSelect({
|
|
|
7632
7691
|
"Loading\u2026"
|
|
7633
7692
|
] }) : visible.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__empty", children: emptyText }) : virtualized ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
7634
7693
|
vTop > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": "true", style: { height: vTop } }) : null,
|
|
7635
|
-
vShown.map((r2) => r2.kind === "group" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__group", children: r2.label }, `g${r2.gi}`) :
|
|
7694
|
+
vShown.map((r2) => r2.kind === "group" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__group", children: r2.label }, `g${r2.gi}`) : renderRow(r2.o, r2.idx)),
|
|
7636
7695
|
vBottom > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": "true", style: { height: vBottom } }) : null
|
|
7637
7696
|
] }) : fGroups.map((g, gi) => /* @__PURE__ */ jsxRuntime.jsxs(React51__namespace.default.Fragment, { children: [
|
|
7638
7697
|
g.group ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__group", children: g.group }) : null,
|
|
7639
7698
|
g.options.map((o) => {
|
|
7640
7699
|
counter += 1;
|
|
7641
7700
|
const idx = counter;
|
|
7642
|
-
return
|
|
7701
|
+
return renderRow(o, idx);
|
|
7643
7702
|
})
|
|
7644
7703
|
] }, gi))
|
|
7645
7704
|
}
|
|
@@ -8152,6 +8211,8 @@ function classifyDiff(from = [], to = [], columns = [], rowKey, opts = {}) {
|
|
|
8152
8211
|
});
|
|
8153
8212
|
return { rows, counts };
|
|
8154
8213
|
}
|
|
8214
|
+
var DT_FILTER_PANEL_W = 620;
|
|
8215
|
+
var DT_FILTER_LOGIC_W = 68;
|
|
8155
8216
|
var DT_CSS = `
|
|
8156
8217
|
.twc-dt { display: flex; flex-direction: column; font-family: var(--font-sans); color: var(--color-text);
|
|
8157
8218
|
border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-lg);
|
|
@@ -8223,6 +8284,11 @@ var DT_CSS = `
|
|
|
8223
8284
|
font-size: var(--text-sm); color: var(--color-text); width: 150px; }
|
|
8224
8285
|
.twc-dt__search input:focus, .twc-dt__search input:focus-visible { outline: none; box-shadow: none; }
|
|
8225
8286
|
.twc-dt__search input::placeholder { color: var(--color-text-subtle); }
|
|
8287
|
+
/* #327: trailing clear (X) for the built-in search inputs (quick-search + column-menu search). */
|
|
8288
|
+
.twc-dt__search-clear { border: none; background: transparent; color: var(--color-text-subtle); cursor: pointer; display: inline-grid; place-items: center; width: 20px; height: 20px; padding: 0; border-radius: var(--radius-sm); flex: none; }
|
|
8289
|
+
.twc-dt__search-clear:hover { color: var(--color-text); background: var(--color-surface-sunken); }
|
|
8290
|
+
.twc-dt__search-clear:focus-visible { outline: none; box-shadow: var(--ring); }
|
|
8291
|
+
.twc-dt__search-clear svg { width: 14px; height: 14px; }
|
|
8226
8292
|
.twc-dt__tlabel { display: inline; }
|
|
8227
8293
|
/* Narrow grid: collapse the toolbar to icon-only buttons (labels survive as
|
|
8228
8294
|
hover tooltips via data-tip) and let the search flex so nothing wraps. */
|
|
@@ -8569,13 +8635,11 @@ th.twc-dt__rownum .twc-dt__th-inner { padding-inline: 8px; gap: 2px; justify-con
|
|
|
8569
8635
|
.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; }
|
|
8570
8636
|
.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); }
|
|
8571
8637
|
.twc-dt__link:hover { background: var(--color-primary-subtle); }
|
|
8572
|
-
/* #
|
|
8573
|
-
|
|
8574
|
-
.
|
|
8575
|
-
.twc-
|
|
8576
|
-
.twc-
|
|
8577
|
-
.twc-dt__flogic-btn[data-active] { background: var(--color-primary); color: var(--color-primary-fg); }
|
|
8578
|
-
.twc-dt__flogic-btn[data-active]:hover { background: var(--color-primary); }
|
|
8638
|
+
/* #330: MUI DataGrid-style per-row And/Or connector. Row 1 shows a static "Where"; the first connector
|
|
8639
|
+
row (row 2) hosts an editable And/Or Select bound to the single filterLogic; rows 3+ echo it as static
|
|
8640
|
+
text. Fixed-width leading cell so the Column/Operator/Value cells stay aligned across rows. */
|
|
8641
|
+
.twc-dt__f-logic { position: relative; flex: none; width: 68px; }
|
|
8642
|
+
.twc-dt__f-where { display: inline-flex; align-items: center; height: 38px; padding-inline: 6px; color: var(--color-text-muted); font-size: var(--text-sm); font-weight: var(--font-medium); }
|
|
8579
8643
|
|
|
8580
8644
|
/* Filter panel \u2014 user-resizable (#292): panel size + per-field widths resolve through a two-tier CSS var
|
|
8581
8645
|
cascade \u2014 user drag (-usr) > #289 measured auto-fit (-fit) > 118px fallback \u2014 entirely in clamp(), so
|
|
@@ -8585,7 +8649,7 @@ th.twc-dt__rownum .twc-dt__th-inner { padding-inline: 8px; gap: 2px; justify-con
|
|
|
8585
8649
|
equal-specificity 'position: relative' defined later would clobber it, dropping the popover into normal
|
|
8586
8650
|
flow below the table. The panel stays position: fixed, which already anchors the absolutely-positioned
|
|
8587
8651
|
.twc-dt__pop-grip; per-field handles anchor to their own position: relative .twc-dt__f-* fields. */
|
|
8588
|
-
width: var(--twc-dt-panel-w,
|
|
8652
|
+
width: var(--twc-dt-panel-w, 620px); max-width: calc(100vw - 32px);
|
|
8589
8653
|
height: var(--twc-dt-panel-h, auto); max-height: calc(100vh - 32px);
|
|
8590
8654
|
display: flex; flex-direction: column;
|
|
8591
8655
|
}
|
|
@@ -9197,6 +9261,9 @@ function Datatable({
|
|
|
9197
9261
|
searchable = true,
|
|
9198
9262
|
quickFilter,
|
|
9199
9263
|
onQuickFilterChange,
|
|
9264
|
+
filterLogic: filterLogicProp,
|
|
9265
|
+
defaultFilterLogic = "and",
|
|
9266
|
+
onFilterLogicChange,
|
|
9200
9267
|
toolbarActions,
|
|
9201
9268
|
resizableFilters = true,
|
|
9202
9269
|
resizablePopovers = true,
|
|
@@ -9290,7 +9357,14 @@ function Datatable({
|
|
|
9290
9357
|
});
|
|
9291
9358
|
const [sort, setSort] = React51__namespace.default.useState(null);
|
|
9292
9359
|
const [filters, setFilters] = React51__namespace.default.useState([]);
|
|
9293
|
-
const
|
|
9360
|
+
const filterLogicControlled = filterLogicProp !== void 0;
|
|
9361
|
+
const [filterLogicInternal, setFilterLogicInternal] = React51__namespace.default.useState(defaultFilterLogic === "or" ? "or" : "and");
|
|
9362
|
+
const filterLogic = filterLogicControlled ? filterLogicProp === "or" ? "or" : "and" : filterLogicInternal;
|
|
9363
|
+
const setFilterLogic = (next) => {
|
|
9364
|
+
const v = next === "or" ? "or" : "and";
|
|
9365
|
+
if (!filterLogicControlled) setFilterLogicInternal(v);
|
|
9366
|
+
onFilterLogicChange?.(v);
|
|
9367
|
+
};
|
|
9294
9368
|
const [hidden, setHidden] = React51__namespace.default.useState(() => /* @__PURE__ */ new Set());
|
|
9295
9369
|
const [showRowNum, setShowRowNum] = React51__namespace.default.useState(rowNumbers);
|
|
9296
9370
|
const [wrapped, setWrapped] = React51__namespace.default.useState(() => new Set((columns || []).filter((c) => c.wrapText).map((c) => c.field)));
|
|
@@ -9386,7 +9460,7 @@ function Datatable({
|
|
|
9386
9460
|
if (!s || typeof s !== "object") return;
|
|
9387
9461
|
const known = new Set(cols.map((c) => c.field).filter(Boolean));
|
|
9388
9462
|
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 })));
|
|
9389
|
-
if (s.filterLogic === "or" || s.filterLogic === "and")
|
|
9463
|
+
if ((s.filterLogic === "or" || s.filterLogic === "and") && !filterLogicControlled) setFilterLogicInternal(s.filterLogic);
|
|
9390
9464
|
if ("sort" in s) setSort(s.sort && known.has(s.sort.field) ? { field: s.sort.field, dir: s.sort.dir === "desc" ? "desc" : "asc" } : null);
|
|
9391
9465
|
if (typeof s.quickFilter === "string" && quickFilter === void 0) setInternalQuick(s.quickFilter);
|
|
9392
9466
|
if (typeof s.page === "number" && !pageControlled) setInternalPage(Math.max(0, Math.floor(s.page)));
|
|
@@ -9591,6 +9665,8 @@ function Datatable({
|
|
|
9591
9665
|
const colMenuRef = React51__namespace.default.useRef(null);
|
|
9592
9666
|
const rowMenuRef = React51__namespace.default.useRef(null);
|
|
9593
9667
|
const exportMenuRef = React51__namespace.default.useRef(null);
|
|
9668
|
+
const quickSearchRef = React51__namespace.default.useRef(null);
|
|
9669
|
+
const colSearchRef = React51__namespace.default.useRef(null);
|
|
9594
9670
|
function focusFirstItem(container) {
|
|
9595
9671
|
if (!container) return;
|
|
9596
9672
|
const first = container.querySelector('[role="menuitem"]:not([disabled])');
|
|
@@ -10777,7 +10853,7 @@ function Datatable({
|
|
|
10777
10853
|
const pad2 = trigger ? (parseFloat(cs.paddingLeft) || 0) + (parseFloat(cs.paddingRight) || 0) : 24;
|
|
10778
10854
|
const chrome = pad2 + 8 + 16 + 4;
|
|
10779
10855
|
const widest = (labels) => labels.reduce((m, l) => Math.max(m, ctx.measureText(String(l)).width), 0);
|
|
10780
|
-
const colCap = Math.max(118, Math.min(210, el.clientWidth - 118 - 30 - 140 -
|
|
10856
|
+
const colCap = Math.max(118, Math.min(210, el.clientWidth - DT_FILTER_LOGIC_W - 118 - 30 - 30 - 140 - 5 * 6 - 2 * 4));
|
|
10781
10857
|
const colW = Math.min(colCap, Math.max(118, Math.ceil(widest(filterableCols.map((c) => c.headerName ?? c.field)) + chrome)));
|
|
10782
10858
|
const opW = Math.min(170, Math.max(118, Math.ceil(widest(ALL_OP_LABELS) + chrome)));
|
|
10783
10859
|
if (Number.isFinite(colW)) el.style.setProperty("--twc-dt-fcol-fit", `${colW}px`);
|
|
@@ -10809,7 +10885,7 @@ function Datatable({
|
|
|
10809
10885
|
if (field === "op") return filterFieldMaxWidth || 260;
|
|
10810
10886
|
const p = filterPanelRef.current;
|
|
10811
10887
|
if (!p) return 640;
|
|
10812
|
-
return clampNum(p.clientWidth - fLiveW("col") - fLiveW("op") - 30 -
|
|
10888
|
+
return clampNum(p.clientWidth - DT_FILTER_LOGIC_W - fLiveW("col") - fLiveW("op") - 30 - 30 - 5 * 6 - 2 * 4, F_MIN.val, 640);
|
|
10813
10889
|
};
|
|
10814
10890
|
const isRtl = () => {
|
|
10815
10891
|
const p = filterPanelRef.current;
|
|
@@ -11506,7 +11582,7 @@ function Datatable({
|
|
|
11506
11582
|
} else {
|
|
11507
11583
|
setPanel("filters");
|
|
11508
11584
|
setColMenu(null);
|
|
11509
|
-
openPanel(e.currentTarget, "left",
|
|
11585
|
+
openPanel(e.currentTarget, "left", DT_FILTER_PANEL_W);
|
|
11510
11586
|
}
|
|
11511
11587
|
},
|
|
11512
11588
|
children: [
|
|
@@ -11605,7 +11681,11 @@ function Datatable({
|
|
|
11605
11681
|
] }) : null,
|
|
11606
11682
|
searchable ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__search", children: [
|
|
11607
11683
|
/* @__PURE__ */ jsxRuntime.jsx(Svg, { d: I.search }),
|
|
11608
|
-
/* @__PURE__ */ jsxRuntime.jsx("input", { placeholder: "Search\u2026", "aria-label": "Search rows", value: quick, onChange: (e) => commitQuick(e.target.value) })
|
|
11684
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { ref: quickSearchRef, placeholder: "Search\u2026", "aria-label": "Search rows", value: quick, onChange: (e) => commitQuick(e.target.value) }),
|
|
11685
|
+
quick ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dt__search-clear", "aria-label": "Clear search", onMouseDown: (e) => e.preventDefault(), onClick: () => {
|
|
11686
|
+
commitQuick("");
|
|
11687
|
+
quickSearchRef.current?.focus();
|
|
11688
|
+
}, children: /* @__PURE__ */ jsxRuntime.jsx(Svg, { d: I.x }) }) : null
|
|
11609
11689
|
] }) : null
|
|
11610
11690
|
] }),
|
|
11611
11691
|
!pivotActive && activeGroupBy.length ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__groupbar", children: [
|
|
@@ -12070,7 +12150,7 @@ function Datatable({
|
|
|
12070
12150
|
closeMenu();
|
|
12071
12151
|
restoreTriggerFocus();
|
|
12072
12152
|
setPanel("filters");
|
|
12073
|
-
openPanel(document.querySelector(".twc-dt__toolbar .twc-dt__tbtn:nth-child(2)"), "left",
|
|
12153
|
+
openPanel(document.querySelector(".twc-dt__toolbar .twc-dt__tbtn:nth-child(2)"), "left", DT_FILTER_PANEL_W);
|
|
12074
12154
|
}, children: [
|
|
12075
12155
|
/* @__PURE__ */ jsxRuntime.jsx(Svg, { d: I.filter }),
|
|
12076
12156
|
" Filter"
|
|
@@ -12198,7 +12278,11 @@ function Datatable({
|
|
|
12198
12278
|
] }),
|
|
12199
12279
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__col-search", children: [
|
|
12200
12280
|
/* @__PURE__ */ jsxRuntime.jsx(Svg, { d: I.search }),
|
|
12201
|
-
/* @__PURE__ */ jsxRuntime.jsx("input", { autoFocus: true, placeholder: "Find column\u2026", "aria-label": "Find column", value: colQuery, onChange: (e) => setColQuery(e.target.value) })
|
|
12281
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { ref: colSearchRef, autoFocus: true, placeholder: "Find column\u2026", "aria-label": "Find column", value: colQuery, onChange: (e) => setColQuery(e.target.value) }),
|
|
12282
|
+
colQuery ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dt__search-clear", "aria-label": "Clear column search", onMouseDown: (e) => e.preventDefault(), onClick: () => {
|
|
12283
|
+
setColQuery("");
|
|
12284
|
+
colSearchRef.current?.focus();
|
|
12285
|
+
}, children: /* @__PURE__ */ jsxRuntime.jsx(Svg, { d: I.x }) }) : null
|
|
12202
12286
|
] }),
|
|
12203
12287
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__col-list", children: [
|
|
12204
12288
|
rowNumMatch ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__col-row", onClick: () => setShowRowNum((v) => !v), children: [
|
|
@@ -12500,10 +12584,6 @@ function Datatable({
|
|
|
12500
12584
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__panel-head", children: [
|
|
12501
12585
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dt__panel-title", children: "Filters" }),
|
|
12502
12586
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: 6 }, children: [
|
|
12503
|
-
filters.length > 1 ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-dt__flogic", role: "group", "aria-label": "Combine filters with", children: [
|
|
12504
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dt__flogic-btn", "data-active": filterLogic === "and" || void 0, "aria-pressed": filterLogic === "and", onClick: () => setFilterLogic("and"), children: "AND" }),
|
|
12505
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dt__flogic-btn", "data-active": filterLogic === "or" || void 0, "aria-pressed": filterLogic === "or", onClick: () => setFilterLogic("or"), children: "OR" })
|
|
12506
|
-
] }) : null,
|
|
12507
12587
|
resizableFilters && hasFilterSizeOverride ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dt__link", onClick: resetFilterSizes, children: "Reset sizes" }) : null,
|
|
12508
12588
|
filters.length ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dt__link", onClick: () => setFilters([]), children: "Clear all" }) : null
|
|
12509
12589
|
] })
|
|
@@ -12513,6 +12593,17 @@ function Datatable({
|
|
|
12513
12593
|
const ops = opsFor(filterTypeOf(col));
|
|
12514
12594
|
const op = ops.find((o) => o.value === f.op) || ops[0];
|
|
12515
12595
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__frow", children: [
|
|
12596
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dt__f-logic", children: i === 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dt__f-where", children: "Where" }) : i === 1 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
12597
|
+
Select,
|
|
12598
|
+
{
|
|
12599
|
+
size: "sm",
|
|
12600
|
+
portal: true,
|
|
12601
|
+
"aria-label": "Combine with the other filters",
|
|
12602
|
+
value: filterLogic,
|
|
12603
|
+
options: [{ value: "and", label: "And" }, { value: "or", label: "Or" }],
|
|
12604
|
+
onChange: (v) => setFilterLogic(v)
|
|
12605
|
+
}
|
|
12606
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dt__f-where", children: filterLogic === "or" ? "Or" : "And" }) }),
|
|
12516
12607
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__f-col", children: [
|
|
12517
12608
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
12518
12609
|
Select,
|
|
@@ -12568,6 +12659,7 @@ function Datatable({
|
|
|
12568
12659
|
Input,
|
|
12569
12660
|
{
|
|
12570
12661
|
size: "sm",
|
|
12662
|
+
clearable: true,
|
|
12571
12663
|
type: filterTypeOf(col) === "number" ? "number" : "text",
|
|
12572
12664
|
placeholder: "Value",
|
|
12573
12665
|
value: f.value,
|
|
@@ -12591,7 +12683,7 @@ function Datatable({
|
|
|
12591
12683
|
role: "slider",
|
|
12592
12684
|
tabIndex: 0,
|
|
12593
12685
|
"aria-label": "Resize filters panel (Enter to reset)",
|
|
12594
|
-
"aria-valuetext": `Width ${Math.round(fPanelSize?.w ??
|
|
12686
|
+
"aria-valuetext": `Width ${Math.round(fPanelSize?.w ?? DT_FILTER_PANEL_W)} pixels, height ${fPanelSize?.h != null ? Math.round(fPanelSize.h) + " pixels" : "auto"}`,
|
|
12595
12687
|
onPointerDown: startPanelResize,
|
|
12596
12688
|
onKeyDown: onPanelGripKey,
|
|
12597
12689
|
onDoubleClick: (e) => {
|
|
@@ -17656,6 +17748,12 @@ var TEXTAREA_CSS = `
|
|
|
17656
17748
|
.twc-textarea__el:disabled { background: var(--color-surface-sunken); opacity: 0.7; cursor: not-allowed; }
|
|
17657
17749
|
.twc-textarea__el[data-readonly] { background: var(--color-surface-sunken); }
|
|
17658
17750
|
.twc-textarea__el[data-autosize="true"] { resize: none; overflow: hidden; }
|
|
17751
|
+
/* #328: clearable (X). Wrap positions the button top-inline-end so it dodges the bottom-right resize handle. */
|
|
17752
|
+
.twc-textarea__wrap { position: relative; display: block; }
|
|
17753
|
+
.twc-textarea__clear { position: absolute; top: 6px; inset-inline-end: 8px; display: inline-grid; place-items: center; width: 22px; height: 22px; padding: 0; border: none; background: var(--color-surface); color: var(--color-text-subtle); cursor: pointer; border-radius: var(--radius-sm); transition: background-color var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard); }
|
|
17754
|
+
.twc-textarea__clear:hover { background: var(--color-surface-sunken); color: var(--color-text); }
|
|
17755
|
+
.twc-textarea__clear:focus-visible { outline: none; box-shadow: var(--ring); }
|
|
17756
|
+
.twc-textarea__clear svg { width: 15px; height: 15px; }
|
|
17659
17757
|
.twc-field__footer { display: flex; align-items: center; gap: var(--space-2); }
|
|
17660
17758
|
.twc-field__count { margin-inline-start: auto; font-size: var(--text-xs); color: var(--color-text-muted); font-variant-numeric: tabular-nums; flex: none; }
|
|
17661
17759
|
.twc-field__count[data-danger="true"] { color: var(--color-danger-subtle-fg); }
|
|
@@ -17672,6 +17770,7 @@ var Textarea = React51__namespace.default.forwardRef(function Textarea2({
|
|
|
17672
17770
|
minRows,
|
|
17673
17771
|
maxRows,
|
|
17674
17772
|
showCount = false,
|
|
17773
|
+
clearable = false,
|
|
17675
17774
|
disabled = false,
|
|
17676
17775
|
id,
|
|
17677
17776
|
className = "",
|
|
@@ -17718,8 +17817,18 @@ var Textarea = React51__namespace.default.forwardRef(function Textarea2({
|
|
|
17718
17817
|
};
|
|
17719
17818
|
const handleChange = (e) => {
|
|
17720
17819
|
rest.onChange?.(e);
|
|
17721
|
-
if (showCount && !countControlled) setLen(e.target.value.length);
|
|
17820
|
+
if ((showCount || clearable) && !countControlled) setLen(e.target.value.length);
|
|
17722
17821
|
};
|
|
17822
|
+
const clearField = () => {
|
|
17823
|
+
const el = innerRef.current;
|
|
17824
|
+
if (!el) return;
|
|
17825
|
+
const setter = typeof HTMLTextAreaElement !== "undefined" && Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, "value")?.set;
|
|
17826
|
+
if (setter) setter.call(el, "");
|
|
17827
|
+
else el.value = "";
|
|
17828
|
+
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
17829
|
+
el.focus();
|
|
17830
|
+
};
|
|
17831
|
+
const showClear = clearable && count > 0 && !disabled && !rest.readOnly;
|
|
17723
17832
|
const ariaInvalid = invalid ? true : rest["aria-invalid"] ?? void 0;
|
|
17724
17833
|
const describedBy = [error || hint ? descId : null, showCounter ? countId : null, rest["aria-describedby"]].filter(Boolean).join(" ") || void 0;
|
|
17725
17834
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, children: [
|
|
@@ -17728,27 +17837,33 @@ var Textarea = React51__namespace.default.forwardRef(function Textarea2({
|
|
|
17728
17837
|
label,
|
|
17729
17838
|
required ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-field__req", children: "*" }) : null
|
|
17730
17839
|
] }) : null,
|
|
17731
|
-
/* @__PURE__ */ jsxRuntime.
|
|
17732
|
-
|
|
17733
|
-
|
|
17734
|
-
|
|
17735
|
-
|
|
17736
|
-
|
|
17737
|
-
|
|
17738
|
-
|
|
17739
|
-
|
|
17740
|
-
|
|
17741
|
-
|
|
17742
|
-
|
|
17743
|
-
|
|
17744
|
-
|
|
17745
|
-
|
|
17746
|
-
|
|
17747
|
-
|
|
17748
|
-
|
|
17749
|
-
|
|
17750
|
-
|
|
17751
|
-
|
|
17840
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-textarea__wrap", children: [
|
|
17841
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17842
|
+
"textarea",
|
|
17843
|
+
{
|
|
17844
|
+
ref: setRef,
|
|
17845
|
+
id: fieldId,
|
|
17846
|
+
className: "twc-textarea__el",
|
|
17847
|
+
"data-size": size,
|
|
17848
|
+
"data-tone": tone,
|
|
17849
|
+
"data-autosize": autosize || void 0,
|
|
17850
|
+
"data-readonly": rest.readOnly || void 0,
|
|
17851
|
+
rows: autosize ? minRows ?? rows : rows,
|
|
17852
|
+
disabled,
|
|
17853
|
+
required: required || void 0,
|
|
17854
|
+
"aria-required": required || void 0,
|
|
17855
|
+
...rest,
|
|
17856
|
+
onInput: handleInput,
|
|
17857
|
+
onChange: handleChange,
|
|
17858
|
+
"aria-invalid": ariaInvalid,
|
|
17859
|
+
"aria-describedby": describedBy
|
|
17860
|
+
}
|
|
17861
|
+
),
|
|
17862
|
+
showClear ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-textarea__clear", "aria-label": "Clear", tabIndex: 0, onMouseDown: (e) => e.preventDefault(), onClick: clearField, children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
17863
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6 6 18" }),
|
|
17864
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "m6 6 12 12" })
|
|
17865
|
+
] }) }) : null
|
|
17866
|
+
] }),
|
|
17752
17867
|
error || hint || showCounter ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-field__footer", children: [
|
|
17753
17868
|
error ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__error", children: error }) : hint ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__hint", children: hint }) : null,
|
|
17754
17869
|
showCounter ? /* @__PURE__ */ jsxRuntime.jsxs("span", { id: countId, className: "twc-field__count", "data-danger": near || void 0, "aria-live": "polite", children: [
|