@alexgorbatchev/shadcn 0.0.38 → 0.0.42

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.
Files changed (39) hide show
  1. package/README.md +35 -1
  2. package/THIRD_PARTY_NOTICES.md +4 -0
  3. package/dist/{chunk-6grcm62s.js → chunk-1hj251b8.js} +1 -1
  4. package/dist/{chunk-f71469fs.js → chunk-4stkzcvt.js} +550 -433
  5. package/dist/{chunk-r9qrb6b6.js → chunk-5bw5h08v.js} +1 -1
  6. package/dist/{chunk-kmvvw1vr.js → chunk-sb00e3gj.js} +1 -1
  7. package/dist/components/DataViewer.d.ts +16 -0
  8. package/dist/components/data-grid/index.js +2 -2
  9. package/dist/components/gantt/index.js +2 -2
  10. package/dist/components/json-render/index.js +3 -3
  11. package/dist/components/virtual-data-grid/index.js +2 -2
  12. package/dist/index.d.ts +2 -0
  13. package/dist/index.js +7 -3
  14. package/dist/prototype.js +3665 -0
  15. package/licenses/commander-MIT.txt +22 -0
  16. package/llms/components/data-display/DataViewer.md +90 -0
  17. package/llms/components/forms/NativeSelect.md +8 -0
  18. package/llms/setup.md +35 -1
  19. package/llms.txt +1 -0
  20. package/package.json +9 -3
  21. package/templates/prototype/AGENTS.md +23 -0
  22. package/templates/prototype/gitignore +5 -0
  23. package/templates/prototype/index.html +12 -0
  24. package/templates/prototype/justfile +18 -0
  25. package/templates/prototype/src/components/ScenarioContent.tsx +41 -0
  26. package/templates/prototype/src/components/Workbench.tsx +132 -0
  27. package/templates/prototype/src/main.tsx +12 -0
  28. package/templates/prototype/src/prototypes/dashboard/components/Dashboard.tsx +80 -0
  29. package/templates/prototype/src/prototypes/dashboard/data.ts +5 -0
  30. package/templates/prototype/src/prototypes/dashboard/prototype.ts +9 -0
  31. package/templates/prototype/src/prototypes/projects/components/Projects.tsx +151 -0
  32. package/templates/prototype/src/prototypes/projects/data.ts +5 -0
  33. package/templates/prototype/src/prototypes/projects/prototype.ts +9 -0
  34. package/templates/prototype/src/prototypes/settings/components/Settings.tsx +72 -0
  35. package/templates/prototype/src/prototypes/settings/prototype.ts +9 -0
  36. package/templates/prototype/src/styles.css +7 -0
  37. package/templates/prototype/src/types.ts +9 -0
  38. package/templates/prototype/tsconfig.json +13 -0
  39. package/templates/prototype/vite.config.ts +8 -0
