twico-ui 1.46.0 → 1.48.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/icons.d.cts +2 -1
- package/dist/icons.d.ts +2 -1
- package/dist/index.cjs +220 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +433 -1
- package/dist/index.d.ts +433 -1
- package/dist/index.mjs +220 -14
- package/dist/index.mjs.map +1 -1
- package/llms.txt +16 -0
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -5880,6 +5880,17 @@ var Checkbox = /* @__PURE__ */ React55__default.forwardRef(function Checkbox2({
|
|
|
5880
5880
|
.twc-field__req { color: var(--color-danger); }
|
|
5881
5881
|
.twc-check__desc { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
5882
5882
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
5883
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
5884
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
5885
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
5886
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
5887
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
5888
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
5889
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
5890
|
+
whether a footer is already there, so no component has to report it. */
|
|
5891
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
5892
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
5893
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
5883
5894
|
.twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
|
|
5884
5895
|
`);
|
|
5885
5896
|
const autoId = React55__default.useId();
|
|
@@ -5934,6 +5945,17 @@ var Checkbox = /* @__PURE__ */ React55__default.forwardRef(function Checkbox2({
|
|
|
5934
5945
|
});
|
|
5935
5946
|
var FIELD_CSS = `
|
|
5936
5947
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
5948
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
5949
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
5950
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
5951
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
5952
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
5953
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
5954
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
5955
|
+
whether a footer is already there, so no component has to report it. */
|
|
5956
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
5957
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
5958
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
5937
5959
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
5938
5960
|
.twc-field__req { color: var(--color-danger); }
|
|
5939
5961
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -6045,6 +6067,7 @@ function ColorPicker({
|
|
|
6045
6067
|
tone = "primary",
|
|
6046
6068
|
onChange,
|
|
6047
6069
|
className = "",
|
|
6070
|
+
reserveFooter = false,
|
|
6048
6071
|
...rest
|
|
6049
6072
|
}) {
|
|
6050
6073
|
const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS);
|
|
@@ -6184,7 +6207,7 @@ function ColorPicker({
|
|
|
6184
6207
|
e.preventDefault();
|
|
6185
6208
|
commit({ ...hsv, a: Math.round(a * 100) / 100 });
|
|
6186
6209
|
};
|
|
6187
|
-
return /* @__PURE__ */ jsxs("div", { className: `twc-cp twc-field ${className}`, ref: wrapRef, ...rest, children: [
|
|
6210
|
+
return /* @__PURE__ */ jsxs("div", { className: `twc-cp twc-field ${className}`, ref: wrapRef, ...rest, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
6188
6211
|
__twcStyles0,
|
|
6189
6212
|
__twcStyles1,
|
|
6190
6213
|
label ? /* @__PURE__ */ jsxs("span", { className: "twc-field__label", id: labelId, children: [
|
|
@@ -6347,6 +6370,17 @@ function ColorPicker({
|
|
|
6347
6370
|
}
|
|
6348
6371
|
var COMBO_CSS = `
|
|
6349
6372
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
6373
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
6374
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
6375
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
6376
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
6377
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
6378
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
6379
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
6380
|
+
whether a footer is already there, so no component has to report it. */
|
|
6381
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
6382
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
6383
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
6350
6384
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
6351
6385
|
.twc-field__req { color: var(--color-danger); }
|
|
6352
6386
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -6485,6 +6519,7 @@ var Combobox = /* @__PURE__ */ React55__default.forwardRef(function Combobox2({
|
|
|
6485
6519
|
className = "",
|
|
6486
6520
|
onFocus,
|
|
6487
6521
|
onKeyDown,
|
|
6522
|
+
reserveFooter = false,
|
|
6488
6523
|
...rest
|
|
6489
6524
|
}, ref) {
|
|
6490
6525
|
const __twcStyles = useScopedStyles("twc-combo-styles", COMBO_CSS);
|
|
@@ -6772,7 +6807,7 @@ var Combobox = /* @__PURE__ */ React55__default.forwardRef(function Combobox2({
|
|
|
6772
6807
|
popEl = /* @__PURE__ */ jsx("div", { className: "twc-pop", ref: popRef, "data-placement": placement === "top" ? "top" : void 0, children: popInner });
|
|
6773
6808
|
}
|
|
6774
6809
|
}
|
|
6775
|
-
return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, ref: wrapRef, children: [
|
|
6810
|
+
return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, ref: wrapRef, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
6776
6811
|
__twcStyles,
|
|
6777
6812
|
name ? /* @__PURE__ */ jsx("input", { type: "hidden", name, value: current ?? "" }) : null,
|
|
6778
6813
|
label ? /* @__PURE__ */ jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
|
|
@@ -7092,6 +7127,17 @@ function clampPrecision(str, precision) {
|
|
|
7092
7127
|
}
|
|
7093
7128
|
var CUR_CSS = `
|
|
7094
7129
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
7130
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
7131
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
7132
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
7133
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
7134
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
7135
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
7136
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
7137
|
+
whether a footer is already there, so no component has to report it. */
|
|
7138
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
7139
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
7140
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
7095
7141
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
7096
7142
|
.twc-field__req { color: var(--color-danger); }
|
|
7097
7143
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -7161,6 +7207,7 @@ function Currency({
|
|
|
7161
7207
|
id,
|
|
7162
7208
|
placeholder = "0.00",
|
|
7163
7209
|
className = "",
|
|
7210
|
+
reserveFooter = false,
|
|
7164
7211
|
...rest
|
|
7165
7212
|
}) {
|
|
7166
7213
|
const __twcStyles = useScopedStyles("twc-cur-styles", CUR_CSS);
|
|
@@ -7209,7 +7256,7 @@ function Currency({
|
|
|
7209
7256
|
if (!controlled) setInternal(fixed);
|
|
7210
7257
|
onValueChange?.(Number(fixed), fixed);
|
|
7211
7258
|
}
|
|
7212
|
-
return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, children: [
|
|
7259
|
+
return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
7213
7260
|
__twcStyles,
|
|
7214
7261
|
label ? /* @__PURE__ */ jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
|
|
7215
7262
|
label,
|
|
@@ -7261,6 +7308,17 @@ function useFallbackName(ref, hasOwnName) {
|
|
|
7261
7308
|
}
|
|
7262
7309
|
var SELECT_CSS = `
|
|
7263
7310
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
7311
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
7312
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
7313
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
7314
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
7315
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
7316
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
7317
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
7318
|
+
whether a footer is already there, so no component has to report it. */
|
|
7319
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
7320
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
7321
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
7264
7322
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
7265
7323
|
.twc-field__req { color: var(--color-danger); }
|
|
7266
7324
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -7397,6 +7455,7 @@ var Select = /* @__PURE__ */ React55__default.forwardRef(function Select2({
|
|
|
7397
7455
|
renderOption,
|
|
7398
7456
|
id,
|
|
7399
7457
|
className = "",
|
|
7458
|
+
reserveFooter = false,
|
|
7400
7459
|
onClick,
|
|
7401
7460
|
onKeyDown,
|
|
7402
7461
|
...rest
|
|
@@ -7742,7 +7801,7 @@ var Select = /* @__PURE__ */ React55__default.forwardRef(function Select2({
|
|
|
7742
7801
|
popEl = /* @__PURE__ */ jsx("div", { className: "twc-pop", ref: popRef, "data-state": popState, "data-placement": placement === "top" ? "top" : "bottom", children: popInner });
|
|
7743
7802
|
}
|
|
7744
7803
|
}
|
|
7745
|
-
return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, ref: wrapRef, children: [
|
|
7804
|
+
return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, ref: wrapRef, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
7746
7805
|
__twcStyles,
|
|
7747
7806
|
name ? /* @__PURE__ */ jsx("input", { type: "hidden", name, value: current ?? "" }) : null,
|
|
7748
7807
|
label ? /* @__PURE__ */ jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
|
|
@@ -7867,6 +7926,7 @@ function CurrencyField({
|
|
|
7867
7926
|
id,
|
|
7868
7927
|
placeholder = "0.00",
|
|
7869
7928
|
className = "",
|
|
7929
|
+
reserveFooter = false,
|
|
7870
7930
|
...rest
|
|
7871
7931
|
}) {
|
|
7872
7932
|
const __twcStyles = useScopedStyles("twc-curf-styles", CURF_CSS);
|
|
@@ -7929,7 +7989,7 @@ function CurrencyField({
|
|
|
7929
7989
|
onValueChange?.(Number(fixed), fixed, cur);
|
|
7930
7990
|
}
|
|
7931
7991
|
const curLabel = typeof label === "string" && label ? `${label} currency` : "Currency";
|
|
7932
|
-
return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, children: [
|
|
7992
|
+
return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
7933
7993
|
__twcStyles,
|
|
7934
7994
|
label ? /* @__PURE__ */ jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
|
|
7935
7995
|
label,
|
|
@@ -7976,6 +8036,17 @@ function CurrencyField({
|
|
|
7976
8036
|
}
|
|
7977
8037
|
var FIELD_CSS2 = `
|
|
7978
8038
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
8039
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
8040
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
8041
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
8042
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
8043
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
8044
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
8045
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
8046
|
+
whether a footer is already there, so no component has to report it. */
|
|
8047
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
8048
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
8049
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
7979
8050
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
7980
8051
|
.twc-field__req { color: var(--color-danger); }
|
|
7981
8052
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -8065,6 +8136,7 @@ var Input = /* @__PURE__ */ React55__default.forwardRef(function Input2({
|
|
|
8065
8136
|
disabled = false,
|
|
8066
8137
|
id,
|
|
8067
8138
|
className = "",
|
|
8139
|
+
reserveFooter = false,
|
|
8068
8140
|
...rest
|
|
8069
8141
|
}, ref) {
|
|
8070
8142
|
const __twcStyles = useScopedStyles("twc-field-styles", FIELD_CSS2);
|
|
@@ -8134,7 +8206,7 @@ var Input = /* @__PURE__ */ React55__default.forwardRef(function Input2({
|
|
|
8134
8206
|
] })
|
|
8135
8207
|
}
|
|
8136
8208
|
) : null;
|
|
8137
|
-
return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, children: [
|
|
8209
|
+
return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
8138
8210
|
__twcStyles,
|
|
8139
8211
|
__twcExtra,
|
|
8140
8212
|
label ? /* @__PURE__ */ jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
|
|
@@ -8179,6 +8251,17 @@ var Input = /* @__PURE__ */ React55__default.forwardRef(function Input2({
|
|
|
8179
8251
|
});
|
|
8180
8252
|
var MULTI_CSS = `
|
|
8181
8253
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
8254
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
8255
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
8256
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
8257
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
8258
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
8259
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
8260
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
8261
|
+
whether a footer is already there, so no component has to report it. */
|
|
8262
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
8263
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
8264
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
8182
8265
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
8183
8266
|
.twc-field__req { color: var(--color-danger); }
|
|
8184
8267
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -8324,6 +8407,7 @@ var MultiSelect = /* @__PURE__ */ React55__default.forwardRef(function MultiSele
|
|
|
8324
8407
|
className = "",
|
|
8325
8408
|
onFocus,
|
|
8326
8409
|
onKeyDown,
|
|
8410
|
+
reserveFooter = false,
|
|
8327
8411
|
...rest
|
|
8328
8412
|
}, ref) {
|
|
8329
8413
|
const __twcStyles = useScopedStyles("twc-ms-styles", MULTI_CSS);
|
|
@@ -8597,7 +8681,7 @@ var MultiSelect = /* @__PURE__ */ React55__default.forwardRef(function MultiSele
|
|
|
8597
8681
|
}
|
|
8598
8682
|
}
|
|
8599
8683
|
const msFallbackName = useFallbackName(inputRef, Boolean(label || rest["aria-label"] || rest["aria-labelledby"])) ? placeholder : void 0;
|
|
8600
|
-
return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, ref: wrapRef, children: [
|
|
8684
|
+
return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, ref: wrapRef, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
8601
8685
|
__twcStyles,
|
|
8602
8686
|
name ? selected.map((v, i) => /* @__PURE__ */ jsx("input", { type: "hidden", name, value: v }, i)) : null,
|
|
8603
8687
|
label ? /* @__PURE__ */ jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
|
|
@@ -9273,9 +9357,20 @@ var DT_CSS = `
|
|
|
9273
9357
|
font-size: var(--text-sm); color: var(--color-text); background: var(--twc-dt-row-bg, var(--color-surface));
|
|
9274
9358
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 0; }
|
|
9275
9359
|
/* Wrap-text columns: content flows onto multiple lines (the row grows down) instead of
|
|
9276
|
-
clipping to one line. height stays a per-row minimum, so single-line cells are unchanged.
|
|
9360
|
+
clipping to one line. height stays a per-row minimum, so single-line cells are unchanged.
|
|
9361
|
+
The top alignment here is only a DEFAULT for wrapped cells - an explicit verticalAlign at any
|
|
9362
|
+
level resolves to data-valign below, which is more specific and wins. Before that existed, a row
|
|
9363
|
+
mixing a wrapped column with normal ones was silently inconsistent: the wrapped cell sat top and
|
|
9364
|
+
its neighbours sat middle, with no way to say otherwise. */
|
|
9277
9365
|
.twc-dt__td[data-wrap="true"] { white-space: normal; overflow: hidden; text-overflow: clip;
|
|
9278
9366
|
word-break: break-word; overflow-wrap: anywhere; vertical-align: top; padding-block: 10px; line-height: 1.45; }
|
|
9367
|
+
/* Vertical alignment. One attribute, already resolved: column > row > table default, computed in
|
|
9368
|
+
renderLeaf rather than left to three competing selectors, so the precedence is readable in the
|
|
9369
|
+
code and cannot be reordered by an unrelated CSS edit. Matters whenever rows vary in height -
|
|
9370
|
+
wrapText, a multi-line renderCell, or a drag-resized row. */
|
|
9371
|
+
.twc-dt__td[data-valign="top"] { vertical-align: top; }
|
|
9372
|
+
.twc-dt__td[data-valign="middle"] { vertical-align: middle; }
|
|
9373
|
+
.twc-dt__td[data-valign="bottom"] { vertical-align: bottom; }
|
|
9279
9374
|
/* #338: "combined" column cell \u2014 one column showing several source columns' values. */
|
|
9280
9375
|
.twc-dt__combine--stack { display: flex; flex-direction: column; gap: 1px; }
|
|
9281
9376
|
.twc-dt__combine-sep { color: var(--color-text-subtle); padding-inline: 3px; }
|
|
@@ -10044,6 +10139,8 @@ function Datatable({
|
|
|
10044
10139
|
batchActions = [],
|
|
10045
10140
|
onRowSelectionChange,
|
|
10046
10141
|
selectionSummary,
|
|
10142
|
+
verticalAlign,
|
|
10143
|
+
getRowVerticalAlign,
|
|
10047
10144
|
getRowStyle,
|
|
10048
10145
|
getRowClassName,
|
|
10049
10146
|
showExport = false,
|
|
@@ -12623,6 +12720,7 @@ function Datatable({
|
|
|
12623
12720
|
const h = rowHeights[k];
|
|
12624
12721
|
const userRowStyle = getRowStyle ? getRowStyle(row, ri) : void 0;
|
|
12625
12722
|
const userRowClass = getRowClassName ? getRowClassName(row, ri) : void 0;
|
|
12723
|
+
const rowValign = getRowVerticalAlign ? getRowVerticalAlign(row, ri) : void 0;
|
|
12626
12724
|
const { background: _rowBgShorthand, backgroundColor: _rowBgColor, ...restRowStyle } = userRowStyle || {};
|
|
12627
12725
|
const usable = (v) => typeof v === "string" && v.trim() !== "" ? v : void 0;
|
|
12628
12726
|
const rowBg = usable(_rowBgShorthand) ?? usable(_rowBgColor);
|
|
@@ -12733,6 +12831,7 @@ function Datatable({
|
|
|
12733
12831
|
"data-num": c.type === "number" || void 0,
|
|
12734
12832
|
"data-actions": isActions || void 0,
|
|
12735
12833
|
"data-align": c.align !== dtNaturalAlign(c) ? c.align : void 0,
|
|
12834
|
+
"data-valign": c.verticalAlign ?? rowValign ?? verticalAlign,
|
|
12736
12835
|
"data-editable": editable && !isEditing || void 0,
|
|
12737
12836
|
"data-editing": isEditing || void 0,
|
|
12738
12837
|
"data-cell-active": cellActive || void 0,
|
|
@@ -14829,6 +14928,17 @@ function DiffTable({
|
|
|
14829
14928
|
}
|
|
14830
14929
|
var FIELD_CSS3 = `
|
|
14831
14930
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
14931
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
14932
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
14933
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
14934
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
14935
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
14936
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
14937
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
14938
|
+
whether a footer is already there, so no component has to report it. */
|
|
14939
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
14940
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
14941
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
14832
14942
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
14833
14943
|
.twc-field__req { color: var(--color-danger); }
|
|
14834
14944
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -14961,6 +15071,7 @@ function DatePicker({
|
|
|
14961
15071
|
onChange,
|
|
14962
15072
|
id,
|
|
14963
15073
|
className = "",
|
|
15074
|
+
reserveFooter = false,
|
|
14964
15075
|
...rest
|
|
14965
15076
|
}) {
|
|
14966
15077
|
const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS3);
|
|
@@ -15163,7 +15274,7 @@ function DatePicker({
|
|
|
15163
15274
|
setFocusDate(next);
|
|
15164
15275
|
if (next.getFullYear() !== y || next.getMonth() !== m) setView(new Date(next.getFullYear(), next.getMonth(), 1));
|
|
15165
15276
|
};
|
|
15166
|
-
return /* @__PURE__ */ jsxs("div", { className: `twc-dp twc-field ${className}`, ref: wrapRef, ...rest, children: [
|
|
15277
|
+
return /* @__PURE__ */ jsxs("div", { className: `twc-dp twc-field ${className}`, ref: wrapRef, ...rest, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
15167
15278
|
__twcStyles0,
|
|
15168
15279
|
__twcStyles1,
|
|
15169
15280
|
label ? /* @__PURE__ */ jsxs("label", { className: "twc-field__label", id: `${fieldId}-label`, children: [
|
|
@@ -15387,6 +15498,17 @@ function DatePicker({
|
|
|
15387
15498
|
}
|
|
15388
15499
|
var FIELD_CSS4 = `
|
|
15389
15500
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
15501
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
15502
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
15503
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
15504
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
15505
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
15506
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
15507
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
15508
|
+
whether a footer is already there, so no component has to report it. */
|
|
15509
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
15510
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
15511
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
15390
15512
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
15391
15513
|
.twc-field__req { color: var(--color-danger); }
|
|
15392
15514
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -15522,6 +15644,7 @@ function DateRangePicker({
|
|
|
15522
15644
|
parse,
|
|
15523
15645
|
onChange,
|
|
15524
15646
|
className = "",
|
|
15647
|
+
reserveFooter = false,
|
|
15525
15648
|
...rest
|
|
15526
15649
|
}) {
|
|
15527
15650
|
const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS4);
|
|
@@ -15766,7 +15889,7 @@ function DateRangePicker({
|
|
|
15766
15889
|
return t > Math.min(s, e) && t < Math.max(s, e);
|
|
15767
15890
|
};
|
|
15768
15891
|
const text = range2.start ? format ? format(range2) : `${fmtD(range2.start, locale)} \u2013 ${range2.end ? fmtD(range2.end, locale) : "\u2026"}` : "";
|
|
15769
|
-
return /* @__PURE__ */ jsxs("div", { className: `twc-drp twc-field ${className}`, ref: wrapRef, ...rest, children: [
|
|
15892
|
+
return /* @__PURE__ */ jsxs("div", { className: `twc-drp twc-field ${className}`, ref: wrapRef, ...rest, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
15770
15893
|
__twcStyles0,
|
|
15771
15894
|
__twcStyles1,
|
|
15772
15895
|
label ? /* @__PURE__ */ jsxs("span", { className: "twc-field__label", id: labelId, children: [
|
|
@@ -15994,6 +16117,17 @@ function DateRangePicker({
|
|
|
15994
16117
|
}
|
|
15995
16118
|
var FIELD_CSS5 = `
|
|
15996
16119
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
16120
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
16121
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
16122
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
16123
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
16124
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
16125
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
16126
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
16127
|
+
whether a footer is already there, so no component has to report it. */
|
|
16128
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
16129
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
16130
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
15997
16131
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
15998
16132
|
.twc-field__req { color: var(--color-danger); }
|
|
15999
16133
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -16092,6 +16226,7 @@ function TimePicker({
|
|
|
16092
16226
|
onChange,
|
|
16093
16227
|
id,
|
|
16094
16228
|
className = "",
|
|
16229
|
+
reserveFooter = false,
|
|
16095
16230
|
...rest
|
|
16096
16231
|
}) {
|
|
16097
16232
|
const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS5);
|
|
@@ -16306,7 +16441,7 @@ function TimePicker({
|
|
|
16306
16441
|
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9" }),
|
|
16307
16442
|
/* @__PURE__ */ jsx("path", { d: "M12 7v5l3 2" })
|
|
16308
16443
|
] });
|
|
16309
|
-
return /* @__PURE__ */ jsxs("div", { className: `twc-tp twc-field ${className}`, ref: wrapRef, ...rest, children: [
|
|
16444
|
+
return /* @__PURE__ */ jsxs("div", { className: `twc-tp twc-field ${className}`, ref: wrapRef, ...rest, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
16310
16445
|
__twcStyles0,
|
|
16311
16446
|
__twcStyles1,
|
|
16312
16447
|
label ? /* @__PURE__ */ jsxs("label", { className: "twc-field__label", id: `${fieldId}-label`, children: [
|
|
@@ -16463,6 +16598,17 @@ function TimePicker({
|
|
|
16463
16598
|
}
|
|
16464
16599
|
var FIELD_CSS6 = `
|
|
16465
16600
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
16601
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
16602
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
16603
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
16604
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
16605
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
16606
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
16607
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
16608
|
+
whether a footer is already there, so no component has to report it. */
|
|
16609
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
16610
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
16611
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
16466
16612
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
16467
16613
|
.twc-field__req { color: var(--color-danger); }
|
|
16468
16614
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -16501,6 +16647,7 @@ function DateTimePicker({
|
|
|
16501
16647
|
onChange,
|
|
16502
16648
|
id,
|
|
16503
16649
|
className = "",
|
|
16650
|
+
reserveFooter = false,
|
|
16504
16651
|
...rest
|
|
16505
16652
|
}) {
|
|
16506
16653
|
const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS6);
|
|
@@ -16525,7 +16672,7 @@ function DateTimePicker({
|
|
|
16525
16672
|
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));
|
|
16526
16673
|
};
|
|
16527
16674
|
const childTone = error ? "danger" : tone;
|
|
16528
|
-
return /* @__PURE__ */ jsxs("div", { className: `twc-dtp twc-field ${className}`, id: fieldId, ...rest, children: [
|
|
16675
|
+
return /* @__PURE__ */ jsxs("div", { className: `twc-dtp twc-field ${className}`, id: fieldId, ...rest, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
16529
16676
|
__twcStyles0,
|
|
16530
16677
|
__twcStyles1,
|
|
16531
16678
|
label ? /* @__PURE__ */ jsxs("label", { className: "twc-field__label", id: `${fieldId}-label`, children: [
|
|
@@ -17103,6 +17250,17 @@ function EmptyState({
|
|
|
17103
17250
|
}
|
|
17104
17251
|
var FIELD_CSS7 = `
|
|
17105
17252
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
17253
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
17254
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
17255
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
17256
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
17257
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
17258
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
17259
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
17260
|
+
whether a footer is already there, so no component has to report it. */
|
|
17261
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
17262
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
17263
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
17106
17264
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
17107
17265
|
.twc-field__req { color: var(--color-danger); }
|
|
17108
17266
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -17117,6 +17275,7 @@ function Field({
|
|
|
17117
17275
|
size = "md",
|
|
17118
17276
|
id,
|
|
17119
17277
|
className = "",
|
|
17278
|
+
reserveFooter = false,
|
|
17120
17279
|
children,
|
|
17121
17280
|
...rest
|
|
17122
17281
|
}) {
|
|
@@ -17129,7 +17288,7 @@ function Field({
|
|
|
17129
17288
|
"aria-describedby": [children.props["aria-describedby"], hasMsg ? descId : null].filter(Boolean).join(" ") || void 0,
|
|
17130
17289
|
"aria-invalid": children.props["aria-invalid"] !== void 0 ? children.props["aria-invalid"] : error ? true : void 0
|
|
17131
17290
|
}) : children;
|
|
17132
|
-
return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, "data-size": size, ...rest, children: [
|
|
17291
|
+
return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, "data-size": size, ...rest, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
17133
17292
|
__twcStyles,
|
|
17134
17293
|
label ? /* @__PURE__ */ jsxs("label", { className: "twc-field__label", htmlFor, children: [
|
|
17135
17294
|
label,
|
|
@@ -18185,6 +18344,17 @@ var Radio = /* @__PURE__ */ React55__default.forwardRef(function Radio2({
|
|
|
18185
18344
|
.twc-field__req { color: var(--color-danger); }
|
|
18186
18345
|
.twc-radio__desc { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
18187
18346
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
18347
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
18348
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
18349
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
18350
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
18351
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
18352
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
18353
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
18354
|
+
whether a footer is already there, so no component has to report it. */
|
|
18355
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
18356
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
18357
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
18188
18358
|
.twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
|
|
18189
18359
|
`);
|
|
18190
18360
|
const autoId = React55__default.useId();
|
|
@@ -18234,6 +18404,17 @@ var Radio = /* @__PURE__ */ React55__default.forwardRef(function Radio2({
|
|
|
18234
18404
|
});
|
|
18235
18405
|
var RADIO_GROUP_CSS = `
|
|
18236
18406
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
18407
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
18408
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
18409
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
18410
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
18411
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
18412
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
18413
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
18414
|
+
whether a footer is already there, so no component has to report it. */
|
|
18415
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
18416
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
18417
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
18237
18418
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
18238
18419
|
.twc-field__req { color: var(--color-danger); }
|
|
18239
18420
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -18258,6 +18439,7 @@ function RadioGroup({
|
|
|
18258
18439
|
tone = "primary",
|
|
18259
18440
|
id,
|
|
18260
18441
|
className = "",
|
|
18442
|
+
reserveFooter = false,
|
|
18261
18443
|
...rest
|
|
18262
18444
|
}) {
|
|
18263
18445
|
const __twcStyles = useScopedStyles("twc-radio-group-styles", RADIO_GROUP_CSS);
|
|
@@ -18305,6 +18487,7 @@ function RadioGroup({
|
|
|
18305
18487
|
"div",
|
|
18306
18488
|
{
|
|
18307
18489
|
className: `twc-field ${className}`,
|
|
18490
|
+
"data-reserve-footer": reserveFooter || void 0,
|
|
18308
18491
|
role: "radiogroup",
|
|
18309
18492
|
"aria-labelledby": label ? labelId : void 0,
|
|
18310
18493
|
"aria-describedby": error || hint ? descId : void 0,
|
|
@@ -19329,6 +19512,17 @@ var Switch = /* @__PURE__ */ React55__default.forwardRef(function Switch2({
|
|
|
19329
19512
|
.twc-field__req { color: var(--color-danger); }
|
|
19330
19513
|
.twc-switch__desc { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
19331
19514
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
19515
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
19516
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
19517
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
19518
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
19519
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
19520
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
19521
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
19522
|
+
whether a footer is already there, so no component has to report it. */
|
|
19523
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
19524
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
19525
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
19332
19526
|
.twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
|
|
19333
19527
|
`);
|
|
19334
19528
|
const autoId = React55__default.useId();
|
|
@@ -19768,6 +19962,17 @@ function Tag({
|
|
|
19768
19962
|
var useIso = typeof document !== "undefined" ? React55__default.useLayoutEffect : React55__default.useEffect;
|
|
19769
19963
|
var TEXTAREA_CSS = `
|
|
19770
19964
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
19965
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
19966
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
19967
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
19968
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
19969
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
19970
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
19971
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
19972
|
+
whether a footer is already there, so no component has to report it. */
|
|
19973
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
19974
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
19975
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
19771
19976
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
19772
19977
|
.twc-field__req { color: var(--color-danger); }
|
|
19773
19978
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -19830,6 +20035,7 @@ var Textarea = /* @__PURE__ */ React55__default.forwardRef(function Textarea2({
|
|
|
19830
20035
|
disabled = false,
|
|
19831
20036
|
id,
|
|
19832
20037
|
className = "",
|
|
20038
|
+
reserveFooter = false,
|
|
19833
20039
|
...rest
|
|
19834
20040
|
}, ref) {
|
|
19835
20041
|
const __twcStyles = useScopedStyles("twc-textarea-styles", TEXTAREA_CSS);
|
|
@@ -19887,7 +20093,7 @@ var Textarea = /* @__PURE__ */ React55__default.forwardRef(function Textarea2({
|
|
|
19887
20093
|
const showClear = clearable && count > 0 && !disabled && !rest.readOnly;
|
|
19888
20094
|
const ariaInvalid = invalid ? true : rest["aria-invalid"] ?? void 0;
|
|
19889
20095
|
const describedBy = [error || hint ? descId : null, showCounter ? countId : null, rest["aria-describedby"]].filter(Boolean).join(" ") || void 0;
|
|
19890
|
-
return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, children: [
|
|
20096
|
+
return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
19891
20097
|
__twcStyles,
|
|
19892
20098
|
label ? /* @__PURE__ */ jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
|
|
19893
20099
|
label,
|