twico-ui 1.46.0 → 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 +7 -0
- package/dist/index.cjs +204 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +392 -0
- package/dist/index.d.ts +392 -0
- package/dist/index.mjs +204 -13
- package/dist/index.mjs.map +1 -1
- package/llms.txt +13 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
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
|
+
|
|
5
12
|
# [1.46.0](https://github.com/EncryptionL/twico-ui/compare/v1.45.1...v1.46.0) (2026-10-21)
|
|
6
13
|
|
|
7
14
|
|
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: [
|
|
@@ -14850,6 +14934,17 @@ function DiffTable({
|
|
|
14850
14934
|
}
|
|
14851
14935
|
var FIELD_CSS3 = `
|
|
14852
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; }
|
|
14853
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; }
|
|
14854
14949
|
.twc-field__req { color: var(--color-danger); }
|
|
14855
14950
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -14982,6 +15077,7 @@ function DatePicker({
|
|
|
14982
15077
|
onChange,
|
|
14983
15078
|
id,
|
|
14984
15079
|
className = "",
|
|
15080
|
+
reserveFooter = false,
|
|
14985
15081
|
...rest
|
|
14986
15082
|
}) {
|
|
14987
15083
|
const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS3);
|
|
@@ -15184,7 +15280,7 @@ function DatePicker({
|
|
|
15184
15280
|
setFocusDate(next);
|
|
15185
15281
|
if (next.getFullYear() !== y || next.getMonth() !== m) setView(new Date(next.getFullYear(), next.getMonth(), 1));
|
|
15186
15282
|
};
|
|
15187
|
-
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: [
|
|
15188
15284
|
__twcStyles0,
|
|
15189
15285
|
__twcStyles1,
|
|
15190
15286
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", id: `${fieldId}-label`, children: [
|
|
@@ -15408,6 +15504,17 @@ function DatePicker({
|
|
|
15408
15504
|
}
|
|
15409
15505
|
var FIELD_CSS4 = `
|
|
15410
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; }
|
|
15411
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; }
|
|
15412
15519
|
.twc-field__req { color: var(--color-danger); }
|
|
15413
15520
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -15543,6 +15650,7 @@ function DateRangePicker({
|
|
|
15543
15650
|
parse,
|
|
15544
15651
|
onChange,
|
|
15545
15652
|
className = "",
|
|
15653
|
+
reserveFooter = false,
|
|
15546
15654
|
...rest
|
|
15547
15655
|
}) {
|
|
15548
15656
|
const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS4);
|
|
@@ -15787,7 +15895,7 @@ function DateRangePicker({
|
|
|
15787
15895
|
return t > Math.min(s, e) && t < Math.max(s, e);
|
|
15788
15896
|
};
|
|
15789
15897
|
const text = range2.start ? format ? format(range2) : `${fmtD(range2.start, locale)} \u2013 ${range2.end ? fmtD(range2.end, locale) : "\u2026"}` : "";
|
|
15790
|
-
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: [
|
|
15791
15899
|
__twcStyles0,
|
|
15792
15900
|
__twcStyles1,
|
|
15793
15901
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-field__label", id: labelId, children: [
|
|
@@ -16015,6 +16123,17 @@ function DateRangePicker({
|
|
|
16015
16123
|
}
|
|
16016
16124
|
var FIELD_CSS5 = `
|
|
16017
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; }
|
|
16018
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; }
|
|
16019
16138
|
.twc-field__req { color: var(--color-danger); }
|
|
16020
16139
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -16113,6 +16232,7 @@ function TimePicker({
|
|
|
16113
16232
|
onChange,
|
|
16114
16233
|
id,
|
|
16115
16234
|
className = "",
|
|
16235
|
+
reserveFooter = false,
|
|
16116
16236
|
...rest
|
|
16117
16237
|
}) {
|
|
16118
16238
|
const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS5);
|
|
@@ -16327,7 +16447,7 @@ function TimePicker({
|
|
|
16327
16447
|
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "9" }),
|
|
16328
16448
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 7v5l3 2" })
|
|
16329
16449
|
] });
|
|
16330
|
-
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: [
|
|
16331
16451
|
__twcStyles0,
|
|
16332
16452
|
__twcStyles1,
|
|
16333
16453
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", id: `${fieldId}-label`, children: [
|
|
@@ -16484,6 +16604,17 @@ function TimePicker({
|
|
|
16484
16604
|
}
|
|
16485
16605
|
var FIELD_CSS6 = `
|
|
16486
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; }
|
|
16487
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; }
|
|
16488
16619
|
.twc-field__req { color: var(--color-danger); }
|
|
16489
16620
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -16522,6 +16653,7 @@ function DateTimePicker({
|
|
|
16522
16653
|
onChange,
|
|
16523
16654
|
id,
|
|
16524
16655
|
className = "",
|
|
16656
|
+
reserveFooter = false,
|
|
16525
16657
|
...rest
|
|
16526
16658
|
}) {
|
|
16527
16659
|
const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS6);
|
|
@@ -16546,7 +16678,7 @@ function DateTimePicker({
|
|
|
16546
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));
|
|
16547
16679
|
};
|
|
16548
16680
|
const childTone = error ? "danger" : tone;
|
|
16549
|
-
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: [
|
|
16550
16682
|
__twcStyles0,
|
|
16551
16683
|
__twcStyles1,
|
|
16552
16684
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", id: `${fieldId}-label`, children: [
|
|
@@ -17124,6 +17256,17 @@ function EmptyState({
|
|
|
17124
17256
|
}
|
|
17125
17257
|
var FIELD_CSS7 = `
|
|
17126
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; }
|
|
17127
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; }
|
|
17128
17271
|
.twc-field__req { color: var(--color-danger); }
|
|
17129
17272
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -17138,6 +17281,7 @@ function Field({
|
|
|
17138
17281
|
size = "md",
|
|
17139
17282
|
id,
|
|
17140
17283
|
className = "",
|
|
17284
|
+
reserveFooter = false,
|
|
17141
17285
|
children,
|
|
17142
17286
|
...rest
|
|
17143
17287
|
}) {
|
|
@@ -17150,7 +17294,7 @@ function Field({
|
|
|
17150
17294
|
"aria-describedby": [children.props["aria-describedby"], hasMsg ? descId : null].filter(Boolean).join(" ") || void 0,
|
|
17151
17295
|
"aria-invalid": children.props["aria-invalid"] !== void 0 ? children.props["aria-invalid"] : error ? true : void 0
|
|
17152
17296
|
}) : children;
|
|
17153
|
-
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: [
|
|
17154
17298
|
__twcStyles,
|
|
17155
17299
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", htmlFor, children: [
|
|
17156
17300
|
label,
|
|
@@ -18206,6 +18350,17 @@ var Radio = /* @__PURE__ */ React55__namespace.default.forwardRef(function Radio
|
|
|
18206
18350
|
.twc-field__req { color: var(--color-danger); }
|
|
18207
18351
|
.twc-radio__desc { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
18208
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; }
|
|
18209
18364
|
.twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
|
|
18210
18365
|
`);
|
|
18211
18366
|
const autoId = React55__namespace.default.useId();
|
|
@@ -18255,6 +18410,17 @@ var Radio = /* @__PURE__ */ React55__namespace.default.forwardRef(function Radio
|
|
|
18255
18410
|
});
|
|
18256
18411
|
var RADIO_GROUP_CSS = `
|
|
18257
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; }
|
|
18258
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; }
|
|
18259
18425
|
.twc-field__req { color: var(--color-danger); }
|
|
18260
18426
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -18279,6 +18445,7 @@ function RadioGroup({
|
|
|
18279
18445
|
tone = "primary",
|
|
18280
18446
|
id,
|
|
18281
18447
|
className = "",
|
|
18448
|
+
reserveFooter = false,
|
|
18282
18449
|
...rest
|
|
18283
18450
|
}) {
|
|
18284
18451
|
const __twcStyles = useScopedStyles("twc-radio-group-styles", RADIO_GROUP_CSS);
|
|
@@ -18326,6 +18493,7 @@ function RadioGroup({
|
|
|
18326
18493
|
"div",
|
|
18327
18494
|
{
|
|
18328
18495
|
className: `twc-field ${className}`,
|
|
18496
|
+
"data-reserve-footer": reserveFooter || void 0,
|
|
18329
18497
|
role: "radiogroup",
|
|
18330
18498
|
"aria-labelledby": label ? labelId : void 0,
|
|
18331
18499
|
"aria-describedby": error || hint ? descId : void 0,
|
|
@@ -19350,6 +19518,17 @@ var Switch = /* @__PURE__ */ React55__namespace.default.forwardRef(function Swit
|
|
|
19350
19518
|
.twc-field__req { color: var(--color-danger); }
|
|
19351
19519
|
.twc-switch__desc { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
19352
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; }
|
|
19353
19532
|
.twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
|
|
19354
19533
|
`);
|
|
19355
19534
|
const autoId = React55__namespace.default.useId();
|
|
@@ -19789,6 +19968,17 @@ function Tag({
|
|
|
19789
19968
|
var useIso = typeof document !== "undefined" ? React55__namespace.default.useLayoutEffect : React55__namespace.default.useEffect;
|
|
19790
19969
|
var TEXTAREA_CSS = `
|
|
19791
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; }
|
|
19792
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; }
|
|
19793
19983
|
.twc-field__req { color: var(--color-danger); }
|
|
19794
19984
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -19851,6 +20041,7 @@ var Textarea = /* @__PURE__ */ React55__namespace.default.forwardRef(function Te
|
|
|
19851
20041
|
disabled = false,
|
|
19852
20042
|
id,
|
|
19853
20043
|
className = "",
|
|
20044
|
+
reserveFooter = false,
|
|
19854
20045
|
...rest
|
|
19855
20046
|
}, ref) {
|
|
19856
20047
|
const __twcStyles = useScopedStyles("twc-textarea-styles", TEXTAREA_CSS);
|
|
@@ -19908,7 +20099,7 @@ var Textarea = /* @__PURE__ */ React55__namespace.default.forwardRef(function Te
|
|
|
19908
20099
|
const showClear = clearable && count > 0 && !disabled && !rest.readOnly;
|
|
19909
20100
|
const ariaInvalid = invalid ? true : rest["aria-invalid"] ?? void 0;
|
|
19910
20101
|
const describedBy = [error || hint ? descId : null, showCounter ? countId : null, rest["aria-describedby"]].filter(Boolean).join(" ") || void 0;
|
|
19911
|
-
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: [
|
|
19912
20103
|
__twcStyles,
|
|
19913
20104
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
|
|
19914
20105
|
label,
|