@@ -0,0 +1,22 @@
1
+ (The MIT License)
2
+
3
+ Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ 'Software'), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,90 @@
1
+ # DataViewer
2
+
3
+ Story: @alexgorbatchev/shadcn/components/data-display/DataViewer
4
+ Source: src/components/stories/data-display/DataViewer.stories.tsx
5
+
6
+ [Full source and local helpers](https://github.com/alexgorbatchev/shadcn/blob/main/src/components/stories/data-display/DataViewer.stories.tsx)
7
+
8
+ ## Public imports
9
+
10
+ ```tsx
11
+ import { DataViewer, formatDataLabel } from "@alexgorbatchev/shadcn";
12
+ ```
13
+
14
+ ## Default component
15
+
16
+ ```tsx
17
+ DataViewer
18
+ ```
19
+
20
+ ## Default args
21
+
22
+ ```tsx
23
+ { label: "Result" }
24
+ ```
25
+
26
+ ## Examples
27
+
28
+ Storybook CSF excerpts. Default args and render apply unless overridden. Local helpers, fixtures, decorators, and providers may be required; these are reference excerpts, not standalone consumer modules. Interaction tests are omitted.
29
+
30
+ ### TrackResult
31
+
32
+ ```tsx
33
+ {
34
+ args: {
35
+ value: {
36
+ track_id: 42,
37
+ playlistIds: [3, 12],
38
+ original_path: "/Music/First light.flac",
39
+ timing_cleared: false,
40
+ reanalysis_required: true,
41
+ album: null,
42
+ comment: "",
43
+ },
44
+ }
45
+ }
46
+ ```
47
+
48
+ ### NestedRecords
49
+
50
+ ```tsx
51
+ {
52
+ args: {
53
+ value: {
54
+ imported: [
55
+ { id: "staged-1", track_id: 7 },
56
+ { id: "staged-2", track_id: 8 },
57
+ ],
58
+ failed: [],
59
+ source: { percent: 50, metadata: {} },
60
+ },
61
+ }
62
+ }
63
+ ```
64
+
65
+ ### PlainValue
66
+
67
+ ```tsx
68
+ {
69
+ args: { value: "Finished", label: "Status" }
70
+ }
71
+ ```
72
+
73
+ ### Empty
74
+
75
+ ```tsx
76
+ {
77
+ args: { value: undefined }
78
+ }
79
+ ```
80
+
81
+ ### CustomLabels
82
+
83
+ ```tsx
84
+ {
85
+ args: {
86
+ value: { bpm: 128, source_url: "https://example.com/track" },
87
+ formatLabel: (key) => (key === "bpm" ? "Tempo (BPM)" : formatDataLabel(key)),
88
+ }
89
+ }
90
+ ```
@@ -69,6 +69,14 @@ Storybook CSF excerpts. Default args and render apply unless overridden. Local h
69
69
  }
70
70
  ```
71
71
 
72
+ ### CompactSmall
73
+
74
+ ```tsx
75
+ {
76
+ args: { density: "compact", size: "sm" }
77
+ }
78
+ ```
79
+
72
80
  ### Disabled
73
81
 
74
82
  ```tsx
package/llms/setup.md CHANGED
@@ -194,6 +194,40 @@ Override tokens after the theme imports. This example uses the existing success
194
194
  }
195
195
  ```
196
196
 
197
+ # Local React Prototypes
198
+
199
+ The package includes `shadcn-prototype`, a local runner for editable React mockups. Its generated workspace includes a preview host and complete dashboard, editable project directory, and settings recipes. Helpers and sample data are included; the recipes do not import Storybook files.
200
+
201
+ 1. Create a workspace using Bun:
202
+
203
+ ```sh
204
+ bunx --bun --package @alexgorbatchev/shadcn shadcn-prototype workspace create ./mockups
205
+ ```
206
+
207
+ 2. Install its declared dependencies and start the preview:
208
+
209
+ ```sh
210
+ cd mockups
211
+ bun install
212
+ bun node_modules/.bin/shadcn-prototype workspace start .
213
+ ```
214
+
215
+ 3. Edit a recipe under `src/prototypes/`, or copy a recipe folder and update its `prototype.ts`. The preview discovers each folder automatically. Use the workbench controls for scenarios, themes, density, viewport sizes, and reset. Open screen gives a direct URL to the canvas.
216
+
217
+ 4. Run `just check` in the generated workspace to typecheck and build. `dist/` contains the static site. Preview requires Bun; the `just` recipes require [just](https://just.systems).
218
+
219
+ The runner creates a new directory and rejects existing destinations. It does not install dependencies automatically. The generated manifest pins this package to the runner's version and declares the libraries used by the recipes and preview. The component package's dependency-installation contract is unchanged. State is in memory and resets when the canvas reloads; the recipes do not contact real services.
220
+
221
+ | Command | Options | Behavior |
222
+ | ------------------------------ | -------------------------------------------------- | ----------------------------------------------------------------------------------- |
223
+ | `workspace create <directory>` | `-h, --help` | Write a complete workspace to a new directory. |
224
+ | `workspace start [directory]` | `-p, --port <number>` (default 5173), `-h, --help` | Run the local preview; directory defaults to `.`. Port 0 chooses an available port. |
225
+ | `workspace build [directory]` | `-h, --help` | Build static output; directory defaults to `.`. |
226
+ | `skill` | `-h, --help` | Print the agent skill installation instructions. |
227
+ | Root | `-V, --version`, `-h, --help` | Show the package version or command tree. |
228
+
229
+ Use `AGENT=1` for concise agent output. Read the [prototype agent skill](https://github.com/alexgorbatchev/shadcn/blob/main/skills/shadcn-prototype/SKILL.md) for the complete workflow, URL parameters, environment, and error handling. The generated recipes are runnable consumer examples; the Storybook excerpts below remain component references.
230
+
197
231
  # LLM Documentation
198
232
 
199
233
  Storybook serves a generated `llms.txt` index at its site root, with linked setup instructions and a reference page for each story file. Pages contain public package imports, documented story controls, default args, and Storybook CSF examples with interaction tests omitted. The excerpts can depend on local helpers and fixtures; each page links to its full source. Story controls describe the configured controls, rather than the complete TypeScript props API.
@@ -364,7 +398,7 @@ The preview shows compact individual badges, up to five labels followed by `+N m
364
398
 
