@godxjp/ui 32.2.1 → 32.3.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/README.md +3 -3
- package/agent/START-HERE.md +5 -5
- package/agent/components/Alert.json +1 -0
- package/agent/components/DataTable.json +1 -1
- package/agent/components/DatePicker.json +6 -0
- package/agent/components/EmptyState.json +7 -0
- package/agent/components/Form.json +1 -0
- package/agent/components/FormField.json +2 -1
- package/agent/components/MobileShell.json +1 -1
- package/agent/components/PageContainer.json +1 -1
- package/agent/components/Toaster.json +2 -1
- package/agent/components.json +22 -5
- package/agent/index.json +5 -5
- package/agent/llms.txt +6 -6
- package/agent/tokens.json +12 -0
- package/dist/components/data-display/data-table.js +13 -3
- package/dist/components/data-display/empty-state.d.ts +1 -1
- package/dist/components/data-display/empty-state.js +2 -1
- package/dist/components/data-entry/calendar.js +16 -1
- package/dist/components/data-entry/date-picker.js +22 -7
- package/dist/components/data-entry/record-picker.js +22 -16
- package/dist/components/feedback/sonner.js +4 -1
- package/dist/components/layout/mobile-shell.js +19 -2
- package/dist/components/layout/page-container.js +4 -1
- package/dist/contracts/measurement.json +1 -1
- package/dist/i18n/messages/en.json +3 -1
- package/dist/i18n/messages/ja.json +3 -1
- package/dist/i18n/messages/vi.json +3 -1
- package/dist/lib/datetime/picker-format.d.ts +8 -1
- package/dist/lib/datetime/picker-format.js +16 -4
- package/dist/lib/toast-clearance.d.ts +25 -0
- package/dist/lib/toast-clearance.js +27 -0
- package/dist/props/components/data-display.prop.d.ts +7 -0
- package/dist/props/components/data-entry.prop.d.ts +12 -0
- package/dist/styles/control.css +57 -0
- package/dist/styles/focus-ring.css +1 -0
- package/dist/styles/layers.json +1 -1
- package/dist/styles/table-layout.css +7 -0
- package/dist/styles.css +1 -1
- package/dist/tokens/components/feedback.css +4 -0
- package/package.json +2 -2
|
@@ -77,6 +77,7 @@ function DatePicker(props) {
|
|
|
77
77
|
placement = "bottom-start",
|
|
78
78
|
renderExtraFooter,
|
|
79
79
|
showSummary = true,
|
|
80
|
+
captionLayout = "label",
|
|
80
81
|
size,
|
|
81
82
|
status,
|
|
82
83
|
variant,
|
|
@@ -114,7 +115,7 @@ function DatePicker(props) {
|
|
|
114
115
|
const allowed = (date) => pickerDateAllowed(date, minimum, maximum, disabledDate);
|
|
115
116
|
const display = (date) => format || showTime ? formatPickerDate(date, format, locale, Boolean(showTime)) : toIsoPeriod(date, picker);
|
|
116
117
|
const displayAll = (date) => Array.isArray(date) ? date.map((day) => display(day)).join(", ") : display(date);
|
|
117
|
-
const parse = (text2) => parsePickerDate(text2, format, parseFormat, Boolean(showTime)) ?? parseIsoPeriod(text2, picker);
|
|
118
|
+
const parse = (text2, strict = false) => parsePickerDate(text2, format, parseFormat, Boolean(showTime), strict) ?? parseIsoPeriod(text2, picker);
|
|
118
119
|
const periodDate = (date) => pickerPeriodStart(date, picker, dayPickerLocale);
|
|
119
120
|
const fieldA11y = range ? pickGroupFieldA11y(ariaProps) : pickFieldA11y(ariaProps);
|
|
120
121
|
const identity = useFieldIdentity({ id, name, "data-field": fieldA11y["data-field"] });
|
|
@@ -241,13 +242,13 @@ function DatePicker(props) {
|
|
|
241
242
|
choose(void 0);
|
|
242
243
|
return;
|
|
243
244
|
}
|
|
244
|
-
const parsed = parse(raw);
|
|
245
|
+
const parsed = parse(raw, true);
|
|
245
246
|
if (parsed && allowed(parsed)) choose(parsed);
|
|
246
247
|
};
|
|
247
|
-
const commitEdge = (edge, raw) => {
|
|
248
|
+
const commitEdge = (edge, raw, strict = false) => {
|
|
248
249
|
if (edgeDisabled(edge)) return;
|
|
249
250
|
const trimmed = raw.trim();
|
|
250
|
-
const parsed = parse(trimmed);
|
|
251
|
+
const parsed = parse(trimmed, strict);
|
|
251
252
|
if (trimmed && (!parsed || !allowed(parsed))) return;
|
|
252
253
|
if (!trimmed && !allowEmpty[edge === "from" ? 0 : 1]) return;
|
|
253
254
|
const base = working;
|
|
@@ -274,11 +275,20 @@ function DatePicker(props) {
|
|
|
274
275
|
const next = periodDate(parsed);
|
|
275
276
|
emit(next);
|
|
276
277
|
setText(display(parsed));
|
|
278
|
+
} else if (!preserveInvalidOnBlur) {
|
|
279
|
+
setText(displayAll(committedSingle));
|
|
277
280
|
}
|
|
278
281
|
setOpen(false);
|
|
279
282
|
};
|
|
280
283
|
const enterEdge = (edge) => () => {
|
|
281
|
-
|
|
284
|
+
const raw = edge === "from" ? fromText : toText;
|
|
285
|
+
const parsed = parse(raw);
|
|
286
|
+
commitEdge(edge, raw);
|
|
287
|
+
if (raw.trim() && (!parsed || !allowed(parsed)) && !preserveInvalidOnBlur) {
|
|
288
|
+
const fallback = display(edge === "from" ? committedRange?.from : committedRange?.to);
|
|
289
|
+
if (edge === "from") setFromText(fallback);
|
|
290
|
+
else setToText(fallback);
|
|
291
|
+
}
|
|
282
292
|
setOpen(false);
|
|
283
293
|
};
|
|
284
294
|
const displayIsCanonical = !format && !showTime && !multiple && !needConfirm && !preserveInvalidOnBlur && picker === "date";
|
|
@@ -426,6 +436,7 @@ function DatePicker(props) {
|
|
|
426
436
|
endMonth: maximum,
|
|
427
437
|
showToday,
|
|
428
438
|
showClose,
|
|
439
|
+
captionLayout,
|
|
429
440
|
onClose: () => setOpen(false)
|
|
430
441
|
};
|
|
431
442
|
const renderDayPanel = () => range ? /* @__PURE__ */ jsx(
|
|
@@ -608,13 +619,15 @@ function DatePicker(props) {
|
|
|
608
619
|
onChange: (event) => {
|
|
609
620
|
if (edge === "from") setFromText(event.target.value);
|
|
610
621
|
else setToText(event.target.value);
|
|
611
|
-
commitEdge(edge, event.target.value);
|
|
622
|
+
commitEdge(edge, event.target.value, true);
|
|
612
623
|
},
|
|
613
624
|
onBlur: (event) => {
|
|
614
|
-
if (preserveInvalidOnBlur) return;
|
|
615
625
|
const parsed = parse(event.target.value);
|
|
616
626
|
const accepted = parsed && allowed(parsed) ? parsed : void 0;
|
|
617
627
|
const fallback = edge === "from" ? committedRange?.from : committedRange?.to;
|
|
628
|
+
if (accepted && +periodDate(accepted) !== +(fallback ?? NaN))
|
|
629
|
+
commitEdge(edge, event.target.value);
|
|
630
|
+
if (preserveInvalidOnBlur) return;
|
|
618
631
|
const next = accepted ? display(accepted) : display(fallback);
|
|
619
632
|
if (edge === "from") setFromText(next);
|
|
620
633
|
else setToText(next);
|
|
@@ -704,6 +717,8 @@ function DatePicker(props) {
|
|
|
704
717
|
onBlur: (event) => {
|
|
705
718
|
const parsed = parse(event.target.value);
|
|
706
719
|
const accepted = parsed && allowed(parsed) && timeAllowed(parsed) ? parsed : void 0;
|
|
720
|
+
if (accepted && !multiple && !needConfirm && +periodDate(accepted) !== +(committedSingle ?? NaN))
|
|
721
|
+
emit(periodDate(accepted));
|
|
707
722
|
if (!preserveInvalidOnBlur)
|
|
708
723
|
setText(accepted ? display(accepted) : displayAll(committedSingle));
|
|
709
724
|
}
|
|
@@ -486,25 +486,31 @@ const DialogPicker = React.forwardRef(function DialogPicker2({
|
|
|
486
486
|
))
|
|
487
487
|
}
|
|
488
488
|
) : null,
|
|
489
|
-
/* @__PURE__ */ jsx(Command, { shouldFilter: false, split: isMultiple, className: "ui-record-picker-list", children: status === "error" ?
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
489
|
+
/* @__PURE__ */ jsx(Command, { shouldFilter: false, split: isMultiple, className: "ui-record-picker-list", children: status === "error" ? (
|
|
490
|
+
// `live` on both outcomes below: each answers the user's own query (gh#1271 —
|
|
491
|
+
// EmptyState is no longer a live region by default).
|
|
492
|
+
/* @__PURE__ */ jsx(
|
|
493
|
+
EmptyState,
|
|
494
|
+
{
|
|
495
|
+
live: true,
|
|
496
|
+
variant: "compact",
|
|
497
|
+
tone: "destructive",
|
|
498
|
+
title: t("dataEntry.recordPicker.error"),
|
|
499
|
+
action: /* @__PURE__ */ jsx(
|
|
500
|
+
Button,
|
|
501
|
+
{
|
|
502
|
+
variant: "outline",
|
|
503
|
+
size: "sm",
|
|
504
|
+
onClick: () => void load(query, filterValues),
|
|
505
|
+
children: t("common.retry")
|
|
506
|
+
}
|
|
507
|
+
)
|
|
508
|
+
}
|
|
509
|
+
)
|
|
505
510
|
) : status === "loading" ? /* @__PURE__ */ jsx("div", { role: "status", className: "ui-record-picker-status", children: t("dataEntry.recordPicker.loading") }) : visible.length === 0 ? /* @__PURE__ */ jsx(
|
|
506
511
|
EmptyState,
|
|
507
512
|
{
|
|
513
|
+
live: true,
|
|
508
514
|
variant: "compact",
|
|
509
515
|
title: notFoundContent ?? t("dataEntry.recordPicker.empty")
|
|
510
516
|
}
|
|
@@ -99,8 +99,11 @@ function Toaster({ style, richColors = true, toastOptions, ...props }) {
|
|
|
99
99
|
...style
|
|
100
100
|
},
|
|
101
101
|
position: "bottom-right",
|
|
102
|
+
offset: {
|
|
103
|
+
bottom: "calc(24px + var(--toast-offset-bottom) + var(--toast-sticky-bar-height))"
|
|
104
|
+
},
|
|
102
105
|
mobileOffset: {
|
|
103
|
-
bottom: "var(--toast-mobile-offset)",
|
|
106
|
+
bottom: "calc(var(--toast-mobile-offset) + var(--toast-offset-bottom) + var(--toast-sticky-bar-height))",
|
|
104
107
|
right: "var(--toast-mobile-offset)"
|
|
105
108
|
},
|
|
106
109
|
duration: tokenDuration,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useRef } from "react";
|
|
3
4
|
import { useTranslation } from "../../i18n/use-translation.js";
|
|
5
|
+
import { useToastStickyBarClearance } from "../../lib/toast-clearance.js";
|
|
4
6
|
import { cn } from "../../lib/utils.js";
|
|
5
7
|
function MobileShell({
|
|
6
8
|
statusBar,
|
|
@@ -13,6 +15,13 @@ function MobileShell({
|
|
|
13
15
|
className
|
|
14
16
|
}) {
|
|
15
17
|
const { t } = useTranslation();
|
|
18
|
+
const actionsRef = useRef(null);
|
|
19
|
+
const tabBarRef = useRef(null);
|
|
20
|
+
const bottomBands = [
|
|
21
|
+
...actions !== void 0 ? [actionsRef] : [],
|
|
22
|
+
...tabBar !== void 0 ? [tabBarRef] : []
|
|
23
|
+
];
|
|
24
|
+
useToastStickyBarClearance(bottomBands, height === "viewport");
|
|
16
25
|
return /* @__PURE__ */ jsxs(
|
|
17
26
|
"div",
|
|
18
27
|
{
|
|
@@ -34,8 +43,16 @@ function MobileShell({
|
|
|
34
43
|
children
|
|
35
44
|
}
|
|
36
45
|
),
|
|
37
|
-
actions !== void 0 && /* @__PURE__ */ jsx("footer", { className: "ui-mobile-shell-actions", children: actions }),
|
|
38
|
-
tabBar !== void 0 && /* @__PURE__ */ jsx(
|
|
46
|
+
actions !== void 0 && /* @__PURE__ */ jsx("footer", { ref: actionsRef, className: "ui-mobile-shell-actions", children: actions }),
|
|
47
|
+
tabBar !== void 0 && /* @__PURE__ */ jsx(
|
|
48
|
+
"nav",
|
|
49
|
+
{
|
|
50
|
+
ref: tabBarRef,
|
|
51
|
+
className: "ui-mobile-shell-tabbar",
|
|
52
|
+
"aria-label": t("layout.sidebar.ariaLabel"),
|
|
53
|
+
children: tabBar
|
|
54
|
+
}
|
|
55
|
+
)
|
|
39
56
|
]
|
|
40
57
|
}
|
|
41
58
|
);
|
|
@@ -4,6 +4,7 @@ import { isValidElement, useEffect, useRef, useState } from "react";
|
|
|
4
4
|
import { ChevronRight } from "lucide-react";
|
|
5
5
|
import { useTranslation } from "../../i18n/use-translation.js";
|
|
6
6
|
import { scrollParent, useInView } from "../../lib/hooks.js";
|
|
7
|
+
import { useToastStickyBarClearance } from "../../lib/toast-clearance.js";
|
|
7
8
|
import { cn } from "../../lib/utils.js";
|
|
8
9
|
import { BreadcrumbCrumbText, trailEllipsis } from "./breadcrumb-ellipsis.js";
|
|
9
10
|
import { densityClass, pageContainerVariantClass, padStyle } from "../../lib/variants.js";
|
|
@@ -62,6 +63,8 @@ function PageContainerRoot({
|
|
|
62
63
|
}) {
|
|
63
64
|
const reveal = stickyFooter && footer != null && footerReveal === "onScroll";
|
|
64
65
|
const { headerRef, revealed } = useFooterReveal(reveal);
|
|
66
|
+
const footerRef = useRef(null);
|
|
67
|
+
useToastStickyBarClearance([footerRef], stickyFooter && !!footer && (!reveal || revealed));
|
|
65
68
|
const { t } = useTranslation();
|
|
66
69
|
const { start: extraStart, end: extraEnd } = resolvePageExtra(extra);
|
|
67
70
|
const titleFocus = titleRef === void 0 ? void 0 : { ref: titleRef, tabIndex: -1 };
|
|
@@ -176,7 +179,7 @@ function PageContainerRoot({
|
|
|
176
179
|
),
|
|
177
180
|
toolbar != null && /* @__PURE__ */ jsx("div", { className: "ui-page-toolbar", style: padStyle(toolbarPad, void 0), children: toolbar }),
|
|
178
181
|
children != null && /* @__PURE__ */ jsx("div", { className: "ui-page-body", children }),
|
|
179
|
-
footer && /* @__PURE__ */ jsx("footer", { className: "ui-page-footer", style: footerPadStyle(footerPad), children: footer })
|
|
182
|
+
footer && /* @__PURE__ */ jsx("footer", { ref: footerRef, className: "ui-page-footer", style: footerPadStyle(footerPad), children: footer })
|
|
180
183
|
]
|
|
181
184
|
}
|
|
182
185
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$comment": "AUTO-GENERATED by scripts/gen-measurement-contract.mjs — do not edit. Read this instead of guessing: docs/MEASUREMENT-CONTRACT.md.",
|
|
3
|
-
"version": "32.
|
|
3
|
+
"version": "32.3.0",
|
|
4
4
|
"targetSize": {
|
|
5
5
|
"standard": "WCAG 2.2 SC 2.5.8 Target Size (Minimum), level AA — 24×24 CSS px",
|
|
6
6
|
"min": 24,
|
|
@@ -4,7 +4,14 @@ import type { DatePickerBaseProp, PickerDateFormatProp } from "../../props/compo
|
|
|
4
4
|
export type DatePickerPicker = NonNullable<DatePickerBaseProp["picker"]>;
|
|
5
5
|
/** Display is independent from the canonical value submitted by the field. */
|
|
6
6
|
export declare function formatPickerDate(date: Date | undefined, format: PickerDateFormatProp | undefined, locale: string, withTime?: boolean): string;
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Read typed text back into a date. Tries, in order: the caller's `parser`, the explicit `format`
|
|
9
|
+
* pattern, then canonical ISO `yyyy-MM-dd` — and, unless `strict`, any unambiguous year-first
|
|
10
|
+
* spelling (see `YEAR_FIRST`). `strict` exists for the KEYSTROKE path: `2026/10/1` is a complete
|
|
11
|
+
* year-first date on its way to `2026/10/15`, so committing it mid-typing would rewrite the text
|
|
12
|
+
* under the user's cursor. The field commits loose spellings on Enter and blur instead.
|
|
13
|
+
*/
|
|
14
|
+
export declare function parsePickerDate(text: string, format: PickerDateFormatProp | undefined, parser?: (text: string) => Date | undefined, withTime?: boolean, strict?: boolean): Date | undefined;
|
|
8
15
|
export declare function pickerDateAllowed(date: Date, min: Date | undefined, max: Date | undefined, disabled?: (date: Date) => boolean): boolean;
|
|
9
16
|
/** Normalize a reporting period with the active calendar's week convention. */
|
|
10
17
|
export declare function pickerPeriodStart(date: Date, picker: DatePickerBaseProp["picker"], locale: Pick<Locale, "options">): Date;
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
startOfQuarter,
|
|
9
9
|
startOfYear
|
|
10
10
|
} from "date-fns";
|
|
11
|
-
import {
|
|
11
|
+
import { toIsoDate } from "./parse.js";
|
|
12
12
|
import { dateTimeFormat } from "../intl-cache.js";
|
|
13
13
|
function formatPickerDate(date, format, locale, withTime = false) {
|
|
14
14
|
if (!date || !isValid(date)) return "";
|
|
@@ -17,7 +17,18 @@ function formatPickerDate(date, format, locale, withTime = false) {
|
|
|
17
17
|
if (typeof format === "string") return formatDate(date, format);
|
|
18
18
|
return withTime ? formatDate(date, "yyyy-MM-dd HH:mm") : toIsoDate(date);
|
|
19
19
|
}
|
|
20
|
-
|
|
20
|
+
const YEAR_FIRST = /^(\d{4})([-/.])(\d{1,2})\2(\d{1,2})$/;
|
|
21
|
+
const YEAR_FIRST_COMPACT = /^(\d{4})(\d{2})(\d{2})$/;
|
|
22
|
+
function parseYearFirst(raw) {
|
|
23
|
+
const m = YEAR_FIRST.exec(raw);
|
|
24
|
+
const parts = m ? [m[1], m[3], m[4]] : YEAR_FIRST_COMPACT.exec(raw)?.slice(1, 4);
|
|
25
|
+
if (!parts) return void 0;
|
|
26
|
+
const [year, month, day] = parts.map(Number);
|
|
27
|
+
const date = new Date(year, month - 1, day);
|
|
28
|
+
date.setFullYear(year, month - 1, day);
|
|
29
|
+
return date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day ? date : void 0;
|
|
30
|
+
}
|
|
31
|
+
function parsePickerDate(text, format, parser, withTime = false, strict = false) {
|
|
21
32
|
const raw = text.trim();
|
|
22
33
|
if (!raw) return void 0;
|
|
23
34
|
const custom = parser?.(raw);
|
|
@@ -27,8 +38,9 @@ function parsePickerDate(text, format, parser, withTime = false) {
|
|
|
27
38
|
const parsed = parse(raw, pattern, new Date(2e3, 0, 1));
|
|
28
39
|
if (isValid(parsed) && formatDate(parsed, pattern) === raw) return parsed;
|
|
29
40
|
}
|
|
30
|
-
if (withTime
|
|
31
|
-
|
|
41
|
+
if (withTime) return void 0;
|
|
42
|
+
if (strict && !/^\d{4}-\d{2}-\d{2}$/.test(raw)) return void 0;
|
|
43
|
+
return parseYearFirst(raw);
|
|
32
44
|
}
|
|
33
45
|
function pickerDateAllowed(date, min, max, disabled) {
|
|
34
46
|
return isValid(date) && (!min || startOfDay(date) >= startOfDay(min)) && (!max || startOfDay(date) <= startOfDay(max)) && !disabled?.(date);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Toast clearance for sticky action bars (gh#1271).
|
|
3
|
+
*
|
|
4
|
+
* The Toaster stacks bottom-right, and a page's sticky action bar (`PageContainer stickyFooter`,
|
|
5
|
+
* `MobileShell actions` + `tabBar`) is pinned to the same edge — so without this a stack of three
|
|
6
|
+
* success toasts sits on top of the submit button that raised them. The bar publishes its own
|
|
7
|
+
* block size on `<html>` as `--toast-sticky-bar-height`; the Toaster adds that length to its bottom
|
|
8
|
+
* offset (`sonner.tsx`). The tier file declares the property `0px`, so with no bar mounted — or
|
|
9
|
+
* the bar hidden — the toaster's offset is exactly what it was before.
|
|
10
|
+
*
|
|
11
|
+
* Internal: reachable from no published subpath.
|
|
12
|
+
*/
|
|
13
|
+
import { type RefObject } from "react";
|
|
14
|
+
/** The custom property the Toaster reads. Declared `0px` in `src/tokens/components/feedback.css`. */
|
|
15
|
+
export declare const TOAST_STICKY_BAR_PROPERTY = "--toast-sticky-bar-height";
|
|
16
|
+
/**
|
|
17
|
+
* While `enabled`, publish the summed border-box block size of `refs` on `<html>` and keep it
|
|
18
|
+
* current with a `ResizeObserver`; remove it when disabled or unmounted.
|
|
19
|
+
*
|
|
20
|
+
* `offsetHeight`, not `getBoundingClientRect()`: the reveal footer slides in with a `transform`,
|
|
21
|
+
* and a transform must not leak into the clearance mid-transition. Everything runs in an effect,
|
|
22
|
+
* so SSR never touches `document`. One page-level bar at a time is the assumed shape: a second
|
|
23
|
+
* mounted bar overwrites the first's value, and either unmount clears it.
|
|
24
|
+
*/
|
|
25
|
+
export declare function useToastStickyBarClearance(refs: ReadonlyArray<RefObject<HTMLElement | null>>, enabled: boolean): void;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
const TOAST_STICKY_BAR_PROPERTY = "--toast-sticky-bar-height";
|
|
4
|
+
function useToastStickyBarClearance(refs, enabled) {
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
const elements = refs.map((ref) => ref.current).filter((el) => el != null);
|
|
7
|
+
if (!enabled || elements.length === 0 || typeof ResizeObserver === "undefined") {
|
|
8
|
+
return void 0;
|
|
9
|
+
}
|
|
10
|
+
const html = document.documentElement;
|
|
11
|
+
const publish = () => {
|
|
12
|
+
const height = elements.reduce((sum, el) => sum + el.offsetHeight, 0);
|
|
13
|
+
html.style.setProperty(TOAST_STICKY_BAR_PROPERTY, `${height}px`);
|
|
14
|
+
};
|
|
15
|
+
publish();
|
|
16
|
+
const observer = new ResizeObserver(publish);
|
|
17
|
+
for (const el of elements) observer.observe(el);
|
|
18
|
+
return () => {
|
|
19
|
+
observer.disconnect();
|
|
20
|
+
html.style.removeProperty(TOAST_STICKY_BAR_PROPERTY);
|
|
21
|
+
};
|
|
22
|
+
}, [enabled, refs.length]);
|
|
23
|
+
}
|
|
24
|
+
export {
|
|
25
|
+
TOAST_STICKY_BAR_PROPERTY,
|
|
26
|
+
useToastStickyBarClearance
|
|
27
|
+
};
|
|
@@ -180,6 +180,13 @@ export type EmptyStateProp = {
|
|
|
180
180
|
* zero-state message is not announced as a heading and cannot skip an outline level.
|
|
181
181
|
*/
|
|
182
182
|
titleAs?: "h1" | "h2" | "h3" | "h4" | "p" | "div";
|
|
183
|
+
/**
|
|
184
|
+
* Announce the empty state as a status message (`role="status"`). Default `false`: a static
|
|
185
|
+
* empty state ("no records yet") is content, not an update, and is not a live region. Set it
|
|
186
|
+
* when the empty state APPEARS as the result of a user action — a search or filter that matched
|
|
187
|
+
* nothing — so a screen-reader user hears the outcome (WCAG 4.1.3).
|
|
188
|
+
*/
|
|
189
|
+
live?: boolean;
|
|
183
190
|
className?: ClassNameProp;
|
|
184
191
|
};
|
|
185
192
|
/**
|
|
@@ -917,6 +917,18 @@ export type DatePickerBaseProp = FieldA11yProps & PickerChromeProp & {
|
|
|
917
917
|
minDate?: Date;
|
|
918
918
|
maxDate?: Date;
|
|
919
919
|
showWeek?: boolean;
|
|
920
|
+
/**
|
|
921
|
+
* How the day panel's month caption is drawn — forwarded to `Calendar`'s react-day-picker
|
|
922
|
+
* `captionLayout`. `"label"` (default) is the plain month name between the ‹ › arrows;
|
|
923
|
+
* `"dropdown"` turns the month and the year into selects, `"dropdown-months"` /
|
|
924
|
+
* `"dropdown-years"` only one of them.
|
|
925
|
+
*
|
|
926
|
+
* Use `"dropdown"` for a BIRTH DATE or any date far from today (gh#1271): with the arrows alone
|
|
927
|
+
* reaching 1999 is ~320 clicks, with the year select it is one. Without `minDate`/`maxDate` the
|
|
928
|
+
* year list spans 100 years back to the current year — set `maxDate` for a date that can lie in
|
|
929
|
+
* the future (an expiry).
|
|
930
|
+
*/
|
|
931
|
+
captionLayout?: DayPickerProps["captionLayout"];
|
|
920
932
|
needConfirm?: boolean;
|
|
921
933
|
/**
|
|
922
934
|
* Which period the PANEL opens on, independently of the value (antd `defaultPickerValue`, and
|
package/dist/styles/control.css
CHANGED
|
@@ -2790,6 +2790,60 @@
|
|
|
2790
2790
|
font-weight: var(--font-weight-medium);
|
|
2791
2791
|
}
|
|
2792
2792
|
|
|
2793
|
+
.ui-calendar-dropdowns {
|
|
2794
|
+
display: inline-flex;
|
|
2795
|
+
min-inline-size: 0;
|
|
2796
|
+
align-items: center;
|
|
2797
|
+
gap: var(--space-1);
|
|
2798
|
+
}
|
|
2799
|
+
|
|
2800
|
+
.ui-calendar-dropdown-root {
|
|
2801
|
+
position: relative;
|
|
2802
|
+
display: inline-flex;
|
|
2803
|
+
min-inline-size: 0;
|
|
2804
|
+
align-items: center;
|
|
2805
|
+
block-size: var(--control-height-sm);
|
|
2806
|
+
padding-inline: var(--space-2) var(--space-1);
|
|
2807
|
+
border: var(--stroke-hairline) solid hsl(var(--border));
|
|
2808
|
+
border-radius: var(--control-radius);
|
|
2809
|
+
background-color: hsl(var(--background));
|
|
2810
|
+
}
|
|
2811
|
+
|
|
2812
|
+
.ui-calendar-dropdown-root:hover {
|
|
2813
|
+
background-color: hsl(var(--accent));
|
|
2814
|
+
}
|
|
2815
|
+
|
|
2816
|
+
.ui-calendar-dropdown-root[data-disabled="true"] {
|
|
2817
|
+
opacity: var(--calendar-nav-rest-alpha);
|
|
2818
|
+
}
|
|
2819
|
+
|
|
2820
|
+
.ui-calendar-dropdown {
|
|
2821
|
+
position: absolute;
|
|
2822
|
+
inset: 0;
|
|
2823
|
+
z-index: 1;
|
|
2824
|
+
inline-size: 100%;
|
|
2825
|
+
margin: 0;
|
|
2826
|
+
padding: 0;
|
|
2827
|
+
border: 0;
|
|
2828
|
+
opacity: 0;
|
|
2829
|
+
appearance: none;
|
|
2830
|
+
font: inherit;
|
|
2831
|
+
cursor: pointer;
|
|
2832
|
+
}
|
|
2833
|
+
|
|
2834
|
+
.ui-calendar-dropdown-root > .ui-calendar-caption-label {
|
|
2835
|
+
display: inline-flex;
|
|
2836
|
+
align-items: center;
|
|
2837
|
+
gap: var(--space-1);
|
|
2838
|
+
white-space: nowrap;
|
|
2839
|
+
}
|
|
2840
|
+
|
|
2841
|
+
.ui-calendar-dropdown-root .ui-calendar-chevron {
|
|
2842
|
+
inline-size: var(--icon-size-sm);
|
|
2843
|
+
block-size: var(--icon-size-sm);
|
|
2844
|
+
opacity: var(--calendar-nav-rest-alpha);
|
|
2845
|
+
}
|
|
2846
|
+
|
|
2793
2847
|
.ui-calendar-nav {
|
|
2794
2848
|
position: absolute;
|
|
2795
2849
|
inset-inline: 0;
|
|
@@ -2800,9 +2854,12 @@
|
|
|
2800
2854
|
align-items: center;
|
|
2801
2855
|
justify-content: space-between;
|
|
2802
2856
|
padding-inline: var(--calendar-nav-space-inline);
|
|
2857
|
+
|
|
2858
|
+
pointer-events: none;
|
|
2803
2859
|
}
|
|
2804
2860
|
|
|
2805
2861
|
.ui-calendar-nav-button {
|
|
2862
|
+
pointer-events: auto;
|
|
2806
2863
|
background: transparent;
|
|
2807
2864
|
padding: 0;
|
|
2808
2865
|
opacity: var(--calendar-nav-rest-alpha);
|
package/dist/styles/layers.json
CHANGED
|
@@ -541,6 +541,13 @@
|
|
|
541
541
|
overflow-wrap: anywhere;
|
|
542
542
|
}
|
|
543
543
|
|
|
544
|
+
.ui-data-table-ellipsis[data-truncated]:focus-visible [data-truncate],
|
|
545
|
+
.ui-data-table-ellipsis[data-truncated]:has(:focus-visible) [data-truncate] {
|
|
546
|
+
overflow: visible;
|
|
547
|
+
white-space: normal;
|
|
548
|
+
overflow-wrap: anywhere;
|
|
549
|
+
}
|
|
550
|
+
|
|
544
551
|
.ui-data-table-surface[data-table-layout="fixed"] [data-slot="table"] {
|
|
545
552
|
table-layout: fixed;
|
|
546
553
|
}
|