@you-agent-factory/components 0.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/README.md +223 -0
- package/docs/README.md +83 -0
- package/docs/button.md +119 -0
- package/docs/charts.md +325 -0
- package/docs/data-display-code-panel.md +120 -0
- package/docs/feedback-alert-panel.md +117 -0
- package/docs/feedback-skeleton.md +54 -0
- package/docs/forms-form-field.md +490 -0
- package/docs/forms-input-primitives.md +311 -0
- package/docs/forms-select-primitives.md +362 -0
- package/docs/graphs.md +250 -0
- package/docs/layout-display-primitives.md +211 -0
- package/docs/overlays.md +414 -0
- package/docs/table-data-table.md +331 -0
- package/docs/typography-roles.md +182 -0
- package/docs/widget-frame-recipes.md +273 -0
- package/package.json +161 -0
- package/src/category-paths.ts +20 -0
- package/src/charts/chart-state-panel.tsx +103 -0
- package/src/charts/chart.tsx +287 -0
- package/src/charts/index.ts +21 -0
- package/src/data-display/code-panel.tsx +67 -0
- package/src/data-display/data-table.tsx +180 -0
- package/src/data-display/description-list.tsx +24 -0
- package/src/data-display/index.ts +34 -0
- package/src/data-display/table-layout.ts +12 -0
- package/src/data-display/table.tsx +131 -0
- package/src/feedback/alert-panel-semantics.ts +75 -0
- package/src/feedback/alert-panel.tsx +187 -0
- package/src/feedback/index.ts +21 -0
- package/src/feedback/skeleton.tsx +16 -0
- package/src/forms/index.ts +73 -0
- package/src/forms/package-checkbox.tsx +50 -0
- package/src/forms/package-enum-select.tsx +185 -0
- package/src/forms/package-file-input.tsx +25 -0
- package/src/forms/package-form-field.tsx +202 -0
- package/src/forms/package-input.tsx +25 -0
- package/src/forms/package-native-select.tsx +25 -0
- package/src/forms/package-select.tsx +209 -0
- package/src/forms/package-textarea.tsx +35 -0
- package/src/forms/select-icons.tsx +35 -0
- package/src/graphs/graph-edge-path.ts +241 -0
- package/src/graphs/graph-edge.tsx +130 -0
- package/src/graphs/graph-interactive-example.tsx +187 -0
- package/src/graphs/graph-node-button.tsx +51 -0
- package/src/graphs/graph-node-handle-badge.tsx +108 -0
- package/src/graphs/graph-node-handle.ts +28 -0
- package/src/graphs/graph-node-shell.tsx +111 -0
- package/src/graphs/graph-node-state-indicator.tsx +47 -0
- package/src/graphs/graph-node-state.ts +111 -0
- package/src/graphs/graph-viewport-surface.tsx +27 -0
- package/src/graphs/index.ts +41 -0
- package/src/icons/index.ts +4 -0
- package/src/index.ts +209 -0
- package/src/layout/action-row.tsx +53 -0
- package/src/layout/index.ts +9 -0
- package/src/layout/surface-panel.tsx +91 -0
- package/src/navigation/index.ts +4 -0
- package/src/overlays/collapsible.tsx +22 -0
- package/src/overlays/dialog.tsx +148 -0
- package/src/overlays/index.ts +31 -0
- package/src/overlays/overlay-layout.ts +9 -0
- package/src/overlays/popover.tsx +29 -0
- package/src/overlays/scroll-area.tsx +78 -0
- package/src/primitives/button-link.tsx +22 -0
- package/src/primitives/button.tsx +258 -0
- package/src/primitives/icon-button-shell.tsx +49 -0
- package/src/primitives/index.ts +31 -0
- package/src/primitives/package-text.tsx +29 -0
- package/src/primitives/typography-roles.ts +12 -0
- package/src/primitives/typography.tsx +165 -0
- package/src/recipes/index.ts +61 -0
- package/src/recipes/widget-frame-content.tsx +114 -0
- package/src/recipes/widget-frame-disclosure.tsx +161 -0
- package/src/recipes/widget-frame-layout.ts +40 -0
- package/src/recipes/widget-frame-skeleton.tsx +16 -0
- package/src/recipes/widget-frame-states.tsx +99 -0
- package/src/recipes/widget-frame-typography.ts +19 -0
- package/src/recipes/widget-frame.tsx +84 -0
- package/src/styles/color-palette-presets.css +167 -0
- package/src/styles/color-role-tokens.css +163 -0
- package/src/styles/layout-role-tokens.css +27 -0
- package/src/styles/text-color-role-tokens.css +29 -0
- package/src/styles/typography-role-tokens.css +72 -0
- package/src/styles/typography-role-utilities.css +73 -0
- package/src/styles/typography-roles.css +53 -0
- package/src/styles.css +32 -0
- package/src/testing/index.ts +13 -0
- package/src/testing/render.tsx +16 -0
- package/src/tokens/index.ts +4 -0
- package/src/utilities/cn.ts +5 -0
- package/src/utilities/index.ts +6 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { ElementType, HTMLAttributes, ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../utilities/cn";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
WIDGET_FRAME_BODY_TEXT_CLASS,
|
|
7
|
+
WIDGET_FRAME_SECTION_HEADING_CLASS,
|
|
8
|
+
WIDGET_FRAME_SUBTITLE_CLASS,
|
|
9
|
+
} from "./widget-frame-typography";
|
|
10
|
+
|
|
11
|
+
const WIDGET_SUBTITLE_CLASS = cn("mt-0", WIDGET_FRAME_SUBTITLE_CLASS);
|
|
12
|
+
const DETAIL_COPY_CLASS = cn("m-0", WIDGET_FRAME_BODY_TEXT_CLASS);
|
|
13
|
+
const EMPTY_STATE_CLASS =
|
|
14
|
+
"grid min-h-60 items-start gap-1.5 rounded-2xl border border-dashed border-outline-variant bg-surface-container-low p-5 [&_h3]:m-0";
|
|
15
|
+
const EMPTY_STATE_COMPACT_CLASS = "min-h-0";
|
|
16
|
+
|
|
17
|
+
export interface WidgetSubtitleProps extends HTMLAttributes<HTMLElement> {
|
|
18
|
+
as?: ElementType;
|
|
19
|
+
children: ReactNode;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function WidgetSubtitle({
|
|
23
|
+
as: Component = "p",
|
|
24
|
+
children,
|
|
25
|
+
className,
|
|
26
|
+
...props
|
|
27
|
+
}: WidgetSubtitleProps) {
|
|
28
|
+
return (
|
|
29
|
+
<Component className={cn(WIDGET_SUBTITLE_CLASS, className)} {...props}>
|
|
30
|
+
{children}
|
|
31
|
+
</Component>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface WidgetDetailCopyProps
|
|
36
|
+
extends HTMLAttributes<HTMLParagraphElement> {
|
|
37
|
+
children: ReactNode;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function WidgetDetailCopy({
|
|
41
|
+
children,
|
|
42
|
+
className,
|
|
43
|
+
...props
|
|
44
|
+
}: WidgetDetailCopyProps) {
|
|
45
|
+
return (
|
|
46
|
+
<p className={cn(DETAIL_COPY_CLASS, className)} {...props}>
|
|
47
|
+
{children}
|
|
48
|
+
</p>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface WidgetEmptyStateProps extends HTMLAttributes<HTMLDivElement> {
|
|
53
|
+
children: ReactNode;
|
|
54
|
+
compact?: boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function WidgetEmptyState({
|
|
58
|
+
children,
|
|
59
|
+
className,
|
|
60
|
+
compact = false,
|
|
61
|
+
...props
|
|
62
|
+
}: WidgetEmptyStateProps) {
|
|
63
|
+
return (
|
|
64
|
+
<div
|
|
65
|
+
className={cn(
|
|
66
|
+
EMPTY_STATE_CLASS,
|
|
67
|
+
compact && EMPTY_STATE_COMPACT_CLASS,
|
|
68
|
+
className,
|
|
69
|
+
)}
|
|
70
|
+
{...props}
|
|
71
|
+
>
|
|
72
|
+
{children}
|
|
73
|
+
</div>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface WidgetEmptyStateTitleProps
|
|
78
|
+
extends HTMLAttributes<HTMLElement> {
|
|
79
|
+
as?: ElementType;
|
|
80
|
+
children: ReactNode;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function WidgetEmptyStateTitle({
|
|
84
|
+
as: Component = "h3",
|
|
85
|
+
children,
|
|
86
|
+
className,
|
|
87
|
+
...props
|
|
88
|
+
}: WidgetEmptyStateTitleProps) {
|
|
89
|
+
return (
|
|
90
|
+
<Component
|
|
91
|
+
className={cn(WIDGET_FRAME_SECTION_HEADING_CLASS, className)}
|
|
92
|
+
{...props}
|
|
93
|
+
>
|
|
94
|
+
{children}
|
|
95
|
+
</Component>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface WidgetEmptyStateTextProps
|
|
100
|
+
extends HTMLAttributes<HTMLParagraphElement> {
|
|
101
|
+
children: ReactNode;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function WidgetEmptyStateText({
|
|
105
|
+
children,
|
|
106
|
+
className,
|
|
107
|
+
...props
|
|
108
|
+
}: WidgetEmptyStateTextProps) {
|
|
109
|
+
return (
|
|
110
|
+
<p className={cn("m-0", WIDGET_FRAME_BODY_TEXT_CLASS, className)} {...props}>
|
|
111
|
+
{children}
|
|
112
|
+
</p>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import {
|
|
2
|
+
forwardRef,
|
|
3
|
+
type ButtonHTMLAttributes,
|
|
4
|
+
type HTMLAttributes,
|
|
5
|
+
type ReactNode,
|
|
6
|
+
} from "react";
|
|
7
|
+
|
|
8
|
+
import { cn } from "../utilities/cn";
|
|
9
|
+
|
|
10
|
+
import { WIDGET_FRAME_BODY_TEXT_CLASS } from "./widget-frame-typography";
|
|
11
|
+
|
|
12
|
+
const WIDGET_FRAME_DISCLOSURE_CLASS = "grid min-w-0 gap-2.5";
|
|
13
|
+
const WIDGET_FRAME_DISCLOSURE_TRIGGER_CLASS = cn(
|
|
14
|
+
"inline-flex min-h-9 shrink-0 cursor-pointer items-center justify-center gap-1.5 rounded-lg border border-outline bg-surface-container-high px-2.5 py-2 text-on-surface-variant transition hover:border-outline-variant hover:bg-af-overlay hover:text-on-surface focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-af-accent disabled:cursor-not-allowed disabled:border-outline disabled:bg-surface-container-low disabled:text-on-surface-disabled",
|
|
15
|
+
WIDGET_FRAME_BODY_TEXT_CLASS,
|
|
16
|
+
);
|
|
17
|
+
const WIDGET_FRAME_DISCLOSURE_ICON_CLASS =
|
|
18
|
+
"h-3.5 w-3.5 shrink-0 text-current transition-transform duration-150";
|
|
19
|
+
|
|
20
|
+
export interface WidgetFrameDisclosureProps
|
|
21
|
+
extends HTMLAttributes<HTMLDivElement> {
|
|
22
|
+
children: ReactNode;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function WidgetFrameDisclosure({
|
|
26
|
+
children,
|
|
27
|
+
className,
|
|
28
|
+
...props
|
|
29
|
+
}: WidgetFrameDisclosureProps) {
|
|
30
|
+
return (
|
|
31
|
+
<div className={cn(WIDGET_FRAME_DISCLOSURE_CLASS, className)} {...props}>
|
|
32
|
+
{children}
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface WidgetFrameDisclosureIconProps {
|
|
38
|
+
expanded: boolean;
|
|
39
|
+
className?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function WidgetFrameDisclosureIcon({
|
|
43
|
+
className,
|
|
44
|
+
expanded,
|
|
45
|
+
}: WidgetFrameDisclosureIconProps) {
|
|
46
|
+
return (
|
|
47
|
+
<svg
|
|
48
|
+
aria-hidden="true"
|
|
49
|
+
className={cn(
|
|
50
|
+
WIDGET_FRAME_DISCLOSURE_ICON_CLASS,
|
|
51
|
+
expanded ? "rotate-180" : "rotate-0",
|
|
52
|
+
className,
|
|
53
|
+
)}
|
|
54
|
+
fill="none"
|
|
55
|
+
focusable="false"
|
|
56
|
+
viewBox="0 0 16 16"
|
|
57
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
58
|
+
>
|
|
59
|
+
<path
|
|
60
|
+
d="M4 6l4 4 4-4"
|
|
61
|
+
stroke="currentColor"
|
|
62
|
+
strokeLinecap="round"
|
|
63
|
+
strokeLinejoin="round"
|
|
64
|
+
strokeWidth="1.8"
|
|
65
|
+
/>
|
|
66
|
+
</svg>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
type WidgetFrameDisclosureTriggerBaseProps = Omit<
|
|
71
|
+
ButtonHTMLAttributes<HTMLButtonElement>,
|
|
72
|
+
"aria-expanded" | "aria-controls" | "children" | "type"
|
|
73
|
+
> & {
|
|
74
|
+
controlsID: string;
|
|
75
|
+
expanded: boolean;
|
|
76
|
+
onExpandedChange?: (expanded: boolean) => void;
|
|
77
|
+
type?: "button";
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
type WidgetFrameDisclosureTriggerWithLabelProps =
|
|
81
|
+
WidgetFrameDisclosureTriggerBaseProps & {
|
|
82
|
+
children: ReactNode;
|
|
83
|
+
"aria-label"?: string;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
type WidgetFrameDisclosureTriggerIconOnlyProps =
|
|
87
|
+
WidgetFrameDisclosureTriggerBaseProps & {
|
|
88
|
+
children?: never;
|
|
89
|
+
"aria-label": string;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export type WidgetFrameDisclosureTriggerProps =
|
|
93
|
+
| WidgetFrameDisclosureTriggerWithLabelProps
|
|
94
|
+
| WidgetFrameDisclosureTriggerIconOnlyProps;
|
|
95
|
+
|
|
96
|
+
export const WidgetFrameDisclosureTrigger = forwardRef<
|
|
97
|
+
HTMLButtonElement,
|
|
98
|
+
WidgetFrameDisclosureTriggerProps
|
|
99
|
+
>(function WidgetFrameDisclosureTrigger(
|
|
100
|
+
{
|
|
101
|
+
children,
|
|
102
|
+
className,
|
|
103
|
+
controlsID,
|
|
104
|
+
expanded,
|
|
105
|
+
onClick,
|
|
106
|
+
onExpandedChange,
|
|
107
|
+
type = "button",
|
|
108
|
+
...props
|
|
109
|
+
},
|
|
110
|
+
ref,
|
|
111
|
+
) {
|
|
112
|
+
const handleClick: ButtonHTMLAttributes<HTMLButtonElement>["onClick"] = (
|
|
113
|
+
event,
|
|
114
|
+
) => {
|
|
115
|
+
onClick?.(event);
|
|
116
|
+
if (!event.defaultPrevented) {
|
|
117
|
+
onExpandedChange?.(!expanded);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<button
|
|
123
|
+
aria-controls={controlsID}
|
|
124
|
+
aria-expanded={expanded}
|
|
125
|
+
className={cn(WIDGET_FRAME_DISCLOSURE_TRIGGER_CLASS, className)}
|
|
126
|
+
onClick={handleClick}
|
|
127
|
+
ref={ref}
|
|
128
|
+
type={type}
|
|
129
|
+
{...props}
|
|
130
|
+
>
|
|
131
|
+
<WidgetFrameDisclosureIcon expanded={expanded} />
|
|
132
|
+
{children}
|
|
133
|
+
</button>
|
|
134
|
+
);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
export interface WidgetFrameDisclosurePanelProps
|
|
138
|
+
extends HTMLAttributes<HTMLDivElement> {
|
|
139
|
+
children: ReactNode;
|
|
140
|
+
expanded: boolean;
|
|
141
|
+
id: string;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function WidgetFrameDisclosurePanel({
|
|
145
|
+
children,
|
|
146
|
+
className,
|
|
147
|
+
expanded,
|
|
148
|
+
id,
|
|
149
|
+
...props
|
|
150
|
+
}: WidgetFrameDisclosurePanelProps) {
|
|
151
|
+
return (
|
|
152
|
+
<div
|
|
153
|
+
className={cn("min-w-0", className)}
|
|
154
|
+
hidden={!expanded}
|
|
155
|
+
id={id}
|
|
156
|
+
{...props}
|
|
157
|
+
>
|
|
158
|
+
{children}
|
|
159
|
+
</div>
|
|
160
|
+
);
|
|
161
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { CSSProperties } from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../utilities/cn";
|
|
4
|
+
|
|
5
|
+
import { WIDGET_FRAME_SUPPORTING_LABELS_CLASS } from "./widget-frame-typography";
|
|
6
|
+
|
|
7
|
+
export const WIDGET_FRAME_MIN_WIDTH_CLASS = "min-w-0";
|
|
8
|
+
|
|
9
|
+
export const WIDGET_FRAME_WIDE_BODY_CLASS = "min-h-72";
|
|
10
|
+
|
|
11
|
+
export const WIDGET_FRAME_RESPONSIVE_SHELL_CLASS = "min-w-0 w-full";
|
|
12
|
+
|
|
13
|
+
export const WIDGET_FRAME_STORY_SHELL_DATA_ATTR = "data-widget-frame-story-shell";
|
|
14
|
+
|
|
15
|
+
export const WIDGET_FRAME_OVERFLOW_TOLERANCE_PX = 1;
|
|
16
|
+
|
|
17
|
+
/** Detail-card layout recipe for description lists inside widget frames. */
|
|
18
|
+
export const widgetFrameDetailCardClass = cn(
|
|
19
|
+
"[&_dd]:m-0 [&_dl]:m-0 [&_dl]:grid [&_dl]:gap-3 [&_dl_div:first-child]:border-t-0 [&_dl_div:first-child]:pt-0 [&_dl_div]:border-t [&_dl_div]:border-outline [&_dl_div]:pt-3 [&_dt]:mb-1 [&_h3]:mt-0",
|
|
20
|
+
WIDGET_FRAME_SUPPORTING_LABELS_CLASS,
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
export function widgetFrameStoryShellStyle(maxWidth: string): {
|
|
24
|
+
style: CSSProperties;
|
|
25
|
+
} {
|
|
26
|
+
return {
|
|
27
|
+
style: {
|
|
28
|
+
maxWidth,
|
|
29
|
+
padding: "1rem",
|
|
30
|
+
width: "100%",
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function widgetFrameHasNoHorizontalOverflow(
|
|
36
|
+
element: HTMLElement,
|
|
37
|
+
tolerancePx = WIDGET_FRAME_OVERFLOW_TOLERANCE_PX,
|
|
38
|
+
): boolean {
|
|
39
|
+
return element.scrollWidth <= element.clientWidth + tolerancePx;
|
|
40
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { HTMLAttributes } from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../utilities/cn";
|
|
4
|
+
|
|
5
|
+
export function WidgetFrameSkeleton({
|
|
6
|
+
className,
|
|
7
|
+
...props
|
|
8
|
+
}: HTMLAttributes<HTMLDivElement>) {
|
|
9
|
+
return (
|
|
10
|
+
<div
|
|
11
|
+
aria-hidden="true"
|
|
12
|
+
className={cn("animate-pulse rounded-xl bg-af-overlay", className)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../utilities/cn";
|
|
4
|
+
|
|
5
|
+
import { WidgetFrameSkeleton } from "./widget-frame-skeleton";
|
|
6
|
+
|
|
7
|
+
const WIDGET_STATE_PANEL_CLASS = "grid gap-2";
|
|
8
|
+
const WIDGET_ERROR_STATE_CLASS =
|
|
9
|
+
"rounded-2xl border border-af-danger-border bg-error-container p-5 text-on-error-container";
|
|
10
|
+
const WIDGET_SUCCESS_STATE_CLASS =
|
|
11
|
+
"rounded-2xl border border-af-success-border bg-success-container p-5 text-on-success-container";
|
|
12
|
+
const WIDGET_LOADING_PLACEHOLDER_CLASS = "grid gap-2 pt-2";
|
|
13
|
+
|
|
14
|
+
export interface WidgetLoadingStateProps extends HTMLAttributes<HTMLDivElement> {
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
placeholder?: ReactNode;
|
|
17
|
+
showDefaultPlaceholder?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function WidgetLoadingState({
|
|
21
|
+
children,
|
|
22
|
+
className,
|
|
23
|
+
placeholder,
|
|
24
|
+
showDefaultPlaceholder = true,
|
|
25
|
+
...props
|
|
26
|
+
}: WidgetLoadingStateProps) {
|
|
27
|
+
const resolvedPlaceholder =
|
|
28
|
+
placeholder ??
|
|
29
|
+
(showDefaultPlaceholder ? (
|
|
30
|
+
<>
|
|
31
|
+
<WidgetFrameSkeleton className="h-4 w-full max-w-48" />
|
|
32
|
+
<WidgetFrameSkeleton className="h-24 w-full" />
|
|
33
|
+
<WidgetFrameSkeleton className="h-4 w-full max-w-48" />
|
|
34
|
+
</>
|
|
35
|
+
) : null);
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div
|
|
39
|
+
aria-busy="true"
|
|
40
|
+
className={cn(WIDGET_STATE_PANEL_CLASS, className)}
|
|
41
|
+
role="status"
|
|
42
|
+
{...props}
|
|
43
|
+
>
|
|
44
|
+
{children}
|
|
45
|
+
{resolvedPlaceholder ? (
|
|
46
|
+
<div
|
|
47
|
+
aria-hidden="true"
|
|
48
|
+
className={WIDGET_LOADING_PLACEHOLDER_CLASS}
|
|
49
|
+
>
|
|
50
|
+
{resolvedPlaceholder}
|
|
51
|
+
</div>
|
|
52
|
+
) : null}
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface WidgetErrorStateProps extends HTMLAttributes<HTMLDivElement> {
|
|
58
|
+
children: ReactNode;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function WidgetErrorState({
|
|
62
|
+
children,
|
|
63
|
+
className,
|
|
64
|
+
...props
|
|
65
|
+
}: WidgetErrorStateProps) {
|
|
66
|
+
return (
|
|
67
|
+
<div
|
|
68
|
+
className={cn(WIDGET_ERROR_STATE_CLASS, WIDGET_STATE_PANEL_CLASS, className)}
|
|
69
|
+
role="alert"
|
|
70
|
+
{...props}
|
|
71
|
+
>
|
|
72
|
+
{children}
|
|
73
|
+
</div>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface WidgetSuccessStateProps extends HTMLAttributes<HTMLDivElement> {
|
|
78
|
+
children: ReactNode;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function WidgetSuccessState({
|
|
82
|
+
children,
|
|
83
|
+
className,
|
|
84
|
+
...props
|
|
85
|
+
}: WidgetSuccessStateProps) {
|
|
86
|
+
return (
|
|
87
|
+
<div
|
|
88
|
+
className={cn(
|
|
89
|
+
WIDGET_SUCCESS_STATE_CLASS,
|
|
90
|
+
WIDGET_STATE_PANEL_CLASS,
|
|
91
|
+
className,
|
|
92
|
+
)}
|
|
93
|
+
role="status"
|
|
94
|
+
{...props}
|
|
95
|
+
>
|
|
96
|
+
{children}
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Section heading typography for widget frame titles and empty-state headings. */
|
|
2
|
+
export const WIDGET_FRAME_SECTION_HEADING_CLASS =
|
|
3
|
+
"text-title-large uppercase text-on-surface";
|
|
4
|
+
|
|
5
|
+
/** Body copy typography for widget frame detail regions. */
|
|
6
|
+
export const WIDGET_FRAME_BODY_TEXT_CLASS =
|
|
7
|
+
"text-body-medium text-on-surface-variant";
|
|
8
|
+
|
|
9
|
+
/** Monospace subtitle typography for metric and metadata values. */
|
|
10
|
+
export const WIDGET_FRAME_SUBTITLE_CLASS =
|
|
11
|
+
"font-mono text-display-small text-on-surface-variant [overflow-wrap:anywhere]";
|
|
12
|
+
|
|
13
|
+
/** Supporting label typography for description-list terms. */
|
|
14
|
+
export const WIDGET_FRAME_SUPPORTING_LABEL_CLASS =
|
|
15
|
+
"text-label-medium uppercase text-on-surface-subtle";
|
|
16
|
+
|
|
17
|
+
/** Description-list label styling for widget detail cards. */
|
|
18
|
+
export const WIDGET_FRAME_SUPPORTING_LABELS_CLASS =
|
|
19
|
+
"[&_dt]:text-label-medium [&_dt]:uppercase [&_dt]:text-on-surface-subtle";
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../utilities/cn";
|
|
4
|
+
|
|
5
|
+
import { WIDGET_FRAME_BODY_TEXT_CLASS } from "./widget-frame-typography";
|
|
6
|
+
import {
|
|
7
|
+
WIDGET_FRAME_MIN_WIDTH_CLASS,
|
|
8
|
+
WIDGET_FRAME_WIDE_BODY_CLASS,
|
|
9
|
+
widgetFrameDetailCardClass,
|
|
10
|
+
} from "./widget-frame-layout";
|
|
11
|
+
|
|
12
|
+
const WIDGET_FRAME_SHELL_CLASS =
|
|
13
|
+
"flex min-w-0 flex-col rounded-lg border border-outline bg-surface-container-high text-on-surface shadow-af-card";
|
|
14
|
+
const WIDGET_FRAME_SCROLL_CLASS = "overflow-hidden";
|
|
15
|
+
const WIDGET_FRAME_HEADER_CLASS =
|
|
16
|
+
"relative z-10 flex min-h-13 shrink-0 items-center justify-between gap-3 border-b border-outline bg-surface-container-high px-3.5 py-3";
|
|
17
|
+
const WIDGET_FRAME_HEADER_TOOLS_CLASS =
|
|
18
|
+
"flex min-w-0 shrink-0 items-center gap-2";
|
|
19
|
+
const WIDGET_FRAME_BODY_SCROLL_CLASS = "min-h-0 flex-1 overflow-auto";
|
|
20
|
+
const WIDGET_FRAME_BODY_CLASS = cn(
|
|
21
|
+
"grid min-h-0 gap-2.5 px-3.5 pt-3.5 pb-4 [&>*]:pb-1 [&_p]:m-0",
|
|
22
|
+
WIDGET_FRAME_BODY_TEXT_CLASS,
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
export interface WidgetFrameProps {
|
|
26
|
+
bodyClassName?: string;
|
|
27
|
+
bodyProps?: HTMLAttributes<HTMLDivElement>;
|
|
28
|
+
bodyScroll?: boolean;
|
|
29
|
+
children: ReactNode;
|
|
30
|
+
className?: string;
|
|
31
|
+
headerAction?: ReactNode;
|
|
32
|
+
title: string;
|
|
33
|
+
wide?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function WidgetFrame({
|
|
37
|
+
bodyClassName,
|
|
38
|
+
bodyProps,
|
|
39
|
+
bodyScroll = true,
|
|
40
|
+
children,
|
|
41
|
+
className,
|
|
42
|
+
headerAction,
|
|
43
|
+
title,
|
|
44
|
+
wide = false,
|
|
45
|
+
}: WidgetFrameProps) {
|
|
46
|
+
const cardClassName = cn(
|
|
47
|
+
WIDGET_FRAME_SHELL_CLASS,
|
|
48
|
+
WIDGET_FRAME_MIN_WIDTH_CLASS,
|
|
49
|
+
widgetFrameDetailCardClass,
|
|
50
|
+
wide && WIDGET_FRAME_WIDE_BODY_CLASS,
|
|
51
|
+
bodyScroll && WIDGET_FRAME_SCROLL_CLASS,
|
|
52
|
+
className,
|
|
53
|
+
);
|
|
54
|
+
const cardBodyClassName = cn(WIDGET_FRAME_BODY_CLASS, bodyClassName);
|
|
55
|
+
const bodyClassNames = cn(
|
|
56
|
+
"min-h-0 flex-1",
|
|
57
|
+
bodyScroll && WIDGET_FRAME_BODY_SCROLL_CLASS,
|
|
58
|
+
cardBodyClassName,
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<article aria-label={title} className={cardClassName}>
|
|
63
|
+
<header className={WIDGET_FRAME_HEADER_CLASS}>
|
|
64
|
+
<div className="min-w-0 flex-1">
|
|
65
|
+
<h3 className="m-0 min-w-0 flex-1 text-title-large uppercase text-on-surface [overflow-wrap:anywhere]">
|
|
66
|
+
{title}
|
|
67
|
+
</h3>
|
|
68
|
+
</div>
|
|
69
|
+
<div className={WIDGET_FRAME_HEADER_TOOLS_CLASS}>
|
|
70
|
+
{headerAction ?? (
|
|
71
|
+
<span
|
|
72
|
+
aria-hidden="true"
|
|
73
|
+
className="block h-10 w-10 shrink-0"
|
|
74
|
+
data-widget-frame-header-action-spacer="true"
|
|
75
|
+
/>
|
|
76
|
+
)}
|
|
77
|
+
</div>
|
|
78
|
+
</header>
|
|
79
|
+
<div className={bodyClassNames} {...bodyProps}>
|
|
80
|
+
{children}
|
|
81
|
+
</div>
|
|
82
|
+
</article>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Predefined Material-style palette presets (US-008).
|
|
3
|
+
*
|
|
4
|
+
* Each preset overrides af-foundation-* keys on :root via data-color-palette.
|
|
5
|
+
* Role tokens in color-role-tokens.css and aliases cascade from these keys.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
:root,
|
|
9
|
+
[data-color-palette="factory-dark"] {
|
|
10
|
+
--color-af-foundation-background: #050b10;
|
|
11
|
+
--color-af-foundation-background-start: #090f15;
|
|
12
|
+
--color-af-foundation-background-mid: #0c151d;
|
|
13
|
+
--color-af-foundation-canvas: #050b10;
|
|
14
|
+
--color-af-foundation-surface: #181f2b;
|
|
15
|
+
--color-af-foundation-ink: #f7f2e8;
|
|
16
|
+
--color-af-foundation-code-ink: #d5fbff;
|
|
17
|
+
--color-af-foundation-overlay: #ffffff;
|
|
18
|
+
--color-af-foundation-accent: #f5c76f;
|
|
19
|
+
--color-af-foundation-accent-strong: #ecbf58;
|
|
20
|
+
--color-af-foundation-accent-ink: #1a2228;
|
|
21
|
+
--color-af-foundation-info: #5ccadd;
|
|
22
|
+
--color-af-foundation-info-strong: #5097be;
|
|
23
|
+
--color-af-foundation-info-bright: #7dd3fc;
|
|
24
|
+
--color-af-foundation-info-ink: #b5edf4;
|
|
25
|
+
--color-af-foundation-secondary-accent: #507f8c;
|
|
26
|
+
--color-af-foundation-secondary-accent-ink: #8aaeb8;
|
|
27
|
+
--color-af-foundation-tertiary-accent: #6d6290;
|
|
28
|
+
--color-af-foundation-tertiary-accent-ink: #a89ec4;
|
|
29
|
+
--color-af-foundation-success: #57c18b;
|
|
30
|
+
--color-af-foundation-success-ink: #a7f0c4;
|
|
31
|
+
--color-af-foundation-warning: #f0b15a;
|
|
32
|
+
--color-af-foundation-warning-ink: #ffd8a3;
|
|
33
|
+
--color-af-foundation-danger: #f05f5f;
|
|
34
|
+
--color-af-foundation-danger-bright: #ff8a8a;
|
|
35
|
+
--color-af-foundation-danger-ink: #ffb2b2;
|
|
36
|
+
--color-af-foundation-worker: #b68cff;
|
|
37
|
+
--color-af-foundation-worker-ink: #dfccff;
|
|
38
|
+
--color-af-shadow: #000000;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
[data-color-palette="factory-light"] {
|
|
42
|
+
--color-af-foundation-background: #f4f1e8;
|
|
43
|
+
--color-af-foundation-background-start: #ece8dc;
|
|
44
|
+
--color-af-foundation-background-mid: #e4ded0;
|
|
45
|
+
--color-af-foundation-canvas: #faf8f3;
|
|
46
|
+
--color-af-foundation-surface: #ffffff;
|
|
47
|
+
--color-af-foundation-ink: #1a2228;
|
|
48
|
+
--color-af-foundation-code-ink: #0f4c5c;
|
|
49
|
+
--color-af-foundation-overlay: #000000;
|
|
50
|
+
--color-af-foundation-accent: #f5c76f;
|
|
51
|
+
--color-af-foundation-accent-strong: #c9972e;
|
|
52
|
+
--color-af-foundation-accent-ink: #1a2228;
|
|
53
|
+
--color-af-foundation-info: #2f8fad;
|
|
54
|
+
--color-af-foundation-info-strong: #256f89;
|
|
55
|
+
--color-af-foundation-info-bright: #4aa9c9;
|
|
56
|
+
--color-af-foundation-info-ink: #1f6178;
|
|
57
|
+
--color-af-foundation-secondary-accent: #4a7a87;
|
|
58
|
+
--color-af-foundation-secondary-accent-ink: #355962;
|
|
59
|
+
--color-af-foundation-tertiary-accent: #5c5280;
|
|
60
|
+
--color-af-foundation-tertiary-accent-ink: #433b61;
|
|
61
|
+
--color-af-foundation-success: #2f9a66;
|
|
62
|
+
--color-af-foundation-success-ink: #1f6f49;
|
|
63
|
+
--color-af-foundation-warning: #c9852d;
|
|
64
|
+
--color-af-foundation-warning-ink: #8f5d16;
|
|
65
|
+
--color-af-foundation-danger: #d94848;
|
|
66
|
+
--color-af-foundation-danger-bright: #ef6b6b;
|
|
67
|
+
--color-af-foundation-danger-ink: #9f2f2f;
|
|
68
|
+
--color-af-foundation-worker: #7f63b8;
|
|
69
|
+
--color-af-foundation-worker-ink: #5d4788;
|
|
70
|
+
--color-af-shadow: #1a2228;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
[data-color-palette="material-baseline"] {
|
|
74
|
+
--color-af-foundation-background: #141218;
|
|
75
|
+
--color-af-foundation-background-start: #17151b;
|
|
76
|
+
--color-af-foundation-background-mid: #1b1920;
|
|
77
|
+
--color-af-foundation-canvas: #0f0d12;
|
|
78
|
+
--color-af-foundation-surface: #1d1b20;
|
|
79
|
+
--color-af-foundation-ink: #e6e0e9;
|
|
80
|
+
--color-af-foundation-code-ink: #c8f0ff;
|
|
81
|
+
--color-af-foundation-overlay: #ffffff;
|
|
82
|
+
--color-af-foundation-accent: #f5c76f;
|
|
83
|
+
--color-af-foundation-accent-strong: #ecbf58;
|
|
84
|
+
--color-af-foundation-accent-ink: #1a2228;
|
|
85
|
+
--color-af-foundation-info: #67cbe0;
|
|
86
|
+
--color-af-foundation-info-strong: #4f9db8;
|
|
87
|
+
--color-af-foundation-info-bright: #8adcf0;
|
|
88
|
+
--color-af-foundation-info-ink: #b8edf8;
|
|
89
|
+
--color-af-foundation-secondary-accent: #5a8894;
|
|
90
|
+
--color-af-foundation-secondary-accent-ink: #94b4bd;
|
|
91
|
+
--color-af-foundation-tertiary-accent: #7a7198;
|
|
92
|
+
--color-af-foundation-tertiary-accent-ink: #b0a8c8;
|
|
93
|
+
--color-af-foundation-success: #63c995;
|
|
94
|
+
--color-af-foundation-success-ink: #a8f0c8;
|
|
95
|
+
--color-af-foundation-warning: #efb865;
|
|
96
|
+
--color-af-foundation-warning-ink: #ffddb0;
|
|
97
|
+
--color-af-foundation-danger: #ef6a6a;
|
|
98
|
+
--color-af-foundation-danger-bright: #ff9494;
|
|
99
|
+
--color-af-foundation-danger-ink: #ffb8b8;
|
|
100
|
+
--color-af-foundation-worker: #c0a8f0;
|
|
101
|
+
--color-af-foundation-worker-ink: #e4d8ff;
|
|
102
|
+
--color-af-shadow: #000000;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
[data-color-palette="slate"] {
|
|
106
|
+
--color-af-foundation-background: #0d1117;
|
|
107
|
+
--color-af-foundation-background-start: #101620;
|
|
108
|
+
--color-af-foundation-background-mid: #131b28;
|
|
109
|
+
--color-af-foundation-canvas: #070b10;
|
|
110
|
+
--color-af-foundation-surface: #161b22;
|
|
111
|
+
--color-af-foundation-ink: #e6edf3;
|
|
112
|
+
--color-af-foundation-code-ink: #c7e7ff;
|
|
113
|
+
--color-af-foundation-overlay: #ffffff;
|
|
114
|
+
--color-af-foundation-accent: #f5c76f;
|
|
115
|
+
--color-af-foundation-accent-strong: #ecbf58;
|
|
116
|
+
--color-af-foundation-accent-ink: #1a2228;
|
|
117
|
+
--color-af-foundation-info: #58b8d8;
|
|
118
|
+
--color-af-foundation-info-strong: #468ea8;
|
|
119
|
+
--color-af-foundation-info-bright: #7ecbf0;
|
|
120
|
+
--color-af-foundation-info-ink: #b0e0f2;
|
|
121
|
+
--color-af-foundation-secondary-accent: #6e9bae;
|
|
122
|
+
--color-af-foundation-secondary-accent-ink: #9bb8c8;
|
|
123
|
+
--color-af-foundation-tertiary-accent: #7b8cde;
|
|
124
|
+
--color-af-foundation-tertiary-accent-ink: #adb6e8;
|
|
125
|
+
--color-af-foundation-success: #56b888;
|
|
126
|
+
--color-af-foundation-success-ink: #a4e8c4;
|
|
127
|
+
--color-af-foundation-warning: #e8ad58;
|
|
128
|
+
--color-af-foundation-warning-ink: #ffd49a;
|
|
129
|
+
--color-af-foundation-danger: #e86262;
|
|
130
|
+
--color-af-foundation-danger-bright: #ff8e8e;
|
|
131
|
+
--color-af-foundation-danger-ink: #ffb0b0;
|
|
132
|
+
--color-af-foundation-worker: #a898f0;
|
|
133
|
+
--color-af-foundation-worker-ink: #d8d0ff;
|
|
134
|
+
--color-af-shadow: #000000;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
[data-color-palette="olive"] {
|
|
138
|
+
--color-af-foundation-background: #13150f;
|
|
139
|
+
--color-af-foundation-background-start: #171912;
|
|
140
|
+
--color-af-foundation-background-mid: #1b1e15;
|
|
141
|
+
--color-af-foundation-canvas: #0a0c08;
|
|
142
|
+
--color-af-foundation-surface: #1a1d15;
|
|
143
|
+
--color-af-foundation-ink: #eef2e4;
|
|
144
|
+
--color-af-foundation-code-ink: #d0f5e8;
|
|
145
|
+
--color-af-foundation-overlay: #ffffff;
|
|
146
|
+
--color-af-foundation-accent: #f5c76f;
|
|
147
|
+
--color-af-foundation-accent-strong: #ecbf58;
|
|
148
|
+
--color-af-foundation-accent-ink: #1a2228;
|
|
149
|
+
--color-af-foundation-info: #58b0a0;
|
|
150
|
+
--color-af-foundation-info-strong: #458a7d;
|
|
151
|
+
--color-af-foundation-info-bright: #7ecfb8;
|
|
152
|
+
--color-af-foundation-info-ink: #b0e8d8;
|
|
153
|
+
--color-af-foundation-secondary-accent: #6a8f72;
|
|
154
|
+
--color-af-foundation-secondary-accent-ink: #98b8a0;
|
|
155
|
+
--color-af-foundation-tertiary-accent: #8a7d5c;
|
|
156
|
+
--color-af-foundation-tertiary-accent-ink: #b8aa88;
|
|
157
|
+
--color-af-foundation-success: #62b878;
|
|
158
|
+
--color-af-foundation-success-ink: #a8e8bc;
|
|
159
|
+
--color-af-foundation-warning: #e8b058;
|
|
160
|
+
--color-af-foundation-warning-ink: #ffd898;
|
|
161
|
+
--color-af-foundation-danger: #e06868;
|
|
162
|
+
--color-af-foundation-danger-ink: #ffb0b0;
|
|
163
|
+
--color-af-foundation-danger-bright: #ff9090;
|
|
164
|
+
--color-af-foundation-worker: #b0a070;
|
|
165
|
+
--color-af-foundation-worker-ink: #e0d4b0;
|
|
166
|
+
--color-af-shadow: #000000;
|
|
167
|
+
}
|