365
399
  `useDropTarget().feedback` retains the last accepted MIME type, Alt modifier, and drop effect after leaving or dropping. Use this snapshot for hint content that remains visible during an exit animation; use `isOver` to control visibility and active highlighting. New accepted drag events update feedback immediately, including modifier changes. The live `type`, `isAlt`, and `effect` fields still describe the current hover.
366
400
 
367
- `EmptyState` composes a title, description, and optional child actions. `JsonViewer` displays a read-only formatted JSON value with configurable `label` and `rows`.
401
+ `EmptyState` composes a title, description, and optional child actions. `DataViewer` presents JSON-compatible data for people: records become labelled terms, lists of plain values read on one line, lists of records are numbered, booleans read Yes or No, and missing or empty values read None or Empty. Keys become sentence-case labels with common acronyms (`track_id` reads "Track ID"); pass `formatLabel` to name domain keys, falling back to the exported `formatDataLabel`. `JsonViewer` displays a read-only formatted JSON value with configurable `label` and `rows` for people who need the JSON text itself.
368
402
 
369
403
  # Devhost Preview
370
404
 
package/llms.txt CHANGED
@@ -336,6 +336,7 @@ Generated from the public package exports and Storybook sources.
336
336
  - [DataGridColumnOptions](./llms/components/data-display/DataGridColumnOptions.md)
337
337
  - [DataGridFilterSummary](./llms/components/data-display/DataGridFilterSummary.md)
338
338
  - [DataGridFilters](./llms/components/data-display/DataGridFilters.md)
339
+ - [DataViewer](./llms/components/data-display/DataViewer.md)
339
340
  - [Item](./llms/components/data-display/Item.md)
340
341
  - [JsonRender](./llms/components/data-display/JsonRender.md)
341
342
  - [JsonViewer](./llms/components/data-display/JsonViewer.md)
package/package.json CHANGED
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "name": "@alexgorbatchev/shadcn",
3
- "version": "0.0.38",
3
+ "version": "0.0.42",
4
4
  "description": "Sharp, monospace React components with compact and comfortable densities.",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/alexgorbatchev/shadcn.git"
9
9
  },
10
+ "bin": {
11
+ "shadcn-prototype": "dist/prototype.js"
12
+ },
10
13
  "files": [
11
14
  "dist",
12
15
  "llms",
@@ -15,7 +18,8 @@
15
18
  "LICENSE",
16
19
  "THIRD_PARTY_NOTICES.md",
17
20
  "assistant-ui-catalog.json",
18
- "licenses"
21
+ "licenses",
22
+ "templates/prototype"
19
23
  ],
20
24
  "type": "module",
