@godxjp/ui 20.2.1 → 22.0.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/dist/app/app-provider.js +2 -1
- package/dist/components/data-display/badge.d.ts +28 -1
- package/dist/components/data-display/badge.js +2 -0
- package/dist/components/data-display/chat-bubble.d.ts +30 -0
- package/dist/components/data-display/chat-bubble.js +229 -0
- package/dist/components/data-display/data-table.d.ts +18 -2
- package/dist/components/data-display/data-table.js +4 -0
- package/dist/components/data-display/descriptions.js +4 -1
- package/dist/components/data-display/index.d.ts +5 -3
- package/dist/components/data-display/index.js +5 -2
- package/dist/components/data-display/popover.d.ts +19 -1
- package/dist/components/data-display/popover.js +12 -3
- package/dist/components/data-display/progress.d.ts +37 -0
- package/dist/components/data-display/progress.js +57 -5
- package/dist/components/data-display/scroll-area.js +26 -1
- package/dist/components/data-display/tree.d.ts +8 -0
- package/dist/components/data-display/tree.js +426 -0
- package/dist/components/data-entry/chat-composer.d.ts +50 -0
- package/dist/components/data-entry/chat-composer.js +163 -0
- package/dist/components/data-entry/chat-suggestion.d.ts +28 -0
- package/dist/components/data-entry/chat-suggestion.js +285 -0
- package/dist/components/data-entry/date-picker.d.ts +10 -3
- package/dist/components/data-entry/date-picker.js +503 -238
- package/dist/components/data-entry/form-field.d.ts +1 -1
- package/dist/components/data-entry/form-field.js +4 -1
- package/dist/components/data-entry/form.js +10 -1
- package/dist/components/data-entry/index.d.ts +4 -6
- package/dist/components/data-entry/index.js +4 -6
- package/dist/components/data-entry/label.d.ts +14 -1
- package/dist/components/data-entry/label.js +6 -1
- package/dist/components/data-entry/radio.d.ts +27 -6
- package/dist/components/data-entry/radio.js +88 -43
- package/dist/components/data-entry/search-select.js +8 -1
- package/dist/components/data-entry/select.js +2 -0
- package/dist/components/data-entry/switch.d.ts +6 -3
- package/dist/components/data-entry/switch.js +34 -11
- package/dist/components/data-entry/textarea.js +3 -1
- package/dist/components/data-entry/tree-utils.d.ts +10 -48
- package/dist/components/data-entry/tree-utils.js +1 -154
- package/dist/components/layout/mobile-shell.d.ts +1 -1
- package/dist/components/layout/mobile-shell.js +2 -0
- package/dist/components/navigation/menubar.d.ts +10 -3
- package/dist/components/navigation/steps.d.ts +1 -1
- package/dist/components/navigation/steps.js +3 -1
- package/dist/components/navigation/tabs-scroll.d.ts +27 -0
- package/dist/components/navigation/tabs-scroll.js +52 -1
- package/dist/components/navigation/tabs.d.ts +2 -2
- package/dist/components/navigation/tabs.js +88 -23
- package/dist/components/ui/index.d.ts +0 -1
- package/dist/components/ui/index.js +0 -1
- package/dist/components/ui/segmented.d.ts +15 -1
- package/dist/components/ui/segmented.js +13 -6
- package/dist/i18n/messages/en.json +48 -2
- package/dist/i18n/messages/ja.json +46 -2
- package/dist/i18n/messages/vi.json +47 -2
- package/dist/lib/datetime/picker-format.d.ts +18 -0
- package/dist/lib/datetime/picker-format.js +35 -1
- package/dist/lib/field-a11y.d.ts +12 -0
- package/dist/lib/field-a11y.js +14 -1
- package/dist/lib/tree.d.ts +53 -0
- package/dist/lib/tree.js +155 -0
- package/dist/props/components/data-display.prop.d.ts +194 -1
- package/dist/props/components/data-entry.prop.d.ts +307 -99
- package/dist/props/components/index.d.ts +1 -1
- package/dist/props/components/layout.prop.d.ts +12 -1
- package/dist/props/components/navigation.prop.d.ts +76 -1
- package/dist/props/registry.d.ts +156 -19
- package/dist/props/registry.js +218 -18
- package/dist/props/vocabulary/data.prop.d.ts +12 -0
- package/dist/props/vocabulary/index.d.ts +2 -2
- package/dist/props/vocabulary/layout.prop.d.ts +11 -0
- package/dist/styles/badge-layout.css +4 -0
- package/dist/styles/card-layout.css +6 -6
- package/dist/styles/control.css +22 -6
- package/dist/styles/data-display-layout.css +385 -68
- package/dist/styles/data-entry-layout.css +64 -0
- package/dist/styles/focus-ring.css +4 -2
- package/dist/styles/navigation-layout.css +25 -0
- package/dist/styles/shell-layout.css +7 -1
- package/dist/styles/table-layout.css +34 -0
- package/dist/tokens/base.css +3 -0
- package/dist/tokens/components/chat-bubble.css +36 -0
- package/dist/tokens/components/chat-composer.css +19 -0
- package/dist/tokens/components/data-display.css +9 -6
- package/dist/tokens/components/descriptions.css +4 -0
- package/dist/tokens/components/navigation.css +6 -0
- package/dist/tokens/components/segmented.css +0 -2
- package/dist/tokens/components/shell.css +3 -0
- package/dist/tokens/components/table.css +3 -0
- package/dist/tokens/components/tree.css +27 -0
- package/dist/tokens/foundation.css +7 -0
- package/docs/COMPONENTS.md +14 -14
- package/docs/CONSUMER-RULES.md +24 -1
- package/docs/FRAME-COVERAGE-LEDGER.md +1 -1
- package/docs/FRAME-COVERAGE-REPORT.md +9 -7
- package/docs/TOKENS.md +65 -0
- package/docs/data-display/badge.tsx +37 -0
- package/docs/data-display/chat-bubble.tsx +397 -0
- package/docs/data-display/data-table/index.tsx +18 -0
- package/docs/data-display/popover.tsx +41 -1
- package/docs/data-display/progress.tsx +28 -0
- package/docs/data-display/timeline.tsx +46 -0
- package/docs/data-display/tree.tsx +394 -0
- package/docs/data-entry/calendar.tsx +2 -2
- package/docs/data-entry/chat-composer.tsx +464 -0
- package/docs/data-entry/chat-suggestion.tsx +301 -0
- package/docs/data-entry/date-picker.tsx +143 -3
- package/docs/data-entry/form/examples/invoice-form.tsx +2 -2
- package/docs/data-entry/form-field/examples/a11y-contract.tsx +3 -5
- package/docs/data-entry/form.tsx +4 -4
- package/docs/data-entry/number-input.tsx +31 -0
- package/docs/data-entry/segmented.tsx +103 -1
- package/docs/layout/mobile-shell.tsx +5 -0
- package/docs/navigation/tabs.tsx +149 -0
- package/docs/roadmap/ai-chat-components.md +207 -0
- package/docs/roadmap/antd-parity.md +155 -0
- package/docs/roadmap/badge-tag-chip-count.md +172 -0
- package/docs/roadmap/list-masonry.md +159 -0
- package/docs/roadmap/parity-audit-data-display-feedback.md +567 -0
- package/docs/roadmap/parity-audit-data-entry.md +357 -0
- package/docs/roadmap/parity-audit-layout-navigation-general.md +464 -0
- package/docs/roadmap/parity-backlog.md +78 -0
- package/docs/roadmap/tree-components.md +151 -0
- package/docs/showcase/acme-portal.tsx +7 -0
- package/docs/showcase/table-tree-rows.tsx +4 -4
- package/package.json +13 -23
- package/scripts/ui-audit.mjs +17 -4
- package/dist/components/data-display/tree-list.d.ts +0 -13
- package/dist/components/data-display/tree-list.js +0 -26
- package/dist/components/data-entry/date-range-picker.d.ts +0 -11
- package/dist/components/data-entry/date-range-picker.js +0 -349
- package/dist/components/data-entry/month-picker.d.ts +0 -10
- package/dist/components/data-entry/month-picker.js +0 -241
- package/dist/components/data-entry/month-range-picker.d.ts +0 -10
- package/dist/components/data-entry/month-range-picker.js +0 -301
- package/dist/components/ui/date-range-picker.d.ts +0 -1
- package/dist/components/ui/date-range-picker.js +0 -2
- package/docs/data-display/tree-list.tsx +0 -107
- package/docs/data-entry/date-range-picker.tsx +0 -175
- package/docs/data-entry/month-picker.tsx +0 -101
- package/docs/data-entry/month-range-picker.tsx +0 -115
package/dist/app/app-provider.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
+
import { I18nProvider } from "react-aria-components";
|
|
4
5
|
import { resolveDefaultDateFormat } from "./date-format-labels.js";
|
|
5
6
|
import { getDateFnsLocale, getDayPickerLocale } from "./locales.js";
|
|
6
7
|
import { syncAppRequestHeaders } from "./request-headers.js";
|
|
@@ -362,7 +363,7 @@ function AppProvider({
|
|
|
362
363
|
setScaling
|
|
363
364
|
]
|
|
364
365
|
);
|
|
365
|
-
return /* @__PURE__ */ jsx(AppContext.Provider, { value, children });
|
|
366
|
+
return /* @__PURE__ */ jsx(I18nProvider, { locale, children: /* @__PURE__ */ jsx(AppContext.Provider, { value, children }) });
|
|
366
367
|
}
|
|
367
368
|
function useAppContext() {
|
|
368
369
|
const ctx = React.useContext(AppContext);
|
|
@@ -45,9 +45,36 @@ export interface BadgeProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "
|
|
|
45
45
|
className?: string;
|
|
46
46
|
}> | null;
|
|
47
47
|
status?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Lining, fixed-width figures — for a chip whose content is a COUNT that sits in a column with
|
|
50
|
+
* other counts (a queue length per row, an unread tally per tab, a defect count per site). With
|
|
51
|
+
* proportional figures a `1` is narrower than a `0`, so the chips jitter and the numbers do not
|
|
52
|
+
* line up down the column; it is the same axis `Text`, `TableCell` and `StatCard` already carry,
|
|
53
|
+
* and its absence here was the asymmetry rather than a decision.
|
|
54
|
+
*
|
|
55
|
+
* MEASURE IT BEFORE YOU EXPECT PIXELS TO MOVE. With the stack this library ships,
|
|
56
|
+
* `font-variant-numeric` changes nothing: measured in Chromium at the chip's own 12.47px,
|
|
57
|
+
* `"1111"` and `"0000"` both advance **31.094px** with and without `tabular-nums`, because the
|
|
58
|
+
* resolved face already gives its figures one advance. What this prop buys is the DECLARATION —
|
|
59
|
+
* it survives a service retuning `--font-family-sans` to a face whose figures are proportional,
|
|
60
|
+
* which is the same reason `Text`, `TableCell` and `StatCard` carry the axis.
|
|
61
|
+
*
|
|
62
|
+
* It is also NOT the fix for chips of DIFFERENT WIDTHS down a column: `11` and `100` are two
|
|
63
|
+
* and three figures, so they are two widths whatever the face does. That is a minimum measure on
|
|
64
|
+
* the chip, and it belongs to the screen.
|
|
65
|
+
*
|
|
66
|
+
* There WAS a legal move — `<Badge><Text size="xs" tabular>12</Text></Badge>` measures the same
|
|
67
|
+
* 12.47px as the chip's own label. It just requires the call site to know that a Badge's type
|
|
68
|
+
* step is `xs`, which is the chip's own `--badge-font-size` token and not a fact a consumer
|
|
69
|
+
* should have to carry: retune that token in a theme and every such call site silently desyncs.
|
|
70
|
+
*
|
|
71
|
+
* Off by default. Tabular figures are wider than proportional ones in a face that has both, so a
|
|
72
|
+
* chip carrying WORDS should not pay for them.
|
|
73
|
+
*/
|
|
74
|
+
tabular?: boolean;
|
|
48
75
|
children?: React.ReactNode;
|
|
49
76
|
}
|
|
50
|
-
export declare function Badge({ as: Element, className, variant, shape, tone, color, icon, status, style, children, ...props }: BadgeProps): React.JSX.Element;
|
|
77
|
+
export declare function Badge({ as: Element, className, variant, shape, tone, color, icon, status, tabular, style, children, ...props }: BadgeProps): React.JSX.Element;
|
|
51
78
|
/**
|
|
52
79
|
* Status-aware badge with the shared domain-status-to-tone mapping. `Badge` remains the
|
|
53
80
|
* general-purpose primitive.
|
|
@@ -97,6 +97,7 @@ function Badge({
|
|
|
97
97
|
color,
|
|
98
98
|
icon,
|
|
99
99
|
status,
|
|
100
|
+
tabular,
|
|
100
101
|
style,
|
|
101
102
|
children,
|
|
102
103
|
...props
|
|
@@ -114,6 +115,7 @@ function Badge({
|
|
|
114
115
|
"data-tone": tinted ? void 0 : resolvedTone,
|
|
115
116
|
"data-tinted": tinted ? "" : void 0,
|
|
116
117
|
"data-shape": shape ?? "default",
|
|
118
|
+
"data-tabular": tabular ? "" : void 0,
|
|
117
119
|
className: cn(
|
|
118
120
|
badgeVariants({
|
|
119
121
|
variant: tinted ? "tinted" : variant ?? "default",
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { ChatBubbleListProp, ChatBubbleProp } from "../../props/components/data-display.prop.js";
|
|
3
|
+
export type { ChatBubbleListProp, ChatBubblePlacementProp, ChatBubbleProp, ChatBubbleToneProp, ChatBubbleTypingProp, ChatBubbleVariantProp, ChatMessageProp, } from "../../props/components/data-display.prop.js";
|
|
4
|
+
export type ChatBubbleProps = ChatBubbleProp & Omit<React.HTMLAttributes<HTMLElement>, keyof ChatBubbleProp>;
|
|
5
|
+
/**
|
|
6
|
+
* ChatBubble — ONE message in a conversation: an optional author mark, a header naming the turn,
|
|
7
|
+
* the message body, and a footer for timestamps or per-message actions.
|
|
8
|
+
*
|
|
9
|
+
* It is an `<article>` inside the feed's `role="log"`, and it deliberately owns NO live region of
|
|
10
|
+
* its own: one live region per bubble floods a screen reader on every token of a stream. The feed
|
|
11
|
+
* (`ChatBubbleList`) owns the single `aria-live="polite"` region; a bubble only ever says "I am
|
|
12
|
+
* still being produced" through `aria-busy`.
|
|
13
|
+
*/
|
|
14
|
+
export declare const ChatBubble: React.ForwardRefExoticComponent<ChatBubbleProp & Omit<React.HTMLAttributes<HTMLElement>, keyof ChatBubbleProp> & React.RefAttributes<HTMLElement>>;
|
|
15
|
+
export type ChatBubbleListProps = ChatBubbleListProp & Omit<React.HTMLAttributes<HTMLDivElement>, keyof ChatBubbleListProp>;
|
|
16
|
+
/**
|
|
17
|
+
* ChatBubbleList — the message feed.
|
|
18
|
+
*
|
|
19
|
+
* The behaviour that earns this a component rather than a `ScrollArea` + `map` is STICK-TO-BOTTOM:
|
|
20
|
+
* new messages keep the viewport pinned only while the reader is ALREADY at the bottom. The moment
|
|
21
|
+
* they scroll up to re-read something the pin is revoked and stays revoked until they ask for it
|
|
22
|
+
* back — silently yanking a reader to the newest message mid-sentence is a change of context they
|
|
23
|
+
* did not request (WCAG 3.2.5), and it is the classic chat-UI defect. The way back is a real,
|
|
24
|
+
* focusable button that also says how many messages arrived while they were away.
|
|
25
|
+
*
|
|
26
|
+
* GIVE IT A DEFINITE HEIGHT — `className="h-96"`, or a flex/grid parent that hands it a track.
|
|
27
|
+
* The feed scrolls INSIDE itself; with no height it simply grows to its content, nothing ever
|
|
28
|
+
* overflows, and every behaviour above has nothing to act on.
|
|
29
|
+
*/
|
|
30
|
+
export declare const ChatBubbleList: React.ForwardRefExoticComponent<ChatBubbleListProp & Omit<React.HTMLAttributes<HTMLDivElement>, keyof ChatBubbleListProp> & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { ArrowDown } from "lucide-react";
|
|
5
|
+
import { useTranslation } from "../../i18n/use-translation.js";
|
|
6
|
+
import { useMediaQuery } from "../../lib/hooks.js";
|
|
7
|
+
import { cn } from "../../lib/utils.js";
|
|
8
|
+
import { Skeleton } from "../feedback/skeleton.js";
|
|
9
|
+
import { Button } from "../general/button.js";
|
|
10
|
+
import { Text } from "../general/typography.js";
|
|
11
|
+
import { VisuallyHidden } from "../general/visually-hidden.js";
|
|
12
|
+
import { ScrollArea } from "./scroll-area.js";
|
|
13
|
+
const BODY_SIZE = {
|
|
14
|
+
xs: "2xs",
|
|
15
|
+
sm: "xs",
|
|
16
|
+
md: "sm",
|
|
17
|
+
lg: "lg"
|
|
18
|
+
};
|
|
19
|
+
const META_SIZE = {
|
|
20
|
+
xs: "2xs",
|
|
21
|
+
sm: "2xs",
|
|
22
|
+
md: "xs",
|
|
23
|
+
lg: "sm"
|
|
24
|
+
};
|
|
25
|
+
const TYPING_STEP = 1;
|
|
26
|
+
const TYPING_INTERVAL = 50;
|
|
27
|
+
function resolveTyping(typing) {
|
|
28
|
+
if (typing === void 0 || typing === false) {
|
|
29
|
+
return { enabled: false, step: TYPING_STEP, interval: TYPING_INTERVAL };
|
|
30
|
+
}
|
|
31
|
+
if (typing === true) return { enabled: true, step: TYPING_STEP, interval: TYPING_INTERVAL };
|
|
32
|
+
return {
|
|
33
|
+
enabled: true,
|
|
34
|
+
// A non-positive step would never reach the end of the string and a non-positive interval
|
|
35
|
+
// would spin the timer — both are "the animation never completes", which is the one outcome
|
|
36
|
+
// a reader can neither read nor dismiss.
|
|
37
|
+
step: typing.step != null && typing.step > 0 ? Math.floor(typing.step) : TYPING_STEP,
|
|
38
|
+
interval: typing.interval != null && typing.interval > 0 ? typing.interval : TYPING_INTERVAL
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function useRevealedLength(text, enabled, step, interval) {
|
|
42
|
+
const full = text?.length ?? 0;
|
|
43
|
+
const [revealed, setRevealed] = React.useState(() => enabled ? 0 : full);
|
|
44
|
+
React.useEffect(() => {
|
|
45
|
+
if (!enabled || text === void 0) {
|
|
46
|
+
setRevealed(text?.length ?? 0);
|
|
47
|
+
return void 0;
|
|
48
|
+
}
|
|
49
|
+
setRevealed(0);
|
|
50
|
+
let shown = 0;
|
|
51
|
+
const timer = setInterval(() => {
|
|
52
|
+
shown = Math.min(text.length, shown + step);
|
|
53
|
+
setRevealed(shown);
|
|
54
|
+
if (shown >= text.length) clearInterval(timer);
|
|
55
|
+
}, interval);
|
|
56
|
+
return () => {
|
|
57
|
+
clearInterval(timer);
|
|
58
|
+
};
|
|
59
|
+
}, [text, enabled, step, interval]);
|
|
60
|
+
return Math.min(revealed, full);
|
|
61
|
+
}
|
|
62
|
+
const TONE_KEY = {
|
|
63
|
+
info: "chat.bubble.tone.info",
|
|
64
|
+
success: "chat.bubble.tone.success",
|
|
65
|
+
warning: "chat.bubble.tone.warning",
|
|
66
|
+
destructive: "chat.bubble.tone.destructive"
|
|
67
|
+
};
|
|
68
|
+
const ChatBubble = React.forwardRef(
|
|
69
|
+
({
|
|
70
|
+
children,
|
|
71
|
+
placement = "start",
|
|
72
|
+
variant = "filled",
|
|
73
|
+
avatar,
|
|
74
|
+
header,
|
|
75
|
+
footer,
|
|
76
|
+
loading = false,
|
|
77
|
+
typing,
|
|
78
|
+
size = "md",
|
|
79
|
+
tone = "default",
|
|
80
|
+
className,
|
|
81
|
+
id,
|
|
82
|
+
...props
|
|
83
|
+
}, ref) => {
|
|
84
|
+
const { t } = useTranslation();
|
|
85
|
+
const generatedId = React.useId();
|
|
86
|
+
const headerId = `${id ?? generatedId}-header`;
|
|
87
|
+
const reduceMotion = useMediaQuery("(prefers-reduced-motion: reduce)");
|
|
88
|
+
const text = typeof children === "string" ? children : void 0;
|
|
89
|
+
const { enabled, step, interval } = resolveTyping(typing);
|
|
90
|
+
const animates = enabled && !loading && text !== void 0 && !reduceMotion;
|
|
91
|
+
const revealed = useRevealedLength(text, animates, step, interval);
|
|
92
|
+
const streaming = animates && revealed < (text?.length ?? 0);
|
|
93
|
+
const body = loading ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
94
|
+
/* @__PURE__ */ jsxs("div", { className: "ui-chat-bubble-skeleton", "aria-hidden": "true", children: [
|
|
95
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "ui-chat-bubble-skeleton-line" }),
|
|
96
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "ui-chat-bubble-skeleton-line ui-chat-bubble-skeleton-line--short" })
|
|
97
|
+
] }),
|
|
98
|
+
/* @__PURE__ */ jsx(VisuallyHidden, { children: t("chat.bubble.loading") })
|
|
99
|
+
] }) : streaming ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
100
|
+
/* @__PURE__ */ jsxs(Text, { as: "div", size: BODY_SIZE[size], whitespace: "pre-wrap", "aria-hidden": "true", children: [
|
|
101
|
+
text?.slice(0, revealed),
|
|
102
|
+
/* @__PURE__ */ jsx("span", { className: "ui-chat-bubble-caret" })
|
|
103
|
+
] }),
|
|
104
|
+
/* @__PURE__ */ jsx(VisuallyHidden, { children: t("chat.bubble.typing") })
|
|
105
|
+
] }) : /* @__PURE__ */ jsx(
|
|
106
|
+
Text,
|
|
107
|
+
{
|
|
108
|
+
as: "div",
|
|
109
|
+
size: BODY_SIZE[size],
|
|
110
|
+
whitespace: text !== void 0 ? "pre-wrap" : void 0,
|
|
111
|
+
children
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
return /* @__PURE__ */ jsxs(
|
|
115
|
+
"article",
|
|
116
|
+
{
|
|
117
|
+
ref,
|
|
118
|
+
id,
|
|
119
|
+
"data-slot": "chat-bubble",
|
|
120
|
+
"data-placement": placement,
|
|
121
|
+
"data-variant": variant,
|
|
122
|
+
"data-size": size,
|
|
123
|
+
"data-tone": tone,
|
|
124
|
+
"aria-busy": loading || streaming ? true : void 0,
|
|
125
|
+
"aria-labelledby": header != null ? headerId : void 0,
|
|
126
|
+
className: cn("ui-chat-bubble", className),
|
|
127
|
+
...props,
|
|
128
|
+
children: [
|
|
129
|
+
avatar != null ? /* @__PURE__ */ jsx("span", { "data-slot": "chat-bubble-avatar", className: "ui-chat-bubble-avatar", children: avatar }) : null,
|
|
130
|
+
/* @__PURE__ */ jsxs("div", { "data-slot": "chat-bubble-column", className: "ui-chat-bubble-column", children: [
|
|
131
|
+
header != null ? /* @__PURE__ */ jsx(
|
|
132
|
+
Text,
|
|
133
|
+
{
|
|
134
|
+
as: "div",
|
|
135
|
+
id: headerId,
|
|
136
|
+
className: "ui-chat-bubble-header",
|
|
137
|
+
size: META_SIZE[size],
|
|
138
|
+
tone: "muted",
|
|
139
|
+
weight: "medium",
|
|
140
|
+
children: header
|
|
141
|
+
}
|
|
142
|
+
) : null,
|
|
143
|
+
/* @__PURE__ */ jsxs("div", { "data-slot": "chat-bubble-body", className: "ui-chat-bubble-body", children: [
|
|
144
|
+
tone !== "default" ? /* @__PURE__ */ jsx(VisuallyHidden, { children: t(TONE_KEY[tone]) }) : null,
|
|
145
|
+
body
|
|
146
|
+
] }),
|
|
147
|
+
footer != null ? /* @__PURE__ */ jsx(Text, { as: "div", className: "ui-chat-bubble-footer", size: META_SIZE[size], tone: "muted", children: footer }) : null
|
|
148
|
+
] })
|
|
149
|
+
]
|
|
150
|
+
}
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
ChatBubble.displayName = "ChatBubble";
|
|
155
|
+
const ChatBubbleList = React.forwardRef(
|
|
156
|
+
({ items, roles, autoScroll = true, label, className, id, ...props }, ref) => {
|
|
157
|
+
const { t } = useTranslation();
|
|
158
|
+
const viewportRef = React.useRef(null);
|
|
159
|
+
const [anchored, setAnchored] = React.useState(true);
|
|
160
|
+
const seen = React.useRef(items.length);
|
|
161
|
+
React.useEffect(() => {
|
|
162
|
+
if (anchored) seen.current = items.length;
|
|
163
|
+
}, [anchored, items.length]);
|
|
164
|
+
const pending = anchored ? 0 : Math.max(0, items.length - seen.current);
|
|
165
|
+
const jumpToLatest = React.useCallback(() => {
|
|
166
|
+
const viewport = viewportRef.current;
|
|
167
|
+
if (viewport) {
|
|
168
|
+
const bottom = Math.max(0, viewport.scrollHeight - viewport.clientHeight);
|
|
169
|
+
if (typeof viewport.scrollTo === "function") {
|
|
170
|
+
viewport.scrollTo({ top: bottom, behavior: "instant" });
|
|
171
|
+
} else {
|
|
172
|
+
viewport.scrollTop = bottom;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
setAnchored(true);
|
|
176
|
+
}, []);
|
|
177
|
+
return /* @__PURE__ */ jsxs(
|
|
178
|
+
"div",
|
|
179
|
+
{
|
|
180
|
+
ref,
|
|
181
|
+
id,
|
|
182
|
+
"data-slot": "chat-bubble-list",
|
|
183
|
+
"data-anchored": anchored ? "" : void 0,
|
|
184
|
+
className: cn("ui-chat-bubble-list", className),
|
|
185
|
+
...props,
|
|
186
|
+
children: [
|
|
187
|
+
/* @__PURE__ */ jsx(
|
|
188
|
+
ScrollArea,
|
|
189
|
+
{
|
|
190
|
+
className: "ui-chat-bubble-list-scroller",
|
|
191
|
+
anchor: autoScroll ? "bottom" : "none",
|
|
192
|
+
viewportRef,
|
|
193
|
+
onAnchoredChange: setAnchored,
|
|
194
|
+
children: /* @__PURE__ */ jsx(
|
|
195
|
+
"div",
|
|
196
|
+
{
|
|
197
|
+
role: "log",
|
|
198
|
+
"aria-live": "polite",
|
|
199
|
+
"aria-label": label ?? t("chat.list.label"),
|
|
200
|
+
"data-slot": "chat-bubble-list-feed",
|
|
201
|
+
className: "ui-chat-bubble-list-feed",
|
|
202
|
+
children: items.map(({ id: messageId, role, content, ...message }) => /* @__PURE__ */ jsx(
|
|
203
|
+
ChatBubble,
|
|
204
|
+
{
|
|
205
|
+
id: messageId,
|
|
206
|
+
...role != null ? roles?.[role] : void 0,
|
|
207
|
+
...message,
|
|
208
|
+
children: content
|
|
209
|
+
},
|
|
210
|
+
messageId
|
|
211
|
+
))
|
|
212
|
+
}
|
|
213
|
+
)
|
|
214
|
+
}
|
|
215
|
+
),
|
|
216
|
+
anchored ? null : /* @__PURE__ */ jsx("div", { "data-slot": "chat-bubble-list-jump", className: "ui-chat-bubble-list-jump", children: /* @__PURE__ */ jsxs(Button, { type: "button", size: "sm", variant: "secondary", shape: "pill", onClick: jumpToLatest, children: [
|
|
217
|
+
/* @__PURE__ */ jsx(ArrowDown, { "aria-hidden": "true" }),
|
|
218
|
+
pending > 0 ? t("chat.list.newMessages", { count: pending }) : t("chat.list.jumpToLatest")
|
|
219
|
+
] }) })
|
|
220
|
+
]
|
|
221
|
+
}
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
);
|
|
225
|
+
ChatBubbleList.displayName = "ChatBubbleList";
|
|
226
|
+
export {
|
|
227
|
+
ChatBubble,
|
|
228
|
+
ChatBubbleList
|
|
229
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { flexRender, type ColumnVisibilityState, type OnChangeFn, type PaginationState } from "@tanstack/react-table";
|
|
3
|
-
import type { BreakpointProp, ColumnDefProp, DensityProp, OnColumnFilterChangeProp, OnRowProp, SortDirectionProp, SortStateProp, TableExpandableProp, TablePaginationProp, TablePresetProp, TableRowSelectionProp, TableScrollProp, TableStickyProp, TableSummaryProp } from "../../props/vocabulary/index.js";
|
|
3
|
+
import type { BreakpointProp, ColumnDefProp, DensityProp, OnColumnFilterChangeProp, OnRowProp, RowToneProp, SortDirectionProp, SortStateProp, TableExpandableProp, TablePaginationProp, TablePresetProp, TableRowSelectionProp, TableScrollProp, TableStickyProp, TableSummaryProp } from "../../props/vocabulary/index.js";
|
|
4
4
|
export type Density = DensityProp;
|
|
5
5
|
/**
|
|
6
6
|
* Lean column definition — the simple, common-case column API. `render` shapes a cell; `sortable`
|
|
@@ -82,6 +82,22 @@ interface DataTableProps<T> {
|
|
|
82
82
|
* classes are appended last, so they win over the built-in hover/selected fills.
|
|
83
83
|
*/
|
|
84
84
|
rowClassName?: (row: T) => string | undefined;
|
|
85
|
+
/**
|
|
86
|
+
* Per-row STATE, drawn as a leading-edge rail plus a wash — the same six tone names `Card accent`
|
|
87
|
+
* carries, so a row that needs attention and a card that needs attention read as one vocabulary.
|
|
88
|
+
* Return `undefined` for an ordinary row.
|
|
89
|
+
*
|
|
90
|
+
* This is the token-driven answer to what `rowClassName` could only do with utilities, and
|
|
91
|
+
* `ui-audit` blocks those in a consumer: `rowClassName` matches the gate's `class`-name suffix
|
|
92
|
+
* rule, so every colour and spacing rule scans the arrow body — a leading-edge border utility
|
|
93
|
+
* plus a raw palette fill in there is two errors, not zero. Consumers fell back to a badge in a
|
|
94
|
+
* cell instead.
|
|
95
|
+
*
|
|
96
|
+
* It is never the ONLY signal: colour alone cannot carry meaning (WCAG 1.4.1), so keep the
|
|
97
|
+
* reason in a cell — a Badge, a status column — and let the rail make that cell findable in a
|
|
98
|
+
* long table.
|
|
99
|
+
*/
|
|
100
|
+
rowTone?: RowToneProp<T>;
|
|
85
101
|
/**
|
|
86
102
|
* Full row-selection configuration (antd `rowSelection`). Supersedes — and can be mixed with —
|
|
87
103
|
* `selectable` / `selected` / `onSelectChange`, which drive the same state.
|
|
@@ -114,7 +130,7 @@ interface DataTableProps<T> {
|
|
|
114
130
|
className?: string;
|
|
115
131
|
children?: React.ReactNode;
|
|
116
132
|
}
|
|
117
|
-
export declare function DataTable<T>({ data, columns, getRowId, selectable, selected: controlledSelected, onSelectChange, onRowClick, density: controlledDensity, onDensityChange, sort, onSortChange, globalFilter: controlledGlobalFilter, onGlobalFilterChange, pagination: paginationProp, onPaginationChange, rowCount, columnVisibility: controlledVisibility, onColumnVisibilityChange, manualSorting, manualFiltering, manualPagination, loading, empty, error, denied, onRetry, striped, hoverable, stickyHeader, preset, collapseBelow, rowClassName, rowSelection, expandable, summary, scroll, sticky, onRow, bordered, showSorterTooltip, sortDirections, onFilterChange, className, children, }: DataTableProps<T>): React.JSX.Element;
|
|
133
|
+
export declare function DataTable<T>({ data, columns, getRowId, selectable, selected: controlledSelected, onSelectChange, onRowClick, density: controlledDensity, onDensityChange, sort, onSortChange, globalFilter: controlledGlobalFilter, onGlobalFilterChange, pagination: paginationProp, onPaginationChange, rowCount, columnVisibility: controlledVisibility, onColumnVisibilityChange, manualSorting, manualFiltering, manualPagination, loading, empty, error, denied, onRetry, striped, hoverable, stickyHeader, preset, collapseBelow, rowClassName, rowTone, rowSelection, expandable, summary, scroll, sticky, onRow, bordered, showSorterTooltip, sortDirections, onFilterChange, className, children, }: DataTableProps<T>): React.JSX.Element;
|
|
118
134
|
export declare namespace DataTable {
|
|
119
135
|
var Toolbar: ({ children, className, }: {
|
|
120
136
|
children?: React.ReactNode;
|
|
@@ -219,6 +219,7 @@ function DataTable({
|
|
|
219
219
|
preset = "default",
|
|
220
220
|
collapseBelow = "sm",
|
|
221
221
|
rowClassName,
|
|
222
|
+
rowTone,
|
|
222
223
|
rowSelection,
|
|
223
224
|
expandable,
|
|
224
225
|
summary,
|
|
@@ -431,6 +432,7 @@ function DataTable({
|
|
|
431
432
|
preset,
|
|
432
433
|
collapseBelow,
|
|
433
434
|
rowClassName,
|
|
435
|
+
rowTone,
|
|
434
436
|
bordered,
|
|
435
437
|
scroll,
|
|
436
438
|
sticky,
|
|
@@ -751,6 +753,7 @@ DataTable.Content = function DataTableContent() {
|
|
|
751
753
|
preset,
|
|
752
754
|
collapseBelow,
|
|
753
755
|
rowClassName,
|
|
756
|
+
rowTone,
|
|
754
757
|
bordered,
|
|
755
758
|
scroll,
|
|
756
759
|
sticky,
|
|
@@ -1106,6 +1109,7 @@ DataTable.Content = function DataTableContent() {
|
|
|
1106
1109
|
{
|
|
1107
1110
|
...rowProps,
|
|
1108
1111
|
"data-state": isSelected ? "selected" : void 0,
|
|
1112
|
+
"data-tone": rowTone?.(original),
|
|
1109
1113
|
tabIndex: onRowClick ? 0 : rowProps.tabIndex,
|
|
1110
1114
|
onClick: (e) => {
|
|
1111
1115
|
rowProps.onClick?.(e);
|
|
@@ -104,7 +104,10 @@ function DescriptionsItem({ label, mono, span, className, children }) {
|
|
|
104
104
|
"data-slot": "descriptions-label",
|
|
105
105
|
"data-label-align": endAlignedLabel ? "end" : void 0,
|
|
106
106
|
className: cn(
|
|
107
|
-
|
|
107
|
+
// Size comes from the token pair, NOT from `text-xs`: a hard-coded utility here left the
|
|
108
|
+
// label one type step below the value beside it, and out of reach of a service theme.
|
|
109
|
+
// The muted colour is what distinguishes a label from its value.
|
|
110
|
+
"text-muted-foreground text-[length:var(--descriptions-label-font-size)] leading-[var(--descriptions-label-line-height)]",
|
|
108
111
|
// `end`-align only ever applies in horizontal layout — same guard `Form` uses, so a
|
|
109
112
|
endAlignedLabel && "text-end"
|
|
110
113
|
),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { Badge, StatusBadge } from "./badge.js";
|
|
2
|
-
export type { BadgeProps } from "./badge.js";
|
|
2
|
+
export type { BadgeProps, BadgeTone } from "./badge.js";
|
|
3
3
|
export { Legend } from "./legend.js";
|
|
4
4
|
export type { LegendItemProp, LegendProp, LegendProps } from "./legend.js";
|
|
5
5
|
export { ListRow } from "./list-row.js";
|
|
@@ -26,8 +26,6 @@ export type { CodeBlockProp, CodeBlockProps } from "./code-block.js";
|
|
|
26
26
|
export { Prose } from "./prose.js";
|
|
27
27
|
export type { ProseProp, ProseProps } from "./prose.js";
|
|
28
28
|
export type { ProgressProps, ProgressSegment, ProgressTone } from "./progress.js";
|
|
29
|
-
export { TreeList } from "./tree-list.js";
|
|
30
|
-
export type { TreeListItem, TreeListProps } from "./tree-list.js";
|
|
31
29
|
export { Timeline } from "./timeline.js";
|
|
32
30
|
export type { TimelineItem, TimelineProps } from "./timeline.js";
|
|
33
31
|
export { TimelineGrid } from "./timeline-grid.js";
|
|
@@ -43,3 +41,7 @@ export { PermissionMatrix } from "./permission-matrix.js";
|
|
|
43
41
|
export type { PermissionMatrixGrantsProp, PermissionMatrixPermissionProp, PermissionMatrixProp, PermissionMatrixProps, PermissionMatrixRoleProp, } from "./permission-matrix.js";
|
|
44
42
|
export { RangeTimeline } from "./range-timeline.js";
|
|
45
43
|
export type { RangeTimelineProps, RangeTimelineRow } from "./range-timeline.js";
|
|
44
|
+
export { ChatBubble, ChatBubbleList } from "./chat-bubble.js";
|
|
45
|
+
export type { ChatBubbleListProp, ChatBubbleListProps, ChatBubblePlacementProp, ChatBubbleProp, ChatBubbleProps, ChatBubbleToneProp, ChatBubbleTypingProp, ChatBubbleVariantProp, ChatMessageProp, } from "./chat-bubble.js";
|
|
46
|
+
export { Tree } from "./tree.js";
|
|
47
|
+
export type { TreeNodeProp, TreeProp, TreeProps } from "./tree.js";
|
|
@@ -28,7 +28,6 @@ import { EmptyState } from "./empty-state.js";
|
|
|
28
28
|
import { Progress } from "./progress.js";
|
|
29
29
|
import { CodeBlock } from "./code-block.js";
|
|
30
30
|
import { Prose } from "./prose.js";
|
|
31
|
-
import { TreeList } from "./tree-list.js";
|
|
32
31
|
import { Timeline } from "./timeline.js";
|
|
33
32
|
import { TimelineGrid } from "./timeline-grid.js";
|
|
34
33
|
import {
|
|
@@ -55,6 +54,8 @@ import {
|
|
|
55
54
|
} from "./carousel.js";
|
|
56
55
|
import { PermissionMatrix } from "./permission-matrix.js";
|
|
57
56
|
import { RangeTimeline } from "./range-timeline.js";
|
|
57
|
+
import { ChatBubble, ChatBubbleList } from "./chat-bubble.js";
|
|
58
|
+
import { Tree } from "./tree.js";
|
|
58
59
|
export {
|
|
59
60
|
Accordion,
|
|
60
61
|
AccordionContent,
|
|
@@ -79,6 +80,8 @@ export {
|
|
|
79
80
|
CarouselItem,
|
|
80
81
|
CarouselNext,
|
|
81
82
|
CarouselPrevious,
|
|
83
|
+
ChatBubble,
|
|
84
|
+
ChatBubbleList,
|
|
82
85
|
CodeBlock,
|
|
83
86
|
Collapsible,
|
|
84
87
|
CollapsibleContent,
|
|
@@ -119,7 +122,7 @@ export {
|
|
|
119
122
|
TableRow,
|
|
120
123
|
Timeline,
|
|
121
124
|
TimelineGrid,
|
|
122
|
-
|
|
125
|
+
Tree,
|
|
123
126
|
flexRender,
|
|
124
127
|
useCarousel
|
|
125
128
|
};
|
|
@@ -33,7 +33,25 @@ export declare function PopoverAnchor({ asChild, ref, ...props }: PopoverAnchorP
|
|
|
33
33
|
type PopoverContentFlush = {
|
|
34
34
|
flush?: FlushProp;
|
|
35
35
|
};
|
|
36
|
+
/**
|
|
37
|
+
* How the panel is MEASURED across the inline axis.
|
|
38
|
+
*
|
|
39
|
+
* `"panel"` (default) is the popover's own measure, `--popover-width` (18rem) — right for prose and
|
|
40
|
+
* for a list the panel itself sizes. `"auto"` lets the CONTENT decide, which is the only correct
|
|
41
|
+
* answer for something that already has a width of its own: a two-month `Calendar` is far wider
|
|
42
|
+
* than 18rem and was being clipped by it. `"trigger"` matches the anchor, the shape every
|
|
43
|
+
* select-like control in this library wants.
|
|
44
|
+
*
|
|
45
|
+
* The axis exists because `flush` had no counterpart. The catalog's own rule says a panel must
|
|
46
|
+
* never zero its padding with a `p-0` utility, "that is a per-call-site constant no service theme
|
|
47
|
+
* can retune" — and until now the only way to get a content-width panel was exactly such a
|
|
48
|
+
* constant, `className="w-auto"`. The library's internal `.ui-control-panel-flush` has always set
|
|
49
|
+
* both knobs; this is the public half of it.
|
|
50
|
+
*/
|
|
51
|
+
type PopoverContentWidth = "panel" | "auto" | "trigger";
|
|
36
52
|
interface PopoverContentProps extends React.ComponentPropsWithRef<"div">, PopoverContentFlush {
|
|
53
|
+
/** Inline measure of the panel — `panel` (default) · `auto` (content decides) · `trigger`. */
|
|
54
|
+
width?: PopoverContentWidth;
|
|
37
55
|
/**
|
|
38
56
|
* Render THIS panel somewhere other than the default. Almost always unnecessary: mounting the
|
|
39
57
|
* whole tree in a shadow root is what `OverlayPortalProvider` is for, and it moves every overlay
|
|
@@ -73,7 +91,7 @@ interface PopoverContentProps extends React.ComponentPropsWithRef<"div">, Popove
|
|
|
73
91
|
*/
|
|
74
92
|
onCloseAutoFocus?: (event: Event) => void;
|
|
75
93
|
}
|
|
76
|
-
export declare function PopoverContent({ className, style, children, ref, flush, side, align, sideOffset, alignOffset, avoidCollisions, collisionPadding, sticky: _sticky, hideWhenDetached: _hideWhenDetached, forceMount: _forceMount, onOpenAutoFocus, onCloseAutoFocus, portalContainer, ...props }: PopoverContentProps): React.JSX.Element;
|
|
94
|
+
export declare function PopoverContent({ className, style, children, ref, flush, width, side, align, sideOffset, alignOffset, avoidCollisions, collisionPadding, sticky: _sticky, hideWhenDetached: _hideWhenDetached, forceMount: _forceMount, onOpenAutoFocus, onCloseAutoFocus, portalContainer, ...props }: PopoverContentProps): React.JSX.Element;
|
|
77
95
|
export declare const PopoverHeader: ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => React.JSX.Element;
|
|
78
96
|
export declare const PopoverTitle: ({ className, id, ...props }: React.HTMLAttributes<HTMLDivElement>) => React.JSX.Element;
|
|
79
97
|
export declare const PopoverDescription: ({ className, id, ...props }: React.HTMLAttributes<HTMLParagraphElement>) => React.JSX.Element;
|
|
@@ -126,12 +126,18 @@ function PopoverAnchor({ asChild, ref, ...props }) {
|
|
|
126
126
|
}, [setAnchored]);
|
|
127
127
|
return /* @__PURE__ */ jsx(Comp, { "data-slot": "popover-anchor", ...props, ref: mergeRefs(ref, root.anchorRef) });
|
|
128
128
|
}
|
|
129
|
+
const POPOVER_SURFACE_INLINE_SIZE = {
|
|
130
|
+
panel: null,
|
|
131
|
+
auto: "auto",
|
|
132
|
+
trigger: "var(--trigger-width)"
|
|
133
|
+
};
|
|
129
134
|
function PopoverContent({
|
|
130
135
|
className,
|
|
131
136
|
style,
|
|
132
137
|
children,
|
|
133
138
|
ref,
|
|
134
139
|
flush,
|
|
140
|
+
width,
|
|
135
141
|
side = "bottom",
|
|
136
142
|
align = "center",
|
|
137
143
|
sideOffset = 4,
|
|
@@ -165,7 +171,7 @@ function PopoverContent({
|
|
|
165
171
|
}
|
|
166
172
|
}
|
|
167
173
|
}, [root.open]);
|
|
168
|
-
const { open, setOpen, triggerRef } = root;
|
|
174
|
+
const { open, setOpen, triggerRef, anchorRef, anchored } = root;
|
|
169
175
|
React.useEffect(() => {
|
|
170
176
|
if (!open) return;
|
|
171
177
|
const onPointerDown = (event) => {
|
|
@@ -173,11 +179,12 @@ function PopoverContent({
|
|
|
173
179
|
if (!target) return;
|
|
174
180
|
if (contentRef.current?.contains(target)) return;
|
|
175
181
|
if (triggerRef.current?.contains(target)) return;
|
|
182
|
+
if (anchored && anchorRef.current?.contains(target)) return;
|
|
176
183
|
setOpen(false);
|
|
177
184
|
};
|
|
178
185
|
document.addEventListener("pointerdown", onPointerDown);
|
|
179
186
|
return () => document.removeEventListener("pointerdown", onPointerDown);
|
|
180
|
-
}, [open, setOpen, triggerRef]);
|
|
187
|
+
}, [open, setOpen, triggerRef, anchorRef, anchored]);
|
|
181
188
|
const overlayPortalContainer = useOverlayPortalContainer(portalContainer);
|
|
182
189
|
return /* @__PURE__ */ jsx(
|
|
183
190
|
AriaPopover,
|
|
@@ -213,6 +220,7 @@ function PopoverContent({
|
|
|
213
220
|
"data-align": align,
|
|
214
221
|
"data-state": isExiting ? "closed" : "open",
|
|
215
222
|
"data-flush": flush ? "" : void 0,
|
|
223
|
+
"data-width": width && width !== "panel" ? width : void 0,
|
|
216
224
|
...props,
|
|
217
225
|
"aria-labelledby": props["aria-labelledby"] ?? (props["aria-label"] ? void 0 : root.hasTitle ? root.titleId : root.triggerId),
|
|
218
226
|
"aria-describedby": props["aria-describedby"] ?? (root.hasDescription ? root.descriptionId : void 0),
|
|
@@ -233,7 +241,8 @@ function PopoverContent({
|
|
|
233
241
|
"--radix-popover-content-available-height": typeof availableHeight === "number" ? `${availableHeight}px` : availableHeight
|
|
234
242
|
},
|
|
235
243
|
...style,
|
|
236
|
-
...flush ? { "--popover-space-inset": "0" } : null
|
|
244
|
+
...flush ? { "--popover-space-inset": "0" } : null,
|
|
245
|
+
...width && POPOVER_SURFACE_INLINE_SIZE[width] ? { "--popover-surface-inline-size": POPOVER_SURFACE_INLINE_SIZE[width] } : null
|
|
237
246
|
},
|
|
238
247
|
children
|
|
239
248
|
}
|
|
@@ -14,6 +14,20 @@ export type ProgressSegment = {
|
|
|
14
14
|
label: string;
|
|
15
15
|
};
|
|
16
16
|
type ProgressBase = Omit<React.HTMLAttributes<HTMLDivElement>, "role" | "children" | "aria-valuenow" | "aria-valuemin" | "aria-valuemax" | "aria-valuetext"> & {
|
|
17
|
+
/**
|
|
18
|
+
* Bar thickness — `md` (default) or `sm`.
|
|
19
|
+
*
|
|
20
|
+
* `md` is the size each form was designed at, and it stays the default for the reason spelled
|
|
21
|
+
* out on the breakdown below: three abutting fills need height before their ratios are
|
|
22
|
+
* comparable. `sm` is for a bar that is not the subject of the screen but a column of one — an
|
|
23
|
+
* in-table capacity bar next to a row of numbers, where a 22px partition outweighs the row it
|
|
24
|
+
* annotates and sets the row height for the whole table.
|
|
25
|
+
*
|
|
26
|
+
* Both steps come from the same token pair (`--progress-*-block-size`,
|
|
27
|
+
* `--progress-*-block-size-sm`), so a theme retunes the scale rather than one call site. Nothing
|
|
28
|
+
* about the ARIA changes: `sm` is thickness, not meaning.
|
|
29
|
+
*/
|
|
30
|
+
size?: "sm" | "md";
|
|
17
31
|
/**
|
|
18
32
|
* Visible caption under the bar; it also becomes the bar's accessible name. Omit it and the bar
|
|
19
33
|
* falls back to the catalogue name — or to whatever `aria-label`/`aria-labelledby` the caller
|
|
@@ -21,10 +35,31 @@ type ProgressBase = Omit<React.HTMLAttributes<HTMLDivElement>, "role" | "childre
|
|
|
21
35
|
*/
|
|
22
36
|
label?: string;
|
|
23
37
|
};
|
|
38
|
+
/**
|
|
39
|
+
* Geometry of the METER — the same measurement, drawn two ways.
|
|
40
|
+
*
|
|
41
|
+
* `bar` is the default and what every existing call site gets. `ring` draws the identical ratio as
|
|
42
|
+
* an arc, for a header with no room for a full-width bar: a phone app bar that has to show
|
|
43
|
+
* "18 of 42 done" beside a title has one square of space, and a bar plus its caption needs two
|
|
44
|
+
* stacked rows. Reach for it when the SPACE is square, not when the number is important — the two
|
|
45
|
+
* shapes say exactly the same thing and carry the same ARIA.
|
|
46
|
+
*
|
|
47
|
+
* It is a meter shape only. A ring around a `segments` breakdown is a pie chart, which is
|
|
48
|
+
* `PieChart donut` and belongs to the charts entry point; that one is a part-to-whole across
|
|
49
|
+
* CATEGORIES with a legend and tooltips, and a screen reader should hear it as an image, not as a
|
|
50
|
+
* progressbar.
|
|
51
|
+
*/
|
|
52
|
+
export type ProgressShape = "bar" | "ring";
|
|
24
53
|
/** A METER: one ratio of one whole. */
|
|
25
54
|
type ProgressMeterProps = ProgressBase & {
|
|
26
55
|
value: number;
|
|
27
56
|
tone?: ProgressTone;
|
|
57
|
+
/**
|
|
58
|
+
* `bar` (default) or `ring` — the same measurement drawn as a full-width bar or as an arc.
|
|
59
|
+
* `label` moves INSIDE the ring, which is the point of it: the readout and its proportion take
|
|
60
|
+
* one square instead of two stacked rows.
|
|
61
|
+
*/
|
|
62
|
+
shape?: ProgressShape;
|
|
28
63
|
/**
|
|
29
64
|
* Allow `value` to exceed 100 and render an OVER-CAPACITY fill: the bar caps at 100% width but
|
|
30
65
|
* gets a diagonal striped overlay + destructive tone, so an over-limit meter (e.g. 252% of a
|
|
@@ -57,6 +92,8 @@ type ProgressBreakdownProps = ProgressBase & {
|
|
|
57
92
|
value?: never;
|
|
58
93
|
tone?: never;
|
|
59
94
|
over?: never;
|
|
95
|
+
/** A ring around a partition is a pie chart — that is `PieChart donut`, not this. */
|
|
96
|
+
shape?: never;
|
|
60
97
|
};
|
|
61
98
|
export type ProgressProps = ProgressMeterProps | ProgressBreakdownProps;
|
|
62
99
|
export declare function Progress(props: ProgressProps): React.JSX.Element;
|