@nadicodeai/ui 12.1.172 → 12.1.174
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 +7 -5
- package/README.md +1 -2
- package/dist/components/account-menu.d.ts +3 -3
- package/dist/components/account-menu.d.ts.map +1 -1
- package/dist/components/account-menu.js +3 -3
- package/dist/components/chart-card.d.ts +5 -7
- package/dist/components/chart-card.d.ts.map +1 -1
- package/dist/components/chart-card.js +5 -5
- package/dist/components/kpi-card.d.ts.map +1 -1
- package/dist/components/kpi-card.js +3 -3
- package/dist/components/numbered-steps.d.ts +1 -1
- package/dist/components/numbered-steps.js +1 -1
- package/dist/components/object-page.d.ts +6 -6
- package/dist/components/object-page.d.ts.map +1 -1
- package/dist/components/object-page.js +4 -5
- package/dist/components/object-workspace.d.ts +17 -9
- package/dist/components/object-workspace.d.ts.map +1 -1
- package/dist/components/object-workspace.js +7 -6
- package/dist/components/overview-page.d.ts +7 -2
- package/dist/components/overview-page.d.ts.map +1 -1
- package/dist/components/overview-page.js +4 -4
- package/dist/components/page-actions.d.ts +22 -0
- package/dist/components/page-actions.d.ts.map +1 -1
- package/dist/components/page-actions.js +16 -0
- package/dist/components/record-list.d.ts +4 -2
- package/dist/components/record-list.d.ts.map +1 -1
- package/dist/components/record-list.js +1 -1
- package/dist/components/settings-facts.d.ts +5 -4
- package/dist/components/settings-facts.d.ts.map +1 -1
- package/dist/components/settings-facts.js +5 -4
- package/dist/components/settings-section.d.ts +19 -8
- package/dist/components/settings-section.d.ts.map +1 -1
- package/dist/components/settings-section.js +4 -3
- package/dist/components/table-card.d.ts +9 -4
- package/dist/components/table-card.d.ts.map +1 -1
- package/dist/components/table-card.js +7 -5
- package/dist/components/text-link.d.ts +6 -6
- package/dist/components/text-link.js +6 -6
- package/dist/showcase/specimens/actions.d.ts +1 -0
- package/dist/showcase/specimens/actions.d.ts.map +1 -1
- package/dist/showcase/specimens/actions.js +5 -1
- package/dist/showcase/specimens/charts.js +1 -1
- package/dist/showcase/specimens/compositions.d.ts.map +1 -1
- package/dist/showcase/specimens/compositions.js +1 -1
- package/dist/showcase/specimens/data.js +1 -1
- package/dist/showcase/specimens/index.d.ts +1 -0
- package/dist/showcase/specimens/index.d.ts.map +1 -1
- package/dist/showcase/usage.generated.d.ts +1 -1
- package/dist/showcase/usage.generated.d.ts.map +1 -1
- package/dist/showcase/usage.generated.js +28 -17
- package/dist/styles/components.css +9 -0
- package/package.json +2 -2
package/AGENTS.md
CHANGED
|
@@ -4,11 +4,13 @@ Shared React components, primitives, the Agent conversation interface, and
|
|
|
4
4
|
product compositions. Source lives in `src/`; consumers receive `dist/` through
|
|
5
5
|
`package.json` exports.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
roles, materials, glass
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
How components look comes from `../design-system/DESIGN.md`, the visual
|
|
8
|
+
identity: tokens, roles, materials, glass and motion. What a component does,
|
|
9
|
+
takes or holds (its sizes, variants, slots, behaviour, words and access) is the
|
|
10
|
+
usage page in the TSDoc of its main export, which `generate:usage` compiles and
|
|
11
|
+
`check:usage` checks; a rule several components share goes in the component
|
|
12
|
+
that draws it for all of them, never in `DESIGN.md`
|
|
13
|
+
([what goes in it](../design-system/AGENTS.md#what-goes-in-designmd)). A component change a person can see goes through
|
|
12
14
|
`product-design` first. This file holds only how the package is kept.
|
|
13
15
|
|
|
14
16
|
- **Styling.** A component uses the semantic roles and the generated tokens, and
|
package/README.md
CHANGED
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
nadicode's React components, published as a built package: shadcn components on
|
|
4
4
|
Base UI, the chart kit, the Agent conversation interface, and the product
|
|
5
5
|
compositions the website, the Portal, Nadia and Alveare share. They build on
|
|
6
|
-
`@nadicodeai/design-system`, whose `DESIGN.md` is the
|
|
7
|
-
authority for tokens, visual direction and the rules components share; a
|
|
6
|
+
`@nadicodeai/design-system`, whose `DESIGN.md` is the visual identity; a
|
|
8
7
|
component's own contract is the usage page in its source.
|
|
9
8
|
|
|
10
9
|
## Install
|
|
@@ -71,7 +71,7 @@ export type AccountMenuProps = {
|
|
|
71
71
|
identity: AccountIdentity;
|
|
72
72
|
/** Names the trigger and the menu: "Account" reads "Account: Ada Rossi". */
|
|
73
73
|
label: string;
|
|
74
|
-
/** The gear beside the identity,
|
|
74
|
+
/** The account's settings: the gear beside the identity, and a Settings row after the choices. */
|
|
75
75
|
settings?: PageCommandAction & {
|
|
76
76
|
label: string;
|
|
77
77
|
};
|
|
@@ -99,9 +99,9 @@ export type AccountMenuProps = {
|
|
|
99
99
|
* and the menu opening on that same identity with what they can do beneath it.
|
|
100
100
|
*
|
|
101
101
|
* @family Menus
|
|
102
|
-
* @when The account at the sidebar's foot in the Portal, Nadia and Alveare: the
|
|
102
|
+
* @when The account at the sidebar's foot in the Portal, Nadia and Alveare: the account's settings, Theme, Language, a product's own settings, its commands, and Sign out.
|
|
103
103
|
* @instead A record's or a page's commands are an ActionMenu; a setting with more than a few options or one that needs a Save belongs on a SettingsPage.
|
|
104
|
-
* @behaviour The menu opens 6 px above its trigger, 288 px wide, as Vercel's does: a header of the name and every line under it with the gear at its end and no picture, one divider, then 36 px rows, each its label leading and its icon or control trailing in muted ink: the product's leading commands first, Theme and Language as small round pills, the product's choices as submenus, its other commands, each command with any reading before its icon, and Sign out last in the body colour, since signing out destroys nothing. Ruled by the owner on 16 October 2026; on
|
|
104
|
+
* @behaviour The menu opens 6 px above its trigger, 288 px wide, as Vercel's does: a header of the name and every line under it with the gear at its end and no picture, one divider, then 36 px rows, each its label leading and its icon or control trailing in muted ink: the product's leading commands first, Theme and Language as small round pills, the product's choices as submenus, the Settings row to the gear's destination, its other commands, each command with any reading before its icon, and Sign out last in the body colour, since signing out destroys nothing. Ruled by the owner on 16 October 2026; on 27 October 2026 the owner asked for the Settings row as well as the gear in every product, as Nadia had it since 19 October. Theme and Language apply at once and keep the menu open. While signing out, Sign out says its pending word, the menu stays open and the trigger is locked; a failure shows under it. A Sign out that `confirms` closes the menu instead, for the product's own confirmation.
|
|
105
105
|
* @words The menu's name ("Account"), the person's name and lines, then each row's noun or verb from the product's catalogue: "Theme", "Language", "Usage", "Sign out" and "Signing out".
|
|
106
106
|
* @access The trigger and the menu are named "{label}: {name}"; the gear carries its label; the pending sign-out is aria-busy and announced through a status; a failure is an alert.
|
|
107
107
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-menu.d.ts","sourceRoot":"","sources":["../../src/components/account-menu.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAgB,SAAS,EAAE,MAAM,OAAO,CAAC;AAGrE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAiB5D,OAAO,EAAiB,KAAK,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AACtF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAEnE,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACrF,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAIjE,2FAA2F;AAC3F,MAAM,MAAM,eAAe,GAAG;IAC5B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,KAAK,CAAC,EAAE,SAAS,EAAE,CAAC;IACpB;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,eAAe,CAAC;IAC1B,8EAA8E;IAC9E,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,SAAS,EACT,QAAQ,EACR,WAAmB,GACpB,EAAE,wBAAwB,+BAoC1B;AAED,MAAM,MAAM,kBAAkB,GAAG,WAAW,GAAG;IAC7C,8CAA8C;IAC9C,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB,CAAC;AAEF,2EAA2E;AAC3E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,mHAAmH;IACnH,OAAO,EAAE,SAAS;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,SAAS,CAAC;QAAC,IAAI,CAAC,EAAE,SAAS,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC3F,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,kBAAkB;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,gGAAgG;IAChG,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,eAAe,CAAC;IAC1B,4EAA4E;IAC5E,KAAK,EAAE,MAAM,CAAC;IACd
|
|
1
|
+
{"version":3,"file":"account-menu.d.ts","sourceRoot":"","sources":["../../src/components/account-menu.tsx"],"names":[],"mappings":"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"}
|
|
@@ -25,16 +25,16 @@ const NO_COMMANDS = [];
|
|
|
25
25
|
* and the menu opening on that same identity with what they can do beneath it.
|
|
26
26
|
*
|
|
27
27
|
* @family Menus
|
|
28
|
-
* @when The account at the sidebar's foot in the Portal, Nadia and Alveare: the
|
|
28
|
+
* @when The account at the sidebar's foot in the Portal, Nadia and Alveare: the account's settings, Theme, Language, a product's own settings, its commands, and Sign out.
|
|
29
29
|
* @instead A record's or a page's commands are an ActionMenu; a setting with more than a few options or one that needs a Save belongs on a SettingsPage.
|
|
30
|
-
* @behaviour The menu opens 6 px above its trigger, 288 px wide, as Vercel's does: a header of the name and every line under it with the gear at its end and no picture, one divider, then 36 px rows, each its label leading and its icon or control trailing in muted ink: the product's leading commands first, Theme and Language as small round pills, the product's choices as submenus, its other commands, each command with any reading before its icon, and Sign out last in the body colour, since signing out destroys nothing. Ruled by the owner on 16 October 2026; on
|
|
30
|
+
* @behaviour The menu opens 6 px above its trigger, 288 px wide, as Vercel's does: a header of the name and every line under it with the gear at its end and no picture, one divider, then 36 px rows, each its label leading and its icon or control trailing in muted ink: the product's leading commands first, Theme and Language as small round pills, the product's choices as submenus, the Settings row to the gear's destination, its other commands, each command with any reading before its icon, and Sign out last in the body colour, since signing out destroys nothing. Ruled by the owner on 16 October 2026; on 27 October 2026 the owner asked for the Settings row as well as the gear in every product, as Nadia had it since 19 October. Theme and Language apply at once and keep the menu open. While signing out, Sign out says its pending word, the menu stays open and the trigger is locked; a failure shows under it. A Sign out that `confirms` closes the menu instead, for the product's own confirmation.
|
|
31
31
|
* @words The menu's name ("Account"), the person's name and lines, then each row's noun or verb from the product's catalogue: "Theme", "Language", "Usage", "Sign out" and "Signing out".
|
|
32
32
|
* @access The trigger and the menu are named "{label}: {name}"; the gear carries its label; the pending sign-out is aria-busy and announced through a status; a failure is an alert.
|
|
33
33
|
*/
|
|
34
34
|
export function AccountMenu({ align = "start", choices = NO_CHOICES, commands = NO_COMMANDS, identity, label, language, leadingCommands = NO_COMMANDS, onOpenChange, pictureOnly = false, settings, side = "top", signOut, theme, }) {
|
|
35
35
|
const name = `${label}: ${identity.name}`;
|
|
36
36
|
const commandRows = (rows) => rows.map(({ value, ...command }) => (_jsx(AccountMenuRow, { "aria-label": command.accessibleName, disabled: command.locked === true || command.disabledReason !== undefined, icon: command.icon, reason: command.locked ? command.reason : command.disabledReason, value: value, ...menuItemAction(command), children: command.label }, command.label)));
|
|
37
|
-
return (_jsxs(DropdownMenu, { onOpenChange: onOpenChange, children: [_jsx(DropdownMenuTrigger, { "aria-label": name, className: cn("group/account flex min-h-9 min-w-0 items-center rounded-full outline-none focus-visible:nc-focus-outline focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50", pictureOnly ? "justify-center" : "w-full pr-2 pl-2.5"), disabled: signOut.pending, children: _jsx(AccountMenuIdentity, { identity: identity, pictureOnly: pictureOnly }) }), _jsxs(DropdownMenuContent, { align: align, "aria-label": name, className: "w-72 max-w-[calc(100vw-2rem)] p-1.5", density: "comfortable", side: side, sideOffset: 6, children: [_jsxs(DropdownMenuGroup, { className: "flex min-w-0 items-center gap-2 py-1.5 pl-2", "data-slot": "account-menu-header", children: [_jsxs("span", { className: "grid min-w-0 flex-1 gap-0.5 text-left", children: [_jsx("span", { className: "truncate text-sm font-medium text-foreground", children: identity.name }), identity.notice ? _jsx("span", { className: "text-xs", children: identity.notice }) : null, identity.lines?.map((line, index) => (_jsx("span", { className: "truncate text-xs text-muted-foreground", children: line }, index)))] }), settings ? (_jsx(DropdownMenuItem, { "aria-label": settings.label, className: "text-muted-foreground", iconOnly: true, ...menuItemAction(settings), children: _jsx(Settings, { "aria-hidden": "true" }) })) : null] }), _jsx(DropdownMenuSeparator, { className: "-mx-1.5 my-1.5" }), _jsxs(DropdownMenuGroup, { children: [commandRows(leadingCommands), _jsx(ThemeModeMenu, { ...theme }), language ? _jsx(MenuChoiceRow, { ...language }) : null, choices.map((choice) => (_jsx(AccountMenuChoiceRow, { choice: choice }, choice.label))), commandRows(commands), _jsx(AccountMenuRow, { "aria-busy": signOut.pending || undefined, closeOnClick: signOut.confirms === true, disabled: signOut.pending, icon: _jsx(LogOut, {}), onClick: () => {
|
|
37
|
+
return (_jsxs(DropdownMenu, { onOpenChange: onOpenChange, children: [_jsx(DropdownMenuTrigger, { "aria-label": name, className: cn("group/account flex min-h-9 min-w-0 items-center rounded-full outline-none focus-visible:nc-focus-outline focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50", pictureOnly ? "justify-center" : "w-full pr-2 pl-2.5"), disabled: signOut.pending, children: _jsx(AccountMenuIdentity, { identity: identity, pictureOnly: pictureOnly }) }), _jsxs(DropdownMenuContent, { align: align, "aria-label": name, className: "w-72 max-w-[calc(100vw-2rem)] p-1.5", density: "comfortable", side: side, sideOffset: 6, children: [_jsxs(DropdownMenuGroup, { className: "flex min-w-0 items-center gap-2 py-1.5 pl-2", "data-slot": "account-menu-header", children: [_jsxs("span", { className: "grid min-w-0 flex-1 gap-0.5 text-left", children: [_jsx("span", { className: "truncate text-sm font-medium text-foreground", children: identity.name }), identity.notice ? _jsx("span", { className: "text-xs", children: identity.notice }) : null, identity.lines?.map((line, index) => (_jsx("span", { className: "truncate text-xs text-muted-foreground", children: line }, index)))] }), settings ? (_jsx(DropdownMenuItem, { "aria-label": settings.label, className: "text-muted-foreground", iconOnly: true, ...menuItemAction(settings), children: _jsx(Settings, { "aria-hidden": "true" }) })) : null] }), _jsx(DropdownMenuSeparator, { className: "-mx-1.5 my-1.5" }), _jsxs(DropdownMenuGroup, { children: [commandRows(leadingCommands), _jsx(ThemeModeMenu, { ...theme }), language ? _jsx(MenuChoiceRow, { ...language }) : null, choices.map((choice) => (_jsx(AccountMenuChoiceRow, { choice: choice }, choice.label))), settings ? (_jsx(AccountMenuRow, { icon: _jsx(Settings, {}), ...menuItemAction(settings), children: settings.label })) : null, commandRows(commands), _jsx(AccountMenuRow, { "aria-busy": signOut.pending || undefined, closeOnClick: signOut.confirms === true, disabled: signOut.pending, icon: _jsx(LogOut, {}), onClick: () => {
|
|
38
38
|
if (!signOut.pending)
|
|
39
39
|
signOut.onSignOut();
|
|
40
40
|
}, children: _jsx(PendingLabel, { pending: signOut.pending, pendingLabel: signOut.pendingLabel, children: signOut.label }) }), signOut.pending ? (_jsx("span", { className: "sr-only", role: "status", children: signOut.pendingLabel })) : null, signOut.failure ? (_jsxs(FieldError, { className: "mx-2 mb-1 wrap-break-word", "data-message-code": signOut.failure.code, children: [_jsx("span", { className: "block font-medium", children: signOut.failure.title }), bodyBeyondTitle(signOut.failure) ? (_jsx("span", { className: "block", children: bodyBeyondTitle(signOut.failure) })) : null] })) : null] })] })] }));
|
|
@@ -34,12 +34,10 @@ type ChartCardFooter = {
|
|
|
34
34
|
/** A command on the reading, such as Refresh; the way to its owner is the header's `link`. */
|
|
35
35
|
link?: PageCommand;
|
|
36
36
|
};
|
|
37
|
-
/** The way to the reading's owner
|
|
37
|
+
/** The way to the reading's owner: a › at the header's trailing edge, as Vercel's Observability preview cards open their page. */
|
|
38
38
|
type ChartCardLink = {
|
|
39
|
-
/**
|
|
39
|
+
/** The link's accessible name, which names its destination: "Explore Usage by model". */
|
|
40
40
|
label: string;
|
|
41
|
-
/** Read instead of the label when it alone does not name the target: "Explore Usage by model". */
|
|
42
|
-
accessibleName?: string;
|
|
43
41
|
/** The app's link element, e.g. `<Link href="/analytics/explore?…" />`. */
|
|
44
42
|
link: React.ReactElement;
|
|
45
43
|
};
|
|
@@ -101,9 +99,9 @@ declare function foldOtherSeries(series: readonly ChartSeries[], data: readonly
|
|
|
101
99
|
* @family Charts
|
|
102
100
|
* @when Every chart and every reading built around one: the frame a page never builds by hand. A chart is a named section of its page.
|
|
103
101
|
* @instead A single figure is a `KpiCard`, never nested inside a chart. A ranking beside the plot goes in the card's `aside`, not a second card.
|
|
104
|
-
* @behaviour The default header puts controls at the trailing edge on wide cards, below the title on narrow cards. The summary variant keeps controls immediately after the title, the owner link at the trailing edge, and the total and period above the plot. The ranking sits beside the plot only when the card itself reaches 48 rem; narrower cards keep a full-width plot and put the ranking below it. Controls wrap on narrow cards. An empty dataset shows its `emptyState` in the plot's place. The footer carries the reading time and at most one command on that reading.
|
|
105
|
-
* @words Title names the measure and its split: "Model spend by Customer". Period as dates. The link: "Explore",
|
|
106
|
-
* @access The title is a heading at the page's level (default 2) and labels the plot. The plot is focusable and shows the 2 px focus outline only on keyboard focus.
|
|
102
|
+
* @behaviour The default header puts controls at the trailing edge on wide cards, below the title on narrow cards. The way to the reading's owner is a › icon link after the controls, as Vercel's Observability preview cards open their page. The summary variant keeps controls immediately after the title, the owner link at the trailing edge, and the total and period above the plot. The ranking sits beside the plot only when the card itself reaches 48 rem; narrower cards keep a full-width plot and put the ranking below it. Controls wrap on narrow cards. An empty dataset shows its `emptyState` in the plot's place. The footer carries the reading time and at most one command on that reading.
|
|
103
|
+
* @words Title names the measure and its split: "Model spend by Customer". Period as dates. The link's name says where it goes: "Explore Usage by model", "Open revenue in Analytics". Footer: the reading's time, "Updated 4 minutes ago" or "Read at 12:42". Sentence case throughout.
|
|
104
|
+
* @access The title is a heading at the page's level (default 2) and labels the plot. The owner link is a native link named by its `label`. The plot is focusable and shows the 2 px focus outline only on keyboard focus.
|
|
107
105
|
*/
|
|
108
106
|
declare function ChartCard({ variant, title, period, controls, link, summary, aside, footer, headingLevel, className, children, }: Pick<ChartCardProps, "title" | "variant" | "period" | "controls" | "link" | "summary" | "aside" | "footer" | "headingLevel" | "className"> & {
|
|
109
107
|
/** Receives the title's id, which the plot takes as `aria-labelledby`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chart-card.d.ts","sourceRoot":"","sources":["../../src/components/chart-card.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAa/B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAChF,OAAO,EAAc,KAAK,cAAc,EAAE,KAAK,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAG3F,KAAK,WAAW,GAAG;IACjB,0EAA0E;IAC1E,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;AAElD,KAAK,aAAa,GAAG,SAAS,GAAG,IAAI,CAAC;AACtC,KAAK,gBAAgB,GAAG,SAAS,GAAG,SAAS,CAAC;AAE9C,KAAK,iBAAiB,GAAG;IACvB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,SAAS,EAAE,cAAc,CAAC;IAC1B,IAAI,EAAE,SAAS,CAAC;IAChB,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,KAAK,iBAAiB,GAAG;IACvB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,OAAO,CAAC,EAAE,gBAAgB,CAAC;CAC5B,CAAC;AAYF,kGAAkG;AAClG,KAAK,eAAe,GAAG;IACrB,gDAAgD;IAChD,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,8FAA8F;IAC9F,IAAI,CAAC,EAAE,WAAW,CAAC;CACpB,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"chart-card.d.ts","sourceRoot":"","sources":["../../src/components/chart-card.tsx"],"names":[],"mappings":"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"}
|
|
@@ -62,16 +62,16 @@ function foldOtherSeries(series, data, entityColorMap, otherLabel) {
|
|
|
62
62
|
* @family Charts
|
|
63
63
|
* @when Every chart and every reading built around one: the frame a page never builds by hand. A chart is a named section of its page.
|
|
64
64
|
* @instead A single figure is a `KpiCard`, never nested inside a chart. A ranking beside the plot goes in the card's `aside`, not a second card.
|
|
65
|
-
* @behaviour The default header puts controls at the trailing edge on wide cards, below the title on narrow cards. The summary variant keeps controls immediately after the title, the owner link at the trailing edge, and the total and period above the plot. The ranking sits beside the plot only when the card itself reaches 48 rem; narrower cards keep a full-width plot and put the ranking below it. Controls wrap on narrow cards. An empty dataset shows its `emptyState` in the plot's place. The footer carries the reading time and at most one command on that reading.
|
|
66
|
-
* @words Title names the measure and its split: "Model spend by Customer". Period as dates. The link: "Explore",
|
|
67
|
-
* @access The title is a heading at the page's level (default 2) and labels the plot. The plot is focusable and shows the 2 px focus outline only on keyboard focus.
|
|
65
|
+
* @behaviour The default header puts controls at the trailing edge on wide cards, below the title on narrow cards. The way to the reading's owner is a › icon link after the controls, as Vercel's Observability preview cards open their page. The summary variant keeps controls immediately after the title, the owner link at the trailing edge, and the total and period above the plot. The ranking sits beside the plot only when the card itself reaches 48 rem; narrower cards keep a full-width plot and put the ranking below it. Controls wrap on narrow cards. An empty dataset shows its `emptyState` in the plot's place. The footer carries the reading time and at most one command on that reading.
|
|
66
|
+
* @words Title names the measure and its split: "Model spend by Customer". Period as dates. The link's name says where it goes: "Explore Usage by model", "Open revenue in Analytics". Footer: the reading's time, "Updated 4 minutes ago" or "Read at 12:42". Sentence case throughout.
|
|
67
|
+
* @access The title is a heading at the page's level (default 2) and labels the plot. The owner link is a native link named by its `label`. The plot is focusable and shows the 2 px focus outline only on keyboard focus.
|
|
68
68
|
*/
|
|
69
69
|
function ChartCard({ variant = "default", title, period, controls, link, summary, aside, footer, headingLevel = 2, className, children, }) {
|
|
70
70
|
const titleId = React.useId();
|
|
71
71
|
return (_jsxs(Card, { "data-slot": "chart-card", className: cn("@container/chart-card", className), children: [_jsxs(CardHeader, { narrowAction: variant === "summary" || controls ? "own" : undefined, className: cn(variant === "summary"
|
|
72
72
|
? "flex flex-wrap items-center gap-3"
|
|
73
|
-
: controls && CONTROLS_UNDER_TITLE_WHEN_NARROW), children: [_jsx(CardTitle, { id: titleId, role: "heading", "aria-level": headingLevel, className: variant === "summary" ? "text-heading-xs" : undefined, children: title }), variant === "summary" ? controls : null, period && variant !== "summary" ? _jsx(CardDescription, { children: period }) : null, (variant === "summary" ? link : controls || link) ? (_jsxs(CardAction, { className: cn("flex flex-wrap items-center gap-2", variant === "summary" && "ml-auto"), children: [variant === "summary" ? null : controls, link ? (_jsx(ButtonLink, { variant: "ghost", size: "sm",
|
|
74
|
-
"grid min-w-0 gap-(--card-spacing) @3xl/chart-card:grid-cols-[minmax(0,2fr)_minmax(16rem,1fr)]"), children: [_jsxs("div", { className: "flex min-w-0 flex-col gap-4", children: [variant === "summary" && (summary || period) ? (_jsxs("div", { "data-slot": "chart-card-summary", className: "flex flex-wrap items-start justify-between gap-3", children: [summary, period ? _jsx(CardDescription, { children: period }) : null] })) : null, children(titleId)] }), aside ? (_jsx("aside", { "data-slot": "chart-card-aside", className: "min-w-0 border-t border-border pt-(--card-spacing) @3xl/chart-card:border-t-0 @3xl/chart-card:border-l @3xl/chart-card:pt-0 @3xl/chart-card:pl-(--card-spacing)", children: aside })) : null] }), footer ? (_jsx(FooterStrip, { hint: footer.reading, hintSlot: "chart-card-reading", children: footer.link ? _jsx(PageCommandButton, { command: footer.link, variant: "
|
|
73
|
+
: controls && CONTROLS_UNDER_TITLE_WHEN_NARROW), children: [_jsx(CardTitle, { id: titleId, role: "heading", "aria-level": headingLevel, className: variant === "summary" ? "text-heading-xs" : undefined, children: title }), variant === "summary" ? controls : null, period && variant !== "summary" ? _jsx(CardDescription, { children: period }) : null, (variant === "summary" ? link : controls || link) ? (_jsxs(CardAction, { className: cn("flex flex-wrap items-center gap-2", variant === "summary" && "ml-auto"), children: [variant === "summary" ? null : controls, link ? (_jsx(ButtonLink, { variant: "ghost", size: "icon-sm", "aria-label": link.label, render: link.link, children: _jsx(ChevronRight, { "aria-hidden": "true" }) })) : null] })) : null] }), summary && variant !== "summary" ? (_jsx("div", { "data-slot": "chart-card-summary", className: "px-(--card-spacing)", children: summary })) : null, _jsxs(CardContent, { className: cn(aside &&
|
|
74
|
+
"grid min-w-0 gap-(--card-spacing) @3xl/chart-card:grid-cols-[minmax(0,2fr)_minmax(16rem,1fr)]"), children: [_jsxs("div", { className: "flex min-w-0 flex-col gap-4", children: [variant === "summary" && (summary || period) ? (_jsxs("div", { "data-slot": "chart-card-summary", className: "flex flex-wrap items-start justify-between gap-3", children: [summary, period ? _jsx(CardDescription, { children: period }) : null] })) : null, children(titleId)] }), aside ? (_jsx("aside", { "data-slot": "chart-card-aside", className: "min-w-0 border-t border-border pt-(--card-spacing) @3xl/chart-card:border-t-0 @3xl/chart-card:border-l @3xl/chart-card:pt-0 @3xl/chart-card:pl-(--card-spacing)", children: aside })) : null] }), footer ? (_jsx(FooterStrip, { hint: footer.reading, hintSlot: "chart-card-reading", children: footer.link ? _jsx(PageCommandButton, { command: footer.link, variant: "outline" }) : null })) : null] }));
|
|
75
75
|
}
|
|
76
76
|
function ChartSummary({ variant = "default", label, value, valueTitle, valueSize = "default", trend, status, }) {
|
|
77
77
|
return (_jsxs("div", { "data-slot": "chart-summary", className: "flex flex-col items-start gap-2", children: [_jsx("div", { className: cn(variant === "summary" ? "sr-only" : "text-muted-foreground text-sm"), children: label }), _jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-3", children: [_jsx("div", { "data-slot": "chart-summary-value", className: cn("truncate tabular-nums", variant === "summary"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kpi-card.d.ts","sourceRoot":"","sources":["../../src/components/kpi-card.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AAEpC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAY3D,OAAO,EAAyB,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAEtF,KAAK,gBAAgB,GAAG,cAAc,CAAC;AACvC,KAAK,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;AAEtD,KAAK,YAAY,GAAG;IAClB,2CAA2C;IAC3C,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,SAAS,EAAE,gBAAgB,CAAC;IAC5B,qEAAqE;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sGAAsG;IACtG,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0GAA0G;IAC1G,UAAU,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,KAAK,YAAY,GAAG;IAClB,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,8EAA8E;IAC9E,MAAM,CAAC,EAAE,SAAS,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC;IACnD,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IACjC;;;;OAIG;IACH,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/B,CAAC;AAQF;;;;;;;;;;;;GAYG;AACH,iBAAS,OAAO,CAAC,EACf,KAAK,EACL,KAAK,EACL,KAAK,EACL,IAAI,EACJ,IAAgB,EAChB,MAAM,EACN,YAAqB,EACrB,SAAS,GACV,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"kpi-card.d.ts","sourceRoot":"","sources":["../../src/components/kpi-card.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AAEpC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAY3D,OAAO,EAAyB,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAEtF,KAAK,gBAAgB,GAAG,cAAc,CAAC;AACvC,KAAK,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;AAEtD,KAAK,YAAY,GAAG;IAClB,2CAA2C;IAC3C,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,SAAS,EAAE,gBAAgB,CAAC;IAC5B,qEAAqE;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sGAAsG;IACtG,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0GAA0G;IAC1G,UAAU,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,KAAK,YAAY,GAAG;IAClB,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,8EAA8E;IAC9E,MAAM,CAAC,EAAE,SAAS,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC;IACnD,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IACjC;;;;OAIG;IACH,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/B,CAAC;AAQF;;;;;;;;;;;;GAYG;AACH,iBAAS,OAAO,CAAC,EACf,KAAK,EACL,KAAK,EACL,KAAK,EACL,IAAI,EACJ,IAAgB,EAChB,MAAM,EACN,YAAqB,EACrB,SAAS,GACV,EAAE,YAAY,qBAwHd;AAED,OAAO,EAAE,OAAO,EAAE,CAAC;AACnB,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC"}
|
|
@@ -27,13 +27,13 @@ const VALUE_TONE_CLASS = {
|
|
|
27
27
|
*/
|
|
28
28
|
function KpiCard({ label, value, delta, note, tone = "default", render, presentation = "card", sparkline, }) {
|
|
29
29
|
if (presentation === "inline") {
|
|
30
|
-
return (_jsxs("div", { className: "grid min-w-0 content-start gap-2", "data-slot": "metric-summary", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: label }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("p", { className: cn("text-2xl font-semibold tabular-nums", VALUE_TONE_CLASS[tone]), children: value }), delta ? (_jsx(TrendBadge, { value: delta.value, direction: delta.direction, tone: trendTone(delta.direction, delta.upIsGood, delta.neutral), directionLabel: delta.directionLabel })) : null] }), delta ? _jsx("p", { className: "text-xs text-muted-foreground", children: delta.comparison }) : null, note ? _jsx("p", { className: "text-xs text-muted-foreground", children: note }) : null] }));
|
|
30
|
+
return (_jsxs("div", { className: "grid min-w-0 content-start gap-2", "data-slot": "metric-summary", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: label }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("p", { className: cn("text-2xl font-semibold tabular-nums", VALUE_TONE_CLASS[tone]), "data-kpi-value": "", children: value }), delta ? (_jsx(TrendBadge, { value: delta.value, direction: delta.direction, tone: trendTone(delta.direction, delta.upIsGood, delta.neutral), directionLabel: delta.directionLabel })) : null] }), delta ? _jsx("p", { className: "text-xs text-muted-foreground", children: delta.comparison }) : null, note ? _jsx("p", { className: "text-xs text-muted-foreground", children: note }) : null] }));
|
|
31
31
|
}
|
|
32
32
|
const footerLine = [delta?.comparison, note].filter(Boolean).join(" · ");
|
|
33
33
|
const badge = delta ? (_jsx(TrendBadge, { value: delta.value, direction: delta.direction, tone: trendTone(delta.direction, delta.upIsGood, delta.neutral), directionLabel: delta.directionLabel })) : null;
|
|
34
34
|
if (sparkline) {
|
|
35
|
-
return (_jsx(Card, { size: "sm", "data-slot": "kpi-card", "data-tone": tone, render: render, variant: render ? "link" : "default", children: _jsxs(CardHeader, { className: "gap-y-1", children: [_jsxs(CardDescription, { className: "col-span-2 flex min-w-0 items-center gap-0.5", children: [_jsx("span", { className: "truncate", title: label, children: label }), render ? _jsx(ChevronRight, { "aria-hidden": "true", className: "size-4 shrink-0" }) : null] }), delta ? (_jsx(CardAction, { "data-kpi-delta": delta.direction, className: "row-span-1 row-start-2 self-center", children: badge })) : null, _jsx(CardTitle, { className: cn("col-start-1 row-start-2 min-w-0 truncate text-heading-md tabular-nums group-data-[size=sm]/card:text-heading-md", VALUE_TONE_CLASS[tone]), children: value }), _jsx("div", { "data-slot": "kpi-card-sparkline", className: "col-span-2 mt-1 h-8 min-w-0", children: sparkline.length > 1 ? (_jsx(ChartSparkline, { data: sparkline, className: "h-8 w-full" })) : null }), _jsx("p", { "data-slot": "kpi-card-change", className: "col-span-2 mt-1 line-clamp-2 min-h-10 min-w-0 text-sm text-muted-foreground", title: footerLine || undefined, children: footerLine })] }) }));
|
|
35
|
+
return (_jsx(Card, { size: "sm", "data-slot": "kpi-card", "data-tone": tone, render: render, variant: render ? "link" : "default", children: _jsxs(CardHeader, { className: "gap-y-1", children: [_jsxs(CardDescription, { className: "col-span-2 flex min-w-0 items-center gap-0.5", children: [_jsx("span", { className: "truncate", title: label, children: label }), render ? _jsx(ChevronRight, { "aria-hidden": "true", className: "size-4 shrink-0" }) : null] }), delta ? (_jsx(CardAction, { "data-kpi-delta": delta.direction, className: "row-span-1 row-start-2 self-center", children: badge })) : null, _jsx(CardTitle, { className: cn("col-start-1 row-start-2 min-w-0 truncate text-heading-md tabular-nums group-data-[size=sm]/card:text-heading-md", VALUE_TONE_CLASS[tone]), "data-kpi-value": "", children: value }), _jsx("div", { "data-slot": "kpi-card-sparkline", className: "col-span-2 mt-1 h-8 min-w-0", children: sparkline.length > 1 ? (_jsx(ChartSparkline, { data: sparkline, className: "h-8 w-full" })) : null }), _jsx("p", { "data-slot": "kpi-card-change", className: "col-span-2 mt-1 line-clamp-2 min-h-10 min-w-0 text-sm text-muted-foreground", title: footerLine || undefined, children: footerLine })] }) }));
|
|
36
36
|
}
|
|
37
|
-
return (_jsxs(Card, { size: "sm", "data-slot": "kpi-card", "data-tone": tone, render: render, variant: render ? "link" : "default", children: [_jsxs(CardHeader, { children: [_jsxs(CardDescription, { className: "flex min-h-10 items-start gap-0.5", children: [_jsx("span", { className: "line-clamp-2", children: label }), render ? _jsx(ChevronRight, { "aria-hidden": "true", className: "mt-0.5 size-4 shrink-0" }) : null] }), _jsx(CardTitle, { className: cn("text-heading-md tabular-nums group-data-[size=sm]/card:text-heading-md", VALUE_TONE_CLASS[tone]), children: value }), delta ? _jsx(CardAction, { "data-kpi-delta": delta.direction, children: badge }) : null] }), delta || note ? (_jsx(CardFooter, { className: "flex-nowrap", children: _jsx("span", { className: "min-w-0 truncate", title: footerLine, children: footerLine }) })) : null] }));
|
|
37
|
+
return (_jsxs(Card, { size: "sm", "data-slot": "kpi-card", "data-tone": tone, render: render, variant: render ? "link" : "default", children: [_jsxs(CardHeader, { children: [_jsxs(CardDescription, { className: "flex min-h-10 items-start gap-0.5", children: [_jsx("span", { className: "line-clamp-2", children: label }), render ? _jsx(ChevronRight, { "aria-hidden": "true", className: "mt-0.5 size-4 shrink-0" }) : null] }), _jsx(CardTitle, { className: cn("text-heading-md tabular-nums group-data-[size=sm]/card:text-heading-md", VALUE_TONE_CLASS[tone]), "data-kpi-value": "", children: value }), delta ? _jsx(CardAction, { "data-kpi-delta": delta.direction, children: badge }) : null] }), delta || note ? (_jsx(CardFooter, { className: "flex-nowrap", children: _jsx("span", { className: "min-w-0 truncate", title: footerLine, children: footerLine }) })) : null] }));
|
|
38
38
|
}
|
|
39
39
|
export { KpiCard };
|
|
@@ -29,7 +29,7 @@ export type NumberedStepsProps = {
|
|
|
29
29
|
* Steps a person follows, each with its number, its words and its own action.
|
|
30
30
|
*
|
|
31
31
|
* @family Other
|
|
32
|
-
* @when A short sequence the person does: installing Nadia from Downloads, connecting Teams, the Dashboard's setup guide; or a sequence whose real progress the page tracks, such as the company check; and, as `columns`, the few steps a page explains, such as the website's how it works. Vercel draws its Speed Insights *Get Started* the same way: three numbered steps, each with its words and its own action.
|
|
32
|
+
* @when A short sequence the person does: installing Nadia from Downloads, connecting Teams, the Dashboard's setup guide, an `ObjectSection` whose body is these steps; or a sequence whose real progress the page tracks, such as the company check; and, as `columns`, the few steps a page explains, such as the website's how it works. Vercel draws its Speed Insights *Get Started* the same way: three numbered steps, each with its words and its own action.
|
|
33
33
|
* @instead Stages the system runs on an entry screen are the EntryStageList; the steps of an Agent's run are a RunTimeline; a setup with a wait is a FormDrawer with its "Step 2 of 3" line.
|
|
34
34
|
* @behaviour Each step is a 28 px square number in label-mono, its title, one muted description and, under it, the step's own action. Instructions mark no step and every number stays muted, because the page cannot know which one the person is on. Where the page tracks real progress, `current` turns that step's number to ink and writes its word beside the title. `framed` draws one bordered list with a seam between steps; `columns` sets the steps side by side as the width it is given allows, each column at least 192 px, so a phone stacks them and a wide page or panel holds three, each number above its words, and is never framed. A step's action never grows past its step: one wider than the step is held to its width. Colour is neutral: the current number is ink on the page's background and the others are muted; no hue marks a step. It is the one owner of a drawn step number.
|
|
35
35
|
* @words The title is what the person does, in the imperative ("Install Nadia"), or a finished step in the past tense ("Sofia looked up acme.it"). The current step's word is short: "Now". An action names its verb and carries its platform line ("macOS · 2.14.0 · Apple silicon").
|
|
@@ -4,7 +4,7 @@ import { cn } from "../lib/utils.js";
|
|
|
4
4
|
* Steps a person follows, each with its number, its words and its own action.
|
|
5
5
|
*
|
|
6
6
|
* @family Other
|
|
7
|
-
* @when A short sequence the person does: installing Nadia from Downloads, connecting Teams, the Dashboard's setup guide; or a sequence whose real progress the page tracks, such as the company check; and, as `columns`, the few steps a page explains, such as the website's how it works. Vercel draws its Speed Insights *Get Started* the same way: three numbered steps, each with its words and its own action.
|
|
7
|
+
* @when A short sequence the person does: installing Nadia from Downloads, connecting Teams, the Dashboard's setup guide, an `ObjectSection` whose body is these steps; or a sequence whose real progress the page tracks, such as the company check; and, as `columns`, the few steps a page explains, such as the website's how it works. Vercel draws its Speed Insights *Get Started* the same way: three numbered steps, each with its words and its own action.
|
|
8
8
|
* @instead Stages the system runs on an entry screen are the EntryStageList; the steps of an Agent's run are a RunTimeline; a setup with a wait is a FormDrawer with its "Step 2 of 3" line.
|
|
9
9
|
* @behaviour Each step is a 28 px square number in label-mono, its title, one muted description and, under it, the step's own action. Instructions mark no step and every number stays muted, because the page cannot know which one the person is on. Where the page tracks real progress, `current` turns that step's number to ink and writes its word beside the title. `framed` draws one bordered list with a seam between steps; `columns` sets the steps side by side as the width it is given allows, each column at least 192 px, so a phone stacks them and a wide page or panel holds three, each number above its words, and is never framed. A step's action never grows past its step: one wider than the step is held to its width. Colour is neutral: the current number is ink on the page's background and the others are muted; no hue marks a step. It is the one owner of a drawn step number.
|
|
10
10
|
* @words The title is what the person does, in the imperative ("Install Nadia"), or a finished step in the past tense ("Sofia looked up acme.it"). The current step's word is short: "Now". An action names its verb and carries its platform line ("macOS · 2.14.0 · Apple silicon").
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ComponentProps, ReactElement, ReactNode } from "react";
|
|
2
2
|
import { type IdentityMarkProps } from "./identity-mark.js";
|
|
3
|
-
import { type
|
|
3
|
+
import { type CardActions } from "./page-actions.js";
|
|
4
4
|
type ObjectPageFact = {
|
|
5
5
|
label: string;
|
|
6
6
|
value: ReactNode;
|
|
@@ -35,10 +35,10 @@ export type ObjectPageProps = {
|
|
|
35
35
|
headingProps?: Omit<ComponentProps<"h1">, "children">;
|
|
36
36
|
/** One line saying what it is: "Customer of Partner Alpha · since 3 August 2026". */
|
|
37
37
|
description?: ReactNode;
|
|
38
|
-
/** Key facts as labelled values in one row
|
|
39
|
-
facts
|
|
38
|
+
/** Key facts as labelled values in one row. Left out only while the object does not exist yet, such as an account with no plan. */
|
|
39
|
+
facts?: readonly [ObjectPageFact, ...ObjectPageFact[]];
|
|
40
40
|
/** The object's commands on its header bar: a secondary, the primary, then the three dots. */
|
|
41
|
-
actions?:
|
|
41
|
+
actions?: CardActions;
|
|
42
42
|
/** Rows that open under the facts, each one part of the object: settings, logs, checks. */
|
|
43
43
|
entries?: readonly ObjectPageEntry[];
|
|
44
44
|
/** The footer: a hint and one link elsewhere. Left out when the object has no such line. */
|
|
@@ -53,9 +53,9 @@ export type ObjectPageProps = {
|
|
|
53
53
|
* @family Compositions
|
|
54
54
|
* @when One thing a person opened from a list and now reads or acts on: a customer, a partner, an Installation, a Library entry.
|
|
55
55
|
* @instead "Use Context Card to reveal entity metadata on hover or focus: a user, a deployment, a project, an API key." (https://vercel.com/geist/context-card) Ours: that hover is a `HoverCard`, never this page. A list of records is a CollectionPage; a page of settings only is a SettingsPage, whose cards are SettingsSections; an Agent's Edit page in Nadia's window opens on its AgentProfileCard, the documented exception. A card of values a person saves is a setting card, never this one.
|
|
56
|
-
* @behaviour "For a static key/value metadata block on a detail page, use `Description`." (https://vercel.com/geist/entity) "The right column holds at most one or two controls. If the row needs more, move secondary actions into a `Dots Menu`." (https://vercel.com/geist/entity) "Lead the left column with a scannable identifier: an `Avatar` or icon, a Title Case label, then sentence-case secondary metadata (`Member since Mar 14, 2026`)." (https://vercel.com/geist/entity) "Don’t put interactive controls in the title slot. Buttons, menus, and links belong in the content (`<dd>`) or in the parent layout, not the label (`<dt>`)." (https://vercel.com/geist/description) Ours: as Vercel's deployment page (Deployment Details, then its steps), the object card is the first card of the page: a header bar with the name at the start and the object's commands at the end (a secondary, then the primary as the filled button, then the three dots, in `PageActions`' order, on the name's row while the name keeps 128 px, as on a phone, and under it otherwise; the primary is the one thing the object waits on its viewer for, such as approving a company that waits for approval, beside its alternative as the secondary, and an object with nothing waiting has none),
|
|
56
|
+
* @behaviour "For a static key/value metadata block on a detail page, use `Description`." (https://vercel.com/geist/entity) "The right column holds at most one or two controls. If the row needs more, move secondary actions into a `Dots Menu`." (https://vercel.com/geist/entity) "Lead the left column with a scannable identifier: an `Avatar` or icon, a Title Case label, then sentence-case secondary metadata (`Member since Mar 14, 2026`)." (https://vercel.com/geist/entity) "Don’t put interactive controls in the title slot. Buttons, menus, and links belong in the content (`<dd>`) or in the parent layout, not the label (`<dt>`)." (https://vercel.com/geist/description) Ours: as Vercel's deployment page (Deployment Details, then its steps), the object card is the first card of the page: a header bar with the name at the start and the object's commands at the end (a secondary, then the primary as the filled button, then the three dots, in `PageActions`' order, on the name's row while the name keeps 128 px, as on a phone, and under it otherwise; the primary is the one thing the object waits on its viewer for, such as approving a company that waits for approval, beside its alternative as the secondary, and an object with nothing waiting has none), the mark at 64 stands beside the name and optional line in the header, for every kind of object, with no preview box, since a product object has no picture of itself as a deployment has its screenshot; full-width facts follow in four columns once they have 36rem and two below (an object that exists always has one; an object not made yet, such as an account's plan before it chooses one, has none, and its card is the header alone, its name saying so and, for a viewer who may, its primary making it), each a muted label over its value in body text, then an optional footer with a hint at the start and one link at the end. Optional `entries` follow in a card of their own 12 px under it: rows that open one by one, each with its chevron first (it turns down when open), its title, and its summary at the trailing edge, as Vercel's Build Logs and Deployment Checks rows; an entry marked `defaultOpen` opens with the page, for a link that names it. The footer takes a link only, so no Save or other command of the object fits in it; the object's changes live in its sections, each a setting card with its own footer. The sections follow, 24 apart, each drawing its own states. The card is neutral; colour belongs to the company's compact identity, a semantic fact value, or an entry's status summary, with no coloured card ground.
|
|
57
57
|
* @words "Keep right-column buttons Verb + Noun (`Remove Member`, `Resend Invite`). Bare verbs like `Remove` or `Confirm` lose context once the row scrolls offscreen." (https://vercel.com/geist/entity) "Title slot is Title Case noun (`Last Deployed`, `Build Duration`); content is sentence case unless the value is a literal identifier, ID, or timestamp that should be preserved verbatim." (https://vercel.com/geist/description) Ours: sentence case throughout. The name as the object is called everywhere else; the line says what it is and whose ("Customer of Partner Alpha · since 3 August 2026"); a fact's label is a noun ("Plan"), its value the figure; an entry's title is a noun phrase; the hint is one sentence ("To update it, push to the main branch."); the link names where it goes ("Deployments"); the menu's trigger reads "More for {name}".
|
|
58
|
-
* @access "Geist renders `<dl>`/`<dt>`/`<dd>` so screen readers announce each key/value pair as a definition." (https://vercel.com/geist/description) "Don’t wrap the component in extra paragraphs that break the list semantics." (https://vercel.com/geist/description) Ours: with `headingProps` the name is the page's h1; without them it is text, for a product whose chrome already titles the page. The mark is decorative beside the
|
|
58
|
+
* @access "Geist renders `<dl>`/`<dt>`/`<dd>` so screen readers announce each key/value pair as a definition." (https://vercel.com/geist/description) "Don’t wrap the component in extra paragraphs that break the list semantics." (https://vercel.com/geist/description) Ours: with `headingProps` the name is the page's h1; without them it is text, for a product whose chrome already titles the page. The mark is decorative beside the name. Each entry is an h2 heading holding a button with `aria-expanded` that controls its panel.
|
|
59
59
|
*/
|
|
60
60
|
export declare function ObjectPage({ mark, name, headingProps, description, facts, actions, entries, footer, children, }: ObjectPageProps): ReactElement;
|
|
61
61
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"object-page.d.ts","sourceRoot":"","sources":["../../src/components/object-page.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAMrE,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"object-page.d.ts","sourceRoot":"","sources":["../../src/components/object-page.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAMrE,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,KAAK,WAAW,EAAqC,MAAM,gBAAgB,CAAC;AAErF,KAAK,cAAc,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,SAAS,CAAA;CAAE,CAAC;AAE1D,MAAM,MAAM,eAAe,GAAG;IAC5B,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAC;IACd,kGAAkG;IAClG,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,2DAA2D;IAC3D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,SAAS,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,IAAI,EAAE,YAAY,CAAC;IACnB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,kHAAkH;IAClH,IAAI,EAAE,MAAM,CAAC;IACb,kGAAkG;IAClG,IAAI,EAAE,cAAc,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,wFAAwF;IACxF,IAAI,EAAE,iBAAiB,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,4FAA4F;IAC5F,YAAY,CAAC,EAAE,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC,CAAC;IACtD,qFAAqF;IACrF,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,mIAAmI;IACnI,KAAK,CAAC,EAAE,SAAS,CAAC,cAAc,EAAE,GAAG,cAAc,EAAE,CAAC,CAAC;IACvD,8FAA8F;IAC9F,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,2FAA2F;IAC3F,OAAO,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACrC,4FAA4F;IAC5F,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,+HAA+H;IAC/H,QAAQ,EAAE,SAAS,CAAC;CACrB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,EACzB,IAAI,EACJ,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,KAAK,EACL,OAAO,EACP,OAAO,EACP,MAAM,EACN,QAAQ,GACT,EAAE,eAAe,GAAG,YAAY,CA0FhC"}
|
|
@@ -4,7 +4,7 @@ import { cn } from "../lib/utils.js";
|
|
|
4
4
|
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "./accordion.js";
|
|
5
5
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "./card.js";
|
|
6
6
|
import { IdentityMark } from "./identity-mark.js";
|
|
7
|
-
import {
|
|
7
|
+
import { CardActionsRow, PageCommandButton } from "./page-actions.js";
|
|
8
8
|
/**
|
|
9
9
|
* One object's page: the object card with its name, commands, facts, rows that open and one link
|
|
10
10
|
* elsewhere, then its sections as cards.
|
|
@@ -12,14 +12,13 @@ import { PageActionsRow, PageCommandButton } from "./page-actions.js";
|
|
|
12
12
|
* @family Compositions
|
|
13
13
|
* @when One thing a person opened from a list and now reads or acts on: a customer, a partner, an Installation, a Library entry.
|
|
14
14
|
* @instead "Use Context Card to reveal entity metadata on hover or focus: a user, a deployment, a project, an API key." (https://vercel.com/geist/context-card) Ours: that hover is a `HoverCard`, never this page. A list of records is a CollectionPage; a page of settings only is a SettingsPage, whose cards are SettingsSections; an Agent's Edit page in Nadia's window opens on its AgentProfileCard, the documented exception. A card of values a person saves is a setting card, never this one.
|
|
15
|
-
* @behaviour "For a static key/value metadata block on a detail page, use `Description`." (https://vercel.com/geist/entity) "The right column holds at most one or two controls. If the row needs more, move secondary actions into a `Dots Menu`." (https://vercel.com/geist/entity) "Lead the left column with a scannable identifier: an `Avatar` or icon, a Title Case label, then sentence-case secondary metadata (`Member since Mar 14, 2026`)." (https://vercel.com/geist/entity) "Don’t put interactive controls in the title slot. Buttons, menus, and links belong in the content (`<dd>`) or in the parent layout, not the label (`<dt>`)." (https://vercel.com/geist/description) Ours: as Vercel's deployment page (Deployment Details, then its steps), the object card is the first card of the page: a header bar with the name at the start and the object's commands at the end (a secondary, then the primary as the filled button, then the three dots, in `PageActions`' order, on the name's row while the name keeps 128 px, as on a phone, and under it otherwise; the primary is the one thing the object waits on its viewer for, such as approving a company that waits for approval, beside its alternative as the secondary, and an object with nothing waiting has none),
|
|
15
|
+
* @behaviour "For a static key/value metadata block on a detail page, use `Description`." (https://vercel.com/geist/entity) "The right column holds at most one or two controls. If the row needs more, move secondary actions into a `Dots Menu`." (https://vercel.com/geist/entity) "Lead the left column with a scannable identifier: an `Avatar` or icon, a Title Case label, then sentence-case secondary metadata (`Member since Mar 14, 2026`)." (https://vercel.com/geist/entity) "Don’t put interactive controls in the title slot. Buttons, menus, and links belong in the content (`<dd>`) or in the parent layout, not the label (`<dt>`)." (https://vercel.com/geist/description) Ours: as Vercel's deployment page (Deployment Details, then its steps), the object card is the first card of the page: a header bar with the name at the start and the object's commands at the end (a secondary, then the primary as the filled button, then the three dots, in `PageActions`' order, on the name's row while the name keeps 128 px, as on a phone, and under it otherwise; the primary is the one thing the object waits on its viewer for, such as approving a company that waits for approval, beside its alternative as the secondary, and an object with nothing waiting has none), the mark at 64 stands beside the name and optional line in the header, for every kind of object, with no preview box, since a product object has no picture of itself as a deployment has its screenshot; full-width facts follow in four columns once they have 36rem and two below (an object that exists always has one; an object not made yet, such as an account's plan before it chooses one, has none, and its card is the header alone, its name saying so and, for a viewer who may, its primary making it), each a muted label over its value in body text, then an optional footer with a hint at the start and one link at the end. Optional `entries` follow in a card of their own 12 px under it: rows that open one by one, each with its chevron first (it turns down when open), its title, and its summary at the trailing edge, as Vercel's Build Logs and Deployment Checks rows; an entry marked `defaultOpen` opens with the page, for a link that names it. The footer takes a link only, so no Save or other command of the object fits in it; the object's changes live in its sections, each a setting card with its own footer. The sections follow, 24 apart, each drawing its own states. The card is neutral; colour belongs to the company's compact identity, a semantic fact value, or an entry's status summary, with no coloured card ground.
|
|
16
16
|
* @words "Keep right-column buttons Verb + Noun (`Remove Member`, `Resend Invite`). Bare verbs like `Remove` or `Confirm` lose context once the row scrolls offscreen." (https://vercel.com/geist/entity) "Title slot is Title Case noun (`Last Deployed`, `Build Duration`); content is sentence case unless the value is a literal identifier, ID, or timestamp that should be preserved verbatim." (https://vercel.com/geist/description) Ours: sentence case throughout. The name as the object is called everywhere else; the line says what it is and whose ("Customer of Partner Alpha · since 3 August 2026"); a fact's label is a noun ("Plan"), its value the figure; an entry's title is a noun phrase; the hint is one sentence ("To update it, push to the main branch."); the link names where it goes ("Deployments"); the menu's trigger reads "More for {name}".
|
|
17
|
-
* @access "Geist renders `<dl>`/`<dt>`/`<dd>` so screen readers announce each key/value pair as a definition." (https://vercel.com/geist/description) "Don’t wrap the component in extra paragraphs that break the list semantics." (https://vercel.com/geist/description) Ours: with `headingProps` the name is the page's h1; without them it is text, for a product whose chrome already titles the page. The mark is decorative beside the
|
|
17
|
+
* @access "Geist renders `<dl>`/`<dt>`/`<dd>` so screen readers announce each key/value pair as a definition." (https://vercel.com/geist/description) "Don’t wrap the component in extra paragraphs that break the list semantics." (https://vercel.com/geist/description) Ours: with `headingProps` the name is the page's h1; without them it is text, for a product whose chrome already titles the page. The mark is decorative beside the name. Each entry is an h2 heading holding a button with `aria-expanded` that controls its panel.
|
|
18
18
|
*/
|
|
19
19
|
export function ObjectPage({ mark, name, headingProps, description, facts, actions, entries, footer, children, }) {
|
|
20
|
-
const company = mark.kind === "company";
|
|
21
20
|
const title = headingProps ? (_jsx("h1", { ...headingProps, className: cn("outline-none", headingProps.className), children: name })) : (name);
|
|
22
|
-
return (_jsxs("div", { className: "grid min-w-0 gap-6", "data-slot": "object-page", children: [_jsxs("div", { className: "grid min-w-0 gap-3", children: [_jsxs(Card, { className: "min-w-0", render: _jsx("div", { "data-slot": "object-card" }), size: "sm", children: [_jsxs(CardHeader, { className: "flex flex-wrap items-center justify-between gap-x-4 gap-y-3 border-b", children: [
|
|
21
|
+
return (_jsxs("div", { className: "grid min-w-0 gap-6", "data-slot": "object-page", children: [_jsxs("div", { className: "grid min-w-0 gap-3", children: [_jsxs(Card, { className: "min-w-0", render: _jsx("div", { "data-slot": "object-card" }), size: "sm", children: [_jsxs(CardHeader, { className: cn("flex flex-wrap items-center justify-between gap-x-4 gap-y-3", facts && "border-b"), children: [_jsxs("div", { className: cn("flex min-w-0 flex-1 items-center gap-4", actions?.menu && !actions.secondary && !actions.primary ? "basis-0" : "basis-64"), children: [_jsx(IdentityMark, { ...mark, size: 64 }), _jsxs("div", { className: "grid min-w-0 gap-1", children: [_jsx(CardTitle, { className: "min-w-0 wrap-break-word group-data-[size=sm]/card:text-heading-sm", children: title }), description ? _jsx(CardDescription, { children: description }) : null] })] }), actions ? _jsx(CardActionsRow, { ...actions }) : null] }), facts ? (_jsx(CardContent, { className: "grid min-w-0 gap-x-6 gap-y-5", children: _jsx("div", { className: "@container/object-page-facts grid min-w-0 content-start gap-4", children: _jsx("dl", { className: "grid min-w-0 grid-cols-2 gap-x-6 gap-y-4 @xl/object-page-facts:grid-cols-4", "data-slot": "object-page-facts", children: facts.map((fact) => (_jsxs("div", { className: "grid min-w-0 content-start gap-1", children: [_jsx("dt", { className: "text-label-sm text-muted-foreground", children: fact.label }), _jsx("dd", { className: "min-w-0 text-body-sm wrap-break-word text-foreground", children: fact.value })] }, fact.label))) }) }) })) : null, footer ? (_jsx(FooterStrip, { hint: footer.hint, hintSlot: "object-page-hint", children: _jsx(PageCommandButton, { command: footer.link, variant: "outline" }) })) : null] }), entries && entries.length > 0 ? (_jsx(Card, { className: "min-w-0 py-0", render: _jsx("div", { "data-slot": "object-page-entries" }), size: "sm", children: _jsx(Accordion, { className: "*:last:border-b-0", defaultValue: entries
|
|
23
22
|
.filter((entry) => entry.defaultOpen)
|
|
24
23
|
.map((entry) => entry.title), multiple: true, size: "small", children: entries.map((entry) => (_jsxs(AccordionItem, { value: entry.title, children: [_jsxs(AccordionTrigger, { className: "justify-start gap-3 px-(--card-spacing) group-data-[size=small]/accordion:text-body-sm focus-visible:-outline-offset-2 *:data-[slot=accordion-trigger-icon]:order-first *:data-[slot=accordion-trigger-icon]:text-muted-foreground", level: 2, children: [_jsx("span", { className: "min-w-0 flex-1", children: entry.title }), entry.summary] }), _jsx(AccordionContent, { className: "ps-[calc(var(--card-spacing)+1.75rem)] pe-(--card-spacing)", children: entry.children })] }, entry.title))) }) })) : null] }), children] }));
|
|
25
24
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
import { type PageCommand } from "./page-actions.js";
|
|
2
|
+
import { type CardActions, type PageCommand } from "./page-actions.js";
|
|
3
3
|
type ObjectFactShape = {
|
|
4
4
|
label: string;
|
|
5
5
|
value: ReactNode;
|
|
@@ -20,24 +20,32 @@ export type ObjectFact = ObjectFactShape & ({
|
|
|
20
20
|
/** A value that ends in an ellipsis on one line instead of wrapping; a string value is also whole in its title. Not available on a `block` fact. */
|
|
21
21
|
ellipsis?: boolean;
|
|
22
22
|
});
|
|
23
|
+
/** A card's footer: one muted sentence at its start and the card's one command at its end. */
|
|
24
|
+
export type CardFooterLine = {
|
|
25
|
+
hint: string;
|
|
26
|
+
command?: PageCommand;
|
|
27
|
+
} | {
|
|
28
|
+
hint?: string;
|
|
29
|
+
command: PageCommand;
|
|
30
|
+
};
|
|
23
31
|
/**
|
|
24
32
|
* A section of an object's page: a card that owns one decision about the object.
|
|
25
33
|
*
|
|
26
34
|
* @family Compositions
|
|
27
|
-
* @when A part of an ObjectPage a person reads or changes on its own: a customer's Company details, an Agent's Instructions, a Library entry's source.
|
|
35
|
+
* @when A part of an ObjectPage a person reads or changes on its own: a customer's Company details, an Agent's Instructions, a Library entry's source, a Partner's services in Billing. Also a setup card on an overview, its body the `NumberedSteps` a person follows, as Vercel's Speed Insights *Get Started*: the Dashboard's setup guide; and a card among a person's settings that holds no setting, such as their nadicode card on Profile.
|
|
28
36
|
* @instead Values a person edits and saves together are a SettingsSection; a list of the object's records is a RecordList with its heading; a passing condition is a MessageNotice.
|
|
29
|
-
* @behaviour
|
|
30
|
-
* @words The title names what the section holds ("Company", "Instructions"); the description says in one sentence what it holds or lets a person change;
|
|
37
|
+
* @behaviour As Vercel's Spend Management, Payment Method and AI Gateway Credit cards on its Billing page: a resting card, its title and the one-sentence description directly under it, both always given; at the header's trailing edge `actions`, drawn as every card's header draws them (`CardActions`: the muted `meta`, then up to two secondaries as outline buttons, the primary as the filled one, then the three dots), so a way to another page is a secondary with its `link`, an outline button named for the page, never a text link; then its body; then the optional `footer`, a strip under a divider with its sentence at the start and its one command at the end as an outline button. No icon in the header. Neutral throughout. A section that edits in place swaps to the same section as a `SettingsSection`, with Cancel (`save.cancel`) before Save.
|
|
38
|
+
* @words The title names what the section holds ("Company", "Instructions"); the description says in one sentence what it holds or lets a person change; a command is a verb on it ("Edit"), and a way to another page is that page's name ("Statements"); `meta` is a short count or time ("2 of 5 Agents"); the footer's sentence is a limit or where the section's object changes ("At most three cards.").
|
|
31
39
|
* @access The title is a heading one level under the page (h2), deeper only under another heading through `headingLevel`.
|
|
32
40
|
*/
|
|
33
|
-
export declare function ObjectSection({ title, description,
|
|
41
|
+
export declare function ObjectSection({ title, description, actions, footer, children, headingLevel, }: {
|
|
34
42
|
title: string;
|
|
35
43
|
/** The one sentence under the title that says what the section holds or changes. */
|
|
36
44
|
description: NonNullable<ReactNode>;
|
|
37
|
-
/** The section's
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
|
|
45
|
+
/** The section's commands at its header's trailing edge, a way to another page among them. */
|
|
46
|
+
actions?: CardActions;
|
|
47
|
+
/** The strip under the body: a limit or where the object changes, and the one command after it. */
|
|
48
|
+
footer?: CardFooterLine;
|
|
41
49
|
children: ReactNode;
|
|
42
50
|
/** A card titles a section of its page; nest it deeper only under another heading. */
|
|
43
51
|
headingLevel?: 2 | 3 | 4;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"object-workspace.d.ts","sourceRoot":"","sources":["../../src/components/object-workspace.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAAS,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"object-workspace.d.ts","sourceRoot":"","sources":["../../src/components/object-workspace.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAAS,MAAM,OAAO,CAAC;AAK9C,OAAO,EACL,KAAK,WAAW,EAEhB,KAAK,WAAW,EAEjB,MAAM,gBAAgB,CAAC;AAGxB,KAAK,eAAe,GAAG;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,CAAC;IACjB,gGAAgG;IAChG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,eAAe,GACtC,CACI;IACE;;;OAGG;IACH,KAAK,EAAE,IAAI,CAAC;IACZ,QAAQ,CAAC,EAAE,KAAK,CAAC;CAClB,GACD;IACE,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,oJAAoJ;IACpJ,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CACJ,CAAC;AAEJ,8FAA8F;AAC9F,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,WAAW,CAAA;CAAE,GACvC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,CAAC;AAE5C;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,WAAW,EACX,OAAO,EACP,MAAM,EACN,QAAQ,EACR,YAAgB,GACjB,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,oFAAoF;IACpF,WAAW,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC;IACpC,8FAA8F;IAC9F,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,mGAAmG;IACnG,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,QAAQ,EAAE,SAAS,CAAC;IACpB,sFAAsF;IACtF,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;CAC1B,+BAsBA;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,OAAmB,GACpB,EAAE;IACD,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IAC7B,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;CACjC,+BAuDA"}
|
|
@@ -2,21 +2,22 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Info } from "lucide-react";
|
|
3
3
|
import { useId } from "react";
|
|
4
4
|
import { cn } from "../lib/utils.js";
|
|
5
|
+
import { FooterStrip } from "../internal/footer-strip.js";
|
|
5
6
|
import { Card, CardAction, CardContent, CardDescription, CardHeader, CardTitle } from "./card.js";
|
|
6
|
-
import { PageCommandButton } from "./page-actions.js";
|
|
7
|
+
import { CardActionsRow, PageCommandButton, } from "./page-actions.js";
|
|
7
8
|
import { Tooltip, TooltipContent, TooltipTrigger } from "./tooltip.js";
|
|
8
9
|
/**
|
|
9
10
|
* A section of an object's page: a card that owns one decision about the object.
|
|
10
11
|
*
|
|
11
12
|
* @family Compositions
|
|
12
|
-
* @when A part of an ObjectPage a person reads or changes on its own: a customer's Company details, an Agent's Instructions, a Library entry's source.
|
|
13
|
+
* @when A part of an ObjectPage a person reads or changes on its own: a customer's Company details, an Agent's Instructions, a Library entry's source, a Partner's services in Billing. Also a setup card on an overview, its body the `NumberedSteps` a person follows, as Vercel's Speed Insights *Get Started*: the Dashboard's setup guide; and a card among a person's settings that holds no setting, such as their nadicode card on Profile.
|
|
13
14
|
* @instead Values a person edits and saves together are a SettingsSection; a list of the object's records is a RecordList with its heading; a passing condition is a MessageNotice.
|
|
14
|
-
* @behaviour
|
|
15
|
-
* @words The title names what the section holds ("Company", "Instructions"); the description says in one sentence what it holds or lets a person change;
|
|
15
|
+
* @behaviour As Vercel's Spend Management, Payment Method and AI Gateway Credit cards on its Billing page: a resting card, its title and the one-sentence description directly under it, both always given; at the header's trailing edge `actions`, drawn as every card's header draws them (`CardActions`: the muted `meta`, then up to two secondaries as outline buttons, the primary as the filled one, then the three dots), so a way to another page is a secondary with its `link`, an outline button named for the page, never a text link; then its body; then the optional `footer`, a strip under a divider with its sentence at the start and its one command at the end as an outline button. No icon in the header. Neutral throughout. A section that edits in place swaps to the same section as a `SettingsSection`, with Cancel (`save.cancel`) before Save.
|
|
16
|
+
* @words The title names what the section holds ("Company", "Instructions"); the description says in one sentence what it holds or lets a person change; a command is a verb on it ("Edit"), and a way to another page is that page's name ("Statements"); `meta` is a short count or time ("2 of 5 Agents"); the footer's sentence is a limit or where the section's object changes ("At most three cards.").
|
|
16
17
|
* @access The title is a heading one level under the page (h2), deeper only under another heading through `headingLevel`.
|
|
17
18
|
*/
|
|
18
|
-
export function ObjectSection({ title, description,
|
|
19
|
-
return (_jsxs(Card, { "data-slot": "object-section", children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { role: "heading", "aria-level": headingLevel, children: title }), _jsx(CardDescription, { children: description }),
|
|
19
|
+
export function ObjectSection({ title, description, actions, footer, children, headingLevel = 2, }) {
|
|
20
|
+
return (_jsxs(Card, { "data-slot": "object-section", children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { role: "heading", "aria-level": headingLevel, children: title }), _jsx(CardDescription, { children: description }), actions ? (_jsx(CardAction, { children: _jsx(CardActionsRow, { ...actions }) })) : null] }), _jsx(CardContent, { children: children }), footer ? (_jsx(FooterStrip, { hint: footer.hint, hintSlot: "object-section-hint", children: footer.command ? _jsx(PageCommandButton, { command: footer.command, variant: "outline" }) : null })) : null] }));
|
|
20
21
|
}
|
|
21
22
|
/**
|
|
22
23
|
* An object's facts as labelled values in a grid, inside one of its sections.
|
|
@@ -5,6 +5,11 @@ export type OverviewPageProps = {
|
|
|
5
5
|
period?: ReactElement;
|
|
6
6
|
/** The figures, one row of equal cards, one column below md. */
|
|
7
7
|
figures?: readonly Omit<KpiCardProps, "presentation">[];
|
|
8
|
+
/**
|
|
9
|
+
* When nothing has happened yet: one line drawn in place of the figures and charts, such as "No
|
|
10
|
+
* commission yet this month." Given only when every figure would read zero.
|
|
11
|
+
*/
|
|
12
|
+
nothingYet?: string;
|
|
8
13
|
/** The chart cards, each with its one header, in reading order. */
|
|
9
14
|
charts?: ReactNode;
|
|
10
15
|
/** The exceptions that need a person, each opening its owner: a RecordList with its heading. */
|
|
@@ -16,9 +21,9 @@ export type OverviewPageProps = {
|
|
|
16
21
|
* @family Compositions
|
|
17
22
|
* @when "For in-product cards and dashboard tiles, use `Card`; Book is too decorative for repeated UI rows." (https://vercel.com/geist/book) Ours: the landing page of an area a person checks: the Portal's Dashboard, Analytics, Statements, System health, an Agent's Usage.
|
|
18
23
|
* @instead A list of records a person finds one of is a `CollectionPage`; one record is an `ObjectPage`. Never a stack of previews of every section: each exception links to its owner instead.
|
|
19
|
-
* @behaviour "Show a Skeleton when async data fills a known layout: table rows, card grids, profile blocks, sidebars." (https://vercel.com/geist/skeleton) "Use the Error component as a block surface when a section or page-level resource failed to load: a panel, a dashboard card, a route boundary." (https://vercel.com/geist/error) Ours: the reading period on the title row, then the figures in one row of equal `KpiCard`s (as many columns as figures up to five, five reading two by two from md, three over two from lg and one row from xl at 1280 px; six as three over three, more in rows of four; one column below md), then the chart cards, then the exceptions. Cards stand 24 px apart with no heading between them (the figures 16 px apart in their row), and the title row has no title: the path names the page. Its cards are dashboard cards: a figure or a chart that reads, drills into its owner and holds no setting; neither an object card nor a setting card appears here. Colour sits in a figure's change and tone, never in the page. It has no states of its own: each card and the exceptions' `RecordList` draw theirs.
|
|
24
|
+
* @behaviour "Show a Skeleton when async data fills a known layout: table rows, card grids, profile blocks, sidebars." (https://vercel.com/geist/skeleton) "Use the Error component as a block surface when a section or page-level resource failed to load: a panel, a dashboard card, a route boundary." (https://vercel.com/geist/error) Ours: the reading period on the title row, then the figures in one row of equal `KpiCard`s (as many columns as figures up to five, five reading two by two from md, three over two from lg and one row from xl at 1280 px; six as three over three, more in rows of four; one column below md), then the chart cards, then the exceptions. Cards stand 24 px apart with no heading between them (the figures 16 px apart in their row), and the title row has no title: the path names the page. Its cards are dashboard cards: a figure or a chart that reads, drills into its owner and holds no setting; neither an object card nor a setting card appears here. Colour sits in a figure's change and tone, never in the page. It has no states of its own but one: when nothing has happened yet, `nothingYet` is one muted line in place of the figures and charts, never a row of figures that all read zero; each card and the exceptions' `RecordList` draw theirs.
|
|
20
25
|
* @words The product words every figure, chart and exception. A figure's label names its measure ("Active Agents", "Spend"), the chart's title names its reading ("Usage"), and the exceptions' heading says what they are ("Needs attention").
|
|
21
26
|
* @access Each chart card is a named section at the page's heading level, and the exceptions carry their own heading; figures are text, never colour alone.
|
|
22
27
|
*/
|
|
23
|
-
export declare function OverviewPage({ period, figures, charts, exceptions, }: OverviewPageProps): import("react").JSX.Element;
|
|
28
|
+
export declare function OverviewPage({ period, figures, nothingYet, charts, exceptions, }: OverviewPageProps): import("react").JSX.Element;
|
|
24
29
|
//# sourceMappingURL=overview-page.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overview-page.d.ts","sourceRoot":"","sources":["../../src/components/overview-page.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGrD,OAAO,EAAW,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGnE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,6CAA6C;IAC7C,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,gEAAgE;IAChE,OAAO,CAAC,EAAE,SAAS,IAAI,CAAC,YAAY,EAAE,cAAc,CAAC,EAAE,CAAC;IACxD,mEAAmE;IACnE,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,gGAAgG;IAChG,UAAU,CAAC,EAAE,SAAS,CAAC;CACxB,CAAC;AAWF;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,OAAoB,EACpB,MAAM,EACN,UAAU,GACX,EAAE,iBAAiB,+
|
|
1
|
+
{"version":3,"file":"overview-page.d.ts","sourceRoot":"","sources":["../../src/components/overview-page.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGrD,OAAO,EAAW,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGnE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,6CAA6C;IAC7C,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,gEAAgE;IAChE,OAAO,CAAC,EAAE,SAAS,IAAI,CAAC,YAAY,EAAE,cAAc,CAAC,EAAE,CAAC;IACxD;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,gGAAgG;IAChG,UAAU,CAAC,EAAE,SAAS,CAAC;CACxB,CAAC;AAWF;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,OAAoB,EACpB,UAAU,EACV,MAAM,EACN,UAAU,GACX,EAAE,iBAAiB,+BAuBnB"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { CardRow } from "./card-row.js";
|
|
4
4
|
import { KpiCard } from "./kpi-card.js";
|
|
5
5
|
import { PageActions } from "./page-actions.js";
|
|
@@ -19,10 +19,10 @@ const NO_FIGURES = [];
|
|
|
19
19
|
* @family Compositions
|
|
20
20
|
* @when "For in-product cards and dashboard tiles, use `Card`; Book is too decorative for repeated UI rows." (https://vercel.com/geist/book) Ours: the landing page of an area a person checks: the Portal's Dashboard, Analytics, Statements, System health, an Agent's Usage.
|
|
21
21
|
* @instead A list of records a person finds one of is a `CollectionPage`; one record is an `ObjectPage`. Never a stack of previews of every section: each exception links to its owner instead.
|
|
22
|
-
* @behaviour "Show a Skeleton when async data fills a known layout: table rows, card grids, profile blocks, sidebars." (https://vercel.com/geist/skeleton) "Use the Error component as a block surface when a section or page-level resource failed to load: a panel, a dashboard card, a route boundary." (https://vercel.com/geist/error) Ours: the reading period on the title row, then the figures in one row of equal `KpiCard`s (as many columns as figures up to five, five reading two by two from md, three over two from lg and one row from xl at 1280 px; six as three over three, more in rows of four; one column below md), then the chart cards, then the exceptions. Cards stand 24 px apart with no heading between them (the figures 16 px apart in their row), and the title row has no title: the path names the page. Its cards are dashboard cards: a figure or a chart that reads, drills into its owner and holds no setting; neither an object card nor a setting card appears here. Colour sits in a figure's change and tone, never in the page. It has no states of its own: each card and the exceptions' `RecordList` draw theirs.
|
|
22
|
+
* @behaviour "Show a Skeleton when async data fills a known layout: table rows, card grids, profile blocks, sidebars." (https://vercel.com/geist/skeleton) "Use the Error component as a block surface when a section or page-level resource failed to load: a panel, a dashboard card, a route boundary." (https://vercel.com/geist/error) Ours: the reading period on the title row, then the figures in one row of equal `KpiCard`s (as many columns as figures up to five, five reading two by two from md, three over two from lg and one row from xl at 1280 px; six as three over three, more in rows of four; one column below md), then the chart cards, then the exceptions. Cards stand 24 px apart with no heading between them (the figures 16 px apart in their row), and the title row has no title: the path names the page. Its cards are dashboard cards: a figure or a chart that reads, drills into its owner and holds no setting; neither an object card nor a setting card appears here. Colour sits in a figure's change and tone, never in the page. It has no states of its own but one: when nothing has happened yet, `nothingYet` is one muted line in place of the figures and charts, never a row of figures that all read zero; each card and the exceptions' `RecordList` draw theirs.
|
|
23
23
|
* @words The product words every figure, chart and exception. A figure's label names its measure ("Active Agents", "Spend"), the chart's title names its reading ("Usage"), and the exceptions' heading says what they are ("Needs attention").
|
|
24
24
|
* @access Each chart card is a named section at the page's heading level, and the exceptions carry their own heading; figures are text, never colour alone.
|
|
25
25
|
*/
|
|
26
|
-
export function OverviewPage({ period, figures = NO_FIGURES, charts, exceptions, }) {
|
|
27
|
-
return (_jsxs("div", { "data-slot": "overview-page", className: "grid min-w-0 gap-6", children: [_jsx(PageActions, { period: period }), figures.length > 0 ? (_jsx(CardRow, { columns: figureColumns(figures.length), from: "md", children: figures.map((figure) => (_jsx(KpiCard, { ...figure }, figure.label))) })) : null, charts, exceptions] }));
|
|
26
|
+
export function OverviewPage({ period, figures = NO_FIGURES, nothingYet, charts, exceptions, }) {
|
|
27
|
+
return (_jsxs("div", { "data-slot": "overview-page", className: "grid min-w-0 gap-6", children: [_jsx(PageActions, { period: period }), nothingYet ? (_jsx("p", { className: "text-muted-foreground nc-type-body-sm", "data-slot": "overview-nothing-yet", children: nothingYet })) : (_jsxs(_Fragment, { children: [figures.length > 0 ? (_jsx(CardRow, { columns: figureColumns(figures.length), from: "md", children: figures.map((figure) => (_jsx(KpiCard, { ...figure }, figure.label))) })) : null, charts] })), exceptions] }));
|
|
28
28
|
}
|