21
25
  "sideEffects": [
@@ -1050,12 +1054,13 @@
1050
1054
  "test": "vitest run --config .storybook/vitest.config.ts && bun run test:native && bun run test:package",
1051
1055
  "test:package": "bun run build && bun test scripts/__tests__ ./.storybook/__tests__/createStorybookViteConfig.test.ts",
1052
1056
  "test:preview": "bun test scripts/storybookPreview.test.ts",
1057
+ "test:prototype": "bun test --config=./cli/bunfig.toml --coverage cli && vitest run --config .storybook/vitest.prototype.config.ts --coverage",
1053
1058
  "test:native": "vitest run --config .storybook/vitest.native.config.ts",
1054
1059
  "test:coverage": "vitest run --config .storybook/vitest.coverage.config.ts --coverage",
1055
1060
  "test:contrast": "vitest run --config .storybook/vitest.contrast.config.ts",
1056
1061
  "build": "bun scripts/build.ts",
1057
1062
  "typecheck": "tsc --noEmit",
1058
- "lint": "oxlint --deny-warnings src .storybook scripts && oxfmt --check . && bun run lint:unused",
1063
+ "lint": "oxlint --deny-warnings src .storybook scripts cli templates/prototype/src templates/prototype/vite.config.ts && oxfmt --check . && bun run lint:unused",
1059
1064
  "lint:unused": "ts-unused check tsconfig.json",
1060
1065
  "storybook": "bun scripts/buildLlmDocs.ts && storybook dev --host 127.0.0.1 -p 6006 --no-open",
1061
1066
  "build-storybook": "bun scripts/buildLlmDocs.ts && storybook build",
@@ -1118,6 +1123,7 @@
1118
1123
  "clsx": "^2.1.1",
1119
1124
  "cmdk": "^1.1.1",
1120
1125
  "colorjs.io": "0.7.1",
1126
+ "commander": "^15.0.0",
1121
1127
  "cssesc": "3.0.0",
1122
1128
  "date-fns": "4.4.0",
1123
1129
  "diff": "9.0.0",
@@ -0,0 +1,23 @@
1
+ # Prototype workspace
2
+
3
+ Build interactive React screens using the installed `@alexgorbatchev/shadcn` package.
4
+
5
+ ## Commands
6
+
7
+ - Install: `just setup`
8
+ - Local preview: `just run`
9
+ - Agent preview: `just run-ai`
10
+ - Typecheck and production build: `just check`
11
+ - Static output: `just build` writes `dist/`
12
+
13
+ Read `node_modules/@alexgorbatchev/shadcn/llms.txt` before using components. Resolve its relative links from the package directory.
14
+
15
+ ## Authoring
16
+
17
+ - Each folder under `src/prototypes/` has a `prototype.ts` default export containing a title, description, and React component. The folder name is the prototype identifier. Copy a recipe folder to add a screen; Vite discovers it automatically.
18
+ - Keep components under the recipe's `components/` directory and sample data beside them. Shared host components and scenario rendering live in `src/components/`; all helpers are included locally.
19
+ - Compose the package's public components and semantic tokens. Install optional feature peers before importing their entry points. Keep reusable component capabilities in the library instead of overriding its internals.
20
+ - `PrototypeProps.scenario` selects populated, empty, loading, or error. Extend the example state handling to match the requested flow. Implement controls and state transitions; declare any simulated backend behavior in the delivery notes.
21
+ - The host renders each screen in an iframe so responsive styles and portals use its actual viewport. Open screen exposes a direct URL. Use one ThemeProvider per document; the host supplies theme and density to the canvas.
22
+ - Recipe state is in memory. Reset data, changing preview settings, or reloading the canvas restores the fixtures. Implement persistence only when required by the prototype.
23
+ - Validate primary interactions, form errors, empty/loading/error states, light/dark themes, both densities, and mobile sizing in a browser. Run `just check` before delivery and report any failures or unverified behavior.
@@ -0,0 +1,5 @@
1
+ node_modules/
2
+ dist/
3
+ .env.local
4
+ .tmp/
5
+ .cache/
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Prototype workbench</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/main.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,18 @@
1
+ default:
2
+ @just --list
3
+
4
+ setup:
5
+ bun install
6
+
7
+ run:
8
+ bun node_modules/.bin/shadcn-prototype workspace start .
9
+
10
+ run-ai:
11
+ AGENT=1 bun node_modules/.bin/shadcn-prototype workspace start .
12
+
13
+ build:
14
+ bun node_modules/.bin/shadcn-prototype workspace build .
15
+
16
+ check:
17
+ bun node_modules/.bin/tsc --noEmit
18
+ just build
@@ -0,0 +1,41 @@
1
+ import type { ReactElement, ReactNode } from "react";
2
+ import {
3
+ Alert,
4
+ AlertDescription,
5
+ AlertTitle,
6
+ Empty,
7
+ EmptyHeader,
8
+ EmptyTitle,
9
+ EmptyDescription,
10
+ Spinner,
11
+ } from "@alexgorbatchev/shadcn";
12
+ import type { Scenario } from "../types";
13
+
14
+ type ScenarioContentProps = { scenario: Scenario; children: ReactNode };
15
+
16
+ export function ScenarioContent({ scenario, children }: ScenarioContentProps): ReactElement {
17
+ if (scenario === "loading")
18
+ return (
19
+ <div role="status" className="flex items-center gap-2">
20
+ <Spinner />
21
+ Loading sample data…
22
+ </div>
23
+ );
24
+ if (scenario === "error")
25
+ return (
26
+ <Alert variant="destructive">
27
+ <AlertTitle>Unable to load data</AlertTitle>
28
+ <AlertDescription>This is the error scenario. Choose Populated to recover.</AlertDescription>
29
+ </Alert>
30
+ );
31
+ if (scenario === "empty")
32
+ return (
33
+ <Empty>
34
+ <EmptyHeader>
35
+ <EmptyTitle>No data yet</EmptyTitle>
36
+ <EmptyDescription>This is the empty scenario. Choose Populated to explore the recipe.</EmptyDescription>
37
+ </EmptyHeader>
38
+ </Empty>
39
+ );
40
+ return <>{children}</>;
41
+ }
@@ -0,0 +1,132 @@
1
+ import { useState, type ReactElement } from "react";
2
+ import {
3
+ Button,
4
+ DensityProvider,
5
+ Field,
6
+ FieldGroup,
7
+ FieldLabel,
8
+ NativeSelect,
9
+ NativeSelectOption,
10
+ ThemeProvider,
11
+ } from "@alexgorbatchev/shadcn";
12
+ import type { PrototypeDefinition, Scenario } from "../types";
13
+
14
+ const prototypes = Object.entries(
15
+ import.meta.glob<PrototypeDefinition>("../prototypes/*/prototype.ts", { eager: true, import: "default" }),
16
+ ).map(([path, definition]) => ({ ...definition, id: path.split("/").at(-2) ?? "" }));
17
+
18
+ export function Workbench(): ReactElement {
19
+ const parameters = new URLSearchParams(window.location.search);
20
+ const [selected, setSelected] = useState(parameters.get("prototype") ?? "dashboard");
21
+ const [scenario, setScenario] = useState<Scenario>(() => {
22
+ const value = parameters.get("scenario");
23
+ return value === "empty" || value === "loading" || value === "error" ? value : "populated";
24
+ });
25
+ const [theme, setTheme] = useState<"light" | "dark">(parameters.get("theme") === "light" ? "light" : "dark");
26
+ const [density, setDensity] = useState<"comfortable" | "compact">(
27
+ parameters.get("density") === "compact" ? "compact" : "comfortable",
28
+ );
29
+ const [viewport, setViewport] = useState("responsive");
30
+ const [revision, setRevision] = useState(0);
31
+ const definition = prototypes.find((prototype) => prototype.id === selected);
32
+ const canvasParameters = new URLSearchParams({ canvas: "1", prototype: selected, scenario, theme, density });
33
+ const Component = definition?.component;
34
+ const isCanvas = parameters.get("canvas") === "1";
35
+
36
+ return (
37
+ <ThemeProvider forcedTheme={theme}>
38
+ <DensityProvider density={density}>
39
+ {isCanvas ? (
40
+ Component ? (
41
+ <Component scenario={scenario} />
42
+ ) : (
43
+ <p role="alert">Unknown prototype: {selected}</p>
44
+ )
45
+ ) : (
46
+ <div className="flex h-dvh flex-col" data-scroll="none">
47
+ <header className="shrink-0 border-b border-border p-4" data-scroll="none">
48
+ <h1 className="mb-3 text-lg font-semibold">Prototype workbench</h1>
49
+ <FieldGroup>
50
+ <div className="grid grid-cols-2 items-end gap-4 md:grid-cols-4 xl:grid-cols-7">
51
+ <Field>
52
+ <FieldLabel htmlFor="prototype">Prototype</FieldLabel>
53
+ <NativeSelect id="prototype" value={selected} onChange={(event) => setSelected(event.target.value)}>
54
+ {prototypes.map((prototype) => (
55
+ <NativeSelectOption key={prototype.id} value={prototype.id}>
56
+ {prototype.title}
57
+ </NativeSelectOption>
58
+ ))}
59
+ </NativeSelect>
60
+ </Field>
61
+ <Field>
62
+ <FieldLabel htmlFor="scenario">Scenario</FieldLabel>
63
+ <NativeSelect
64
+ id="scenario"
65
+ value={scenario}
66
+ onChange={(event) => {
67
+ const value = event.target.value;
68
+ if (value === "populated" || value === "empty" || value === "loading" || value === "error")
69
+ setScenario(value);
70
+ }}
71
+ >
72
+ <NativeSelectOption value="populated">Populated</NativeSelectOption>
73
+ <NativeSelectOption value="empty">Empty</NativeSelectOption>
74
+ <NativeSelectOption value="loading">Loading</NativeSelectOption>
75
+ <NativeSelectOption value="error">Error</NativeSelectOption>
76
+ </NativeSelect>
77
+ </Field>
78
+ <Field>
79
+ <FieldLabel htmlFor="theme">Theme</FieldLabel>
80
+ <NativeSelect
81
+ id="theme"
82
+ value={theme}
83
+ onChange={(event) => setTheme(event.target.value === "light" ? "light" : "dark")}
84
+ >
85
+ <NativeSelectOption value="dark">Dark</NativeSelectOption>
86
+ <NativeSelectOption value="light">Light</NativeSelectOption>
87
+ </NativeSelect>
88
+ </Field>
89
+ <Field>
90
+ <FieldLabel htmlFor="density">Density</FieldLabel>
91
+ <NativeSelect
92
+ id="density"
93
+ value={density}
94
+ onChange={(event) => setDensity(event.target.value === "compact" ? "compact" : "comfortable")}
95
+ >
96
+ <NativeSelectOption value="comfortable">Comfortable</NativeSelectOption>
97
+ <NativeSelectOption value="compact">Compact</NativeSelectOption>
98
+ </NativeSelect>
99
+ </Field>
100
+ <Field>
101
+ <FieldLabel htmlFor="viewport">Viewport</FieldLabel>
102
+ <NativeSelect id="viewport" value={viewport} onChange={(event) => setViewport(event.target.value)}>
103
+ <NativeSelectOption value="responsive">Responsive</NativeSelectOption>
104
+ <NativeSelectOption value="390">Mobile · 390px</NativeSelectOption>
105
+ <NativeSelectOption value="1280">Desktop · 1280px</NativeSelectOption>
106
+ </NativeSelect>
107
+ </Field>
108
+ <Button onClick={() => setRevision((value) => value + 1)}>Reset data</Button>
109
+ <a className="text-sm underline" href={`?${canvasParameters}`} target="_blank" rel="noreferrer">
110
+ Open screen
111
+ </a>
112
+ </div>
113
+ </FieldGroup>
114
+ <p className="mt-3 text-sm text-muted-foreground">
115
+ {definition?.description ?? "Choose a prototype"} · Reset or reload restores sample data.
116
+ </p>
117
+ </header>
118
+ <main className="min-h-0 flex-1 overflow-auto p-4" aria-label="Preview viewport">
119
+ <iframe
120
+ key={revision}
121
+ title="Prototype preview"
122
+ src={`?${canvasParameters}`}
123
+ className="mx-auto block h-full min-h-96 outline outline-border"
124
+ style={{ width: viewport === "responsive" ? "100%" : `${viewport}px` }}
125
+ />
126
+ </main>
127
+ </div>
128
+ )}
129
+ </DensityProvider>
130
+ </ThemeProvider>
131
+ );
132
+ }
@@ -0,0 +1,12 @@
1
+ import { StrictMode } from "react";
2
+ import { createRoot } from "react-dom/client";
3
+ import { Workbench } from "./components/Workbench";
4
+ import "./styles.css";
5
+
6
+ const root = document.getElementById("root");
7
+ if (!root) throw new Error("Missing application root");
8
+ createRoot(root).render(
9
+ <StrictMode>
10
+ <Workbench />
11
+ </StrictMode>,
12
+ );
@@ -0,0 +1,80 @@
1
+ import { useState, type ReactElement } from "react";
2
+ import {
3
+ AppShell,
4
+ Badge,
5
+ Card,
6
+ CardHeader,
7
+ CardTitle,
8
+ CardDescription,
9
+ CardContent,
10
+ Field,
11
+ FieldGroup,
12
+ FieldLabel,
13
+ NativeSelect,
14
+ NativeSelectOption,
15
+ PageHeader,
16
+ StatCard,
17
+ Table,
18
+ TableBody,
19
+ TableCell,
20
+ TableHead,
21
+ TableHeader,
22
+ TableRow,
23
+ } from "@alexgorbatchev/shadcn";
24
+ import type { PrototypeProps } from "../../../types";
25
+ import { ScenarioContent } from "../../../components/ScenarioContent";
26
+ import { deliveries } from "../data";
27
+
28
+ export function Dashboard({ scenario }: PrototypeProps): ReactElement {
29
+ const [team, setTeam] = useState("All teams");
30
+ const visible = deliveries.filter((delivery) => team === "All teams" || delivery.team === team);
31
+ return (
32
+ <AppShell brand="Northstar" header="Overview" navigation={[]}>
33
+ <PageHeader title="Delivery overview" description="A shared view of the work moving this week." />
34
+ <ScenarioContent scenario={scenario}>
35
+ <FieldGroup>
36
+ <Field>
37
+ <FieldLabel htmlFor="team">Team</FieldLabel>
38
+ <NativeSelect id="team" value={team} onChange={(event) => setTeam(event.target.value)}>
39
+ <NativeSelectOption>All teams</NativeSelectOption>
40
+ <NativeSelectOption>Product</NativeSelectOption>
41
+ <NativeSelectOption>Engineering</NativeSelectOption>
42
+ </NativeSelect>
43
+ </Field>
44
+ </FieldGroup>
45
+ <div className="grid gap-4 sm:grid-cols-2">
46
+ <StatCard label="Active projects" value={visible.length} />
47
+ <StatCard label="Ready for review" value={visible.filter((delivery) => delivery.status === "Ready").length} />
48
+ </div>
49
+ <Card>
50
+ <CardHeader>
51
+ <CardTitle>Delivery queue</CardTitle>
52
+ <CardDescription>Projects for {team.toLowerCase()}.</CardDescription>
53
+ </CardHeader>
54
+ <CardContent>
55
+ <Table>
56
+ <TableHeader>
57
+ <TableRow>
58
+ <TableHead>Project</TableHead>
59
+ <TableHead>Team</TableHead>
60
+ <TableHead>Status</TableHead>
61
+ </TableRow>
62
+ </TableHeader>
63
+ <TableBody>
64
+ {visible.map((delivery) => (
65
+ <TableRow key={delivery.name}>
66
+ <TableCell>{delivery.name}</TableCell>
67
+ <TableCell>{delivery.team}</TableCell>
68
+ <TableCell>
69
+ <Badge variant="secondary">{delivery.status}</Badge>
70
+ </TableCell>
71
+ </TableRow>
72
+ ))}
73
+ </TableBody>
74
+ </Table>
75
+ </CardContent>
76
+ </Card>
77
+ </ScenarioContent>
78
+ </AppShell>
79
+ );
80
+ }
@@ -0,0 +1,5 @@
1
+ export const deliveries = [
2
+ { name: "Onboarding redesign", team: "Product", status: "In progress" },
3
+ { name: "Usage reporting", team: "Engineering", status: "Ready" },
4
+ { name: "Customer interviews", team: "Product", status: "Ready" },
5
+ ];
@@ -0,0 +1,9 @@
1
+ import type { PrototypeDefinition } from "../../types";
2
+ import { Dashboard } from "./components/Dashboard";
3
+
4
+ const prototype: PrototypeDefinition = {
5
+ title: "Dashboard",
6
+ description: "Review activity and filter a delivery dashboard.",
7
+ component: Dashboard,
8
+ };
9
+ export default prototype;