twico-ui 1.45.1 → 1.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/index.cjs +220 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +432 -0
- package/dist/index.d.ts +432 -0
- package/dist/index.mjs +220 -14
- package/dist/index.mjs.map +1 -1
- package/llms.txt +16 -1
- package/package.json +1 -1
package/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: [
|
|
@@ -9537,6 +9621,12 @@ th.twc-dt__rownum .twc-dt__th-inner { padding-inline: 8px; gap: 2px; justify-con
|
|
|
9537
9621
|
.twc-dt__batch-x:hover { background: color-mix(in srgb, var(--color-primary) 20%, transparent); }
|
|
9538
9622
|
.twc-dt__batch-x svg { width: 16px; height: 16px; }
|
|
9539
9623
|
.twc-dt__batch-count { font-size: var(--text-sm); font-weight: var(--font-bold); color: var(--color-primary-subtle-fg); white-space: nowrap; }
|
|
9624
|
+
/* #492: the host's running total, sitting with the count it qualifies. Same colour as the count,
|
|
9625
|
+
differentiated by WEIGHT rather than by a lighter token: this bar is --color-primary-subtle, and
|
|
9626
|
+
--color-text-subtle on that is 4.26:1, under the 4.5:1 needed for normal text. */
|
|
9627
|
+
.twc-dt__batch-summary { display: inline-flex; align-items: center; gap: 6px; font-size: var(--text-sm);
|
|
9628
|
+
color: var(--color-primary-subtle-fg); white-space: nowrap; }
|
|
9629
|
+
.twc-dt__batch-summary::before { content: ""; width: var(--border-thin); height: 14px; background: currentColor; opacity: 0.35; }
|
|
9540
9630
|
.twc-dt__batch-actions { margin-inline-start: auto; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
|
|
9541
9631
|
.twc-dt__batch-btn { display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 12px;
|
|
9542
9632
|
border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-md); background: var(--color-surface);
|
|
@@ -10037,6 +10127,7 @@ function Datatable({
|
|
|
10037
10127
|
onColumnVisibilityChange,
|
|
10038
10128
|
batchActions = [],
|
|
10039
10129
|
onRowSelectionChange,
|
|
10130
|
+
selectionSummary,
|
|
10040
10131
|
getRowStyle,
|
|
10041
10132
|
getRowClassName,
|
|
10042
10133
|
showExport = false,
|
|
@@ -12906,13 +12997,13 @@ function Datatable({
|
|
|
12906
12997
|
return /* @__PURE__ */ jsxs("div", { className: `twc-dt ${className}`, ref: rootRef, "data-density": density, "data-resizing": resizing || void 0, "data-editing": editing ? "" : void 0, ...rest, children: [
|
|
12907
12998
|
__twcStyles,
|
|
12908
12999
|
/* @__PURE__ */ jsxs("div", { className: "twc-dt__toolbar", "data-compact": compact || void 0, children: [
|
|
12909
|
-
toolbarActions != null ? /* @__PURE__ */ jsx("div", { className: "twc-dt__toolbar-actions", children: toolbarActions }) : null,
|
|
12910
13000
|
(batchActions.length || hasBatchEditor) && selected.size > 0 ? /* @__PURE__ */ jsxs("div", { className: "twc-dt__batch", children: [
|
|
12911
13001
|
/* @__PURE__ */ jsx("button", { type: "button", className: "twc-dt__batch-x", onClick: clearSelection, "aria-label": "Clear selection", children: /* @__PURE__ */ jsx(Svg, { d: I.x }) }),
|
|
12912
13002
|
/* @__PURE__ */ jsxs("span", { className: "twc-dt__batch-count", children: [
|
|
12913
13003
|
selected.size,
|
|
12914
13004
|
" selected"
|
|
12915
13005
|
] }),
|
|
13006
|
+
selectionSummary != null ? /* @__PURE__ */ jsx("span", { className: "twc-dt__batch-summary", children: typeof selectionSummary === "function" ? selectionSummary(selKeys, selectedRows) : selectionSummary }) : null,
|
|
12916
13007
|
/* @__PURE__ */ jsxs("div", { className: "twc-dt__batch-actions", children: [
|
|
12917
13008
|
hasBatchEditor ? /* @__PURE__ */ jsxs("button", { type: "button", className: "twc-dt__batch-btn", onClick: (e) => openBatchEditor(e.currentTarget), children: [
|
|
12918
13009
|
/* @__PURE__ */ jsx(Svg, { d: I.pencil }),
|
|
@@ -13107,6 +13198,7 @@ function Datatable({
|
|
|
13107
13198
|
}
|
|
13108
13199
|
)
|
|
13109
13200
|
] }) : null,
|
|
13201
|
+
toolbarActions != null ? /* @__PURE__ */ jsx("div", { className: "twc-dt__toolbar-actions", children: toolbarActions }) : null,
|
|
13110
13202
|
searchable ? /* @__PURE__ */ jsxs("div", { className: "twc-dt__search", children: [
|
|
13111
13203
|
/* @__PURE__ */ jsx(Svg, { d: I.search }),
|
|
13112
13204
|
/* @__PURE__ */ jsx("input", { ref: quickSearchRef, placeholder: "Search\u2026", "aria-label": "Search rows", value: quick, onChange: (e) => commitQuick(e.target.value) }),
|
|
@@ -14395,6 +14487,11 @@ var CARDGRID_CSS = `
|
|
|
14395
14487
|
padding: var(--space-2) var(--space-3); border: var(--border-thin) solid var(--color-primary-border);
|
|
14396
14488
|
background: var(--color-primary-subtle); border-radius: var(--radius-md); }
|
|
14397
14489
|
.twc-cardgrid__batch-count { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-primary-subtle-fg); }
|
|
14490
|
+
/* #492: same slot, same contract as Datatable. Weight, not a lighter token - this bar is
|
|
14491
|
+
--color-primary-subtle, where --color-text-subtle is 4.26:1 and fails normal-text contrast. */
|
|
14492
|
+
.twc-cardgrid__batch-summary { display: inline-flex; align-items: center; gap: var(--space-2);
|
|
14493
|
+
font-size: var(--text-sm); color: var(--color-primary-subtle-fg); }
|
|
14494
|
+
.twc-cardgrid__batch-summary::before { content: ""; width: var(--border-thin); height: 14px; background: currentColor; opacity: 0.35; }
|
|
14398
14495
|
.twc-cardgrid__batch-actions { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin-inline-start: auto; }
|
|
14399
14496
|
.twc-cardgrid__batch-btn { display: inline-flex; align-items: center; gap: var(--space-2); height: var(--control-h-sm);
|
|
14400
14497
|
padding-inline: var(--space-3); border: var(--border-thin) solid var(--color-border); background: var(--color-surface);
|
|
@@ -14450,6 +14547,7 @@ function CardGrid({
|
|
|
14450
14547
|
// the whole point, and it was the only Datatable affordance CardGrid lacked.
|
|
14451
14548
|
checkboxSelection = false,
|
|
14452
14549
|
batchActions = [],
|
|
14550
|
+
selectionSummary,
|
|
14453
14551
|
onRowSelectionChange,
|
|
14454
14552
|
selectionPlacement = "top-start",
|
|
14455
14553
|
selectionLabel,
|
|
@@ -14657,6 +14755,7 @@ function CardGrid({
|
|
|
14657
14755
|
selected.size,
|
|
14658
14756
|
" selected"
|
|
14659
14757
|
] }),
|
|
14758
|
+
selectionSummary != null ? /* @__PURE__ */ jsx("span", { className: "twc-cardgrid__batch-summary", children: typeof selectionSummary === "function" ? selectionSummary(selKeys, selectedRows) : selectionSummary }) : null,
|
|
14660
14759
|
showSelectAll && pageRows.length > 0 ? /* @__PURE__ */ jsxs("label", { className: "twc-cardgrid__selall", children: [
|
|
14661
14760
|
/* @__PURE__ */ jsx(
|
|
14662
14761
|
Checkbox,
|
|
@@ -14814,6 +14913,17 @@ function DiffTable({
|
|
|
14814
14913
|
}
|
|
14815
14914
|
var FIELD_CSS3 = `
|
|
14816
14915
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
14916
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
14917
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
14918
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
14919
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
14920
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
14921
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
14922
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
14923
|
+
whether a footer is already there, so no component has to report it. */
|
|
14924
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
14925
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
14926
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
14817
14927
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
14818
14928
|
.twc-field__req { color: var(--color-danger); }
|
|
14819
14929
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -14946,6 +15056,7 @@ function DatePicker({
|
|
|
14946
15056
|
onChange,
|
|
14947
15057
|
id,
|
|
14948
15058
|
className = "",
|
|
15059
|
+
reserveFooter = false,
|
|
14949
15060
|
...rest
|
|
14950
15061
|
}) {
|
|
14951
15062
|
const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS3);
|
|
@@ -15148,7 +15259,7 @@ function DatePicker({
|
|
|
15148
15259
|
setFocusDate(next);
|
|
15149
15260
|
if (next.getFullYear() !== y || next.getMonth() !== m) setView(new Date(next.getFullYear(), next.getMonth(), 1));
|
|
15150
15261
|
};
|
|
15151
|
-
return /* @__PURE__ */ jsxs("div", { className: `twc-dp twc-field ${className}`, ref: wrapRef, ...rest, children: [
|
|
15262
|
+
return /* @__PURE__ */ jsxs("div", { className: `twc-dp twc-field ${className}`, ref: wrapRef, ...rest, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
15152
15263
|
__twcStyles0,
|
|
15153
15264
|
__twcStyles1,
|
|
15154
15265
|
label ? /* @__PURE__ */ jsxs("label", { className: "twc-field__label", id: `${fieldId}-label`, children: [
|
|
@@ -15372,6 +15483,17 @@ function DatePicker({
|
|
|
15372
15483
|
}
|
|
15373
15484
|
var FIELD_CSS4 = `
|
|
15374
15485
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
15486
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
15487
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
15488
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
15489
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
15490
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
15491
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
15492
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
15493
|
+
whether a footer is already there, so no component has to report it. */
|
|
15494
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
15495
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
15496
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
15375
15497
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
15376
15498
|
.twc-field__req { color: var(--color-danger); }
|
|
15377
15499
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -15507,6 +15629,7 @@ function DateRangePicker({
|
|
|
15507
15629
|
parse,
|
|
15508
15630
|
onChange,
|
|
15509
15631
|
className = "",
|
|
15632
|
+
reserveFooter = false,
|
|
15510
15633
|
...rest
|
|
15511
15634
|
}) {
|
|
15512
15635
|
const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS4);
|
|
@@ -15751,7 +15874,7 @@ function DateRangePicker({
|
|
|
15751
15874
|
return t > Math.min(s, e) && t < Math.max(s, e);
|
|
15752
15875
|
};
|
|
15753
15876
|
const text = range2.start ? format ? format(range2) : `${fmtD(range2.start, locale)} \u2013 ${range2.end ? fmtD(range2.end, locale) : "\u2026"}` : "";
|
|
15754
|
-
return /* @__PURE__ */ jsxs("div", { className: `twc-drp twc-field ${className}`, ref: wrapRef, ...rest, children: [
|
|
15877
|
+
return /* @__PURE__ */ jsxs("div", { className: `twc-drp twc-field ${className}`, ref: wrapRef, ...rest, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
15755
15878
|
__twcStyles0,
|
|
15756
15879
|
__twcStyles1,
|
|
15757
15880
|
label ? /* @__PURE__ */ jsxs("span", { className: "twc-field__label", id: labelId, children: [
|
|
@@ -15979,6 +16102,17 @@ function DateRangePicker({
|
|
|
15979
16102
|
}
|
|
15980
16103
|
var FIELD_CSS5 = `
|
|
15981
16104
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
16105
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
16106
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
16107
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
16108
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
16109
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
16110
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
16111
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
16112
|
+
whether a footer is already there, so no component has to report it. */
|
|
16113
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
16114
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
16115
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
15982
16116
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
15983
16117
|
.twc-field__req { color: var(--color-danger); }
|
|
15984
16118
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -16077,6 +16211,7 @@ function TimePicker({
|
|
|
16077
16211
|
onChange,
|
|
16078
16212
|
id,
|
|
16079
16213
|
className = "",
|
|
16214
|
+
reserveFooter = false,
|
|
16080
16215
|
...rest
|
|
16081
16216
|
}) {
|
|
16082
16217
|
const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS5);
|
|
@@ -16291,7 +16426,7 @@ function TimePicker({
|
|
|
16291
16426
|
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9" }),
|
|
16292
16427
|
/* @__PURE__ */ jsx("path", { d: "M12 7v5l3 2" })
|
|
16293
16428
|
] });
|
|
16294
|
-
return /* @__PURE__ */ jsxs("div", { className: `twc-tp twc-field ${className}`, ref: wrapRef, ...rest, children: [
|
|
16429
|
+
return /* @__PURE__ */ jsxs("div", { className: `twc-tp twc-field ${className}`, ref: wrapRef, ...rest, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
16295
16430
|
__twcStyles0,
|
|
16296
16431
|
__twcStyles1,
|
|
16297
16432
|
label ? /* @__PURE__ */ jsxs("label", { className: "twc-field__label", id: `${fieldId}-label`, children: [
|
|
@@ -16448,6 +16583,17 @@ function TimePicker({
|
|
|
16448
16583
|
}
|
|
16449
16584
|
var FIELD_CSS6 = `
|
|
16450
16585
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
16586
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
16587
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
16588
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
16589
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
16590
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
16591
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
16592
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
16593
|
+
whether a footer is already there, so no component has to report it. */
|
|
16594
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
16595
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
16596
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
16451
16597
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
16452
16598
|
.twc-field__req { color: var(--color-danger); }
|
|
16453
16599
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -16486,6 +16632,7 @@ function DateTimePicker({
|
|
|
16486
16632
|
onChange,
|
|
16487
16633
|
id,
|
|
16488
16634
|
className = "",
|
|
16635
|
+
reserveFooter = false,
|
|
16489
16636
|
...rest
|
|
16490
16637
|
}) {
|
|
16491
16638
|
const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS6);
|
|
@@ -16510,7 +16657,7 @@ function DateTimePicker({
|
|
|
16510
16657
|
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));
|
|
16511
16658
|
};
|
|
16512
16659
|
const childTone = error ? "danger" : tone;
|
|
16513
|
-
return /* @__PURE__ */ jsxs("div", { className: `twc-dtp twc-field ${className}`, id: fieldId, ...rest, children: [
|
|
16660
|
+
return /* @__PURE__ */ jsxs("div", { className: `twc-dtp twc-field ${className}`, id: fieldId, ...rest, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
16514
16661
|
__twcStyles0,
|
|
16515
16662
|
__twcStyles1,
|
|
16516
16663
|
label ? /* @__PURE__ */ jsxs("label", { className: "twc-field__label", id: `${fieldId}-label`, children: [
|
|
@@ -17088,6 +17235,17 @@ function EmptyState({
|
|
|
17088
17235
|
}
|
|
17089
17236
|
var FIELD_CSS7 = `
|
|
17090
17237
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
17238
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
17239
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
17240
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
17241
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
17242
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
17243
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
17244
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
17245
|
+
whether a footer is already there, so no component has to report it. */
|
|
17246
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
17247
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
17248
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
17091
17249
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
17092
17250
|
.twc-field__req { color: var(--color-danger); }
|
|
17093
17251
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -17102,6 +17260,7 @@ function Field({
|
|
|
17102
17260
|
size = "md",
|
|
17103
17261
|
id,
|
|
17104
17262
|
className = "",
|
|
17263
|
+
reserveFooter = false,
|
|
17105
17264
|
children,
|
|
17106
17265
|
...rest
|
|
17107
17266
|
}) {
|
|
@@ -17114,7 +17273,7 @@ function Field({
|
|
|
17114
17273
|
"aria-describedby": [children.props["aria-describedby"], hasMsg ? descId : null].filter(Boolean).join(" ") || void 0,
|
|
17115
17274
|
"aria-invalid": children.props["aria-invalid"] !== void 0 ? children.props["aria-invalid"] : error ? true : void 0
|
|
17116
17275
|
}) : children;
|
|
17117
|
-
return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, "data-size": size, ...rest, children: [
|
|
17276
|
+
return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, "data-size": size, ...rest, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
17118
17277
|
__twcStyles,
|
|
17119
17278
|
label ? /* @__PURE__ */ jsxs("label", { className: "twc-field__label", htmlFor, children: [
|
|
17120
17279
|
label,
|
|
@@ -18170,6 +18329,17 @@ var Radio = /* @__PURE__ */ React55__default.forwardRef(function Radio2({
|
|
|
18170
18329
|
.twc-field__req { color: var(--color-danger); }
|
|
18171
18330
|
.twc-radio__desc { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
18172
18331
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
18332
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
18333
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
18334
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
18335
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
18336
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
18337
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
18338
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
18339
|
+
whether a footer is already there, so no component has to report it. */
|
|
18340
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
18341
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
18342
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
18173
18343
|
.twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
|
|
18174
18344
|
`);
|
|
18175
18345
|
const autoId = React55__default.useId();
|
|
@@ -18219,6 +18389,17 @@ var Radio = /* @__PURE__ */ React55__default.forwardRef(function Radio2({
|
|
|
18219
18389
|
});
|
|
18220
18390
|
var RADIO_GROUP_CSS = `
|
|
18221
18391
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
18392
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
18393
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
18394
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
18395
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
18396
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
18397
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
18398
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
18399
|
+
whether a footer is already there, so no component has to report it. */
|
|
18400
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
18401
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
18402
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
18222
18403
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
18223
18404
|
.twc-field__req { color: var(--color-danger); }
|
|
18224
18405
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -18243,6 +18424,7 @@ function RadioGroup({
|
|
|
18243
18424
|
tone = "primary",
|
|
18244
18425
|
id,
|
|
18245
18426
|
className = "",
|
|
18427
|
+
reserveFooter = false,
|
|
18246
18428
|
...rest
|
|
18247
18429
|
}) {
|
|
18248
18430
|
const __twcStyles = useScopedStyles("twc-radio-group-styles", RADIO_GROUP_CSS);
|
|
@@ -18290,6 +18472,7 @@ function RadioGroup({
|
|
|
18290
18472
|
"div",
|
|
18291
18473
|
{
|
|
18292
18474
|
className: `twc-field ${className}`,
|
|
18475
|
+
"data-reserve-footer": reserveFooter || void 0,
|
|
18293
18476
|
role: "radiogroup",
|
|
18294
18477
|
"aria-labelledby": label ? labelId : void 0,
|
|
18295
18478
|
"aria-describedby": error || hint ? descId : void 0,
|
|
@@ -19314,6 +19497,17 @@ var Switch = /* @__PURE__ */ React55__default.forwardRef(function Switch2({
|
|
|
19314
19497
|
.twc-field__req { color: var(--color-danger); }
|
|
19315
19498
|
.twc-switch__desc { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
19316
19499
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
19500
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
19501
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
19502
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
19503
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
19504
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
19505
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
19506
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
19507
|
+
whether a footer is already there, so no component has to report it. */
|
|
19508
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
19509
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
19510
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
19317
19511
|
.twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
|
|
19318
19512
|
`);
|
|
19319
19513
|
const autoId = React55__default.useId();
|
|
@@ -19753,6 +19947,17 @@ function Tag({
|
|
|
19753
19947
|
var useIso = typeof document !== "undefined" ? React55__default.useLayoutEffect : React55__default.useEffect;
|
|
19754
19948
|
var TEXTAREA_CSS = `
|
|
19755
19949
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
19950
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
19951
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
19952
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
19953
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
19954
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
19955
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
19956
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
19957
|
+
whether a footer is already there, so no component has to report it. */
|
|
19958
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
19959
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
19960
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
19756
19961
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
19757
19962
|
.twc-field__req { color: var(--color-danger); }
|
|
19758
19963
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -19815,6 +20020,7 @@ var Textarea = /* @__PURE__ */ React55__default.forwardRef(function Textarea2({
|
|
|
19815
20020
|
disabled = false,
|
|
19816
20021
|
id,
|
|
19817
20022
|
className = "",
|
|
20023
|
+
reserveFooter = false,
|
|
19818
20024
|
...rest
|
|
19819
20025
|
}, ref) {
|
|
19820
20026
|
const __twcStyles = useScopedStyles("twc-textarea-styles", TEXTAREA_CSS);
|
|
@@ -19872,7 +20078,7 @@ var Textarea = /* @__PURE__ */ React55__default.forwardRef(function Textarea2({
|
|
|
19872
20078
|
const showClear = clearable && count > 0 && !disabled && !rest.readOnly;
|
|
19873
20079
|
const ariaInvalid = invalid ? true : rest["aria-invalid"] ?? void 0;
|
|
19874
20080
|
const describedBy = [error || hint ? descId : null, showCounter ? countId : null, rest["aria-describedby"]].filter(Boolean).join(" ") || void 0;
|
|
19875
|
-
return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, children: [
|
|
20081
|
+
return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
19876
20082
|
__twcStyles,
|
|
19877
20083
|
label ? /* @__PURE__ */ jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
|
|
19878
20084
|
label,
|