@sableui/react 0.9.1 → 0.10.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 +1 -1
- package/CHANGELOG.md +95 -0
- package/CONTRIBUTING.md +2 -2
- package/DESIGN.md +55 -16
- package/README.md +12 -0
- package/dist/{_contract-C2M1SjBf.d.ts → _contract-CkqXDVww.d.ts} +25 -2
- package/dist/blocks/danger-zone-block.js +3 -3
- package/dist/blocks/danger-zone-block.meta.js +1 -1
- package/dist/blocks/faq-block.js +3 -3
- package/dist/blocks/goal-progress-block.js +1 -1
- package/dist/blocks/notification-list-block.js +1 -1
- package/dist/blocks/pricing-table-block.js +12 -12
- package/dist/blocks/product-card-block.js +3 -3
- package/dist/blocks/product-card-block.meta.js +3 -3
- package/dist/blocks/profile-card-block.js +1 -1
- package/dist/blocks/profile-form-block.js +3 -3
- package/dist/blocks/revenue-breakdown-block.js +5 -5
- package/dist/blocks/stat-row-block.js +3 -3
- package/dist/blocks/task-list-block.js +2 -2
- package/dist/blocks/team-members-block.js +1 -1
- package/dist/blocks/wallet-block.js +4 -4
- package/dist/blocks/wallet-block.meta.js +3 -3
- package/dist/components/accordion/accordion.meta.js +2 -2
- package/dist/components/app-header/app-header.meta.js +1 -1
- package/dist/components/app-shell/app-shell.meta.js +2 -2
- package/dist/components/area-chart/area-chart.meta.js +3 -3
- package/dist/components/badge/badge.js +13 -4
- package/dist/components/badge/badge.meta.js +24 -13
- package/dist/components/badge/badge.variants.js +37 -23
- package/dist/components/banner/banner.meta.js +8 -8
- package/dist/components/banner/banner.variants.js +3 -3
- package/dist/components/bar-chart/bar-chart.meta.js +4 -4
- package/dist/components/breadcrumbs/breadcrumbs.meta.js +1 -1
- package/dist/components/button/button.meta.js +3 -3
- package/dist/components/card/card.js +16 -7
- package/dist/components/card/card.meta.js +4 -4
- package/dist/components/checkbox-card/checkbox-card.meta.js +10 -10
- package/dist/components/checkbox-card/checkbox-card.variants.js +15 -15
- package/dist/components/code-block/code-block.meta.js +1 -1
- package/dist/components/command-menu/command-menu.meta.js +1 -1
- package/dist/components/context-card/context-card.meta.js +2 -2
- package/dist/components/destructive-action-modal/destructive-action-modal.meta.js +1 -1
- package/dist/components/dialog/dialog.meta.js +2 -2
- package/dist/components/disclosure-picker/disclosure-picker.js +48 -2
- package/dist/components/disclosure-picker/disclosure-picker.meta.js +3 -2
- package/dist/components/empty-state/empty-state.meta.js +4 -4
- package/dist/components/gradient-art/gradient-art.meta.js +3 -3
- package/dist/components/label/label.meta.js +1 -1
- package/dist/components/line-chart/line-chart.meta.js +3 -3
- package/dist/components/meter/meter.js +1 -1
- package/dist/components/meter/meter.meta.js +2 -2
- package/dist/components/meter/meter.variants.js +1 -1
- package/dist/components/middle-truncate/middle-truncate.meta.js +4 -4
- package/dist/components/note/note.meta.js +3 -3
- package/dist/components/page-header/page-header.js +1 -1
- package/dist/components/pagination/pagination.meta.js +2 -2
- package/dist/components/phone/phone.meta.js +1 -1
- package/dist/components/pie-chart/pie-chart.meta.js +3 -3
- package/dist/components/pill/pill.js +4 -2
- package/dist/components/pill/pill.meta.js +19 -2
- package/dist/components/pill/pill.variants.js +23 -2
- package/dist/components/popover/popover.meta.js +2 -2
- package/dist/components/radar-chart/radar-chart.meta.js +3 -3
- package/dist/components/radial-chart/radial-chart.meta.js +3 -3
- package/dist/components/radio/radio.meta.js +2 -2
- package/dist/components/radio-card/radio-card.meta.js +6 -6
- package/dist/components/radio-card/radio-card.variants.js +15 -15
- package/dist/components/scroller/scroller.meta.js +1 -1
- package/dist/components/search-input/search-input.meta.js +1 -1
- package/dist/components/section-header/section-header.js +1 -1
- package/dist/components/segmented-bar/segmented-bar.js +1 -1
- package/dist/components/segmented-bar/segmented-bar.meta.js +6 -6
- package/dist/components/segmented-bar/segmented-bar.variants.js +4 -4
- package/dist/components/select/select.meta.js +2 -2
- package/dist/components/sheet/sheet.meta.js +1 -1
- package/dist/components/side-nav/side-nav.meta.js +1 -1
- package/dist/components/sidebar/sidebar.meta.js +1 -1
- package/dist/components/sparkline/sparkline.meta.js +4 -4
- package/dist/components/spinner/spinner.meta.js +1 -1
- package/dist/components/status-dot/status-dot.js +4 -1
- package/dist/components/status-dot/status-dot.meta.js +8 -16
- package/dist/components/status-dot/status-dot.variants.js +13 -4
- package/dist/components/table/table.meta.js +4 -4
- package/dist/components/tabs/tabs.meta.js +1 -1
- package/dist/components/textarea/textarea.meta.js +2 -2
- package/dist/components/toast/toast.meta.js +1 -1
- package/dist/components/tooltip/tooltip.meta.js +2 -2
- package/dist/file-hashes.json +120 -118
- package/dist/foundations/spacing.js +8 -8
- package/dist/index.d.ts +137 -72
- package/dist/index.js +12 -1
- package/dist/lib/chart/types.js +3 -3
- package/dist/lib/palette.js +103 -0
- package/dist/lib/selection-card.js +4 -4
- package/dist/palette.css +669 -0
- package/dist/patterns/auth-page.js +6 -6
- package/dist/patterns/auth-page.meta.js +3 -3
- package/dist/patterns/brand-landing-page.js +2 -2
- package/dist/patterns/chat-panel.js +2 -2
- package/dist/patterns/dashboard-page.js +71 -71
- package/dist/patterns/dashboard-page.meta.js +5 -5
- package/dist/patterns/feed-page.js +25 -25
- package/dist/patterns/feed-page.meta.js +3 -3
- package/dist/patterns/landing-page.js +1 -1
- package/dist/patterns/list-detail-page.js +17 -17
- package/dist/patterns/settings-page.js +17 -17
- package/dist/presets/index.d.ts +1 -1
- package/dist/presets/index.js +17 -2
- package/dist/styles.css +115 -50
- package/dist/{theme-provider-S0NVJs2Y.d.ts → theme-provider-CdSicAad.d.ts} +1 -1
- package/dist/themes/_contract.js +22 -8
- package/dist/themes/atelier/atelier.css +13 -13
- package/dist/themes/atelier/index.d.ts +1 -1
- package/dist/themes/ember/ember.css +13 -13
- package/dist/themes/ember/index.d.ts +1 -1
- package/dist/themes/index.d.ts +2 -2
- package/dist/themes/meridian/index.d.ts +1 -1
- package/dist/themes/meridian/meridian.css +13 -13
- package/dist/themes/poppy/index.d.ts +1 -1
- package/dist/themes/poppy/poppy.css +19 -16
- package/dist/themes/prism/index.d.ts +1 -1
- package/dist/themes/prism/prism.css +18 -15
- package/dist/themes/sable/index.d.ts +1 -1
- package/dist/themes/sable/sable.css +18 -22
- package/dist/themes/verdant/index.d.ts +1 -1
- package/dist/themes/verdant/verdant.css +15 -15
- package/llms/blocks/danger-zone-block.md +4 -4
- package/llms/blocks/faq-block.md +3 -3
- package/llms/blocks/goal-progress-block.md +1 -1
- package/llms/blocks/notification-list-block.md +1 -1
- package/llms/blocks/pricing-table-block.md +12 -12
- package/llms/blocks/product-card-block.md +6 -6
- package/llms/blocks/profile-card-block.md +1 -1
- package/llms/blocks/profile-form-block.md +3 -3
- package/llms/blocks/revenue-breakdown-block.md +5 -5
- package/llms/blocks/stat-row-block.md +3 -3
- package/llms/blocks/task-list-block.md +2 -2
- package/llms/blocks/team-members-block.md +1 -1
- package/llms/blocks/wallet-block.md +7 -7
- package/llms/components/accordion.md +2 -2
- package/llms/components/app-header.md +1 -1
- package/llms/components/app-shell.md +2 -2
- package/llms/components/area-chart.md +1 -1
- package/llms/components/badge.md +15 -7
- package/llms/components/banner.md +6 -6
- package/llms/components/bar-chart.md +2 -2
- package/llms/components/breadcrumbs.md +1 -1
- package/llms/components/button.md +3 -3
- package/llms/components/card.md +4 -4
- package/llms/components/checkbox-card.md +5 -5
- package/llms/components/code-block.md +1 -1
- package/llms/components/command-menu.md +1 -1
- package/llms/components/context-card.md +2 -2
- package/llms/components/destructive-action-modal.md +1 -1
- package/llms/components/dialog.md +2 -2
- package/llms/components/disclosure-picker.md +2 -1
- package/llms/components/empty-state.md +4 -4
- package/llms/components/gradient-art.md +3 -3
- package/llms/components/label.md +1 -1
- package/llms/components/line-chart.md +1 -1
- package/llms/components/meter.md +2 -2
- package/llms/components/middle-truncate.md +5 -5
- package/llms/components/note.md +3 -3
- package/llms/components/pagination.md +2 -2
- package/llms/components/phone.md +1 -1
- package/llms/components/pie-chart.md +1 -1
- package/llms/components/pill.md +12 -2
- package/llms/components/popover.md +2 -2
- package/llms/components/radar-chart.md +1 -1
- package/llms/components/radial-chart.md +1 -1
- package/llms/components/radio-card.md +4 -4
- package/llms/components/radio.md +2 -2
- package/llms/components/scroller.md +1 -1
- package/llms/components/search-input.md +1 -1
- package/llms/components/segmented-bar.md +7 -7
- package/llms/components/select.md +2 -2
- package/llms/components/sheet.md +1 -1
- package/llms/components/side-nav.md +1 -1
- package/llms/components/sidebar.md +1 -1
- package/llms/components/sparkline.md +2 -2
- package/llms/components/spinner.md +1 -1
- package/llms/components/status-dot.md +4 -4
- package/llms/components/table.md +8 -8
- package/llms/components/tabs.md +1 -1
- package/llms/components/textarea.md +2 -2
- package/llms/components/toast.md +1 -1
- package/llms/components/tooltip.md +2 -2
- package/llms/foundations/spacing.md +7 -7
- package/llms/layout/grid.md +1 -1
- package/llms/layout/page.md +1 -1
- package/llms/layout/region.md +1 -1
- package/llms/layout/split-pane.md +1 -1
- package/llms/patterns/auth-page.md +11 -11
- package/llms/patterns/brand-landing-page.md +2 -2
- package/llms/patterns/chat-panel.md +2 -2
- package/llms/patterns/dashboard-page.md +78 -78
- package/llms/patterns/feed-page.md +31 -31
- package/llms/patterns/landing-page.md +1 -1
- package/llms/patterns/list-detail-page.md +18 -18
- package/llms/patterns/settings-page.md +18 -18
- package/llms/reference/token-intent.md +25 -11
- package/llms/reference/utilities.md +1 -1
- package/llms/tasks/adding-a-component.md +4 -1
- package/llms/tasks/building-a-page.md +40 -0
- package/llms/tasks/converting-an-app.md +3 -1
- package/llms/tasks/using-presets.md +4 -0
- package/llms-full.txt +429 -347
- package/llms.txt +7 -7
- package/package.json +4 -4
- package/registry.json +369 -233
- package/scripts/lint-rules.mjs +26 -5
- package/skills/sable/SKILL.md +14 -3
|
@@ -24,7 +24,7 @@ function AuthPage() {
|
|
|
24
24
|
/* @__PURE__ */ jsxs(Stack, { gap: 4, justify: "center", className: "flex-1 py-12", children: [
|
|
25
25
|
/* @__PURE__ */ jsxs(Stack, { gap: 1.5, children: [
|
|
26
26
|
/* @__PURE__ */ jsx("h1", { className: "text-heading-1", children: "Welcome back" }),
|
|
27
|
-
/* @__PURE__ */ jsx("p", { className: "text-body-lg text-muted-foreground", children: "Sign in to
|
|
27
|
+
/* @__PURE__ */ jsx("p", { className: "text-body-lg text-muted-foreground", children: "Sign in to pick up where you and your team left off." })
|
|
28
28
|
] }),
|
|
29
29
|
/* @__PURE__ */ jsxs(
|
|
30
30
|
Form,
|
|
@@ -51,7 +51,7 @@ function AuthPage() {
|
|
|
51
51
|
/* @__PURE__ */ jsx(Separator, { label: "or" }),
|
|
52
52
|
/* @__PURE__ */ jsxs(Stack, { gap: 1.5, children: [
|
|
53
53
|
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "cta", className: "w-full", iconStart: /* @__PURE__ */ jsx(Icon, { name: "key" }), children: "Sign in with a passkey" }),
|
|
54
|
-
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "cta", className: "w-full", iconStart: /* @__PURE__ */ jsx(Icon, { name: "
|
|
54
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "cta", className: "w-full", iconStart: /* @__PURE__ */ jsx(Icon, { name: "mail" }), children: "Email me a sign-in link" })
|
|
55
55
|
] }),
|
|
56
56
|
/* @__PURE__ */ jsxs("p", { className: "text-body-md text-muted-foreground", children: [
|
|
57
57
|
"No account?",
|
|
@@ -70,16 +70,16 @@ function AuthPage() {
|
|
|
70
70
|
"."
|
|
71
71
|
] })
|
|
72
72
|
] }) }),
|
|
73
|
-
/* @__PURE__ */ jsx("aside", { "aria-label": "
|
|
74
|
-
/* @__PURE__ */ jsx(Badge, { tone: "
|
|
75
|
-
/* @__PURE__ */ jsx("p", { className: "text-title-lg", children: "\
|
|
73
|
+
/* @__PURE__ */ jsx("aside", { "aria-label": "Customer story", className: "hidden p-3 lg:flex", children: /* @__PURE__ */ jsx(GradientArt, { seed: "Untitled (gold rush)", className: "flex flex-1 flex-col justify-end rounded-xl p-8", children: /* @__PURE__ */ jsx(Card, { className: "max-w-md", children: /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsxs(Stack, { gap: 3, children: [
|
|
74
|
+
/* @__PURE__ */ jsx(Badge, { tone: "1", className: "self-start", children: "Customer story" }),
|
|
75
|
+
/* @__PURE__ */ jsx("p", { className: "text-title-lg", children: "\u201CWe moved three teams over in a week, and nobody asked for the old tool back.\u201D" }),
|
|
76
76
|
/* @__PURE__ */ jsx(
|
|
77
77
|
Entity,
|
|
78
78
|
{
|
|
79
79
|
className: "p-0",
|
|
80
80
|
leading: /* @__PURE__ */ jsx(Avatar, { variant: "art", fallback: "MO" }),
|
|
81
81
|
title: "Mara Okafor",
|
|
82
|
-
subtitle: "
|
|
82
|
+
subtitle: "Head of Design, Northwind"
|
|
83
83
|
}
|
|
84
84
|
)
|
|
85
85
|
] }) }) }) }) })
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/patterns/auth-page.meta.ts
|
|
2
2
|
var authPageMeta = {
|
|
3
3
|
name: "AuthPage",
|
|
4
|
-
summary: "Split sign-in page: a form column (brand mark, heading, email + password, primary sign-in, passkey and
|
|
4
|
+
summary: "Split sign-in page: a form column (brand mark, heading, email + password, primary sign-in, passkey and email-link alternatives, sign-up switch, terms) beside a brand panel painted from the theme's tones with a customer story card. Below lg the panel drops away and the form is the whole screen.",
|
|
5
5
|
intent: "Reach for this for sign-in, sign-up, forgot-password, and any single-purpose one-column form route (invite accept, unsubscribe). Not for multi-section forms (use SettingsPage).",
|
|
6
6
|
notes: [
|
|
7
7
|
"The form column is a `form`-capped Region (480px), so the controls keep the same measure whether the panel is present or not.",
|
|
@@ -9,13 +9,13 @@ var authPageMeta = {
|
|
|
9
9
|
"The heading is `text-heading-1` in an h1 \u2014 it is the page outline, so it takes a heading tier, not a card title.",
|
|
10
10
|
"Brand mark, form and terms are a three-part column: mark pinned to the top, form vertically centred in the remaining space, terms pinned to the bottom.",
|
|
11
11
|
"Forgot password sits under the password field, right-aligned, before the submit button \u2014 so it comes right after the field in tab order. Field has no label-row action slot yet.",
|
|
12
|
-
"One primary action. Passkey and
|
|
12
|
+
"One primary action. Passkey and email link are outline buttons under an `or` Separator; switching to sign-up is a quiet `link-underline` link.",
|
|
13
13
|
"Validation uses Field `validate` for product copy instead of the browser's generic required message. A rejected sign-in comes back through Form `errors` on the password field; the submit Button shows `loading` while the request is in flight.",
|
|
14
14
|
"The brand panel artwork is a mesh of the theme's decorative tones over `--primary`, so it re-paints for every theme and mode. The relative-colour `oklch(from var(--tone-*) \u2026)` lift needs an `inline-color` disable with a reason.",
|
|
15
15
|
"The panel is `hidden lg:flex`: on a phone the form is the whole screen, with no decorative scroll above it."
|
|
16
16
|
],
|
|
17
17
|
a11y: [
|
|
18
|
-
'The route renders inside <main> with one h1; the brand panel is an <aside> labelled "
|
|
18
|
+
'The route renders inside <main> with one h1; the brand panel is an <aside> labelled "Customer story".',
|
|
19
19
|
"Field auto-wires labels, describedby, and invalid state onto the inputs; on submit, the first invalid field is focused.",
|
|
20
20
|
"Autocomplete attributes (`email webauthn`, `current-password`) keep password managers and passkey autofill working.",
|
|
21
21
|
"The Forgot password and Create one links use `hit-target` to meet the 44px touch floor without changing layout.",
|
|
@@ -29,7 +29,7 @@ var spaces = [
|
|
|
29
29
|
];
|
|
30
30
|
var routes = [
|
|
31
31
|
{ label: "Intake", state: "Open", tone: "info" },
|
|
32
|
-
{ label: "Review", state: "Live", tone: "
|
|
32
|
+
{ label: "Review", state: "Live", tone: "1" },
|
|
33
33
|
{ label: "Archive", state: "Safe", tone: "success" }
|
|
34
34
|
];
|
|
35
35
|
var bars = [
|
|
@@ -164,7 +164,7 @@ function BrandLandingPage() {
|
|
|
164
164
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b border-border px-4 py-3", children: [
|
|
165
165
|
/* @__PURE__ */ jsxs(Row, { gap: 1, children: [
|
|
166
166
|
/* @__PURE__ */ jsx(StatusDot, { tone: "warning" }),
|
|
167
|
-
/* @__PURE__ */ jsx(StatusDot, { tone: "
|
|
167
|
+
/* @__PURE__ */ jsx(StatusDot, { tone: "1" }),
|
|
168
168
|
/* @__PURE__ */ jsx(StatusDot, { tone: "success" })
|
|
169
169
|
] }),
|
|
170
170
|
/* @__PURE__ */ jsx("span", { className: "text-label-sm text-muted-foreground", children: "Live workspace" })
|
|
@@ -60,7 +60,7 @@ function ChatPanel() {
|
|
|
60
60
|
render: /* @__PURE__ */ jsx("section", { "aria-labelledby": "chat-title" }),
|
|
61
61
|
className: "flex h-[calc(100dvh_-_--spacing(12))] flex-col overflow-hidden",
|
|
62
62
|
children: [
|
|
63
|
-
/* @__PURE__ */ jsxs(CardHeader, { className: "
|
|
63
|
+
/* @__PURE__ */ jsxs(CardHeader, { className: "pb-(--card-p) shadow-[inset_0_-1px_0_var(--border)]", children: [
|
|
64
64
|
/* @__PURE__ */ jsx(CardTitle, { children: /* @__PURE__ */ jsx("h1", { id: "chat-title", children: "Scroll behaviour" }) }),
|
|
65
65
|
/* @__PURE__ */ jsx(CardDescription, { children: "Assistant \xB7 answers from your team's docs" }),
|
|
66
66
|
/* @__PURE__ */ jsx(CardAction, { children: /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", children: [
|
|
@@ -136,7 +136,7 @@ function ChatPanel() {
|
|
|
136
136
|
}
|
|
137
137
|
)
|
|
138
138
|
] }),
|
|
139
|
-
/* @__PURE__ */ jsx(CardFooter, { className: "
|
|
139
|
+
/* @__PURE__ */ jsx(CardFooter, { className: "pt-(--card-p) shadow-[inset_0_1px_0_var(--border)]", children: /* @__PURE__ */ jsx(
|
|
140
140
|
ChatComposer,
|
|
141
141
|
{
|
|
142
142
|
className: "flex-1",
|
|
@@ -34,8 +34,8 @@ import {
|
|
|
34
34
|
} from "../components/sidebar/index.js";
|
|
35
35
|
import { Grid, Page, Row, Stack } from "../layout/index.js";
|
|
36
36
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
37
|
-
var STATUS_TONE = {
|
|
38
|
-
var STATUS_LABEL = {
|
|
37
|
+
var STATUS_TONE = { active: "success", onboarding: "info", full: "1", cancelled: "neutral" };
|
|
38
|
+
var STATUS_LABEL = { active: "Active", onboarding: "Onboarding", full: "Seats full", cancelled: "Cancelled" };
|
|
39
39
|
var MIN = 60 * 1e3;
|
|
40
40
|
var HOUR = 60 * MIN;
|
|
41
41
|
var DAY = 24 * HOUR;
|
|
@@ -44,49 +44,49 @@ var usdCents = new Intl.NumberFormat("en-US", { style: "currency", currency: "US
|
|
|
44
44
|
var shortDate = (offset) => new Date(Date.now() + offset).toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
|
45
45
|
var stats = [
|
|
46
46
|
{ label: "Revenue", value: "$18,240", delta: "+8.2%", up: true, note: "vs. $16,857" },
|
|
47
|
-
{ label: "
|
|
48
|
-
{ label: "
|
|
49
|
-
{ label: "
|
|
47
|
+
{ label: "Active seats", value: "1,284", delta: "+3.1%", up: true, note: "182 added" },
|
|
48
|
+
{ label: "Accounts", value: "312", delta: "+12.4%", up: true, note: "Best month yet" },
|
|
49
|
+
{ label: "Seat usage", value: "64%", delta: "\u22121.8%", up: false, note: "6 near their limit" }
|
|
50
50
|
];
|
|
51
|
-
var
|
|
52
|
-
{ id: "
|
|
53
|
-
{ id: "
|
|
54
|
-
{ id: "
|
|
55
|
-
{ id: "
|
|
56
|
-
{ id: "
|
|
57
|
-
{ id: "
|
|
58
|
-
{ id: "
|
|
59
|
-
{ id: "
|
|
60
|
-
{ id: "
|
|
51
|
+
var accounts = [
|
|
52
|
+
{ id: "ac_01", title: "Northwind", status: "active", used: 184, seats: 250, price: 16, started: -6 * DAY },
|
|
53
|
+
{ id: "ac_02", title: "Harbor Labs", status: "full", used: 100, seats: 100, price: 20, started: -19 * DAY },
|
|
54
|
+
{ id: "ac_03", title: "Brightline", status: "active", used: 61, seats: 150, price: 16, started: -2 * DAY },
|
|
55
|
+
{ id: "ac_04", title: "Tidewater Co.", status: "active", used: 212, seats: 300, price: 12, started: -27 * DAY },
|
|
56
|
+
{ id: "ac_05", title: "Saltworks", status: "cancelled", used: 38, seats: 75, price: 20, started: -44 * DAY },
|
|
57
|
+
{ id: "ac_06", title: "Paper Moon", status: "full", used: 50, seats: 50, price: 24, started: -58 * DAY },
|
|
58
|
+
{ id: "ac_07", title: "Signal Studio", status: "onboarding", used: 0, seats: 200, price: 16, started: 3 * DAY },
|
|
59
|
+
{ id: "ac_08", title: "Bluewater", status: "cancelled", used: 72, seats: 120, price: 12, started: -71 * DAY },
|
|
60
|
+
{ id: "ac_09", title: "Ember & Field", status: "full", used: 80, seats: 80, price: 20, started: -86 * DAY }
|
|
61
61
|
];
|
|
62
|
-
var
|
|
62
|
+
var accountCount = 312;
|
|
63
63
|
var payout = {
|
|
64
|
-
amount:
|
|
64
|
+
amount: 8862.64,
|
|
65
65
|
date: 5 * DAY,
|
|
66
66
|
lines: [
|
|
67
|
-
{ label: "
|
|
68
|
-
{ label: "
|
|
67
|
+
{ label: "Subscriptions", value: 9127.33 },
|
|
68
|
+
{ label: "Processing (2.9%)", value: -264.69 },
|
|
69
69
|
{ label: "Deposits to", value: "Checking \u2022\u20224821" }
|
|
70
70
|
]
|
|
71
71
|
};
|
|
72
|
-
var
|
|
73
|
-
{ id: "
|
|
74
|
-
{ id: "
|
|
75
|
-
{ id: "
|
|
76
|
-
{ id: "
|
|
72
|
+
var customers = [
|
|
73
|
+
{ id: "cu_01", name: "Northwind", handle: "northwind.io", initials: "NW", seats: 184, spent: 2944 },
|
|
74
|
+
{ id: "cu_02", name: "Tidewater Co.", handle: "tidewater.co", initials: "TC", seats: 212, spent: 2544 },
|
|
75
|
+
{ id: "cu_03", name: "Harbor Labs", handle: "harborlabs.com", initials: "HL", seats: 100, spent: 2e3 },
|
|
76
|
+
{ id: "cu_04", name: "Ember & Field", handle: "emberfield.com", initials: "EF", seats: 80, spent: 1600 }
|
|
77
77
|
];
|
|
78
78
|
var upcoming = [
|
|
79
|
-
{ id: "up_01", title: "
|
|
80
|
-
{ id: "up_02", title: "
|
|
81
|
-
{ id: "up_03", title: "
|
|
82
|
-
{ id: "up_04", title: "
|
|
79
|
+
{ id: "up_01", title: "Version 2.4", detail: "Shared reports \xB7 All plans", at: 3 * DAY },
|
|
80
|
+
{ id: "up_02", title: "Audit log export", detail: "Business plan \xB7 Beta", at: 12 * DAY },
|
|
81
|
+
{ id: "up_03", title: "Version 2.5", detail: "Offline mode \xB7 All plans", at: 26 * DAY },
|
|
82
|
+
{ id: "up_04", title: "Usage-based billing", detail: "Studio and Business \xB7 Beta", at: 34 * DAY }
|
|
83
83
|
];
|
|
84
84
|
var activity = [
|
|
85
|
-
{ id: "
|
|
86
|
-
{ id: "
|
|
87
|
-
{ id: "
|
|
88
|
-
{ id: "
|
|
89
|
-
{ id: "
|
|
85
|
+
{ id: "ev_01", who: "Nina Simone", initials: "NS", what: "added 12 seats to Northwind \xB7 184 of 250", when: 2 * MIN },
|
|
86
|
+
{ id: "ev_02", who: "Grace Hopper", initials: "GH", what: "upgraded Brightline to Studio", when: 26 * MIN },
|
|
87
|
+
{ id: "ev_03", who: "Frida Kahlo", initials: "FK", what: "paid invoice INV-0042 for Tidewater Co.", when: 1 * HOUR },
|
|
88
|
+
{ id: "ev_04", who: "Mary Shelley", initials: "MS", what: "joined the Harbor Labs workspace", when: 3 * HOUR },
|
|
89
|
+
{ id: "ev_05", who: "Nina Simone", initials: "NS", what: "booked onboarding for Signal Studio", when: 6 * HOUR }
|
|
90
90
|
];
|
|
91
91
|
function DashboardPage() {
|
|
92
92
|
return /* @__PURE__ */ jsx(
|
|
@@ -104,8 +104,8 @@ function DashboardPage() {
|
|
|
104
104
|
),
|
|
105
105
|
bottomNav: /* @__PURE__ */ jsxs(BottomNav, { "aria-label": "Primary", children: [
|
|
106
106
|
/* @__PURE__ */ jsx(BottomNavItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "grid" }), label: "Overview", showLabel: true, active: true, render: /* @__PURE__ */ jsx("a", { href: "/" }) }),
|
|
107
|
-
/* @__PURE__ */ jsx(BottomNavItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "
|
|
108
|
-
/* @__PURE__ */ jsx(BottomNavItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "user" }), label: "
|
|
107
|
+
/* @__PURE__ */ jsx(BottomNavItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "folder" }), label: "Accounts", showLabel: true, render: /* @__PURE__ */ jsx("a", { href: "/accounts" }) }),
|
|
108
|
+
/* @__PURE__ */ jsx(BottomNavItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "user" }), label: "Customers", showLabel: true, render: /* @__PURE__ */ jsx("a", { href: "/customers" }) }),
|
|
109
109
|
/* @__PURE__ */ jsx(BottomNavItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "bell" }), label: "Activity", showLabel: true, render: /* @__PURE__ */ jsx("a", { href: "/activity" }) }),
|
|
110
110
|
/* @__PURE__ */ jsx(BottomNavItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "gear" }), label: "Settings", showLabel: true, render: /* @__PURE__ */ jsx("a", { href: "/settings" }) })
|
|
111
111
|
] }),
|
|
@@ -113,8 +113,8 @@ function DashboardPage() {
|
|
|
113
113
|
/* @__PURE__ */ jsx(SidebarHeader, { children: /* @__PURE__ */ jsx(SidebarBrand, { mark: /* @__PURE__ */ jsx(Icon, { name: "gem", variant: "solid" }), label: "Atelier", render: /* @__PURE__ */ jsx("a", { href: "/" }) }) }),
|
|
114
114
|
/* @__PURE__ */ jsxs(SidebarNav, { children: [
|
|
115
115
|
/* @__PURE__ */ jsx(SidebarItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "grid" }), label: "Overview", active: true, render: /* @__PURE__ */ jsx("a", { href: "/" }) }),
|
|
116
|
-
/* @__PURE__ */ jsx(SidebarItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "
|
|
117
|
-
/* @__PURE__ */ jsx(SidebarItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "user" }), label: "
|
|
116
|
+
/* @__PURE__ */ jsx(SidebarItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "folder" }), label: "Accounts", render: /* @__PURE__ */ jsx("a", { href: "/accounts" }) }),
|
|
117
|
+
/* @__PURE__ */ jsx(SidebarItem, { icon: /* @__PURE__ */ jsx(Icon, { name: "user" }), label: "Customers", render: /* @__PURE__ */ jsx("a", { href: "/customers" }) }),
|
|
118
118
|
/* @__PURE__ */ jsx(
|
|
119
119
|
SidebarItem,
|
|
120
120
|
{
|
|
@@ -147,7 +147,7 @@ function DashboardPage() {
|
|
|
147
147
|
PageHeader,
|
|
148
148
|
{
|
|
149
149
|
title: "Overview",
|
|
150
|
-
description: "
|
|
150
|
+
description: "Revenue and account activity for the last 30 days.",
|
|
151
151
|
actions: /* @__PURE__ */ jsxs(Row, { gap: 2, children: [
|
|
152
152
|
/* @__PURE__ */ jsxs(Button, { variant: "outline", children: [
|
|
153
153
|
/* @__PURE__ */ jsx(Icon, { name: "download" }),
|
|
@@ -155,7 +155,7 @@ function DashboardPage() {
|
|
|
155
155
|
] }),
|
|
156
156
|
/* @__PURE__ */ jsxs(Button, { children: [
|
|
157
157
|
/* @__PURE__ */ jsx(Icon, { name: "plus" }),
|
|
158
|
-
"New
|
|
158
|
+
"New account"
|
|
159
159
|
] })
|
|
160
160
|
] })
|
|
161
161
|
}
|
|
@@ -181,44 +181,44 @@ function DashboardPage() {
|
|
|
181
181
|
/* @__PURE__ */ jsxs(Grid, { cols: { base: 1, xl: 3 }, gap: 3, children: [
|
|
182
182
|
/* @__PURE__ */ jsxs(Card, { className: "flex flex-col xl:col-span-2", children: [
|
|
183
183
|
/* @__PURE__ */ jsxs(CardHeader, { children: [
|
|
184
|
-
/* @__PURE__ */ jsx(CardTitle, { children: "
|
|
185
|
-
/* @__PURE__ */ jsx(CardDescription, { children: "
|
|
184
|
+
/* @__PURE__ */ jsx(CardTitle, { children: "Accounts" }),
|
|
185
|
+
/* @__PURE__ */ jsx(CardDescription, { children: "Seats across your customers, newest first." })
|
|
186
186
|
] }),
|
|
187
187
|
/* @__PURE__ */ jsx(CardContent, { flush: true, className: "mt-4 flex-1", children: /* @__PURE__ */ jsx(
|
|
188
188
|
DataTable,
|
|
189
189
|
{
|
|
190
|
-
data:
|
|
190
|
+
data: accounts,
|
|
191
191
|
rowKey: (row) => row.id,
|
|
192
|
-
defaultSort: { key: "
|
|
192
|
+
defaultSort: { key: "started", direction: "desc" },
|
|
193
193
|
columns: [
|
|
194
|
-
{ key: "title", header: "
|
|
194
|
+
{ key: "title", header: "Account", sortable: true, className: "text-label-md whitespace-nowrap" },
|
|
195
195
|
{
|
|
196
196
|
key: "status",
|
|
197
197
|
header: "Status",
|
|
198
198
|
cell: (row) => /* @__PURE__ */ jsx(Badge, { appearance: "plain", tone: STATUS_TONE[row.status], dot: true, children: STATUS_LABEL[row.status] })
|
|
199
199
|
},
|
|
200
200
|
{
|
|
201
|
-
key: "
|
|
202
|
-
header: "
|
|
201
|
+
key: "used",
|
|
202
|
+
header: "Seats",
|
|
203
203
|
sortable: true,
|
|
204
|
-
sortValue: (row) => row.
|
|
204
|
+
sortValue: (row) => row.used / row.seats,
|
|
205
205
|
cell: (row) => /* @__PURE__ */ jsxs(Row, { gap: 3, align: "center", className: "min-w-40", children: [
|
|
206
206
|
/* @__PURE__ */ jsx(
|
|
207
207
|
Meter,
|
|
208
208
|
{
|
|
209
|
-
value: row.
|
|
210
|
-
max: row.
|
|
209
|
+
value: row.used,
|
|
210
|
+
max: row.seats,
|
|
211
211
|
size: "sm",
|
|
212
|
-
tone: row.
|
|
213
|
-
"aria-label": `${row.title}
|
|
214
|
-
getAriaValueText: (_, v) => `${v} of ${row.
|
|
212
|
+
tone: row.used === row.seats ? "success" : "primary",
|
|
213
|
+
"aria-label": `${row.title} seats used`,
|
|
214
|
+
getAriaValueText: (_, v) => `${v} of ${row.seats} seats used`,
|
|
215
215
|
className: "flex-1"
|
|
216
216
|
}
|
|
217
217
|
),
|
|
218
218
|
/* @__PURE__ */ jsxs("span", { className: "text-mono-caption whitespace-nowrap text-muted-foreground", children: [
|
|
219
|
-
row.
|
|
219
|
+
row.used,
|
|
220
220
|
"/",
|
|
221
|
-
row.
|
|
221
|
+
row.seats
|
|
222
222
|
] })
|
|
223
223
|
] })
|
|
224
224
|
},
|
|
@@ -227,15 +227,15 @@ function DashboardPage() {
|
|
|
227
227
|
header: "Revenue",
|
|
228
228
|
align: "end",
|
|
229
229
|
sortable: true,
|
|
230
|
-
sortValue: (row) => row.
|
|
231
|
-
cell: (row) => row.
|
|
230
|
+
sortValue: (row) => row.used * row.price,
|
|
231
|
+
cell: (row) => row.used > 0 ? /* @__PURE__ */ jsx("span", { className: "text-mono-sm", children: usd.format(row.used * row.price) }) : /* @__PURE__ */ jsx("span", { className: "text-mono-sm text-muted-foreground", children: "\u2014" })
|
|
232
232
|
},
|
|
233
233
|
{
|
|
234
|
-
key: "
|
|
235
|
-
header: "
|
|
234
|
+
key: "started",
|
|
235
|
+
header: "Started",
|
|
236
236
|
align: "end",
|
|
237
237
|
sortable: true,
|
|
238
|
-
cell: (row) => /* @__PURE__ */ jsx("span", { className: "text-body-sm whitespace-nowrap text-muted-foreground", children: shortDate(row.
|
|
238
|
+
cell: (row) => /* @__PURE__ */ jsx("span", { className: "text-body-sm whitespace-nowrap text-muted-foreground", children: shortDate(row.started) })
|
|
239
239
|
}
|
|
240
240
|
]
|
|
241
241
|
}
|
|
@@ -243,13 +243,13 @@ function DashboardPage() {
|
|
|
243
243
|
/* @__PURE__ */ jsxs(CardFooter, { className: "justify-between", children: [
|
|
244
244
|
/* @__PURE__ */ jsxs("span", { className: "text-caption text-muted-foreground", children: [
|
|
245
245
|
"Showing ",
|
|
246
|
-
|
|
246
|
+
accounts.length,
|
|
247
247
|
" of ",
|
|
248
|
-
|
|
249
|
-
"
|
|
248
|
+
accountCount,
|
|
249
|
+
" accounts"
|
|
250
250
|
] }),
|
|
251
|
-
/* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", bleed: "end", render: /* @__PURE__ */ jsx("a", { href: "/
|
|
252
|
-
"View all
|
|
251
|
+
/* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", bleed: "end", render: /* @__PURE__ */ jsx("a", { href: "/accounts" }), children: [
|
|
252
|
+
"View all accounts",
|
|
253
253
|
/* @__PURE__ */ jsx(Icon, { name: "arrow-right" })
|
|
254
254
|
] })
|
|
255
255
|
] })
|
|
@@ -268,16 +268,16 @@ function DashboardPage() {
|
|
|
268
268
|
] }),
|
|
269
269
|
/* @__PURE__ */ jsxs(Card, { children: [
|
|
270
270
|
/* @__PURE__ */ jsxs(CardHeader, { children: [
|
|
271
|
-
/* @__PURE__ */ jsx(CardTitle, { children: "Top
|
|
271
|
+
/* @__PURE__ */ jsx(CardTitle, { children: "Top customers" }),
|
|
272
272
|
/* @__PURE__ */ jsx(CardDescription, { children: "By spend this month." }),
|
|
273
|
-
/* @__PURE__ */ jsx(CardAction, { children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", render: /* @__PURE__ */ jsx("a", { href: "/
|
|
273
|
+
/* @__PURE__ */ jsx(CardAction, { children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", render: /* @__PURE__ */ jsx("a", { href: "/customers" }), children: "View all" }) })
|
|
274
274
|
] }),
|
|
275
|
-
/* @__PURE__ */ jsx(CardList, { children:
|
|
275
|
+
/* @__PURE__ */ jsx(CardList, { children: customers.map((person) => /* @__PURE__ */ jsx(
|
|
276
276
|
Entity,
|
|
277
277
|
{
|
|
278
278
|
leading: /* @__PURE__ */ jsx(Avatar, { variant: "art", size: "sm", fallback: person.initials, alt: "" }),
|
|
279
279
|
title: person.name,
|
|
280
|
-
subtitle: `${person.handle} \xB7 ${person.
|
|
280
|
+
subtitle: `${person.handle} \xB7 ${person.seats} seats`,
|
|
281
281
|
action: /* @__PURE__ */ jsx("span", { className: "text-mono-sm", children: usd.format(person.spent) })
|
|
282
282
|
},
|
|
283
283
|
person.id
|
|
@@ -289,7 +289,7 @@ function DashboardPage() {
|
|
|
289
289
|
/* @__PURE__ */ jsxs(Card, { className: "flex flex-col", children: [
|
|
290
290
|
/* @__PURE__ */ jsxs(CardHeader, { children: [
|
|
291
291
|
/* @__PURE__ */ jsx(CardTitle, { children: "Upcoming releases" }),
|
|
292
|
-
/* @__PURE__ */ jsx(CardDescription, { children: "Scheduled to
|
|
292
|
+
/* @__PURE__ */ jsx(CardDescription, { children: "Scheduled to ship to customers." })
|
|
293
293
|
] }),
|
|
294
294
|
/* @__PURE__ */ jsx(CardList, { children: upcoming.map((release) => /* @__PURE__ */ jsx(
|
|
295
295
|
Entity,
|
|
@@ -300,7 +300,7 @@ function DashboardPage() {
|
|
|
300
300
|
},
|
|
301
301
|
release.id
|
|
302
302
|
)) }),
|
|
303
|
-
/* @__PURE__ */ jsx(CardFooter, { className: "mt-auto", children: /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", render: /* @__PURE__ */ jsx("a", { href: "/
|
|
303
|
+
/* @__PURE__ */ jsx(CardFooter, { className: "mt-auto", children: /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", render: /* @__PURE__ */ jsx("a", { href: "/releases/new" }), children: [
|
|
304
304
|
/* @__PURE__ */ jsx(Icon, { name: "calendar" }),
|
|
305
305
|
"Schedule a release"
|
|
306
306
|
] }) })
|
|
@@ -308,7 +308,7 @@ function DashboardPage() {
|
|
|
308
308
|
/* @__PURE__ */ jsxs(Card, { className: "xl:col-span-2", children: [
|
|
309
309
|
/* @__PURE__ */ jsxs(CardHeader, { children: [
|
|
310
310
|
/* @__PURE__ */ jsx(CardTitle, { children: "Activity" }),
|
|
311
|
-
/* @__PURE__ */ jsx(CardDescription, { children: "
|
|
311
|
+
/* @__PURE__ */ jsx(CardDescription, { children: "Seats, upgrades, and invoices." }),
|
|
312
312
|
/* @__PURE__ */ jsx(CardAction, { children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", render: /* @__PURE__ */ jsx("a", { href: "/activity" }), children: "View all" }) })
|
|
313
313
|
] }),
|
|
314
314
|
/* @__PURE__ */ jsx(CardList, { children: activity.map((item) => /* @__PURE__ */ jsx(
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
// src/patterns/dashboard-page.meta.ts
|
|
2
2
|
var dashboardPageMeta = {
|
|
3
3
|
name: "DashboardPage",
|
|
4
|
-
summary: "App-shell
|
|
5
|
-
intent: "Reach for this when a route's job is monitoring or managing many records (sales, orders,
|
|
4
|
+
summary: "App-shell admin dashboard: a branded Sidebar with an account footer (AppHeader + BottomNav below lg), a KPI row, an edge-to-edge accounts table with seat-usage Meters, and supporting payout, top-customer, release and activity cards \u2014 the default shape for an operator or analytics home route.",
|
|
5
|
+
intent: "Reach for this when a route's job is monitoring or managing many records (sales, orders, accounts, posts) with a few headline numbers. Not for reading-focused routes (use FeedPage) or single-record routes (use ListDetailPage's detail side).",
|
|
6
6
|
notes: [
|
|
7
7
|
"Region cap is `wide` (1280px): dashboards hold tables and multi-column grids, never a reading column.",
|
|
8
|
-
"Hierarchy runs headline \u2192 records \u2192 context: KPI row, then the primary table beside its two closest companions (payout, top
|
|
8
|
+
"Hierarchy runs headline \u2192 records \u2192 context: KPI row, then the primary table beside its two closest companions (payout, top customers), then the lower-priority lists (upcoming, activity). Every row of the grid is filled so the route ends on content, not empty canvas.",
|
|
9
9
|
"Stat cards: CardHeader carries the label (CardDescription) and the value; KPI values are `text-mono-xl` \u2014 the metric mono tier, tabular figures \u2014 never a heading tier. The trend Badge sits with its comparison note in CardContent, so the value keeps the full card width when the row folds to 2\xD72 on a phone.",
|
|
10
10
|
"Stat cards use an explicit cols map (2 \u2192 4) \u2014 a 4-stat row must fold to 2\xD72, never strand an orphan the way auto-fit can.",
|
|
11
11
|
"The table runs flush to the card edges with `<CardContent flush>`, which hands the card's padding to the table (`--table-edge-px`), so the first and last columns line up with the CardHeader at any card density \u2014 never hand-set pl-6/pr-6. Numeric columns align end in mono; Table scrolls horizontally inside the card on narrow screens.",
|
|
12
|
-
'
|
|
12
|
+
'Seats used / seats is a `Meter` (how full a known range is), not `Progress` (task progress). Status is `Badge appearance="plain" dot`, with the app\'s own states mapped to tones once at the top of the file.',
|
|
13
13
|
"Lists are Entity rows in CardList, inset to the card padding and divided by hairlines \u2014 never CardContent with hand-tuned px. A card that shares a grid row with a taller one becomes `flex flex-col` and pins its CardFooter with `mt-auto`.",
|
|
14
14
|
"Brand and account live in the Sidebar: SidebarHeader holds the mark + wordmark, SidebarFooter holds the signed-in Entity with an OverflowMenu. Sidebar rebinds the page ink tokens to its own, so the Entity and OverflowMenu read correctly even when a theme paints the rail dark on a light page.",
|
|
15
15
|
"The page has exactly one heading-1 (PageHeader); card titles are Title tier, not headings.",
|
|
@@ -20,7 +20,7 @@ var dashboardPageMeta = {
|
|
|
20
20
|
'AppShell provides the skip link and <main id="main-content"> landmark.',
|
|
21
21
|
"The active SidebarItem and BottomNavItem carry aria-current via `active`.",
|
|
22
22
|
"DataTable headers are real <th>; sortable columns are announced.",
|
|
23
|
-
'Each
|
|
23
|
+
'Each seat-usage Meter has an aria-label naming the account and aria-valuetext reading "184 of 250 seats used".',
|
|
24
24
|
"Avatars beside a visible name use an empty alt so the name is not read twice."
|
|
25
25
|
]
|
|
26
26
|
};
|
|
@@ -22,9 +22,9 @@ var posts = [
|
|
|
22
22
|
initials: "AL",
|
|
23
23
|
time: "2h",
|
|
24
24
|
following: true,
|
|
25
|
-
body: "
|
|
26
|
-
media: { src: "/media/edition-cover.svg", alt: "
|
|
27
|
-
|
|
25
|
+
body: "Registration for the spring workshop is open. Two days of plotter drawing in a small group. See you there.",
|
|
26
|
+
media: { src: "/media/edition-cover.svg", alt: "Workshop poster, a gold-leaf study in overlapping arcs" },
|
|
27
|
+
event: { title: "Spring plotter workshop", price: "$120", registered: 28, capacity: 40 },
|
|
28
28
|
likes: 128,
|
|
29
29
|
comments: 24
|
|
30
30
|
},
|
|
@@ -47,7 +47,7 @@ var posts = [
|
|
|
47
47
|
initials: "NS",
|
|
48
48
|
time: "8h",
|
|
49
49
|
following: false,
|
|
50
|
-
body: "
|
|
50
|
+
body: "Thanks to everyone at the Dusk listening session \u2014 the full recording is now in your library. Headphones on.",
|
|
51
51
|
likes: 214,
|
|
52
52
|
comments: 31
|
|
53
53
|
}
|
|
@@ -57,8 +57,8 @@ var suggestions = [
|
|
|
57
57
|
{ id: "u_02", name: "Nina Simone", handle: "@nina", initials: "NS" }
|
|
58
58
|
];
|
|
59
59
|
var upcoming = [
|
|
60
|
-
{ id: "
|
|
61
|
-
{ id: "
|
|
60
|
+
{ id: "e_01", title: "Quiet Harbor talk", host: "Mary Shelley", initials: "MS", date: "Sat, Oct 12" },
|
|
61
|
+
{ id: "e_02", title: "Dusk, live", host: "Nina Simone", initials: "NS", date: "Tue, Oct 15" }
|
|
62
62
|
];
|
|
63
63
|
var renderPost = (post) => /* @__PURE__ */ jsx("article", { children: /* @__PURE__ */ jsxs(Card, { children: [
|
|
64
64
|
/* @__PURE__ */ jsx(CardList, { children: /* @__PURE__ */ jsx(
|
|
@@ -82,27 +82,27 @@ var renderPost = (post) => /* @__PURE__ */ jsx("article", { children: /* @__PURE
|
|
|
82
82
|
/* @__PURE__ */ jsx("p", { className: "text-body-md text-foreground", children: post.body }),
|
|
83
83
|
post.media ? /* @__PURE__ */ jsxs(AspectBox, { ratio: 16 / 9, className: "overflow-hidden rounded-concentric-lg/6 bg-muted", children: [
|
|
84
84
|
/* @__PURE__ */ jsx("img", { src: post.media.src, alt: post.media.alt, className: "size-full object-cover" }),
|
|
85
|
-
post.
|
|
85
|
+
post.event ? /* @__PURE__ */ jsx("div", { className: "absolute start-3 top-3", children: /* @__PURE__ */ jsx(Badge, { appearance: "overlay", size: "sm", dot: true, children: "Open now" }) }) : null
|
|
86
86
|
] }) : null,
|
|
87
|
-
post.
|
|
87
|
+
post.event ? /* @__PURE__ */ jsxs(Stack, { gap: 1.5, children: [
|
|
88
88
|
/* @__PURE__ */ jsxs(Row, { gap: 2, align: "center", justify: "between", children: [
|
|
89
89
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
90
|
-
/* @__PURE__ */ jsx("p", { className: "text-title-md text-foreground", children: post.
|
|
91
|
-
/* @__PURE__ */ jsx("p", { className: "text-body-sm text-muted-foreground", children: `${post.
|
|
90
|
+
/* @__PURE__ */ jsx("p", { className: "text-title-md text-foreground", children: post.event.title }),
|
|
91
|
+
/* @__PURE__ */ jsx("p", { className: "text-body-sm text-muted-foreground", children: `${post.event.price} \xB7 ${post.event.capacity} seats` })
|
|
92
92
|
] }),
|
|
93
|
-
/* @__PURE__ */ jsx(Button, { children: `
|
|
93
|
+
/* @__PURE__ */ jsx(Button, { children: `Register for ${post.event.price}` })
|
|
94
94
|
] }),
|
|
95
95
|
/* @__PURE__ */ jsxs(
|
|
96
96
|
Meter,
|
|
97
97
|
{
|
|
98
|
-
value: post.
|
|
99
|
-
max: post.
|
|
98
|
+
value: post.event.registered,
|
|
99
|
+
max: post.event.capacity,
|
|
100
100
|
size: "sm",
|
|
101
|
-
getAriaValueText: (_, value) => `${value} of ${post.
|
|
101
|
+
getAriaValueText: (_, value) => `${value} of ${post.event?.capacity} seats taken`,
|
|
102
102
|
children: [
|
|
103
103
|
/* @__PURE__ */ jsxs(Row, { gap: 2, align: "center", justify: "between", className: "mb-2", children: [
|
|
104
|
-
/* @__PURE__ */ jsx(MeterLabel, { children: `${post.
|
|
105
|
-
/* @__PURE__ */ jsx(MeterValue, { children: (_, value) => `${(post.
|
|
104
|
+
/* @__PURE__ */ jsx(MeterLabel, { children: `${post.event.registered} registered` }),
|
|
105
|
+
/* @__PURE__ */ jsx(MeterValue, { children: (_, value) => `${(post.event?.capacity ?? 0) - value} left` })
|
|
106
106
|
] }),
|
|
107
107
|
/* @__PURE__ */ jsx(MeterTrack, {})
|
|
108
108
|
]
|
|
@@ -135,13 +135,13 @@ function FeedPage() {
|
|
|
135
135
|
autoResize: true,
|
|
136
136
|
rows: 2,
|
|
137
137
|
"aria-label": "New post",
|
|
138
|
-
placeholder: "Share a process shot or an upcoming
|
|
138
|
+
placeholder: "Share a process shot or an upcoming event"
|
|
139
139
|
}
|
|
140
140
|
),
|
|
141
141
|
/* @__PURE__ */ jsxs(Row, { gap: 1, align: "center", justify: "between", children: [
|
|
142
142
|
/* @__PURE__ */ jsxs(Row, { gap: 0.5, children: [
|
|
143
143
|
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Add images", children: /* @__PURE__ */ jsx(Icon, { name: "image-stack" }) }),
|
|
144
|
-
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "
|
|
144
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Add an event", children: /* @__PURE__ */ jsx(Icon, { name: "calendar" }) })
|
|
145
145
|
] }),
|
|
146
146
|
/* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", children: "Post" })
|
|
147
147
|
] })
|
|
@@ -185,16 +185,16 @@ function FeedPage() {
|
|
|
185
185
|
)) })
|
|
186
186
|
] }),
|
|
187
187
|
/* @__PURE__ */ jsxs(Card, { children: [
|
|
188
|
-
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsx(CardTitle, { render: /* @__PURE__ */ jsx("h2", {}), children: "Upcoming
|
|
189
|
-
/* @__PURE__ */ jsx(CardList, { children: upcoming.map((
|
|
188
|
+
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsx(CardTitle, { render: /* @__PURE__ */ jsx("h2", {}), children: "Upcoming events" }) }),
|
|
189
|
+
/* @__PURE__ */ jsx(CardList, { children: upcoming.map((event) => /* @__PURE__ */ jsx(
|
|
190
190
|
Entity,
|
|
191
191
|
{
|
|
192
|
-
leading: /* @__PURE__ */ jsx(Avatar, { variant: "art", fallback:
|
|
193
|
-
title:
|
|
194
|
-
subtitle: `${
|
|
195
|
-
action: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": `Remind me about ${
|
|
192
|
+
leading: /* @__PURE__ */ jsx(Avatar, { variant: "art", fallback: event.initials, alt: event.host }),
|
|
193
|
+
title: event.title,
|
|
194
|
+
subtitle: `${event.host} \xB7 ${event.date}`,
|
|
195
|
+
action: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": `Remind me about ${event.title}`, children: /* @__PURE__ */ jsx(Icon, { name: "bell" }) })
|
|
196
196
|
},
|
|
197
|
-
|
|
197
|
+
event.id
|
|
198
198
|
)) })
|
|
199
199
|
] })
|
|
200
200
|
] }) })
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// src/patterns/feed-page.meta.ts
|
|
2
2
|
var feedPageMeta = {
|
|
3
3
|
name: "FeedPage",
|
|
4
|
-
summary: "Social reading feed with a side rail: a feed-width main column (composer, For you / Following Tabs, one Card per post, an
|
|
4
|
+
summary: "Social reading feed with a side rail: a feed-width main column (composer, For you / Following Tabs, one Card per post, an event post with price, capacity Meter and Register, a Skeleton for the next page) beside a sticky rail of who-to-follow and upcoming events that drops away below lg.",
|
|
5
5
|
intent: "Reach for this for timelines, activity streams, and any reverse-chronological reading surface where people also act on what they read. Not for record management (DashboardPage) or side-by-side browsing (ListDetailPage).",
|
|
6
6
|
notes: [
|
|
7
7
|
"The main column is capped at `--region-feed` (640px) inside a `wide` page, so posts keep a reading measure while the rail fills the desktop width. The rail is a fixed `w-80` aside, `sticky` at lg and `hidden` below it \u2014 its content is supplementary, so nothing is lost on mobile.",
|
|
8
8
|
"Feed scope is `Tabs` (underline, stretched to the column with `flex`): peer views of the same feed. The composer sits above them because posting applies to both.",
|
|
9
9
|
"The composer is a Card holding `Textarea autoResize` plus ghost icon Buttons and a secondary `Post` \u2014 not ChatComposer, which submits on Enter and is for conversations.",
|
|
10
10
|
"Each post is a Card: an Entity byline in a CardList (with an OverflowMenu for post actions), body copy, then media in an AspectBox at the asset's own ratio, inset with `rounded-concentric-lg/6` so its corners follow the card's in every theme.",
|
|
11
|
-
'An
|
|
11
|
+
'An event post adds a `Badge appearance="overlay"` on the image, title and price, the one primary Button on the page (`Register`), and a `Meter` for capacity \u2014 seats taken against a known maximum is a level, not task progress.',
|
|
12
12
|
"Actions are ghost sm Buttons (icon + count) in a Row pulled out by the button's own padding (`-ms-3`) so the first icon sits on the text edge.",
|
|
13
13
|
"Loading the next page shows a Skeleton post in the shape the post will land in, above the explicit load-more Button in its `loading` state \u2014 predictable for keyboard and AT users, unlike infinite scroll."
|
|
14
14
|
],
|
|
@@ -16,7 +16,7 @@ var feedPageMeta = {
|
|
|
16
16
|
"Each post is an <article>, so AT users can jump between posts; the rail is a labelled <aside> landmark.",
|
|
17
17
|
"Rail card titles render as h2 (CardTitle `render={<h2 />}`) under the page's one h1.",
|
|
18
18
|
"Icon-only buttons and every Follow / Remind button carry aria-labels naming their target; counts stay in the accessible name.",
|
|
19
|
-
'The
|
|
19
|
+
'The capacity Meter is role=meter with a getAriaValueText reading ("28 of 40 seats taken").',
|
|
20
20
|
'The Skeleton card is aria-busy; the loading Button keeps its label ("Loading posts").'
|
|
21
21
|
]
|
|
22
22
|
};
|
|
@@ -223,7 +223,7 @@ function LandingPage() {
|
|
|
223
223
|
/* @__PURE__ */ jsx(CardTitle, { children: "Build minutes" }),
|
|
224
224
|
/* @__PURE__ */ jsx(CardDescription, { children: "Team plan" })
|
|
225
225
|
] }),
|
|
226
|
-
/* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsxs(Meter, { value: 64, tone: "
|
|
226
|
+
/* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsxs(Meter, { value: 64, tone: "3", children: [
|
|
227
227
|
/* @__PURE__ */ jsxs(Row, { justify: "between", wrap: true, className: "mb-2", children: [
|
|
228
228
|
/* @__PURE__ */ jsx(MeterLabel, { children: "Used" }),
|
|
229
229
|
/* @__PURE__ */ jsx(MeterValue, { children: (_, value) => `${(value * 30).toLocaleString("en-US")} of 3,000` })
|