twico-ui 1.45.1 → 1.47.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 +14 -0
- package/dist/index.cjs +220 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +432 -0
- package/dist/index.d.ts +432 -0
- package/dist/index.mjs +220 -14
- package/dist/index.mjs.map +1 -1
- package/llms.txt +16 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
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.47.0](https://github.com/EncryptionL/twico-ui/compare/v1.46.0...v1.47.0) (2026-10-23)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **inputs:** reserveFooter — keep a field's footer lane open so rows stay aligned ([223ef88](https://github.com/EncryptionL/twico-ui/commit/223ef8841b41b8fc10e6224530c392ceda2b9a98)), closes [#471](https://github.com/EncryptionL/twico-ui/issues/471)
|
|
11
|
+
|
|
12
|
+
# [1.46.0](https://github.com/EncryptionL/twico-ui/compare/v1.45.1...v1.46.0) (2026-10-21)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* **datatable,cardgrid:** selectionSummary slot, and move toolbarActions after the built-ins ([8ddbad9](https://github.com/EncryptionL/twico-ui/commit/8ddbad9c3d3321e585d895115ed4bb0b1a9c0c27)), closes [#492](https://github.com/EncryptionL/twico-ui/issues/492) [#478](https://github.com/EncryptionL/twico-ui/issues/478) [#286](https://github.com/EncryptionL/twico-ui/issues/286)
|
|
18
|
+
|
|
5
19
|
## [1.45.1](https://github.com/EncryptionL/twico-ui/compare/v1.45.0...v1.45.1) (2026-10-21)
|
|
6
20
|
|
|
7
21
|
|
package/dist/index.cjs
CHANGED
|
@@ -5901,6 +5901,17 @@ var Checkbox = /* @__PURE__ */ React55__namespace.default.forwardRef(function Ch
|
|
|
5901
5901
|
.twc-field__req { color: var(--color-danger); }
|
|
5902
5902
|
.twc-check__desc { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
5903
5903
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
5904
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
5905
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
5906
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
5907
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
5908
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
5909
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
5910
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
5911
|
+
whether a footer is already there, so no component has to report it. */
|
|
5912
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
5913
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
5914
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
5904
5915
|
.twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
|
|
5905
5916
|
`);
|
|
5906
5917
|
const autoId = React55__namespace.default.useId();
|
|
@@ -5955,6 +5966,17 @@ var Checkbox = /* @__PURE__ */ React55__namespace.default.forwardRef(function Ch
|
|
|
5955
5966
|
});
|
|
5956
5967
|
var FIELD_CSS = `
|
|
5957
5968
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
5969
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
5970
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
5971
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
5972
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
5973
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
5974
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
5975
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
5976
|
+
whether a footer is already there, so no component has to report it. */
|
|
5977
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
5978
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
5979
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
5958
5980
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
5959
5981
|
.twc-field__req { color: var(--color-danger); }
|
|
5960
5982
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -6066,6 +6088,7 @@ function ColorPicker({
|
|
|
6066
6088
|
tone = "primary",
|
|
6067
6089
|
onChange,
|
|
6068
6090
|
className = "",
|
|
6091
|
+
reserveFooter = false,
|
|
6069
6092
|
...rest
|
|
6070
6093
|
}) {
|
|
6071
6094
|
const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS);
|
|
@@ -6205,7 +6228,7 @@ function ColorPicker({
|
|
|
6205
6228
|
e.preventDefault();
|
|
6206
6229
|
commit({ ...hsv, a: Math.round(a * 100) / 100 });
|
|
6207
6230
|
};
|
|
6208
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-cp twc-field ${className}`, ref: wrapRef, ...rest, children: [
|
|
6231
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-cp twc-field ${className}`, ref: wrapRef, ...rest, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
6209
6232
|
__twcStyles0,
|
|
6210
6233
|
__twcStyles1,
|
|
6211
6234
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-field__label", id: labelId, children: [
|
|
@@ -6368,6 +6391,17 @@ function ColorPicker({
|
|
|
6368
6391
|
}
|
|
6369
6392
|
var COMBO_CSS = `
|
|
6370
6393
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
6394
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
6395
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
6396
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
6397
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
6398
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
6399
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
6400
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
6401
|
+
whether a footer is already there, so no component has to report it. */
|
|
6402
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
6403
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
6404
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
6371
6405
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
6372
6406
|
.twc-field__req { color: var(--color-danger); }
|
|
6373
6407
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -6506,6 +6540,7 @@ var Combobox = /* @__PURE__ */ React55__namespace.default.forwardRef(function Co
|
|
|
6506
6540
|
className = "",
|
|
6507
6541
|
onFocus,
|
|
6508
6542
|
onKeyDown,
|
|
6543
|
+
reserveFooter = false,
|
|
6509
6544
|
...rest
|
|
6510
6545
|
}, ref) {
|
|
6511
6546
|
const __twcStyles = useScopedStyles("twc-combo-styles", COMBO_CSS);
|
|
@@ -6793,7 +6828,7 @@ var Combobox = /* @__PURE__ */ React55__namespace.default.forwardRef(function Co
|
|
|
6793
6828
|
popEl = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop", ref: popRef, "data-placement": placement === "top" ? "top" : void 0, children: popInner });
|
|
6794
6829
|
}
|
|
6795
6830
|
}
|
|
6796
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, ref: wrapRef, children: [
|
|
6831
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, ref: wrapRef, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
6797
6832
|
__twcStyles,
|
|
6798
6833
|
name ? /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: current ?? "" }) : null,
|
|
6799
6834
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
|
|
@@ -7113,6 +7148,17 @@ function clampPrecision(str, precision) {
|
|
|
7113
7148
|
}
|
|
7114
7149
|
var CUR_CSS = `
|
|
7115
7150
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
7151
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
7152
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
7153
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
7154
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
7155
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
7156
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
7157
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
7158
|
+
whether a footer is already there, so no component has to report it. */
|
|
7159
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
7160
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
7161
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
7116
7162
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
7117
7163
|
.twc-field__req { color: var(--color-danger); }
|
|
7118
7164
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -7182,6 +7228,7 @@ function Currency({
|
|
|
7182
7228
|
id,
|
|
7183
7229
|
placeholder = "0.00",
|
|
7184
7230
|
className = "",
|
|
7231
|
+
reserveFooter = false,
|
|
7185
7232
|
...rest
|
|
7186
7233
|
}) {
|
|
7187
7234
|
const __twcStyles = useScopedStyles("twc-cur-styles", CUR_CSS);
|
|
@@ -7230,7 +7277,7 @@ function Currency({
|
|
|
7230
7277
|
if (!controlled) setInternal(fixed);
|
|
7231
7278
|
onValueChange?.(Number(fixed), fixed);
|
|
7232
7279
|
}
|
|
7233
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, children: [
|
|
7280
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
7234
7281
|
__twcStyles,
|
|
7235
7282
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
|
|
7236
7283
|
label,
|
|
@@ -7282,6 +7329,17 @@ function useFallbackName(ref, hasOwnName) {
|
|
|
7282
7329
|
}
|
|
7283
7330
|
var SELECT_CSS = `
|
|
7284
7331
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
7332
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
7333
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
7334
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
7335
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
7336
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
7337
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
7338
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
7339
|
+
whether a footer is already there, so no component has to report it. */
|
|
7340
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
7341
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
7342
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
7285
7343
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
7286
7344
|
.twc-field__req { color: var(--color-danger); }
|
|
7287
7345
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -7418,6 +7476,7 @@ var Select = /* @__PURE__ */ React55__namespace.default.forwardRef(function Sele
|
|
|
7418
7476
|
renderOption,
|
|
7419
7477
|
id,
|
|
7420
7478
|
className = "",
|
|
7479
|
+
reserveFooter = false,
|
|
7421
7480
|
onClick,
|
|
7422
7481
|
onKeyDown,
|
|
7423
7482
|
...rest
|
|
@@ -7763,7 +7822,7 @@ var Select = /* @__PURE__ */ React55__namespace.default.forwardRef(function Sele
|
|
|
7763
7822
|
popEl = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop", ref: popRef, "data-state": popState, "data-placement": placement === "top" ? "top" : "bottom", children: popInner });
|
|
7764
7823
|
}
|
|
7765
7824
|
}
|
|
7766
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, ref: wrapRef, children: [
|
|
7825
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, ref: wrapRef, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
7767
7826
|
__twcStyles,
|
|
7768
7827
|
name ? /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: current ?? "" }) : null,
|
|
7769
7828
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
|
|
@@ -7888,6 +7947,7 @@ function CurrencyField({
|
|
|
7888
7947
|
id,
|
|
7889
7948
|
placeholder = "0.00",
|
|
7890
7949
|
className = "",
|
|
7950
|
+
reserveFooter = false,
|
|
7891
7951
|
...rest
|
|
7892
7952
|
}) {
|
|
7893
7953
|
const __twcStyles = useScopedStyles("twc-curf-styles", CURF_CSS);
|
|
@@ -7950,7 +8010,7 @@ function CurrencyField({
|
|
|
7950
8010
|
onValueChange?.(Number(fixed), fixed, cur);
|
|
7951
8011
|
}
|
|
7952
8012
|
const curLabel = typeof label === "string" && label ? `${label} currency` : "Currency";
|
|
7953
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, children: [
|
|
8013
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
7954
8014
|
__twcStyles,
|
|
7955
8015
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
|
|
7956
8016
|
label,
|
|
@@ -7997,6 +8057,17 @@ function CurrencyField({
|
|
|
7997
8057
|
}
|
|
7998
8058
|
var FIELD_CSS2 = `
|
|
7999
8059
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
8060
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
8061
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
8062
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
8063
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
8064
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
8065
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
8066
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
8067
|
+
whether a footer is already there, so no component has to report it. */
|
|
8068
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
8069
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
8070
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
8000
8071
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
8001
8072
|
.twc-field__req { color: var(--color-danger); }
|
|
8002
8073
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -8086,6 +8157,7 @@ var Input = /* @__PURE__ */ React55__namespace.default.forwardRef(function Input
|
|
|
8086
8157
|
disabled = false,
|
|
8087
8158
|
id,
|
|
8088
8159
|
className = "",
|
|
8160
|
+
reserveFooter = false,
|
|
8089
8161
|
...rest
|
|
8090
8162
|
}, ref) {
|
|
8091
8163
|
const __twcStyles = useScopedStyles("twc-field-styles", FIELD_CSS2);
|
|
@@ -8155,7 +8227,7 @@ var Input = /* @__PURE__ */ React55__namespace.default.forwardRef(function Input
|
|
|
8155
8227
|
] })
|
|
8156
8228
|
}
|
|
8157
8229
|
) : null;
|
|
8158
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, children: [
|
|
8230
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
8159
8231
|
__twcStyles,
|
|
8160
8232
|
__twcExtra,
|
|
8161
8233
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
|
|
@@ -8200,6 +8272,17 @@ var Input = /* @__PURE__ */ React55__namespace.default.forwardRef(function Input
|
|
|
8200
8272
|
});
|
|
8201
8273
|
var MULTI_CSS = `
|
|
8202
8274
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
8275
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
8276
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
8277
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
8278
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
8279
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
8280
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
8281
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
8282
|
+
whether a footer is already there, so no component has to report it. */
|
|
8283
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
8284
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
8285
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
8203
8286
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
8204
8287
|
.twc-field__req { color: var(--color-danger); }
|
|
8205
8288
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -8345,6 +8428,7 @@ var MultiSelect = /* @__PURE__ */ React55__namespace.default.forwardRef(function
|
|
|
8345
8428
|
className = "",
|
|
8346
8429
|
onFocus,
|
|
8347
8430
|
onKeyDown,
|
|
8431
|
+
reserveFooter = false,
|
|
8348
8432
|
...rest
|
|
8349
8433
|
}, ref) {
|
|
8350
8434
|
const __twcStyles = useScopedStyles("twc-ms-styles", MULTI_CSS);
|
|
@@ -8618,7 +8702,7 @@ var MultiSelect = /* @__PURE__ */ React55__namespace.default.forwardRef(function
|
|
|
8618
8702
|
}
|
|
8619
8703
|
}
|
|
8620
8704
|
const msFallbackName = useFallbackName(inputRef, Boolean(label || rest["aria-label"] || rest["aria-labelledby"])) ? placeholder : void 0;
|
|
8621
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, ref: wrapRef, children: [
|
|
8705
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, ref: wrapRef, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
8622
8706
|
__twcStyles,
|
|
8623
8707
|
name ? selected.map((v, i) => /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: v }, i)) : null,
|
|
8624
8708
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
|
|
@@ -9558,6 +9642,12 @@ th.twc-dt__rownum .twc-dt__th-inner { padding-inline: 8px; gap: 2px; justify-con
|
|
|
9558
9642
|
.twc-dt__batch-x:hover { background: color-mix(in srgb, var(--color-primary) 20%, transparent); }
|
|
9559
9643
|
.twc-dt__batch-x svg { width: 16px; height: 16px; }
|
|
9560
9644
|
.twc-dt__batch-count { font-size: var(--text-sm); font-weight: var(--font-bold); color: var(--color-primary-subtle-fg); white-space: nowrap; }
|
|
9645
|
+
/* #492: the host's running total, sitting with the count it qualifies. Same colour as the count,
|
|
9646
|
+
differentiated by WEIGHT rather than by a lighter token: this bar is --color-primary-subtle, and
|
|
9647
|
+
--color-text-subtle on that is 4.26:1, under the 4.5:1 needed for normal text. */
|
|
9648
|
+
.twc-dt__batch-summary { display: inline-flex; align-items: center; gap: 6px; font-size: var(--text-sm);
|
|
9649
|
+
color: var(--color-primary-subtle-fg); white-space: nowrap; }
|
|
9650
|
+
.twc-dt__batch-summary::before { content: ""; width: var(--border-thin); height: 14px; background: currentColor; opacity: 0.35; }
|
|
9561
9651
|
.twc-dt__batch-actions { margin-inline-start: auto; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
|
|
9562
9652
|
.twc-dt__batch-btn { display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 12px;
|
|
9563
9653
|
border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-md); background: var(--color-surface);
|
|
@@ -10058,6 +10148,7 @@ function Datatable({
|
|
|
10058
10148
|
onColumnVisibilityChange,
|
|
10059
10149
|
batchActions = [],
|
|
10060
10150
|
onRowSelectionChange,
|
|
10151
|
+
selectionSummary,
|
|
10061
10152
|
getRowStyle,
|
|
10062
10153
|
getRowClassName,
|
|
10063
10154
|
showExport = false,
|
|
@@ -12927,13 +13018,13 @@ function Datatable({
|
|
|
12927
13018
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-dt ${className}`, ref: rootRef, "data-density": density, "data-resizing": resizing || void 0, "data-editing": editing ? "" : void 0, ...rest, children: [
|
|
12928
13019
|
__twcStyles,
|
|
12929
13020
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__toolbar", "data-compact": compact || void 0, children: [
|
|
12930
|
-
toolbarActions != null ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dt__toolbar-actions", children: toolbarActions }) : null,
|
|
12931
13021
|
(batchActions.length || hasBatchEditor) && selected.size > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__batch", children: [
|
|
12932
13022
|
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dt__batch-x", onClick: clearSelection, "aria-label": "Clear selection", children: /* @__PURE__ */ jsxRuntime.jsx(Svg, { d: I.x }) }),
|
|
12933
13023
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-dt__batch-count", children: [
|
|
12934
13024
|
selected.size,
|
|
12935
13025
|
" selected"
|
|
12936
13026
|
] }),
|
|
13027
|
+
selectionSummary != null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dt__batch-summary", children: typeof selectionSummary === "function" ? selectionSummary(selKeys, selectedRows) : selectionSummary }) : null,
|
|
12937
13028
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__batch-actions", children: [
|
|
12938
13029
|
hasBatchEditor ? /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: "twc-dt__batch-btn", onClick: (e) => openBatchEditor(e.currentTarget), children: [
|
|
12939
13030
|
/* @__PURE__ */ jsxRuntime.jsx(Svg, { d: I.pencil }),
|
|
@@ -13128,6 +13219,7 @@ function Datatable({
|
|
|
13128
13219
|
}
|
|
13129
13220
|
)
|
|
13130
13221
|
] }) : null,
|
|
13222
|
+
toolbarActions != null ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dt__toolbar-actions", children: toolbarActions }) : null,
|
|
13131
13223
|
searchable ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__search", children: [
|
|
13132
13224
|
/* @__PURE__ */ jsxRuntime.jsx(Svg, { d: I.search }),
|
|
13133
13225
|
/* @__PURE__ */ jsxRuntime.jsx("input", { ref: quickSearchRef, placeholder: "Search\u2026", "aria-label": "Search rows", value: quick, onChange: (e) => commitQuick(e.target.value) }),
|
|
@@ -14416,6 +14508,11 @@ var CARDGRID_CSS = `
|
|
|
14416
14508
|
padding: var(--space-2) var(--space-3); border: var(--border-thin) solid var(--color-primary-border);
|
|
14417
14509
|
background: var(--color-primary-subtle); border-radius: var(--radius-md); }
|
|
14418
14510
|
.twc-cardgrid__batch-count { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-primary-subtle-fg); }
|
|
14511
|
+
/* #492: same slot, same contract as Datatable. Weight, not a lighter token - this bar is
|
|
14512
|
+
--color-primary-subtle, where --color-text-subtle is 4.26:1 and fails normal-text contrast. */
|
|
14513
|
+
.twc-cardgrid__batch-summary { display: inline-flex; align-items: center; gap: var(--space-2);
|
|
14514
|
+
font-size: var(--text-sm); color: var(--color-primary-subtle-fg); }
|
|
14515
|
+
.twc-cardgrid__batch-summary::before { content: ""; width: var(--border-thin); height: 14px; background: currentColor; opacity: 0.35; }
|
|
14419
14516
|
.twc-cardgrid__batch-actions { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin-inline-start: auto; }
|
|
14420
14517
|
.twc-cardgrid__batch-btn { display: inline-flex; align-items: center; gap: var(--space-2); height: var(--control-h-sm);
|
|
14421
14518
|
padding-inline: var(--space-3); border: var(--border-thin) solid var(--color-border); background: var(--color-surface);
|
|
@@ -14471,6 +14568,7 @@ function CardGrid({
|
|
|
14471
14568
|
// the whole point, and it was the only Datatable affordance CardGrid lacked.
|
|
14472
14569
|
checkboxSelection = false,
|
|
14473
14570
|
batchActions = [],
|
|
14571
|
+
selectionSummary,
|
|
14474
14572
|
onRowSelectionChange,
|
|
14475
14573
|
selectionPlacement = "top-start",
|
|
14476
14574
|
selectionLabel,
|
|
@@ -14678,6 +14776,7 @@ function CardGrid({
|
|
|
14678
14776
|
selected.size,
|
|
14679
14777
|
" selected"
|
|
14680
14778
|
] }),
|
|
14779
|
+
selectionSummary != null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-cardgrid__batch-summary", children: typeof selectionSummary === "function" ? selectionSummary(selKeys, selectedRows) : selectionSummary }) : null,
|
|
14681
14780
|
showSelectAll && pageRows.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-cardgrid__selall", children: [
|
|
14682
14781
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
14683
14782
|
Checkbox,
|
|
@@ -14835,6 +14934,17 @@ function DiffTable({
|
|
|
14835
14934
|
}
|
|
14836
14935
|
var FIELD_CSS3 = `
|
|
14837
14936
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
14937
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
14938
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
14939
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
14940
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
14941
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
14942
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
14943
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
14944
|
+
whether a footer is already there, so no component has to report it. */
|
|
14945
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
14946
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
14947
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
14838
14948
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
14839
14949
|
.twc-field__req { color: var(--color-danger); }
|
|
14840
14950
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -14967,6 +15077,7 @@ function DatePicker({
|
|
|
14967
15077
|
onChange,
|
|
14968
15078
|
id,
|
|
14969
15079
|
className = "",
|
|
15080
|
+
reserveFooter = false,
|
|
14970
15081
|
...rest
|
|
14971
15082
|
}) {
|
|
14972
15083
|
const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS3);
|
|
@@ -15169,7 +15280,7 @@ function DatePicker({
|
|
|
15169
15280
|
setFocusDate(next);
|
|
15170
15281
|
if (next.getFullYear() !== y || next.getMonth() !== m) setView(new Date(next.getFullYear(), next.getMonth(), 1));
|
|
15171
15282
|
};
|
|
15172
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-dp twc-field ${className}`, ref: wrapRef, ...rest, children: [
|
|
15283
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-dp twc-field ${className}`, ref: wrapRef, ...rest, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
15173
15284
|
__twcStyles0,
|
|
15174
15285
|
__twcStyles1,
|
|
15175
15286
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", id: `${fieldId}-label`, children: [
|
|
@@ -15393,6 +15504,17 @@ function DatePicker({
|
|
|
15393
15504
|
}
|
|
15394
15505
|
var FIELD_CSS4 = `
|
|
15395
15506
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
15507
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
15508
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
15509
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
15510
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
15511
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
15512
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
15513
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
15514
|
+
whether a footer is already there, so no component has to report it. */
|
|
15515
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
15516
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
15517
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
15396
15518
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
15397
15519
|
.twc-field__req { color: var(--color-danger); }
|
|
15398
15520
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -15528,6 +15650,7 @@ function DateRangePicker({
|
|
|
15528
15650
|
parse,
|
|
15529
15651
|
onChange,
|
|
15530
15652
|
className = "",
|
|
15653
|
+
reserveFooter = false,
|
|
15531
15654
|
...rest
|
|
15532
15655
|
}) {
|
|
15533
15656
|
const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS4);
|
|
@@ -15772,7 +15895,7 @@ function DateRangePicker({
|
|
|
15772
15895
|
return t > Math.min(s, e) && t < Math.max(s, e);
|
|
15773
15896
|
};
|
|
15774
15897
|
const text = range2.start ? format ? format(range2) : `${fmtD(range2.start, locale)} \u2013 ${range2.end ? fmtD(range2.end, locale) : "\u2026"}` : "";
|
|
15775
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-drp twc-field ${className}`, ref: wrapRef, ...rest, children: [
|
|
15898
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-drp twc-field ${className}`, ref: wrapRef, ...rest, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
15776
15899
|
__twcStyles0,
|
|
15777
15900
|
__twcStyles1,
|
|
15778
15901
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-field__label", id: labelId, children: [
|
|
@@ -16000,6 +16123,17 @@ function DateRangePicker({
|
|
|
16000
16123
|
}
|
|
16001
16124
|
var FIELD_CSS5 = `
|
|
16002
16125
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
16126
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
16127
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
16128
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
16129
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
16130
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
16131
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
16132
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
16133
|
+
whether a footer is already there, so no component has to report it. */
|
|
16134
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
16135
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
16136
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
16003
16137
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
16004
16138
|
.twc-field__req { color: var(--color-danger); }
|
|
16005
16139
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -16098,6 +16232,7 @@ function TimePicker({
|
|
|
16098
16232
|
onChange,
|
|
16099
16233
|
id,
|
|
16100
16234
|
className = "",
|
|
16235
|
+
reserveFooter = false,
|
|
16101
16236
|
...rest
|
|
16102
16237
|
}) {
|
|
16103
16238
|
const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS5);
|
|
@@ -16312,7 +16447,7 @@ function TimePicker({
|
|
|
16312
16447
|
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "9" }),
|
|
16313
16448
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 7v5l3 2" })
|
|
16314
16449
|
] });
|
|
16315
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-tp twc-field ${className}`, ref: wrapRef, ...rest, children: [
|
|
16450
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-tp twc-field ${className}`, ref: wrapRef, ...rest, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
16316
16451
|
__twcStyles0,
|
|
16317
16452
|
__twcStyles1,
|
|
16318
16453
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", id: `${fieldId}-label`, children: [
|
|
@@ -16469,6 +16604,17 @@ function TimePicker({
|
|
|
16469
16604
|
}
|
|
16470
16605
|
var FIELD_CSS6 = `
|
|
16471
16606
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
16607
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
16608
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
16609
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
16610
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
16611
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
16612
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
16613
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
16614
|
+
whether a footer is already there, so no component has to report it. */
|
|
16615
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
16616
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
16617
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
16472
16618
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
16473
16619
|
.twc-field__req { color: var(--color-danger); }
|
|
16474
16620
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -16507,6 +16653,7 @@ function DateTimePicker({
|
|
|
16507
16653
|
onChange,
|
|
16508
16654
|
id,
|
|
16509
16655
|
className = "",
|
|
16656
|
+
reserveFooter = false,
|
|
16510
16657
|
...rest
|
|
16511
16658
|
}) {
|
|
16512
16659
|
const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS6);
|
|
@@ -16531,7 +16678,7 @@ function DateTimePicker({
|
|
|
16531
16678
|
commitVal(new Date(b ? b.getFullYear() : t.getFullYear(), b ? b.getMonth() : t.getMonth(), b ? b.getDate() : t.getDate(), t.getHours(), t.getMinutes(), t.getSeconds(), 0));
|
|
16532
16679
|
};
|
|
16533
16680
|
const childTone = error ? "danger" : tone;
|
|
16534
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-dtp twc-field ${className}`, id: fieldId, ...rest, children: [
|
|
16681
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-dtp twc-field ${className}`, id: fieldId, ...rest, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
16535
16682
|
__twcStyles0,
|
|
16536
16683
|
__twcStyles1,
|
|
16537
16684
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", id: `${fieldId}-label`, children: [
|
|
@@ -17109,6 +17256,17 @@ function EmptyState({
|
|
|
17109
17256
|
}
|
|
17110
17257
|
var FIELD_CSS7 = `
|
|
17111
17258
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
17259
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
17260
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
17261
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
17262
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
17263
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
17264
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
17265
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
17266
|
+
whether a footer is already there, so no component has to report it. */
|
|
17267
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
17268
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
17269
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
17112
17270
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
17113
17271
|
.twc-field__req { color: var(--color-danger); }
|
|
17114
17272
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -17123,6 +17281,7 @@ function Field({
|
|
|
17123
17281
|
size = "md",
|
|
17124
17282
|
id,
|
|
17125
17283
|
className = "",
|
|
17284
|
+
reserveFooter = false,
|
|
17126
17285
|
children,
|
|
17127
17286
|
...rest
|
|
17128
17287
|
}) {
|
|
@@ -17135,7 +17294,7 @@ function Field({
|
|
|
17135
17294
|
"aria-describedby": [children.props["aria-describedby"], hasMsg ? descId : null].filter(Boolean).join(" ") || void 0,
|
|
17136
17295
|
"aria-invalid": children.props["aria-invalid"] !== void 0 ? children.props["aria-invalid"] : error ? true : void 0
|
|
17137
17296
|
}) : children;
|
|
17138
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, "data-size": size, ...rest, children: [
|
|
17297
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, "data-size": size, ...rest, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
17139
17298
|
__twcStyles,
|
|
17140
17299
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", htmlFor, children: [
|
|
17141
17300
|
label,
|
|
@@ -18191,6 +18350,17 @@ var Radio = /* @__PURE__ */ React55__namespace.default.forwardRef(function Radio
|
|
|
18191
18350
|
.twc-field__req { color: var(--color-danger); }
|
|
18192
18351
|
.twc-radio__desc { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
18193
18352
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
18353
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
18354
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
18355
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
18356
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
18357
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
18358
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
18359
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
18360
|
+
whether a footer is already there, so no component has to report it. */
|
|
18361
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
18362
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
18363
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
18194
18364
|
.twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
|
|
18195
18365
|
`);
|
|
18196
18366
|
const autoId = React55__namespace.default.useId();
|
|
@@ -18240,6 +18410,17 @@ var Radio = /* @__PURE__ */ React55__namespace.default.forwardRef(function Radio
|
|
|
18240
18410
|
});
|
|
18241
18411
|
var RADIO_GROUP_CSS = `
|
|
18242
18412
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
18413
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
18414
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
18415
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
18416
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
18417
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
18418
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
18419
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
18420
|
+
whether a footer is already there, so no component has to report it. */
|
|
18421
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
18422
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
18423
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
18243
18424
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
18244
18425
|
.twc-field__req { color: var(--color-danger); }
|
|
18245
18426
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -18264,6 +18445,7 @@ function RadioGroup({
|
|
|
18264
18445
|
tone = "primary",
|
|
18265
18446
|
id,
|
|
18266
18447
|
className = "",
|
|
18448
|
+
reserveFooter = false,
|
|
18267
18449
|
...rest
|
|
18268
18450
|
}) {
|
|
18269
18451
|
const __twcStyles = useScopedStyles("twc-radio-group-styles", RADIO_GROUP_CSS);
|
|
@@ -18311,6 +18493,7 @@ function RadioGroup({
|
|
|
18311
18493
|
"div",
|
|
18312
18494
|
{
|
|
18313
18495
|
className: `twc-field ${className}`,
|
|
18496
|
+
"data-reserve-footer": reserveFooter || void 0,
|
|
18314
18497
|
role: "radiogroup",
|
|
18315
18498
|
"aria-labelledby": label ? labelId : void 0,
|
|
18316
18499
|
"aria-describedby": error || hint ? descId : void 0,
|
|
@@ -19335,6 +19518,17 @@ var Switch = /* @__PURE__ */ React55__namespace.default.forwardRef(function Swit
|
|
|
19335
19518
|
.twc-field__req { color: var(--color-danger); }
|
|
19336
19519
|
.twc-switch__desc { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
19337
19520
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
19521
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
19522
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
19523
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
19524
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
19525
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
19526
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
19527
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
19528
|
+
whether a footer is already there, so no component has to report it. */
|
|
19529
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
19530
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
19531
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
19338
19532
|
.twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
|
|
19339
19533
|
`);
|
|
19340
19534
|
const autoId = React55__namespace.default.useId();
|
|
@@ -19774,6 +19968,17 @@ function Tag({
|
|
|
19774
19968
|
var useIso = typeof document !== "undefined" ? React55__namespace.default.useLayoutEffect : React55__namespace.default.useEffect;
|
|
19775
19969
|
var TEXTAREA_CSS = `
|
|
19776
19970
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
19971
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
19972
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
19973
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
19974
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
19975
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
19976
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
19977
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
19978
|
+
whether a footer is already there, so no component has to report it. */
|
|
19979
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
19980
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
19981
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
19777
19982
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
19778
19983
|
.twc-field__req { color: var(--color-danger); }
|
|
19779
19984
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -19836,6 +20041,7 @@ var Textarea = /* @__PURE__ */ React55__namespace.default.forwardRef(function Te
|
|
|
19836
20041
|
disabled = false,
|
|
19837
20042
|
id,
|
|
19838
20043
|
className = "",
|
|
20044
|
+
reserveFooter = false,
|
|
19839
20045
|
...rest
|
|
19840
20046
|
}, ref) {
|
|
19841
20047
|
const __twcStyles = useScopedStyles("twc-textarea-styles", TEXTAREA_CSS);
|
|
@@ -19893,7 +20099,7 @@ var Textarea = /* @__PURE__ */ React55__namespace.default.forwardRef(function Te
|
|
|
19893
20099
|
const showClear = clearable && count > 0 && !disabled && !rest.readOnly;
|
|
19894
20100
|
const ariaInvalid = invalid ? true : rest["aria-invalid"] ?? void 0;
|
|
19895
20101
|
const describedBy = [error || hint ? descId : null, showCounter ? countId : null, rest["aria-describedby"]].filter(Boolean).join(" ") || void 0;
|
|
19896
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, children: [
|
|
20102
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
19897
20103
|
__twcStyles,
|
|
19898
20104
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
|
|
19899
20105
|
label,
|