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/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to Twico UI are documented here (newest first). This file is generated automatically on each release by [semantic-release](https://semantic-release.gitbook.io/) from [Conventional Commits](https://www.conventionalcommits.org/) — do not edit it by hand. The same notes appear on each [GitHub Release](https://github.com/EncryptionL/twico-ui/releases).
|
|
4
4
|
|
|
5
|
+
# [1.48.0](https://github.com/EncryptionL/twico-ui/compare/v1.47.0...v1.48.0) (2026-10-23)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **datatable:** vertical alignment at three levels — table, row, column ([221978b](https://github.com/EncryptionL/twico-ui/commit/221978b1db3527d97b93335a9b36b9274d2c8b10))
|
|
11
|
+
|
|
12
|
+
# [1.47.0](https://github.com/EncryptionL/twico-ui/compare/v1.46.0...v1.47.0) (2026-10-23)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* **inputs:** reserveFooter — keep a field's footer lane open so rows stay aligned ([223ef88](https://github.com/EncryptionL/twico-ui/commit/223ef8841b41b8fc10e6224530c392ceda2b9a98)), closes [#471](https://github.com/EncryptionL/twico-ui/issues/471)
|
|
18
|
+
|
|
5
19
|
# [1.46.0](https://github.com/EncryptionL/twico-ui/compare/v1.45.1...v1.46.0) (2026-10-21)
|
|
6
20
|
|
|
7
21
|
|
package/dist/icons.d.cts
CHANGED
|
@@ -56,4 +56,5 @@ declare const brandIcons: BrandIconMeta[];
|
|
|
56
56
|
/** Just the exported component names of every brand icon. */
|
|
57
57
|
declare const brandIconNames: string[];
|
|
58
58
|
|
|
59
|
-
export { AppleBrandIcon, BlueskyIcon,
|
|
59
|
+
export { AppleBrandIcon, BlueskyIcon, DiscordIcon, DockerIcon, DribbbleIcon, FacebookIcon, FigmaIcon, GithubIcon, GitlabIcon, GoogleIcon, InstagramIcon, MastodonIcon, MediumIcon, NextjsIcon, NodejsIcon, NpmIcon, PinterestIcon, ReactjsIcon, RedditIcon, SpotifyIcon, StackOverflowIcon, TailwindIcon, TelegramIcon, ThreadsIcon, TikTokIcon, TwitchIcon, TypeScriptIcon, VercelIcon, WhatsAppIcon, XTwitterIcon, YoutubeIcon, brandIconNames, brandIcons };
|
|
60
|
+
export type { BrandIcon, BrandIconMeta, BrandIconProps };
|
package/dist/icons.d.ts
CHANGED
|
@@ -56,4 +56,5 @@ declare const brandIcons: BrandIconMeta[];
|
|
|
56
56
|
/** Just the exported component names of every brand icon. */
|
|
57
57
|
declare const brandIconNames: string[];
|
|
58
58
|
|
|
59
|
-
export { AppleBrandIcon, BlueskyIcon,
|
|
59
|
+
export { AppleBrandIcon, BlueskyIcon, DiscordIcon, DockerIcon, DribbbleIcon, FacebookIcon, FigmaIcon, GithubIcon, GitlabIcon, GoogleIcon, InstagramIcon, MastodonIcon, MediumIcon, NextjsIcon, NodejsIcon, NpmIcon, PinterestIcon, ReactjsIcon, RedditIcon, SpotifyIcon, StackOverflowIcon, TailwindIcon, TelegramIcon, ThreadsIcon, TikTokIcon, TwitchIcon, TypeScriptIcon, VercelIcon, WhatsAppIcon, XTwitterIcon, YoutubeIcon, brandIconNames, brandIcons };
|
|
60
|
+
export type { BrandIcon, BrandIconMeta, BrandIconProps };
|
package/dist/index.cjs
CHANGED
|
@@ -5901,6 +5901,17 @@ var Checkbox = /* @__PURE__ */ React55__namespace.default.forwardRef(function Ch
|
|
|
5901
5901
|
.twc-field__req { color: var(--color-danger); }
|
|
5902
5902
|
.twc-check__desc { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
5903
5903
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
5904
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
5905
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
5906
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
5907
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
5908
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
5909
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
5910
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
5911
|
+
whether a footer is already there, so no component has to report it. */
|
|
5912
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
5913
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
5914
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
5904
5915
|
.twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
|
|
5905
5916
|
`);
|
|
5906
5917
|
const autoId = React55__namespace.default.useId();
|
|
@@ -5955,6 +5966,17 @@ var Checkbox = /* @__PURE__ */ React55__namespace.default.forwardRef(function Ch
|
|
|
5955
5966
|
});
|
|
5956
5967
|
var FIELD_CSS = `
|
|
5957
5968
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
5969
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
5970
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
5971
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
5972
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
5973
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
5974
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
5975
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
5976
|
+
whether a footer is already there, so no component has to report it. */
|
|
5977
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
5978
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
5979
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
5958
5980
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
5959
5981
|
.twc-field__req { color: var(--color-danger); }
|
|
5960
5982
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -6066,6 +6088,7 @@ function ColorPicker({
|
|
|
6066
6088
|
tone = "primary",
|
|
6067
6089
|
onChange,
|
|
6068
6090
|
className = "",
|
|
6091
|
+
reserveFooter = false,
|
|
6069
6092
|
...rest
|
|
6070
6093
|
}) {
|
|
6071
6094
|
const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS);
|
|
@@ -6205,7 +6228,7 @@ function ColorPicker({
|
|
|
6205
6228
|
e.preventDefault();
|
|
6206
6229
|
commit({ ...hsv, a: Math.round(a * 100) / 100 });
|
|
6207
6230
|
};
|
|
6208
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-cp twc-field ${className}`, ref: wrapRef, ...rest, children: [
|
|
6231
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-cp twc-field ${className}`, ref: wrapRef, ...rest, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
6209
6232
|
__twcStyles0,
|
|
6210
6233
|
__twcStyles1,
|
|
6211
6234
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-field__label", id: labelId, children: [
|
|
@@ -6368,6 +6391,17 @@ function ColorPicker({
|
|
|
6368
6391
|
}
|
|
6369
6392
|
var COMBO_CSS = `
|
|
6370
6393
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
6394
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
6395
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
6396
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
6397
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
6398
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
6399
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
6400
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
6401
|
+
whether a footer is already there, so no component has to report it. */
|
|
6402
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
6403
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
6404
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
6371
6405
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
6372
6406
|
.twc-field__req { color: var(--color-danger); }
|
|
6373
6407
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -6506,6 +6540,7 @@ var Combobox = /* @__PURE__ */ React55__namespace.default.forwardRef(function Co
|
|
|
6506
6540
|
className = "",
|
|
6507
6541
|
onFocus,
|
|
6508
6542
|
onKeyDown,
|
|
6543
|
+
reserveFooter = false,
|
|
6509
6544
|
...rest
|
|
6510
6545
|
}, ref) {
|
|
6511
6546
|
const __twcStyles = useScopedStyles("twc-combo-styles", COMBO_CSS);
|
|
@@ -6793,7 +6828,7 @@ var Combobox = /* @__PURE__ */ React55__namespace.default.forwardRef(function Co
|
|
|
6793
6828
|
popEl = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop", ref: popRef, "data-placement": placement === "top" ? "top" : void 0, children: popInner });
|
|
6794
6829
|
}
|
|
6795
6830
|
}
|
|
6796
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, ref: wrapRef, children: [
|
|
6831
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, ref: wrapRef, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
6797
6832
|
__twcStyles,
|
|
6798
6833
|
name ? /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: current ?? "" }) : null,
|
|
6799
6834
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
|
|
@@ -7113,6 +7148,17 @@ function clampPrecision(str, precision) {
|
|
|
7113
7148
|
}
|
|
7114
7149
|
var CUR_CSS = `
|
|
7115
7150
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
7151
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
7152
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
7153
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
7154
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
7155
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
7156
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
7157
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
7158
|
+
whether a footer is already there, so no component has to report it. */
|
|
7159
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
7160
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
7161
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
7116
7162
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
7117
7163
|
.twc-field__req { color: var(--color-danger); }
|
|
7118
7164
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -7182,6 +7228,7 @@ function Currency({
|
|
|
7182
7228
|
id,
|
|
7183
7229
|
placeholder = "0.00",
|
|
7184
7230
|
className = "",
|
|
7231
|
+
reserveFooter = false,
|
|
7185
7232
|
...rest
|
|
7186
7233
|
}) {
|
|
7187
7234
|
const __twcStyles = useScopedStyles("twc-cur-styles", CUR_CSS);
|
|
@@ -7230,7 +7277,7 @@ function Currency({
|
|
|
7230
7277
|
if (!controlled) setInternal(fixed);
|
|
7231
7278
|
onValueChange?.(Number(fixed), fixed);
|
|
7232
7279
|
}
|
|
7233
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, children: [
|
|
7280
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
7234
7281
|
__twcStyles,
|
|
7235
7282
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
|
|
7236
7283
|
label,
|
|
@@ -7282,6 +7329,17 @@ function useFallbackName(ref, hasOwnName) {
|
|
|
7282
7329
|
}
|
|
7283
7330
|
var SELECT_CSS = `
|
|
7284
7331
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
7332
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
7333
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
7334
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
7335
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
7336
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
7337
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
7338
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
7339
|
+
whether a footer is already there, so no component has to report it. */
|
|
7340
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
7341
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
7342
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
7285
7343
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
7286
7344
|
.twc-field__req { color: var(--color-danger); }
|
|
7287
7345
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -7418,6 +7476,7 @@ var Select = /* @__PURE__ */ React55__namespace.default.forwardRef(function Sele
|
|
|
7418
7476
|
renderOption,
|
|
7419
7477
|
id,
|
|
7420
7478
|
className = "",
|
|
7479
|
+
reserveFooter = false,
|
|
7421
7480
|
onClick,
|
|
7422
7481
|
onKeyDown,
|
|
7423
7482
|
...rest
|
|
@@ -7763,7 +7822,7 @@ var Select = /* @__PURE__ */ React55__namespace.default.forwardRef(function Sele
|
|
|
7763
7822
|
popEl = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop", ref: popRef, "data-state": popState, "data-placement": placement === "top" ? "top" : "bottom", children: popInner });
|
|
7764
7823
|
}
|
|
7765
7824
|
}
|
|
7766
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, ref: wrapRef, children: [
|
|
7825
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, ref: wrapRef, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
7767
7826
|
__twcStyles,
|
|
7768
7827
|
name ? /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: current ?? "" }) : null,
|
|
7769
7828
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
|
|
@@ -7888,6 +7947,7 @@ function CurrencyField({
|
|
|
7888
7947
|
id,
|
|
7889
7948
|
placeholder = "0.00",
|
|
7890
7949
|
className = "",
|
|
7950
|
+
reserveFooter = false,
|
|
7891
7951
|
...rest
|
|
7892
7952
|
}) {
|
|
7893
7953
|
const __twcStyles = useScopedStyles("twc-curf-styles", CURF_CSS);
|
|
@@ -7950,7 +8010,7 @@ function CurrencyField({
|
|
|
7950
8010
|
onValueChange?.(Number(fixed), fixed, cur);
|
|
7951
8011
|
}
|
|
7952
8012
|
const curLabel = typeof label === "string" && label ? `${label} currency` : "Currency";
|
|
7953
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, children: [
|
|
8013
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
7954
8014
|
__twcStyles,
|
|
7955
8015
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
|
|
7956
8016
|
label,
|
|
@@ -7997,6 +8057,17 @@ function CurrencyField({
|
|
|
7997
8057
|
}
|
|
7998
8058
|
var FIELD_CSS2 = `
|
|
7999
8059
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
8060
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
8061
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
8062
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
8063
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
8064
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
8065
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
8066
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
8067
|
+
whether a footer is already there, so no component has to report it. */
|
|
8068
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
8069
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
8070
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
8000
8071
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
8001
8072
|
.twc-field__req { color: var(--color-danger); }
|
|
8002
8073
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -8086,6 +8157,7 @@ var Input = /* @__PURE__ */ React55__namespace.default.forwardRef(function Input
|
|
|
8086
8157
|
disabled = false,
|
|
8087
8158
|
id,
|
|
8088
8159
|
className = "",
|
|
8160
|
+
reserveFooter = false,
|
|
8089
8161
|
...rest
|
|
8090
8162
|
}, ref) {
|
|
8091
8163
|
const __twcStyles = useScopedStyles("twc-field-styles", FIELD_CSS2);
|
|
@@ -8155,7 +8227,7 @@ var Input = /* @__PURE__ */ React55__namespace.default.forwardRef(function Input
|
|
|
8155
8227
|
] })
|
|
8156
8228
|
}
|
|
8157
8229
|
) : null;
|
|
8158
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, children: [
|
|
8230
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
8159
8231
|
__twcStyles,
|
|
8160
8232
|
__twcExtra,
|
|
8161
8233
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
|
|
@@ -8200,6 +8272,17 @@ var Input = /* @__PURE__ */ React55__namespace.default.forwardRef(function Input
|
|
|
8200
8272
|
});
|
|
8201
8273
|
var MULTI_CSS = `
|
|
8202
8274
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
8275
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
8276
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
8277
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
8278
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
8279
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
8280
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
8281
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
8282
|
+
whether a footer is already there, so no component has to report it. */
|
|
8283
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
8284
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
8285
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
8203
8286
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
8204
8287
|
.twc-field__req { color: var(--color-danger); }
|
|
8205
8288
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -8345,6 +8428,7 @@ var MultiSelect = /* @__PURE__ */ React55__namespace.default.forwardRef(function
|
|
|
8345
8428
|
className = "",
|
|
8346
8429
|
onFocus,
|
|
8347
8430
|
onKeyDown,
|
|
8431
|
+
reserveFooter = false,
|
|
8348
8432
|
...rest
|
|
8349
8433
|
}, ref) {
|
|
8350
8434
|
const __twcStyles = useScopedStyles("twc-ms-styles", MULTI_CSS);
|
|
@@ -8618,7 +8702,7 @@ var MultiSelect = /* @__PURE__ */ React55__namespace.default.forwardRef(function
|
|
|
8618
8702
|
}
|
|
8619
8703
|
}
|
|
8620
8704
|
const msFallbackName = useFallbackName(inputRef, Boolean(label || rest["aria-label"] || rest["aria-labelledby"])) ? placeholder : void 0;
|
|
8621
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, ref: wrapRef, children: [
|
|
8705
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, ref: wrapRef, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
8622
8706
|
__twcStyles,
|
|
8623
8707
|
name ? selected.map((v, i) => /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: v }, i)) : null,
|
|
8624
8708
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
|
|
@@ -9294,9 +9378,20 @@ var DT_CSS = `
|
|
|
9294
9378
|
font-size: var(--text-sm); color: var(--color-text); background: var(--twc-dt-row-bg, var(--color-surface));
|
|
9295
9379
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 0; }
|
|
9296
9380
|
/* Wrap-text columns: content flows onto multiple lines (the row grows down) instead of
|
|
9297
|
-
clipping to one line. height stays a per-row minimum, so single-line cells are unchanged.
|
|
9381
|
+
clipping to one line. height stays a per-row minimum, so single-line cells are unchanged.
|
|
9382
|
+
The top alignment here is only a DEFAULT for wrapped cells - an explicit verticalAlign at any
|
|
9383
|
+
level resolves to data-valign below, which is more specific and wins. Before that existed, a row
|
|
9384
|
+
mixing a wrapped column with normal ones was silently inconsistent: the wrapped cell sat top and
|
|
9385
|
+
its neighbours sat middle, with no way to say otherwise. */
|
|
9298
9386
|
.twc-dt__td[data-wrap="true"] { white-space: normal; overflow: hidden; text-overflow: clip;
|
|
9299
9387
|
word-break: break-word; overflow-wrap: anywhere; vertical-align: top; padding-block: 10px; line-height: 1.45; }
|
|
9388
|
+
/* Vertical alignment. One attribute, already resolved: column > row > table default, computed in
|
|
9389
|
+
renderLeaf rather than left to three competing selectors, so the precedence is readable in the
|
|
9390
|
+
code and cannot be reordered by an unrelated CSS edit. Matters whenever rows vary in height -
|
|
9391
|
+
wrapText, a multi-line renderCell, or a drag-resized row. */
|
|
9392
|
+
.twc-dt__td[data-valign="top"] { vertical-align: top; }
|
|
9393
|
+
.twc-dt__td[data-valign="middle"] { vertical-align: middle; }
|
|
9394
|
+
.twc-dt__td[data-valign="bottom"] { vertical-align: bottom; }
|
|
9300
9395
|
/* #338: "combined" column cell \u2014 one column showing several source columns' values. */
|
|
9301
9396
|
.twc-dt__combine--stack { display: flex; flex-direction: column; gap: 1px; }
|
|
9302
9397
|
.twc-dt__combine-sep { color: var(--color-text-subtle); padding-inline: 3px; }
|
|
@@ -10065,6 +10160,8 @@ function Datatable({
|
|
|
10065
10160
|
batchActions = [],
|
|
10066
10161
|
onRowSelectionChange,
|
|
10067
10162
|
selectionSummary,
|
|
10163
|
+
verticalAlign,
|
|
10164
|
+
getRowVerticalAlign,
|
|
10068
10165
|
getRowStyle,
|
|
10069
10166
|
getRowClassName,
|
|
10070
10167
|
showExport = false,
|
|
@@ -12644,6 +12741,7 @@ function Datatable({
|
|
|
12644
12741
|
const h = rowHeights[k];
|
|
12645
12742
|
const userRowStyle = getRowStyle ? getRowStyle(row, ri) : void 0;
|
|
12646
12743
|
const userRowClass = getRowClassName ? getRowClassName(row, ri) : void 0;
|
|
12744
|
+
const rowValign = getRowVerticalAlign ? getRowVerticalAlign(row, ri) : void 0;
|
|
12647
12745
|
const { background: _rowBgShorthand, backgroundColor: _rowBgColor, ...restRowStyle } = userRowStyle || {};
|
|
12648
12746
|
const usable = (v) => typeof v === "string" && v.trim() !== "" ? v : void 0;
|
|
12649
12747
|
const rowBg = usable(_rowBgShorthand) ?? usable(_rowBgColor);
|
|
@@ -12754,6 +12852,7 @@ function Datatable({
|
|
|
12754
12852
|
"data-num": c.type === "number" || void 0,
|
|
12755
12853
|
"data-actions": isActions || void 0,
|
|
12756
12854
|
"data-align": c.align !== dtNaturalAlign(c) ? c.align : void 0,
|
|
12855
|
+
"data-valign": c.verticalAlign ?? rowValign ?? verticalAlign,
|
|
12757
12856
|
"data-editable": editable && !isEditing || void 0,
|
|
12758
12857
|
"data-editing": isEditing || void 0,
|
|
12759
12858
|
"data-cell-active": cellActive || void 0,
|
|
@@ -14850,6 +14949,17 @@ function DiffTable({
|
|
|
14850
14949
|
}
|
|
14851
14950
|
var FIELD_CSS3 = `
|
|
14852
14951
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
14952
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
14953
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
14954
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
14955
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
14956
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
14957
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
14958
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
14959
|
+
whether a footer is already there, so no component has to report it. */
|
|
14960
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
14961
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
14962
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
14853
14963
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
14854
14964
|
.twc-field__req { color: var(--color-danger); }
|
|
14855
14965
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -14982,6 +15092,7 @@ function DatePicker({
|
|
|
14982
15092
|
onChange,
|
|
14983
15093
|
id,
|
|
14984
15094
|
className = "",
|
|
15095
|
+
reserveFooter = false,
|
|
14985
15096
|
...rest
|
|
14986
15097
|
}) {
|
|
14987
15098
|
const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS3);
|
|
@@ -15184,7 +15295,7 @@ function DatePicker({
|
|
|
15184
15295
|
setFocusDate(next);
|
|
15185
15296
|
if (next.getFullYear() !== y || next.getMonth() !== m) setView(new Date(next.getFullYear(), next.getMonth(), 1));
|
|
15186
15297
|
};
|
|
15187
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-dp twc-field ${className}`, ref: wrapRef, ...rest, children: [
|
|
15298
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-dp twc-field ${className}`, ref: wrapRef, ...rest, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
15188
15299
|
__twcStyles0,
|
|
15189
15300
|
__twcStyles1,
|
|
15190
15301
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", id: `${fieldId}-label`, children: [
|
|
@@ -15408,6 +15519,17 @@ function DatePicker({
|
|
|
15408
15519
|
}
|
|
15409
15520
|
var FIELD_CSS4 = `
|
|
15410
15521
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
15522
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
15523
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
15524
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
15525
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
15526
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
15527
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
15528
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
15529
|
+
whether a footer is already there, so no component has to report it. */
|
|
15530
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
15531
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
15532
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
15411
15533
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
15412
15534
|
.twc-field__req { color: var(--color-danger); }
|
|
15413
15535
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -15543,6 +15665,7 @@ function DateRangePicker({
|
|
|
15543
15665
|
parse,
|
|
15544
15666
|
onChange,
|
|
15545
15667
|
className = "",
|
|
15668
|
+
reserveFooter = false,
|
|
15546
15669
|
...rest
|
|
15547
15670
|
}) {
|
|
15548
15671
|
const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS4);
|
|
@@ -15787,7 +15910,7 @@ function DateRangePicker({
|
|
|
15787
15910
|
return t > Math.min(s, e) && t < Math.max(s, e);
|
|
15788
15911
|
};
|
|
15789
15912
|
const text = range2.start ? format ? format(range2) : `${fmtD(range2.start, locale)} \u2013 ${range2.end ? fmtD(range2.end, locale) : "\u2026"}` : "";
|
|
15790
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-drp twc-field ${className}`, ref: wrapRef, ...rest, children: [
|
|
15913
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-drp twc-field ${className}`, ref: wrapRef, ...rest, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
15791
15914
|
__twcStyles0,
|
|
15792
15915
|
__twcStyles1,
|
|
15793
15916
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-field__label", id: labelId, children: [
|
|
@@ -16015,6 +16138,17 @@ function DateRangePicker({
|
|
|
16015
16138
|
}
|
|
16016
16139
|
var FIELD_CSS5 = `
|
|
16017
16140
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
16141
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
16142
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
16143
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
16144
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
16145
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
16146
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
16147
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
16148
|
+
whether a footer is already there, so no component has to report it. */
|
|
16149
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
16150
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
16151
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
16018
16152
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
16019
16153
|
.twc-field__req { color: var(--color-danger); }
|
|
16020
16154
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -16113,6 +16247,7 @@ function TimePicker({
|
|
|
16113
16247
|
onChange,
|
|
16114
16248
|
id,
|
|
16115
16249
|
className = "",
|
|
16250
|
+
reserveFooter = false,
|
|
16116
16251
|
...rest
|
|
16117
16252
|
}) {
|
|
16118
16253
|
const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS5);
|
|
@@ -16327,7 +16462,7 @@ function TimePicker({
|
|
|
16327
16462
|
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "9" }),
|
|
16328
16463
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 7v5l3 2" })
|
|
16329
16464
|
] });
|
|
16330
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-tp twc-field ${className}`, ref: wrapRef, ...rest, children: [
|
|
16465
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-tp twc-field ${className}`, ref: wrapRef, ...rest, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
16331
16466
|
__twcStyles0,
|
|
16332
16467
|
__twcStyles1,
|
|
16333
16468
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", id: `${fieldId}-label`, children: [
|
|
@@ -16484,6 +16619,17 @@ function TimePicker({
|
|
|
16484
16619
|
}
|
|
16485
16620
|
var FIELD_CSS6 = `
|
|
16486
16621
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
16622
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
16623
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
16624
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
16625
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
16626
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
16627
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
16628
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
16629
|
+
whether a footer is already there, so no component has to report it. */
|
|
16630
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
16631
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
16632
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
16487
16633
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
16488
16634
|
.twc-field__req { color: var(--color-danger); }
|
|
16489
16635
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -16522,6 +16668,7 @@ function DateTimePicker({
|
|
|
16522
16668
|
onChange,
|
|
16523
16669
|
id,
|
|
16524
16670
|
className = "",
|
|
16671
|
+
reserveFooter = false,
|
|
16525
16672
|
...rest
|
|
16526
16673
|
}) {
|
|
16527
16674
|
const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS6);
|
|
@@ -16546,7 +16693,7 @@ function DateTimePicker({
|
|
|
16546
16693
|
commitVal(new Date(b ? b.getFullYear() : t.getFullYear(), b ? b.getMonth() : t.getMonth(), b ? b.getDate() : t.getDate(), t.getHours(), t.getMinutes(), t.getSeconds(), 0));
|
|
16547
16694
|
};
|
|
16548
16695
|
const childTone = error ? "danger" : tone;
|
|
16549
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-dtp twc-field ${className}`, id: fieldId, ...rest, children: [
|
|
16696
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-dtp twc-field ${className}`, id: fieldId, ...rest, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
16550
16697
|
__twcStyles0,
|
|
16551
16698
|
__twcStyles1,
|
|
16552
16699
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", id: `${fieldId}-label`, children: [
|
|
@@ -17124,6 +17271,17 @@ function EmptyState({
|
|
|
17124
17271
|
}
|
|
17125
17272
|
var FIELD_CSS7 = `
|
|
17126
17273
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
17274
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
17275
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
17276
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
17277
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
17278
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
17279
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
17280
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
17281
|
+
whether a footer is already there, so no component has to report it. */
|
|
17282
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
17283
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
17284
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
17127
17285
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
17128
17286
|
.twc-field__req { color: var(--color-danger); }
|
|
17129
17287
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -17138,6 +17296,7 @@ function Field({
|
|
|
17138
17296
|
size = "md",
|
|
17139
17297
|
id,
|
|
17140
17298
|
className = "",
|
|
17299
|
+
reserveFooter = false,
|
|
17141
17300
|
children,
|
|
17142
17301
|
...rest
|
|
17143
17302
|
}) {
|
|
@@ -17150,7 +17309,7 @@ function Field({
|
|
|
17150
17309
|
"aria-describedby": [children.props["aria-describedby"], hasMsg ? descId : null].filter(Boolean).join(" ") || void 0,
|
|
17151
17310
|
"aria-invalid": children.props["aria-invalid"] !== void 0 ? children.props["aria-invalid"] : error ? true : void 0
|
|
17152
17311
|
}) : children;
|
|
17153
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, "data-size": size, ...rest, children: [
|
|
17312
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, "data-size": size, ...rest, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
17154
17313
|
__twcStyles,
|
|
17155
17314
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", htmlFor, children: [
|
|
17156
17315
|
label,
|
|
@@ -18206,6 +18365,17 @@ var Radio = /* @__PURE__ */ React55__namespace.default.forwardRef(function Radio
|
|
|
18206
18365
|
.twc-field__req { color: var(--color-danger); }
|
|
18207
18366
|
.twc-radio__desc { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
18208
18367
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
18368
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
18369
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
18370
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
18371
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
18372
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
18373
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
18374
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
18375
|
+
whether a footer is already there, so no component has to report it. */
|
|
18376
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
18377
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
18378
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
18209
18379
|
.twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
|
|
18210
18380
|
`);
|
|
18211
18381
|
const autoId = React55__namespace.default.useId();
|
|
@@ -18255,6 +18425,17 @@ var Radio = /* @__PURE__ */ React55__namespace.default.forwardRef(function Radio
|
|
|
18255
18425
|
});
|
|
18256
18426
|
var RADIO_GROUP_CSS = `
|
|
18257
18427
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
18428
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
18429
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
18430
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
18431
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
18432
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
18433
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
18434
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
18435
|
+
whether a footer is already there, so no component has to report it. */
|
|
18436
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
18437
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
18438
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
18258
18439
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
18259
18440
|
.twc-field__req { color: var(--color-danger); }
|
|
18260
18441
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -18279,6 +18460,7 @@ function RadioGroup({
|
|
|
18279
18460
|
tone = "primary",
|
|
18280
18461
|
id,
|
|
18281
18462
|
className = "",
|
|
18463
|
+
reserveFooter = false,
|
|
18282
18464
|
...rest
|
|
18283
18465
|
}) {
|
|
18284
18466
|
const __twcStyles = useScopedStyles("twc-radio-group-styles", RADIO_GROUP_CSS);
|
|
@@ -18326,6 +18508,7 @@ function RadioGroup({
|
|
|
18326
18508
|
"div",
|
|
18327
18509
|
{
|
|
18328
18510
|
className: `twc-field ${className}`,
|
|
18511
|
+
"data-reserve-footer": reserveFooter || void 0,
|
|
18329
18512
|
role: "radiogroup",
|
|
18330
18513
|
"aria-labelledby": label ? labelId : void 0,
|
|
18331
18514
|
"aria-describedby": error || hint ? descId : void 0,
|
|
@@ -19350,6 +19533,17 @@ var Switch = /* @__PURE__ */ React55__namespace.default.forwardRef(function Swit
|
|
|
19350
19533
|
.twc-field__req { color: var(--color-danger); }
|
|
19351
19534
|
.twc-switch__desc { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
19352
19535
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
19536
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
19537
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
19538
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
19539
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
19540
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
19541
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
19542
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
19543
|
+
whether a footer is already there, so no component has to report it. */
|
|
19544
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
19545
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
19546
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
19353
19547
|
.twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
|
|
19354
19548
|
`);
|
|
19355
19549
|
const autoId = React55__namespace.default.useId();
|
|
@@ -19789,6 +19983,17 @@ function Tag({
|
|
|
19789
19983
|
var useIso = typeof document !== "undefined" ? React55__namespace.default.useLayoutEffect : React55__namespace.default.useEffect;
|
|
19790
19984
|
var TEXTAREA_CSS = `
|
|
19791
19985
|
.twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
|
|
19986
|
+
/* #494: reserve the footer lane. A row of fields aligned with align="flex-end" splits in two the
|
|
19987
|
+
moment ONE of them has a hint, because that field grows a footer and the others do not - and the
|
|
19988
|
+
caption then moves a DIFFERENT control, which nothing in the JSX suggests. align= cannot fix it:
|
|
19989
|
+
flex-start lines up the labels instead, so the controls part company as soon as one label wraps.
|
|
19990
|
+
The lane is held open by generated content rather than a fixed height so it matches the real
|
|
19991
|
+
footer by construction - same font metrics, same flex gap - instead of by a number that drifts.
|
|
19992
|
+
Whitespace-only generated content is not announced, and :has() lets the CSS tell on its own
|
|
19993
|
+
whether a footer is already there, so no component has to report it. */
|
|
19994
|
+
.twc-field[data-reserve-footer]:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after,
|
|
19995
|
+
[data-reserve-footer] .twc-field:not(:has(> .twc-field__footer, > .twc-field__hint, > .twc-field__error))::after {
|
|
19996
|
+
content: "\\00a0"; font-size: var(--text-xs); user-select: none; }
|
|
19792
19997
|
.twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
|
|
19793
19998
|
.twc-field__req { color: var(--color-danger); }
|
|
19794
19999
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
@@ -19851,6 +20056,7 @@ var Textarea = /* @__PURE__ */ React55__namespace.default.forwardRef(function Te
|
|
|
19851
20056
|
disabled = false,
|
|
19852
20057
|
id,
|
|
19853
20058
|
className = "",
|
|
20059
|
+
reserveFooter = false,
|
|
19854
20060
|
...rest
|
|
19855
20061
|
}, ref) {
|
|
19856
20062
|
const __twcStyles = useScopedStyles("twc-textarea-styles", TEXTAREA_CSS);
|
|
@@ -19908,7 +20114,7 @@ var Textarea = /* @__PURE__ */ React55__namespace.default.forwardRef(function Te
|
|
|
19908
20114
|
const showClear = clearable && count > 0 && !disabled && !rest.readOnly;
|
|
19909
20115
|
const ariaInvalid = invalid ? true : rest["aria-invalid"] ?? void 0;
|
|
19910
20116
|
const describedBy = [error || hint ? descId : null, showCounter ? countId : null, rest["aria-describedby"]].filter(Boolean).join(" ") || void 0;
|
|
19911
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, children: [
|
|
20117
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, "data-reserve-footer": reserveFooter || void 0, children: [
|
|
19912
20118
|
__twcStyles,
|
|
19913
20119
|
label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
|
|
19914
20120
|
label,
|