@tonycodes-ui/mortar 0.0.0-stage → 0.11.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/AGENTS.md +10 -0
- package/CHANGELOG.md +276 -0
- package/CONTRIBUTING.md +48 -0
- package/DESIGN.md +51 -0
- package/LICENSE +21 -0
- package/README.md +155 -2
- package/TESTING.md +83 -0
- package/baseline-policy.json +16 -0
- package/bin/mortar.mjs +258 -0
- package/catalog.json +9703 -0
- package/cli/audit.mjs +943 -0
- package/cli/data.mjs +176 -0
- package/cli/mcp.mjs +216 -0
- package/cli/migrations.mjs +128 -0
- package/cli/packet.mjs +267 -0
- package/cli/search.mjs +246 -0
- package/cli/tools.mjs +229 -0
- package/design-principles.json +30 -0
- package/dist/agent-data.json +1 -0
- package/dist/application.d.ts +340 -0
- package/dist/application.js +240 -0
- package/dist/auth.d.ts +121 -0
- package/dist/auth.js +97 -0
- package/dist/brand.d.ts +69 -0
- package/dist/brand.js +266 -0
- package/dist/calendar.d.ts +75 -0
- package/dist/calendar.js +310 -0
- package/dist/choice.d.ts +36 -0
- package/dist/choice.js +36 -0
- package/dist/composer.d.ts +28 -0
- package/dist/composer.js +47 -0
- package/dist/display.d.ts +136 -0
- package/dist/display.js +75 -0
- package/dist/docs.d.ts +145 -0
- package/dist/docs.js +141 -0
- package/dist/ecommerce.d.ts +261 -0
- package/dist/ecommerce.js +177 -0
- package/dist/feedback.d.ts +80 -0
- package/dist/feedback.js +98 -0
- package/dist/foundation.d.ts +42 -0
- package/dist/foundation.js +121 -0
- package/dist/heading.d.ts +5 -0
- package/dist/heading.js +9 -0
- package/dist/icons.d.ts +50 -0
- package/dist/icons.js +70 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +17 -0
- package/dist/inspection.d.ts +24 -0
- package/dist/inspection.js +14 -0
- package/dist/internal.d.ts +492 -0
- package/dist/internal.js +273 -0
- package/dist/layout.d.ts +107 -0
- package/dist/layout.js +66 -0
- package/dist/marketing-sections.d.ts +161 -0
- package/dist/marketing-sections.js +58 -0
- package/dist/marketing.d.ts +254 -0
- package/dist/marketing.js +150 -0
- package/dist/navigation.d.ts +35 -0
- package/dist/navigation.js +44 -0
- package/dist/primitives.d.ts +258 -0
- package/dist/primitives.js +244 -0
- package/dist/showcase.d.ts +29 -0
- package/dist/showcase.js +10 -0
- package/dist/workspace.d.ts +80 -0
- package/dist/workspace.js +48 -0
- package/docs/agents.md +127 -0
- package/docs/auth-flows.md +71 -0
- package/docs/migrating-to-0.6.md +94 -0
- package/docs/theming.md +170 -0
- package/package.json +94 -4
- package/skills/mortar/SKILL.md +36 -0
- package/src/application.css +1487 -0
- package/src/application.tsx +1413 -0
- package/src/auth.tsx +452 -0
- package/src/brand.ts +335 -0
- package/src/calendar.css +468 -0
- package/src/calendar.tsx +868 -0
- package/src/choice.tsx +240 -0
- package/src/composer.tsx +158 -0
- package/src/data.css +690 -0
- package/src/display.tsx +452 -0
- package/src/docs.css +704 -0
- package/src/docs.tsx +737 -0
- package/src/ecommerce.css +810 -0
- package/src/ecommerce.tsx +1033 -0
- package/src/feedback.tsx +388 -0
- package/src/foundation.css +1867 -0
- package/src/foundation.tsx +227 -0
- package/src/heading.ts +13 -0
- package/src/icons.tsx +237 -0
- package/src/index.ts +230 -0
- package/src/inspection.tsx +91 -0
- package/src/internal.tsx +614 -0
- package/src/layout.tsx +342 -0
- package/src/marketing-sections.tsx +621 -0
- package/src/marketing.css +2400 -0
- package/src/marketing.tsx +1156 -0
- package/src/navigation.tsx +118 -0
- package/src/overlays.css +350 -0
- package/src/primitives.tsx +1135 -0
- package/src/showcase.tsx +120 -0
- package/src/tokens.css +449 -0
- package/src/workspace.tsx +333 -0
- package/styles.css +56 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { NavigationContext, type Navigate } from "./navigation.js";
|
|
2
|
+
import { SegmentedControl } from "./inspection.js";
|
|
3
|
+
import {
|
|
4
|
+
AsComponentRoot,
|
|
5
|
+
ThemeScopeContext,
|
|
6
|
+
componentRoot,
|
|
7
|
+
} from "./internal.js";
|
|
8
|
+
import { Portal } from "@headlessui/react";
|
|
9
|
+
import { brandToCss, type BrandDefinition } from "./brand.js";
|
|
10
|
+
import {
|
|
11
|
+
createContext,
|
|
12
|
+
useContext,
|
|
13
|
+
useEffect,
|
|
14
|
+
useLayoutEffect,
|
|
15
|
+
useState,
|
|
16
|
+
useSyncExternalStore,
|
|
17
|
+
type ReactNode,
|
|
18
|
+
} from "react";
|
|
19
|
+
|
|
20
|
+
export type Appearance = "light" | "dark" | "system";
|
|
21
|
+
/**
|
|
22
|
+
* A built-in brand, or the name of one declared in CSS
|
|
23
|
+
* (`[data-mortar-brand="name"]`) or through ThemeProvider's `brandTokens`.
|
|
24
|
+
*/
|
|
25
|
+
export type Brand = "neutral" | "green" | (string & {});
|
|
26
|
+
export type Density = "compact" | "comfortable" | "spacious";
|
|
27
|
+
type Theme = {
|
|
28
|
+
appearance: Appearance;
|
|
29
|
+
setAppearance: (value: Appearance) => void;
|
|
30
|
+
brand: Brand;
|
|
31
|
+
density: Density;
|
|
32
|
+
};
|
|
33
|
+
const ThemeContext = createContext<Theme | null>(null);
|
|
34
|
+
export interface ThemeProviderProps {
|
|
35
|
+
children: ReactNode;
|
|
36
|
+
initialAppearance?: Appearance;
|
|
37
|
+
appearance?: Appearance;
|
|
38
|
+
onAppearanceChange?: (value: Appearance) => void;
|
|
39
|
+
/** Connect the application router once for every kit link. A nested provider without it keeps the outer router. */
|
|
40
|
+
navigate?: Navigate;
|
|
41
|
+
brand?: Brand;
|
|
42
|
+
/**
|
|
43
|
+
* Defines `brand` at runtime from data instead of CSS, e.g. a tenant's
|
|
44
|
+
* colours. Rendered as a `<style>` beside the provider's content (and
|
|
45
|
+
* server-rendered); overlays opened inside the provider share it.
|
|
46
|
+
* Omitted values derive from `primary`.
|
|
47
|
+
*/
|
|
48
|
+
brandTokens?: BrandDefinition;
|
|
49
|
+
density?: Density;
|
|
50
|
+
/**
|
|
51
|
+
* document writes the root element. container paints a wrapper and leaves
|
|
52
|
+
* the document unchanged; menus, listboxes and dialogs opened inside it keep
|
|
53
|
+
* its theme.
|
|
54
|
+
*/
|
|
55
|
+
scope?: "document" | "container";
|
|
56
|
+
/** On the container wrapper (container scope only). */
|
|
57
|
+
className?: string;
|
|
58
|
+
}
|
|
59
|
+
const DARK = "(prefers-color-scheme: dark)";
|
|
60
|
+
function subscribeToScheme(update: () => void) {
|
|
61
|
+
const media = window.matchMedia(DARK);
|
|
62
|
+
media.addEventListener("change", update);
|
|
63
|
+
return () => media.removeEventListener("change", update);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The system colour scheme. The server snapshot is light, and React hydrates
|
|
67
|
+
* with it before switching to the real value, so markup always matches.
|
|
68
|
+
*/
|
|
69
|
+
function useSystemDark() {
|
|
70
|
+
return useSyncExternalStore(
|
|
71
|
+
subscribeToScheme,
|
|
72
|
+
() => window.matchMedia(DARK).matches,
|
|
73
|
+
() => false,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* A body-level element that receives the container's overlays (menus,
|
|
78
|
+
* listboxes, popovers) so they keep its theme without being clipped by it.
|
|
79
|
+
*/
|
|
80
|
+
function usePortalHost(enabled: boolean, attributes: Record<string, string>) {
|
|
81
|
+
// Created during the first client render so portals can target it at once.
|
|
82
|
+
const [target] = useState(() => ({
|
|
83
|
+
current:
|
|
84
|
+
typeof document === "undefined" ? null : document.createElement("div"),
|
|
85
|
+
}));
|
|
86
|
+
const host = target.current;
|
|
87
|
+
useLayoutEffect(() => {
|
|
88
|
+
if (!host || !enabled) return;
|
|
89
|
+
host.className = "m-theme-scope";
|
|
90
|
+
for (const [key, value] of Object.entries(attributes))
|
|
91
|
+
host.setAttribute(key, value);
|
|
92
|
+
});
|
|
93
|
+
// Headless UI attaches the host when an overlay opens; remove it with us.
|
|
94
|
+
useEffect(() => () => host?.remove(), [host]);
|
|
95
|
+
return target;
|
|
96
|
+
}
|
|
97
|
+
/** One provider per application. Selection never requires a hosted account. */
|
|
98
|
+
export function ThemeProvider({
|
|
99
|
+
children,
|
|
100
|
+
initialAppearance = "system",
|
|
101
|
+
appearance: controlledAppearance,
|
|
102
|
+
onAppearanceChange,
|
|
103
|
+
navigate,
|
|
104
|
+
brand = "neutral",
|
|
105
|
+
brandTokens,
|
|
106
|
+
density = "comfortable",
|
|
107
|
+
scope = "document",
|
|
108
|
+
className,
|
|
109
|
+
}: ThemeProviderProps) {
|
|
110
|
+
const [localAppearance, setLocalAppearance] =
|
|
111
|
+
useState<Appearance>(initialAppearance);
|
|
112
|
+
const appearance = controlledAppearance ?? localAppearance;
|
|
113
|
+
const setAppearance = (value: Appearance) => {
|
|
114
|
+
setLocalAppearance(value);
|
|
115
|
+
onAppearanceChange?.(value);
|
|
116
|
+
};
|
|
117
|
+
const systemDark = useSystemDark();
|
|
118
|
+
const outerNavigate = useContext(NavigationContext);
|
|
119
|
+
const resolvedAppearance = (
|
|
120
|
+
appearance === "system" ? (systemDark ? "dark" : "light") : appearance
|
|
121
|
+
) as "light" | "dark";
|
|
122
|
+
const attributes = {
|
|
123
|
+
"data-mortar": "",
|
|
124
|
+
"data-mortar-brand": brand,
|
|
125
|
+
"data-mortar-density": density,
|
|
126
|
+
"data-mortar-appearance": resolvedAppearance,
|
|
127
|
+
};
|
|
128
|
+
const host = usePortalHost(scope === "container", attributes);
|
|
129
|
+
useEffect(() => {
|
|
130
|
+
if (scope !== "document") return;
|
|
131
|
+
const element = document.documentElement;
|
|
132
|
+
const attributes = {
|
|
133
|
+
"data-mortar": "",
|
|
134
|
+
"data-mortar-brand": brand,
|
|
135
|
+
"data-mortar-density": density,
|
|
136
|
+
};
|
|
137
|
+
const previous = Object.keys(attributes).map(
|
|
138
|
+
(key) => [key, element.getAttribute(key)] as const,
|
|
139
|
+
);
|
|
140
|
+
for (const [key, value] of Object.entries(attributes))
|
|
141
|
+
element.setAttribute(key, value);
|
|
142
|
+
return () => {
|
|
143
|
+
for (const [key, value] of previous)
|
|
144
|
+
value === null
|
|
145
|
+
? element.removeAttribute(key)
|
|
146
|
+
: element.setAttribute(key, value);
|
|
147
|
+
};
|
|
148
|
+
}, [brand, density, scope]);
|
|
149
|
+
useEffect(() => {
|
|
150
|
+
if (scope !== "document") return;
|
|
151
|
+
const element = document.documentElement;
|
|
152
|
+
const previous = element.getAttribute("data-mortar-appearance");
|
|
153
|
+
element.setAttribute("data-mortar-appearance", resolvedAppearance);
|
|
154
|
+
return () => {
|
|
155
|
+
previous === null
|
|
156
|
+
? element.removeAttribute("data-mortar-appearance")
|
|
157
|
+
: element.setAttribute("data-mortar-appearance", previous);
|
|
158
|
+
};
|
|
159
|
+
}, [resolvedAppearance, scope]);
|
|
160
|
+
const brandCss = brandTokens ? brandToCss(brand, brandTokens) : undefined;
|
|
161
|
+
const theme = (
|
|
162
|
+
<NavigationContext.Provider value={navigate ?? outerNavigate}>
|
|
163
|
+
{brandCss && (
|
|
164
|
+
// An ordinary element, not a hoisted resource: React updates its
|
|
165
|
+
// content when the tokens change and removes it on unmount, and it
|
|
166
|
+
// is server-rendered. The rule keys on the brand attribute, so
|
|
167
|
+
// portalled overlays match it wherever they render.
|
|
168
|
+
<style data-mortar-brand-style={brand}>{brandCss}</style>
|
|
169
|
+
)}
|
|
170
|
+
<ThemeContext.Provider
|
|
171
|
+
value={{ appearance, setAppearance, brand, density }}
|
|
172
|
+
>
|
|
173
|
+
{children}
|
|
174
|
+
</ThemeContext.Provider>
|
|
175
|
+
</NavigationContext.Provider>
|
|
176
|
+
);
|
|
177
|
+
if (scope === "container")
|
|
178
|
+
return (
|
|
179
|
+
<div
|
|
180
|
+
{...componentRoot("ThemeProvider")}
|
|
181
|
+
{...attributes}
|
|
182
|
+
className={className}
|
|
183
|
+
>
|
|
184
|
+
<ThemeScopeContext.Provider value={attributes}>
|
|
185
|
+
<Portal.Group target={host}>{theme}</Portal.Group>
|
|
186
|
+
</ThemeScopeContext.Provider>
|
|
187
|
+
</div>
|
|
188
|
+
);
|
|
189
|
+
return theme;
|
|
190
|
+
}
|
|
191
|
+
export function AppearanceControl({
|
|
192
|
+
value,
|
|
193
|
+
onChange,
|
|
194
|
+
className,
|
|
195
|
+
}: {
|
|
196
|
+
value?: Appearance;
|
|
197
|
+
onChange?: (value: Appearance) => void;
|
|
198
|
+
className?: string;
|
|
199
|
+
} = {}) {
|
|
200
|
+
const controlled = value !== undefined || onChange !== undefined;
|
|
201
|
+
if ((value === undefined) !== (onChange === undefined))
|
|
202
|
+
throw new Error(
|
|
203
|
+
"AppearanceControl requires both value and onChange, or neither.",
|
|
204
|
+
);
|
|
205
|
+
const context = useContext(ThemeContext);
|
|
206
|
+
if (!controlled && !context)
|
|
207
|
+
throw new Error("AppearanceControl requires ThemeProvider");
|
|
208
|
+
const appearance = controlled ? value : context!.appearance;
|
|
209
|
+
const select = (next: Appearance) => {
|
|
210
|
+
if (controlled) onChange?.(next);
|
|
211
|
+
else context!.setAppearance(next);
|
|
212
|
+
};
|
|
213
|
+
return (
|
|
214
|
+
<AsComponentRoot name="AppearanceControl" of="SegmentedControl">
|
|
215
|
+
<SegmentedControl
|
|
216
|
+
label="Appearance"
|
|
217
|
+
value={appearance!}
|
|
218
|
+
className={className}
|
|
219
|
+
options={(["light", "dark", "system"] as const).map((option) => ({
|
|
220
|
+
value: option,
|
|
221
|
+
label: option[0].toUpperCase() + option.slice(1),
|
|
222
|
+
}))}
|
|
223
|
+
onChange={(next) => select(next as Appearance)}
|
|
224
|
+
/>
|
|
225
|
+
</AsComponentRoot>
|
|
226
|
+
);
|
|
227
|
+
}
|
package/src/heading.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Shared heading-level helpers. Internal: not exported from the package entry.
|
|
2
|
+
|
|
3
|
+
export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
|
|
4
|
+
|
|
5
|
+
/** The heading element for a level, e.g. `headingTag(2)` is `"h2"`. */
|
|
6
|
+
export function headingTag<L extends HeadingLevel>(level: L) {
|
|
7
|
+
return `h${level}` as `h${L}`;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** The level for headings nested under `level`, capped at `max` (default h6). */
|
|
11
|
+
export function childLevel(level: HeadingLevel, max: HeadingLevel = 6) {
|
|
12
|
+
return Math.min(level + 1, max) as HeadingLevel;
|
|
13
|
+
}
|
package/src/icons.tsx
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
// Shared decorative icons. Internal: not exported from the package entry.
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Decorative SVG on the 20×20 grid. Hidden from assistive technology unless
|
|
6
|
+
* `decorative` is false; meaning is carried by surrounding text.
|
|
7
|
+
*/
|
|
8
|
+
export function Icon({
|
|
9
|
+
className,
|
|
10
|
+
viewBox = "0 0 20 20",
|
|
11
|
+
fill = "none",
|
|
12
|
+
stroke,
|
|
13
|
+
strokeWidth,
|
|
14
|
+
decorative = true,
|
|
15
|
+
focusable = false,
|
|
16
|
+
children,
|
|
17
|
+
}: {
|
|
18
|
+
className?: string;
|
|
19
|
+
viewBox?: string;
|
|
20
|
+
/** SVG fill; `null` omits the attribute (paths set their own). */
|
|
21
|
+
fill?: string | null;
|
|
22
|
+
stroke?: string;
|
|
23
|
+
strokeWidth?: string;
|
|
24
|
+
/** Adds aria-hidden="true". */
|
|
25
|
+
decorative?: boolean;
|
|
26
|
+
/** Keep the SVG out of the tab order (focusable="false"); `true` omits the attribute. */
|
|
27
|
+
focusable?: boolean;
|
|
28
|
+
children: ReactNode;
|
|
29
|
+
}) {
|
|
30
|
+
return (
|
|
31
|
+
<svg
|
|
32
|
+
className={className}
|
|
33
|
+
viewBox={viewBox}
|
|
34
|
+
fill={fill ?? undefined}
|
|
35
|
+
stroke={stroke}
|
|
36
|
+
strokeWidth={strokeWidth}
|
|
37
|
+
aria-hidden={decorative ? "true" : undefined}
|
|
38
|
+
focusable={decorative && !focusable ? "false" : undefined}
|
|
39
|
+
>
|
|
40
|
+
{children}
|
|
41
|
+
</svg>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Round-capped currentColor stroke. */
|
|
46
|
+
function Stroke({
|
|
47
|
+
d,
|
|
48
|
+
width,
|
|
49
|
+
fill,
|
|
50
|
+
join = true,
|
|
51
|
+
}: {
|
|
52
|
+
d: string;
|
|
53
|
+
width: string;
|
|
54
|
+
fill?: string;
|
|
55
|
+
/** Round line joins; the close glyph has none. */
|
|
56
|
+
join?: boolean;
|
|
57
|
+
}) {
|
|
58
|
+
return (
|
|
59
|
+
<path
|
|
60
|
+
d={d}
|
|
61
|
+
fill={fill}
|
|
62
|
+
stroke="currentColor"
|
|
63
|
+
strokeWidth={width}
|
|
64
|
+
strokeLinecap="round"
|
|
65
|
+
strokeLinejoin={join ? "round" : undefined}
|
|
66
|
+
/>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function ChevronDown() {
|
|
71
|
+
return (
|
|
72
|
+
<Icon>
|
|
73
|
+
<Stroke d="m6 8 4 4 4-4" width="1.5" />
|
|
74
|
+
</Icon>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function ChevronLeft() {
|
|
79
|
+
return (
|
|
80
|
+
<Icon>
|
|
81
|
+
<Stroke d="m12 5-5 5 5 5" width="1.75" />
|
|
82
|
+
</Icon>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function ChevronRight() {
|
|
87
|
+
return (
|
|
88
|
+
<Icon>
|
|
89
|
+
<Stroke d="m8 5 5 5-5 5" width="1.75" />
|
|
90
|
+
</Icon>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Check mark. The three call sites predate this module and differ:
|
|
96
|
+
* `option` (listbox/combobox selected option), `list` (CheckList, 16×16 grid,
|
|
97
|
+
* fill on the path) and `step` (ProgressSteps circles: stroke on the svg,
|
|
98
|
+
* heavier weight, not aria-hidden because its marker span already is).
|
|
99
|
+
*/
|
|
100
|
+
export function Check({
|
|
101
|
+
variant = "option",
|
|
102
|
+
className,
|
|
103
|
+
}: {
|
|
104
|
+
variant?: "option" | "list" | "step";
|
|
105
|
+
className?: string;
|
|
106
|
+
}) {
|
|
107
|
+
if (variant === "list")
|
|
108
|
+
return (
|
|
109
|
+
<Icon viewBox="0 0 16 16" fill={null}>
|
|
110
|
+
<Stroke d="M3.5 8.5 6.5 11.5 12.5 4.5" width="1.75" fill="none" />
|
|
111
|
+
</Icon>
|
|
112
|
+
);
|
|
113
|
+
if (variant === "step")
|
|
114
|
+
return (
|
|
115
|
+
<Icon stroke="currentColor" strokeWidth="2.5" decorative={false}>
|
|
116
|
+
<path
|
|
117
|
+
d="M5 10.5l3.5 3.5L15 7"
|
|
118
|
+
strokeLinecap="round"
|
|
119
|
+
strokeLinejoin="round"
|
|
120
|
+
/>
|
|
121
|
+
</Icon>
|
|
122
|
+
);
|
|
123
|
+
return (
|
|
124
|
+
<Icon className={className}>
|
|
125
|
+
<Stroke d="m5 10 3.5 3.5L15 7" width="1.75" />
|
|
126
|
+
</Icon>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function Close() {
|
|
131
|
+
return (
|
|
132
|
+
<Icon>
|
|
133
|
+
<Stroke d="m5 5 10 10M15 5 5 15" width="1.75" join={false} />
|
|
134
|
+
</Icon>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function ArrowUp() {
|
|
139
|
+
return (
|
|
140
|
+
<Icon>
|
|
141
|
+
<Stroke d="M10 15V5m0 0-4 4m4-4 4 4" width="1.75" />
|
|
142
|
+
</Icon>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function ArrowDown() {
|
|
147
|
+
return (
|
|
148
|
+
<Icon>
|
|
149
|
+
<Stroke d="M10 5v10m0 0-4-4m4 4 4-4" width="1.75" />
|
|
150
|
+
</Icon>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function Star() {
|
|
155
|
+
return (
|
|
156
|
+
<Icon fill={null}>
|
|
157
|
+
<path
|
|
158
|
+
d="M10 1.8l2.5 5.3 5.8.7-4.3 4 1.1 5.7L10 14.7l-5.1 2.8 1.1-5.7-4.3-4 5.8-.7z"
|
|
159
|
+
fill="currentColor"
|
|
160
|
+
/>
|
|
161
|
+
</Icon>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function Paperclip() {
|
|
166
|
+
return (
|
|
167
|
+
<Icon>
|
|
168
|
+
<Stroke
|
|
169
|
+
d="M7 10.5l5-5a2.5 2.5 0 0 1 3.5 3.5l-6.5 6.5a4 4 0 0 1-5.7-5.7l6.2-6.2"
|
|
170
|
+
width="1.5"
|
|
171
|
+
/>
|
|
172
|
+
</Icon>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Generic person silhouette for avatars without a photo (24×24 grid, filled). */
|
|
177
|
+
export function Person({ className }: { className?: string }) {
|
|
178
|
+
return (
|
|
179
|
+
<Icon
|
|
180
|
+
className={className}
|
|
181
|
+
viewBox="0 0 24 24"
|
|
182
|
+
fill="currentColor"
|
|
183
|
+
focusable
|
|
184
|
+
>
|
|
185
|
+
<path d="M24 20.993V24H0v-2.996A14.977 14.977 0 0 1 12.004 15c4.904 0 9.26 2.354 11.996 5.993zM16.002 8.999a4 4 0 1 1-8 0 4 4 0 0 1 8 0z" />
|
|
186
|
+
</Icon>
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Table sort indicator; the active direction's chevron is marked data-active. */
|
|
191
|
+
export function SortArrows({
|
|
192
|
+
direction,
|
|
193
|
+
}: {
|
|
194
|
+
direction?: "ascending" | "descending";
|
|
195
|
+
}) {
|
|
196
|
+
return (
|
|
197
|
+
<Icon viewBox="0 0 12 12" fill={null}>
|
|
198
|
+
<path
|
|
199
|
+
d="M3.5 4.5 6 2l2.5 2.5"
|
|
200
|
+
data-active={direction === "ascending" || undefined}
|
|
201
|
+
/>
|
|
202
|
+
<path
|
|
203
|
+
d="M3.5 7.5 6 10l2.5-2.5"
|
|
204
|
+
data-active={direction === "descending" || undefined}
|
|
205
|
+
/>
|
|
206
|
+
</Icon>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const EYE =
|
|
211
|
+
"M1.75 10S4.75 4.25 10 4.25 18.25 10 18.25 10 15.25 15.75 10 15.75 1.75 10 1.75 10zM10 12.5a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5z";
|
|
212
|
+
/** Open eye: shown while a password is hidden. */
|
|
213
|
+
export function Eye() {
|
|
214
|
+
return (
|
|
215
|
+
<Icon>
|
|
216
|
+
<Stroke d={EYE} width="1.5" />
|
|
217
|
+
</Icon>
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** Struck-through eye: shown while a password is visible. */
|
|
222
|
+
export function EyeOff() {
|
|
223
|
+
return (
|
|
224
|
+
<Icon>
|
|
225
|
+
<Stroke d={EYE + "M3 3l14 14"} width="1.5" />
|
|
226
|
+
</Icon>
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** Hollow circle: a requirement that is not met yet. */
|
|
231
|
+
export function Circle() {
|
|
232
|
+
return (
|
|
233
|
+
<Icon>
|
|
234
|
+
<Stroke d="M10 15a5 5 0 1 0 0-10 5 5 0 0 0 0 10z" width="1.5" />
|
|
235
|
+
</Icon>
|
|
236
|
+
);
|
|
237
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
export { ThemeProvider, AppearanceControl } from "./foundation.js";
|
|
2
|
+
export type { BrandDefinition, BrandAppearanceTokens } from "./brand.js";
|
|
3
|
+
export type {
|
|
4
|
+
ThemeProviderProps,
|
|
5
|
+
Appearance,
|
|
6
|
+
Brand,
|
|
7
|
+
Density,
|
|
8
|
+
} from "./foundation.js";
|
|
9
|
+
export {
|
|
10
|
+
Button,
|
|
11
|
+
ButtonGroup,
|
|
12
|
+
ActionLink,
|
|
13
|
+
Input,
|
|
14
|
+
Select,
|
|
15
|
+
TextArea,
|
|
16
|
+
Checkbox,
|
|
17
|
+
Fieldset,
|
|
18
|
+
RadioGroup,
|
|
19
|
+
Switch,
|
|
20
|
+
Badge,
|
|
21
|
+
Tabs,
|
|
22
|
+
Modal,
|
|
23
|
+
Dropdown,
|
|
24
|
+
Notice,
|
|
25
|
+
EmptyState,
|
|
26
|
+
ErrorSummary,
|
|
27
|
+
} from "./primitives.js";
|
|
28
|
+
export type {
|
|
29
|
+
ButtonProps,
|
|
30
|
+
ButtonGroupItem,
|
|
31
|
+
DropdownItem,
|
|
32
|
+
ErrorSummaryItem,
|
|
33
|
+
InputProps,
|
|
34
|
+
LinkAction,
|
|
35
|
+
Tone,
|
|
36
|
+
} from "./primitives.js";
|
|
37
|
+
export {
|
|
38
|
+
AppShell,
|
|
39
|
+
PageHeading,
|
|
40
|
+
MetricGroup,
|
|
41
|
+
DataTable,
|
|
42
|
+
Table,
|
|
43
|
+
Pagination,
|
|
44
|
+
SettingsSection,
|
|
45
|
+
ActivityFeed,
|
|
46
|
+
SignInPanel,
|
|
47
|
+
Sidebar,
|
|
48
|
+
Navbar,
|
|
49
|
+
StackedShell,
|
|
50
|
+
Breadcrumbs,
|
|
51
|
+
ProgressSteps,
|
|
52
|
+
TabNav,
|
|
53
|
+
ActionPanel,
|
|
54
|
+
} from "./application.js";
|
|
55
|
+
export type {
|
|
56
|
+
AppShellProps,
|
|
57
|
+
NavigationItem,
|
|
58
|
+
DataTableProps,
|
|
59
|
+
DataTableColumn,
|
|
60
|
+
TableSort,
|
|
61
|
+
SortDirection,
|
|
62
|
+
SignInProvider,
|
|
63
|
+
SidebarProps,
|
|
64
|
+
BreadcrumbItem,
|
|
65
|
+
ProgressStep,
|
|
66
|
+
TabNavItem,
|
|
67
|
+
StackedShellProps,
|
|
68
|
+
NavbarProps,
|
|
69
|
+
BreadcrumbsProps,
|
|
70
|
+
TabNavProps,
|
|
71
|
+
ProgressStepsProps,
|
|
72
|
+
ActionPanelProps,
|
|
73
|
+
PageHeadingProps,
|
|
74
|
+
MetricGroupProps,
|
|
75
|
+
TableProps,
|
|
76
|
+
PaginationProps,
|
|
77
|
+
SettingsSectionProps,
|
|
78
|
+
ActivityFeedProps,
|
|
79
|
+
SignInPanelProps,
|
|
80
|
+
} from "./application.js";
|
|
81
|
+
export {
|
|
82
|
+
AuthLayout,
|
|
83
|
+
ProviderButtons,
|
|
84
|
+
PasswordInput,
|
|
85
|
+
CodeInput,
|
|
86
|
+
} from "./auth.js";
|
|
87
|
+
export type {
|
|
88
|
+
AuthLayoutProps,
|
|
89
|
+
ProviderButtonsProps,
|
|
90
|
+
PasswordInputProps,
|
|
91
|
+
PasswordRequirement,
|
|
92
|
+
PasswordStrength,
|
|
93
|
+
CodeInputProps,
|
|
94
|
+
} from "./auth.js";
|
|
95
|
+
export {
|
|
96
|
+
MarketingHeader,
|
|
97
|
+
Hero,
|
|
98
|
+
FeatureGrid,
|
|
99
|
+
FeatureSection,
|
|
100
|
+
ProcessSteps,
|
|
101
|
+
Testimonial,
|
|
102
|
+
LogoCloud,
|
|
103
|
+
Pricing,
|
|
104
|
+
FAQ,
|
|
105
|
+
CallToAction,
|
|
106
|
+
Footer,
|
|
107
|
+
HeaderSection,
|
|
108
|
+
ContentSection,
|
|
109
|
+
} from "./marketing.js";
|
|
110
|
+
export type {
|
|
111
|
+
PricingPlan,
|
|
112
|
+
PricingExtra,
|
|
113
|
+
HeaderLink,
|
|
114
|
+
FlyoutItem,
|
|
115
|
+
} from "./marketing.js";
|
|
116
|
+
export { SegmentedControl, CodeBlock, PreviewFrame } from "./inspection.js";
|
|
117
|
+
export {
|
|
118
|
+
Avatar,
|
|
119
|
+
AvatarGroup,
|
|
120
|
+
Kbd,
|
|
121
|
+
ProgressBar,
|
|
122
|
+
FilterChip,
|
|
123
|
+
CheckList,
|
|
124
|
+
DescriptionList,
|
|
125
|
+
GridList,
|
|
126
|
+
AttachmentList,
|
|
127
|
+
} from "./display.js";
|
|
128
|
+
export type {
|
|
129
|
+
GridListItem,
|
|
130
|
+
Attachment,
|
|
131
|
+
AvatarProps,
|
|
132
|
+
AvatarGroupProps,
|
|
133
|
+
KbdProps,
|
|
134
|
+
ProgressBarProps,
|
|
135
|
+
FilterChipProps,
|
|
136
|
+
CheckListProps,
|
|
137
|
+
DescriptionListProps,
|
|
138
|
+
GridListProps,
|
|
139
|
+
AttachmentListProps,
|
|
140
|
+
} from "./display.js";
|
|
141
|
+
export {
|
|
142
|
+
Stack,
|
|
143
|
+
Container,
|
|
144
|
+
Text,
|
|
145
|
+
Section,
|
|
146
|
+
Divider,
|
|
147
|
+
Card,
|
|
148
|
+
MediaObject,
|
|
149
|
+
SectionHeading,
|
|
150
|
+
Prose,
|
|
151
|
+
} from "./layout.js";
|
|
152
|
+
export type {
|
|
153
|
+
SectionTone,
|
|
154
|
+
StackProps,
|
|
155
|
+
ContainerProps,
|
|
156
|
+
TextProps,
|
|
157
|
+
SectionProps,
|
|
158
|
+
DividerProps,
|
|
159
|
+
CardProps,
|
|
160
|
+
MediaObjectProps,
|
|
161
|
+
SectionHeadingProps,
|
|
162
|
+
ProseProps,
|
|
163
|
+
} from "./layout.js";
|
|
164
|
+
export { MockWindow, ChatPreview } from "./showcase.js";
|
|
165
|
+
export type { ChatMessage } from "./showcase.js";
|
|
166
|
+
export { Toast, Toaster, ConfirmDialog, SaveBar, Banner } from "./feedback.js";
|
|
167
|
+
export type { ToastItem, ToastAction } from "./feedback.js";
|
|
168
|
+
export { SlideOver, CommandPalette, StackedList } from "./workspace.js";
|
|
169
|
+
export type {
|
|
170
|
+
CommandItem,
|
|
171
|
+
StackedListItem,
|
|
172
|
+
SlideOverProps,
|
|
173
|
+
CommandPaletteProps,
|
|
174
|
+
StackedListProps,
|
|
175
|
+
} from "./workspace.js";
|
|
176
|
+
export {
|
|
177
|
+
BentoGrid,
|
|
178
|
+
StatGroup,
|
|
179
|
+
TestimonialGrid,
|
|
180
|
+
PlanComparison,
|
|
181
|
+
PostList,
|
|
182
|
+
TeamGrid,
|
|
183
|
+
ContactSection,
|
|
184
|
+
Newsletter,
|
|
185
|
+
NotFound,
|
|
186
|
+
} from "./marketing-sections.js";
|
|
187
|
+
export {
|
|
188
|
+
DocsShell,
|
|
189
|
+
SearchTrigger,
|
|
190
|
+
SearchDialog,
|
|
191
|
+
Steps,
|
|
192
|
+
Callout,
|
|
193
|
+
FileTree,
|
|
194
|
+
CodeTabs,
|
|
195
|
+
Release,
|
|
196
|
+
ApiEndpoint,
|
|
197
|
+
PageFeedback,
|
|
198
|
+
PagerLinks,
|
|
199
|
+
LinkCards,
|
|
200
|
+
} from "./docs.js";
|
|
201
|
+
export type { DocsShellProps, FileNode } from "./docs.js";
|
|
202
|
+
export { Listbox, Combobox } from "./choice.js";
|
|
203
|
+
export type { ChoiceOption } from "./choice.js";
|
|
204
|
+
export { Calendar } from "./calendar.js";
|
|
205
|
+
export type { CalendarEvent, CalendarView, CalendarProps } from "./calendar.js";
|
|
206
|
+
export {
|
|
207
|
+
ProductList,
|
|
208
|
+
ProductOverview,
|
|
209
|
+
ShoppingCart,
|
|
210
|
+
OrderSummary,
|
|
211
|
+
ProductQuickView,
|
|
212
|
+
ProductFilters,
|
|
213
|
+
ProductReviews,
|
|
214
|
+
OrderHistory,
|
|
215
|
+
} from "./ecommerce.js";
|
|
216
|
+
export type {
|
|
217
|
+
ProductImage,
|
|
218
|
+
ProductCard,
|
|
219
|
+
ProductOption,
|
|
220
|
+
CartItem,
|
|
221
|
+
FilterFacet,
|
|
222
|
+
Review,
|
|
223
|
+
Order,
|
|
224
|
+
ProductListProps,
|
|
225
|
+
ProductOverviewProps,
|
|
226
|
+
ShoppingCartProps,
|
|
227
|
+
} from "./ecommerce.js";
|
|
228
|
+
export { CommentComposer } from "./composer.js";
|
|
229
|
+
export type { LinkItem, ItemGroup } from "./navigation.js";
|
|
230
|
+
export type { HeadingLevel } from "./heading.js";
|