@setsquare/ui 0.1.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/CHANGELOG.md +70 -0
- package/LICENSE +202 -0
- package/NOTICE +49 -0
- package/README.md +313 -0
- package/dist/index.d.ts +129 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +78 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/cn.d.ts +4 -0
- package/dist/lib/cn.d.ts.map +1 -0
- package/dist/lib/cn.js +59 -0
- package/dist/lib/cn.js.map +1 -0
- package/dist/lib/tokens.d.ts +5 -0
- package/dist/lib/tokens.d.ts.map +1 -0
- package/dist/lib/tokens.js +22 -0
- package/dist/lib/tokens.js.map +1 -0
- package/dist/lib/use-element-width.d.ts +4 -0
- package/dist/lib/use-element-width.d.ts.map +1 -0
- package/dist/lib/use-element-width.js +17 -0
- package/dist/lib/use-element-width.js.map +1 -0
- package/dist/lint/index.d.ts +41 -0
- package/dist/lint/index.d.ts.map +1 -0
- package/dist/lint/index.js +648 -0
- package/dist/lint/index.js.map +1 -0
- package/dist/shell/app-header.d.ts +13 -0
- package/dist/shell/app-header.d.ts.map +1 -0
- package/dist/shell/app-header.js +11 -0
- package/dist/shell/app-header.js.map +1 -0
- package/dist/shell/dashboard-shell.d.ts +67 -0
- package/dist/shell/dashboard-shell.d.ts.map +1 -0
- package/dist/shell/dashboard-shell.js +168 -0
- package/dist/shell/dashboard-shell.js.map +1 -0
- package/dist/shell/page-header.d.ts +19 -0
- package/dist/shell/page-header.d.ts.map +1 -0
- package/dist/shell/page-header.js +11 -0
- package/dist/shell/page-header.js.map +1 -0
- package/dist/shell/right-panel.d.ts +25 -0
- package/dist/shell/right-panel.d.ts.map +1 -0
- package/dist/shell/right-panel.js +37 -0
- package/dist/shell/right-panel.js.map +1 -0
- package/dist/shell/sidebar.d.ts +68 -0
- package/dist/shell/sidebar.d.ts.map +1 -0
- package/dist/shell/sidebar.js +68 -0
- package/dist/shell/sidebar.js.map +1 -0
- package/dist/shell/site-footer.d.ts +28 -0
- package/dist/shell/site-footer.d.ts.map +1 -0
- package/dist/shell/site-footer.js +15 -0
- package/dist/shell/site-footer.js.map +1 -0
- package/dist/shell/site-header-padding.d.ts +7 -0
- package/dist/shell/site-header-padding.d.ts.map +1 -0
- package/dist/shell/site-header-padding.js +27 -0
- package/dist/shell/site-header-padding.js.map +1 -0
- package/dist/shell/site-header.d.ts +23 -0
- package/dist/shell/site-header.d.ts.map +1 -0
- package/dist/shell/site-header.js +13 -0
- package/dist/shell/site-header.js.map +1 -0
- package/dist/shell/site-menu.d.ts +27 -0
- package/dist/shell/site-menu.d.ts.map +1 -0
- package/dist/shell/site-menu.js +56 -0
- package/dist/shell/site-menu.js.map +1 -0
- package/dist/shell/site-nav-link.d.ts +24 -0
- package/dist/shell/site-nav-link.d.ts.map +1 -0
- package/dist/shell/site-nav-link.js +35 -0
- package/dist/shell/site-nav-link.js.map +1 -0
- package/dist/shell/site-nav.d.ts +12 -0
- package/dist/shell/site-nav.d.ts.map +1 -0
- package/dist/shell/site-nav.js +9 -0
- package/dist/shell/site-nav.js.map +1 -0
- package/dist/shell/site-path.d.ts +8 -0
- package/dist/shell/site-path.d.ts.map +1 -0
- package/dist/shell/site-path.js +11 -0
- package/dist/shell/site-path.js.map +1 -0
- package/dist/shell/site-shell.d.ts +25 -0
- package/dist/shell/site-shell.d.ts.map +1 -0
- package/dist/shell/site-shell.js +17 -0
- package/dist/shell/site-shell.js.map +1 -0
- package/dist/styles/styles.css +10 -0
- package/dist/styles/theme.css +579 -0
- package/dist/styles/tokens.css +295 -0
- package/dist/testing/index.d.ts +321 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +1175 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/tokens.json +982 -0
- package/dist/ui/alert.d.ts +35 -0
- package/dist/ui/alert.d.ts.map +1 -0
- package/dist/ui/alert.js +65 -0
- package/dist/ui/alert.js.map +1 -0
- package/dist/ui/avatar.d.ts +16 -0
- package/dist/ui/avatar.d.ts.map +1 -0
- package/dist/ui/avatar.js +13 -0
- package/dist/ui/avatar.js.map +1 -0
- package/dist/ui/badge.d.ts +13 -0
- package/dist/ui/badge.d.ts.map +1 -0
- package/dist/ui/badge.js +24 -0
- package/dist/ui/badge.js.map +1 -0
- package/dist/ui/breadcrumbs.d.ts +13 -0
- package/dist/ui/breadcrumbs.d.ts.map +1 -0
- package/dist/ui/breadcrumbs.js +12 -0
- package/dist/ui/breadcrumbs.js.map +1 -0
- package/dist/ui/button-styles.d.ts +24 -0
- package/dist/ui/button-styles.d.ts.map +1 -0
- package/dist/ui/button-styles.js +50 -0
- package/dist/ui/button-styles.js.map +1 -0
- package/dist/ui/button.d.ts +27 -0
- package/dist/ui/button.d.ts.map +1 -0
- package/dist/ui/button.js +30 -0
- package/dist/ui/button.js.map +1 -0
- package/dist/ui/calendar.d.ts +19 -0
- package/dist/ui/calendar.d.ts.map +1 -0
- package/dist/ui/calendar.js +52 -0
- package/dist/ui/calendar.js.map +1 -0
- package/dist/ui/card.d.ts +30 -0
- package/dist/ui/card.d.ts.map +1 -0
- package/dist/ui/card.js +18 -0
- package/dist/ui/card.js.map +1 -0
- package/dist/ui/checkbox.d.ts +36 -0
- package/dist/ui/checkbox.d.ts.map +1 -0
- package/dist/ui/checkbox.js +38 -0
- package/dist/ui/checkbox.js.map +1 -0
- package/dist/ui/code-block.d.ts +20 -0
- package/dist/ui/code-block.d.ts.map +1 -0
- package/dist/ui/code-block.js +22 -0
- package/dist/ui/code-block.js.map +1 -0
- package/dist/ui/combo-box.d.ts +17 -0
- package/dist/ui/combo-box.d.ts.map +1 -0
- package/dist/ui/combo-box.js +14 -0
- package/dist/ui/combo-box.js.map +1 -0
- package/dist/ui/copy-button.d.ts +14 -0
- package/dist/ui/copy-button.d.ts.map +1 -0
- package/dist/ui/copy-button.js +36 -0
- package/dist/ui/copy-button.js.map +1 -0
- package/dist/ui/date-field.d.ts +38 -0
- package/dist/ui/date-field.d.ts.map +1 -0
- package/dist/ui/date-field.js +78 -0
- package/dist/ui/date-field.js.map +1 -0
- package/dist/ui/dialog.d.ts +41 -0
- package/dist/ui/dialog.d.ts.map +1 -0
- package/dist/ui/dialog.js +69 -0
- package/dist/ui/dialog.js.map +1 -0
- package/dist/ui/disclosure.d.ts +18 -0
- package/dist/ui/disclosure.d.ts.map +1 -0
- package/dist/ui/disclosure.js +15 -0
- package/dist/ui/disclosure.js.map +1 -0
- package/dist/ui/empty-state.d.ts +17 -0
- package/dist/ui/empty-state.d.ts.map +1 -0
- package/dist/ui/empty-state.js +9 -0
- package/dist/ui/empty-state.js.map +1 -0
- package/dist/ui/error-state.d.ts +45 -0
- package/dist/ui/error-state.d.ts.map +1 -0
- package/dist/ui/error-state.js +88 -0
- package/dist/ui/error-state.js.map +1 -0
- package/dist/ui/field.d.ts +46 -0
- package/dist/ui/field.d.ts.map +1 -0
- package/dist/ui/field.js +79 -0
- package/dist/ui/field.js.map +1 -0
- package/dist/ui/file-upload.d.ts +54 -0
- package/dist/ui/file-upload.d.ts.map +1 -0
- package/dist/ui/file-upload.js +170 -0
- package/dist/ui/file-upload.js.map +1 -0
- package/dist/ui/form.d.ts +56 -0
- package/dist/ui/form.d.ts.map +1 -0
- package/dist/ui/form.js +137 -0
- package/dist/ui/form.js.map +1 -0
- package/dist/ui/grid-list.d.ts +24 -0
- package/dist/ui/grid-list.d.ts.map +1 -0
- package/dist/ui/grid-list.js +21 -0
- package/dist/ui/grid-list.js.map +1 -0
- package/dist/ui/hero.d.ts +36 -0
- package/dist/ui/hero.d.ts.map +1 -0
- package/dist/ui/hero.js +19 -0
- package/dist/ui/hero.js.map +1 -0
- package/dist/ui/icon-button.d.ts +18 -0
- package/dist/ui/icon-button.d.ts.map +1 -0
- package/dist/ui/icon-button.js +13 -0
- package/dist/ui/icon-button.js.map +1 -0
- package/dist/ui/icon.d.ts +20 -0
- package/dist/ui/icon.d.ts.map +1 -0
- package/dist/ui/icon.js +26 -0
- package/dist/ui/icon.js.map +1 -0
- package/dist/ui/kbd.d.ts +7 -0
- package/dist/ui/kbd.d.ts.map +1 -0
- package/dist/ui/kbd.js +7 -0
- package/dist/ui/kbd.js.map +1 -0
- package/dist/ui/link-card.d.ts +23 -0
- package/dist/ui/link-card.d.ts.map +1 -0
- package/dist/ui/link-card.js +19 -0
- package/dist/ui/link-card.js.map +1 -0
- package/dist/ui/list-box.d.ts +17 -0
- package/dist/ui/list-box.d.ts.map +1 -0
- package/dist/ui/list-box.js +18 -0
- package/dist/ui/list-box.js.map +1 -0
- package/dist/ui/menu.d.ts +31 -0
- package/dist/ui/menu.d.ts.map +1 -0
- package/dist/ui/menu.js +40 -0
- package/dist/ui/menu.js.map +1 -0
- package/dist/ui/number-field.d.ts +19 -0
- package/dist/ui/number-field.d.ts.map +1 -0
- package/dist/ui/number-field.js +19 -0
- package/dist/ui/number-field.js.map +1 -0
- package/dist/ui/prose.d.ts +12 -0
- package/dist/ui/prose.d.ts.map +1 -0
- package/dist/ui/prose.js +11 -0
- package/dist/ui/prose.js.map +1 -0
- package/dist/ui/radio-group.d.ts +20 -0
- package/dist/ui/radio-group.d.ts.map +1 -0
- package/dist/ui/radio-group.js +16 -0
- package/dist/ui/radio-group.js.map +1 -0
- package/dist/ui/scroll-region.d.ts +18 -0
- package/dist/ui/scroll-region.d.ts.map +1 -0
- package/dist/ui/scroll-region.js +29 -0
- package/dist/ui/scroll-region.js.map +1 -0
- package/dist/ui/search-field.d.ts +12 -0
- package/dist/ui/search-field.d.ts.map +1 -0
- package/dist/ui/search-field.js +12 -0
- package/dist/ui/search-field.js.map +1 -0
- package/dist/ui/search.d.ts +43 -0
- package/dist/ui/search.d.ts.map +1 -0
- package/dist/ui/search.js +45 -0
- package/dist/ui/search.js.map +1 -0
- package/dist/ui/section.d.ts +50 -0
- package/dist/ui/section.d.ts.map +1 -0
- package/dist/ui/section.js +25 -0
- package/dist/ui/section.js.map +1 -0
- package/dist/ui/segmented-control.d.ts +18 -0
- package/dist/ui/segmented-control.d.ts.map +1 -0
- package/dist/ui/segmented-control.js +17 -0
- package/dist/ui/segmented-control.js.map +1 -0
- package/dist/ui/select.d.ts +18 -0
- package/dist/ui/select.d.ts.map +1 -0
- package/dist/ui/select.js +13 -0
- package/dist/ui/select.js.map +1 -0
- package/dist/ui/selection-bar.d.ts +28 -0
- package/dist/ui/selection-bar.d.ts.map +1 -0
- package/dist/ui/selection-bar.js +35 -0
- package/dist/ui/selection-bar.js.map +1 -0
- package/dist/ui/sheet.d.ts +21 -0
- package/dist/ui/sheet.d.ts.map +1 -0
- package/dist/ui/sheet.js +16 -0
- package/dist/ui/sheet.js.map +1 -0
- package/dist/ui/skeleton.d.ts +31 -0
- package/dist/ui/skeleton.d.ts.map +1 -0
- package/dist/ui/skeleton.js +38 -0
- package/dist/ui/skeleton.js.map +1 -0
- package/dist/ui/slider.d.ts +31 -0
- package/dist/ui/slider.d.ts.map +1 -0
- package/dist/ui/slider.js +33 -0
- package/dist/ui/slider.js.map +1 -0
- package/dist/ui/spinner.d.ts +27 -0
- package/dist/ui/spinner.d.ts.map +1 -0
- package/dist/ui/spinner.js +21 -0
- package/dist/ui/spinner.js.map +1 -0
- package/dist/ui/split.d.ts +14 -0
- package/dist/ui/split.d.ts.map +1 -0
- package/dist/ui/split.js +11 -0
- package/dist/ui/split.js.map +1 -0
- package/dist/ui/stat-list.d.ts +25 -0
- package/dist/ui/stat-list.d.ts.map +1 -0
- package/dist/ui/stat-list.js +14 -0
- package/dist/ui/stat-list.js.map +1 -0
- package/dist/ui/steps.d.ts +23 -0
- package/dist/ui/steps.d.ts.map +1 -0
- package/dist/ui/steps.js +16 -0
- package/dist/ui/steps.js.map +1 -0
- package/dist/ui/switch.d.ts +13 -0
- package/dist/ui/switch.d.ts.map +1 -0
- package/dist/ui/switch.js +12 -0
- package/dist/ui/switch.js.map +1 -0
- package/dist/ui/table.d.ts +25 -0
- package/dist/ui/table.d.ts.map +1 -0
- package/dist/ui/table.js +29 -0
- package/dist/ui/table.js.map +1 -0
- package/dist/ui/tag.d.ts +14 -0
- package/dist/ui/tag.d.ts.map +1 -0
- package/dist/ui/tag.js +11 -0
- package/dist/ui/tag.js.map +1 -0
- package/dist/ui/text-field.d.ts +27 -0
- package/dist/ui/text-field.d.ts.map +1 -0
- package/dist/ui/text-field.js +17 -0
- package/dist/ui/text-field.js.map +1 -0
- package/dist/ui/text-link.d.ts +17 -0
- package/dist/ui/text-link.d.ts.map +1 -0
- package/dist/ui/text-link.js +15 -0
- package/dist/ui/text-link.js.map +1 -0
- package/dist/ui/theme-switcher.d.ts +26 -0
- package/dist/ui/theme-switcher.d.ts.map +1 -0
- package/dist/ui/theme-switcher.js +32 -0
- package/dist/ui/theme-switcher.js.map +1 -0
- package/dist/ui/theme.d.ts +54 -0
- package/dist/ui/theme.d.ts.map +1 -0
- package/dist/ui/theme.js +135 -0
- package/dist/ui/theme.js.map +1 -0
- package/dist/ui/toast.d.ts +34 -0
- package/dist/ui/toast.d.ts.map +1 -0
- package/dist/ui/toast.js +69 -0
- package/dist/ui/toast.js.map +1 -0
- package/dist/ui/tooltip.d.ts +10 -0
- package/dist/ui/tooltip.d.ts.map +1 -0
- package/dist/ui/tooltip.js +10 -0
- package/dist/ui/tooltip.js.map +1 -0
- package/package.json +123 -0
- package/templates/next/error.tsx +32 -0
- package/templates/next/loading.tsx +19 -0
- package/templates/next/not-found.tsx +18 -0
- package/templates/next/page-transition.tsx +33 -0
- package/templates/next/portal-layout.tsx +70 -0
- package/templates/next/prefetch-on-intent.ts +31 -0
- package/templates/next/root-layout.tsx +16 -0
- package/templates/next/site-layout.tsx +58 -0
- package/templates/next/site-nav.ts +16 -0
- package/templates/next/site-not-found.tsx +21 -0
- package/templates/next/site-router.tsx +17 -0
package/package.json
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@setsquare/ui",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "React Aria components, design tokens, a dashboard shell and a site shell, with a switch between WCAG 2.2 AA and AAA sizing, spacing and contrast.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"homepage": "https://github.com/Worktree-Amigos/setsquare-ui#readme",
|
|
7
|
+
"bugs": {
|
|
8
|
+
"url": "https://github.com/Worktree-Amigos/setsquare-ui/issues"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/Worktree-Amigos/setsquare-ui.git"
|
|
13
|
+
},
|
|
14
|
+
"type": "module",
|
|
15
|
+
"main": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"default": "./dist/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./lint": {
|
|
23
|
+
"types": "./dist/lint/index.d.ts",
|
|
24
|
+
"default": "./dist/lint/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./testing": {
|
|
27
|
+
"types": "./dist/testing/index.d.ts",
|
|
28
|
+
"default": "./dist/testing/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./styles.css": "./dist/styles/styles.css",
|
|
31
|
+
"./theme.css": "./dist/styles/theme.css",
|
|
32
|
+
"./tokens.css": "./dist/styles/tokens.css",
|
|
33
|
+
"./tokens.json": "./dist/tokens.json",
|
|
34
|
+
"./package.json": "./package.json"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist",
|
|
38
|
+
"templates",
|
|
39
|
+
"README.md",
|
|
40
|
+
"CHANGELOG.md",
|
|
41
|
+
"LICENSE",
|
|
42
|
+
"NOTICE"
|
|
43
|
+
],
|
|
44
|
+
"sideEffects": [
|
|
45
|
+
"*.css"
|
|
46
|
+
],
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=22"
|
|
49
|
+
},
|
|
50
|
+
"keywords": [
|
|
51
|
+
"react",
|
|
52
|
+
"react-aria",
|
|
53
|
+
"components",
|
|
54
|
+
"design-system",
|
|
55
|
+
"tailwindcss",
|
|
56
|
+
"accessibility",
|
|
57
|
+
"a11y",
|
|
58
|
+
"wcag",
|
|
59
|
+
"nextjs"
|
|
60
|
+
],
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"@internationalized/date": "^3.12.4",
|
|
63
|
+
"react": ">=18.3.0",
|
|
64
|
+
"react-aria-components": "^1.21.1",
|
|
65
|
+
"react-dom": ">=18.3.0",
|
|
66
|
+
"tailwindcss": "^4.3.0"
|
|
67
|
+
},
|
|
68
|
+
"peerDependenciesMeta": {
|
|
69
|
+
"tailwindcss": {
|
|
70
|
+
"optional": true
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"dependencies": {
|
|
74
|
+
"clsx": "2.1.1",
|
|
75
|
+
"lucide-react": "1.48.0",
|
|
76
|
+
"tailwind-merge": "3.7.0"
|
|
77
|
+
},
|
|
78
|
+
"devDependencies": {
|
|
79
|
+
"@axe-core/playwright": "4.13.0",
|
|
80
|
+
"@internationalized/date": "3.12.4",
|
|
81
|
+
"@playwright/test": "1.63.0",
|
|
82
|
+
"@setsquare/review-sdk": "0.8.0",
|
|
83
|
+
"@tailwindcss/cli": "4.3.3",
|
|
84
|
+
"@types/node": "22.20.4",
|
|
85
|
+
"@types/react": "19.3.0",
|
|
86
|
+
"@types/react-dom": "19.3.0",
|
|
87
|
+
"axe-core": "4.13.0",
|
|
88
|
+
"esbuild": "0.28.2",
|
|
89
|
+
"eslint": "10.11.0",
|
|
90
|
+
"next": "16.3.6",
|
|
91
|
+
"oxfmt": "0.70.0",
|
|
92
|
+
"oxlint": "1.85.0",
|
|
93
|
+
"react": "19.3.0",
|
|
94
|
+
"react-18": "npm:react@18.3.1",
|
|
95
|
+
"react-aria-components": "1.21.1",
|
|
96
|
+
"react-dom": "19.3.0",
|
|
97
|
+
"react-dom-18": "npm:react-dom@18.3.1",
|
|
98
|
+
"tailwindcss": "4.3.3",
|
|
99
|
+
"typescript": "7.0.2",
|
|
100
|
+
"vitest": "5.0.2"
|
|
101
|
+
},
|
|
102
|
+
"publishConfig": {
|
|
103
|
+
"access": "public"
|
|
104
|
+
},
|
|
105
|
+
"scripts": {
|
|
106
|
+
"build": "node scripts/build-css.mjs && tsc -p tsconfig.json",
|
|
107
|
+
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json",
|
|
108
|
+
"typecheck:react18": "node scripts/typecheck-react18.mjs",
|
|
109
|
+
"lint": "oxlint && node scripts/check-contrast.mjs",
|
|
110
|
+
"format": "oxfmt",
|
|
111
|
+
"format:check": "oxfmt --check",
|
|
112
|
+
"test": "vitest run",
|
|
113
|
+
"test:browser": "node test/browser/run.mjs",
|
|
114
|
+
"docs:build": "node scripts/build-docs.mjs",
|
|
115
|
+
"package:verify": "node scripts/verify-package.mjs",
|
|
116
|
+
"ci:local": "node scripts/ci-local.mjs",
|
|
117
|
+
"test:axe": "node test/browser/axe.mjs",
|
|
118
|
+
"docs:site": "pnpm --filter @setsquare/ui-docs dev",
|
|
119
|
+
"docs:site:build": "pnpm --filter @setsquare/ui-docs build",
|
|
120
|
+
"test:site": "node test/site/pages.mjs",
|
|
121
|
+
"test:setsquare": "node test/setsquare/run.mjs"
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// app/(portal)/error.tsx — catches errors in any portal page; the shell (layout) stays usable around it.
|
|
2
|
+
'use client';
|
|
3
|
+
|
|
4
|
+
import { useEffect } from 'react';
|
|
5
|
+
import { ShellMain, ErrorState } from '@setsquare/ui';
|
|
6
|
+
|
|
7
|
+
export default function Error({
|
|
8
|
+
error,
|
|
9
|
+
retry,
|
|
10
|
+
}: {
|
|
11
|
+
error: Error & { digest?: string };
|
|
12
|
+
retry: () => void;
|
|
13
|
+
}) {
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
// Send to your error reporter here. Never show error.message to people: it can leak internals.
|
|
16
|
+
console.error(error);
|
|
17
|
+
}, [error]);
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<ShellMain>
|
|
21
|
+
{/* The page's content was replaced by this error: move focus to its heading so it is announced. */}
|
|
22
|
+
<ErrorState
|
|
23
|
+
kind="server"
|
|
24
|
+
headingLevel={1}
|
|
25
|
+
// oxlint-disable-next-line jsx-a11y/no-autofocus
|
|
26
|
+
autoFocus
|
|
27
|
+
onRetry={retry}
|
|
28
|
+
reference={error.digest}
|
|
29
|
+
/>
|
|
30
|
+
</ShellMain>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// app/(portal)/loading.tsx — Next shows this while a portal page streams in. Shape it like the page that is coming.
|
|
2
|
+
|
|
3
|
+
import { ShellMain, PageContent, LoadingState, Skeleton, TableSkeleton } from '@setsquare/ui';
|
|
4
|
+
|
|
5
|
+
export default function Loading() {
|
|
6
|
+
return (
|
|
7
|
+
<ShellMain>
|
|
8
|
+
<div className="px-(--gutter) pt-5 pb-4">
|
|
9
|
+
<Skeleton className="h-8 w-56" />
|
|
10
|
+
<Skeleton className="mt-2 h-4 w-96 max-w-full" />
|
|
11
|
+
</div>
|
|
12
|
+
<PageContent>
|
|
13
|
+
<LoadingState label="Loading page">
|
|
14
|
+
<TableSkeleton />
|
|
15
|
+
</LoadingState>
|
|
16
|
+
</PageContent>
|
|
17
|
+
</ShellMain>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// app/(portal)/not-found.tsx — call notFound() from a page for records that don't exist or aren't visible to this person.
|
|
2
|
+
|
|
3
|
+
import { ShellMain, LinkButton, ErrorState } from '@setsquare/ui';
|
|
4
|
+
export default function NotFound() {
|
|
5
|
+
return (
|
|
6
|
+
<ShellMain>
|
|
7
|
+
<ErrorState
|
|
8
|
+
kind="not-found"
|
|
9
|
+
headingLevel={1}
|
|
10
|
+
actions={
|
|
11
|
+
<LinkButton href="/" variant="primary">
|
|
12
|
+
Go to the overview
|
|
13
|
+
</LinkButton>
|
|
14
|
+
}
|
|
15
|
+
/>
|
|
16
|
+
</ShellMain>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Next.js only (React 19.3+): not part of the design-system bundle, which runs on React 18.
|
|
2
|
+
// Wrap each page.tsx's ShellMain — <PageTransition><ShellMain>…</ShellMain></PageTransition> — never the other way
|
|
3
|
+
// round: a ViewTransition only enters and exits if it comes before every DOM node, and ShellMain is a <main>.
|
|
4
|
+
// Never in a layout.tsx either, where enter and exit never fire.
|
|
5
|
+
import { ViewTransition, type ReactNode } from 'react';
|
|
6
|
+
|
|
7
|
+
/** Two classes, so the stylesheet can cover the old page's place while it leaves (see page-exit there). */
|
|
8
|
+
export function PageTransition({ children }: { children: ReactNode }) {
|
|
9
|
+
return (
|
|
10
|
+
<ViewTransition
|
|
11
|
+
enter={{ 'nav-forward': 'page-enter', default: 'none' }}
|
|
12
|
+
exit={{ 'nav-forward': 'page-exit', default: 'none' }}
|
|
13
|
+
default="none"
|
|
14
|
+
>
|
|
15
|
+
{children}
|
|
16
|
+
</ViewTransition>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Wrap the right panel's content, keyed by the selection, so a new selection crossfades in place. */
|
|
21
|
+
export function PanelTransition({ id, children }: { id: string; children: ReactNode }) {
|
|
22
|
+
return (
|
|
23
|
+
<ViewTransition
|
|
24
|
+
key={id}
|
|
25
|
+
name="panel-content"
|
|
26
|
+
share="panel-swap"
|
|
27
|
+
enter="panel-swap"
|
|
28
|
+
default="none"
|
|
29
|
+
>
|
|
30
|
+
{children}
|
|
31
|
+
</ViewTransition>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// app/(portal)/layout.tsx — the shell every portal page shares. Pages render <PageTransition><ShellMain>…</ShellMain></PageTransition> and, optionally, <RightPanel>.
|
|
2
|
+
'use client';
|
|
3
|
+
|
|
4
|
+
import type { ReactNode } from 'react';
|
|
5
|
+
import { CircleHelp, Folder, House, SlidersHorizontal } from 'lucide-react';
|
|
6
|
+
import { usePathname, useRouter } from 'next/navigation';
|
|
7
|
+
import { RouterProvider } from 'react-aria-components';
|
|
8
|
+
import {
|
|
9
|
+
AppHeader,
|
|
10
|
+
DashboardShell,
|
|
11
|
+
ShellBody,
|
|
12
|
+
ShellInset,
|
|
13
|
+
Sidebar,
|
|
14
|
+
SidebarContent,
|
|
15
|
+
SidebarFooter,
|
|
16
|
+
SidebarGroup,
|
|
17
|
+
SidebarItem,
|
|
18
|
+
SidebarList,
|
|
19
|
+
OfflineAlert,
|
|
20
|
+
Toaster,
|
|
21
|
+
} from '@setsquare/ui';
|
|
22
|
+
import { usePrefetchOnIntent } from './prefetch-on-intent';
|
|
23
|
+
|
|
24
|
+
const NAV = [
|
|
25
|
+
{ href: '/', label: 'Overview', icon: House },
|
|
26
|
+
{ href: '/projects', label: 'Projects', icon: Folder },
|
|
27
|
+
{ href: '/settings', label: 'Settings', icon: SlidersHorizontal },
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
export default function PortalLayout({ children }: { children: ReactNode }) {
|
|
31
|
+
const router = useRouter();
|
|
32
|
+
const pathname = usePathname();
|
|
33
|
+
usePrefetchOnIntent(router);
|
|
34
|
+
return (
|
|
35
|
+
// React Aria links and menu items navigate through Next's router, forward, so PageTransition animates.
|
|
36
|
+
<RouterProvider navigate={(href) => router.push(href, { transitionTypes: ['nav-forward'] })}>
|
|
37
|
+
<DashboardShell productName={process.env.NEXT_PUBLIC_PRODUCT_NAME ?? 'Product'}>
|
|
38
|
+
<Sidebar>
|
|
39
|
+
<SidebarContent>
|
|
40
|
+
<SidebarGroup label="Workspace">
|
|
41
|
+
{NAV.map((item) => (
|
|
42
|
+
<SidebarItem
|
|
43
|
+
key={item.href}
|
|
44
|
+
href={item.href}
|
|
45
|
+
icon={item.icon}
|
|
46
|
+
isCurrent={pathname === item.href}
|
|
47
|
+
>
|
|
48
|
+
{item.label}
|
|
49
|
+
</SidebarItem>
|
|
50
|
+
))}
|
|
51
|
+
</SidebarGroup>
|
|
52
|
+
</SidebarContent>
|
|
53
|
+
<SidebarFooter>
|
|
54
|
+
<SidebarList>
|
|
55
|
+
<SidebarItem href="/help" icon={CircleHelp}>
|
|
56
|
+
Help and support
|
|
57
|
+
</SidebarItem>
|
|
58
|
+
</SidebarList>
|
|
59
|
+
</SidebarFooter>
|
|
60
|
+
</Sidebar>
|
|
61
|
+
<ShellInset>
|
|
62
|
+
<AppHeader />
|
|
63
|
+
<OfflineAlert />
|
|
64
|
+
<ShellBody>{children}</ShellBody>
|
|
65
|
+
</ShellInset>
|
|
66
|
+
<Toaster />
|
|
67
|
+
</DashboardShell>
|
|
68
|
+
</RouterProvider>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// app/(portal)/prefetch-on-intent.ts — Next.js only: prefetches a page when someone is about to open it.
|
|
2
|
+
'use client';
|
|
3
|
+
|
|
4
|
+
import { useEffect } from 'react';
|
|
5
|
+
import type { useRouter } from 'next/navigation';
|
|
6
|
+
// router.prefetch's own options type names this enum, and Next exports it from nowhere public.
|
|
7
|
+
import { PrefetchKind } from 'next/dist/client/components/router-reducer/router-reducer-types';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Warms a page when a pointer moves over a link to it or the link takes focus, as Next's own <Link> does —
|
|
11
|
+
* React Aria's links (SidebarItem, LinkButton, TextLink, menu items) never prefetch. It also keeps the page
|
|
12
|
+
* transition: a navigation that has to wait for the server can have its `nav-forward` type claimed by a commit
|
|
13
|
+
* in between (React 19.3 queues transition types per root, not per transition), and the page then changes
|
|
14
|
+
* with no animation. From the prefetch cache it commits at once.
|
|
15
|
+
*/
|
|
16
|
+
export function usePrefetchOnIntent(router: ReturnType<typeof useRouter>) {
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
const warm = (e: Event) => {
|
|
19
|
+
const link = e.target instanceof Element ? e.target.closest('a[href^="/"]') : null;
|
|
20
|
+
const href = link?.getAttribute('href')?.replace(/#.*$/, '');
|
|
21
|
+
if (href && href !== window.location.pathname)
|
|
22
|
+
router.prefetch(href, { kind: PrefetchKind.FULL });
|
|
23
|
+
};
|
|
24
|
+
document.addEventListener('pointerover', warm);
|
|
25
|
+
document.addEventListener('focusin', warm);
|
|
26
|
+
return () => {
|
|
27
|
+
document.removeEventListener('pointerover', warm);
|
|
28
|
+
document.removeEventListener('focusin', warm);
|
|
29
|
+
};
|
|
30
|
+
}, [router]);
|
|
31
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// app/layout.tsx — the root layout, for every route group: <html>, <head> and the theme. ThemeScript puts the stored theme on <html> before the first paint; suppressHydrationWarning, because it changes <html> before React hydrates.
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
import { ThemeScript } from '@setsquare/ui';
|
|
4
|
+
import './globals.css';
|
|
5
|
+
|
|
6
|
+
export default function RootLayout({ children }: { children: ReactNode }) {
|
|
7
|
+
return (
|
|
8
|
+
<html lang="en" suppressHydrationWarning>
|
|
9
|
+
<head>
|
|
10
|
+
{/* With a strict Content-Security-Policy, list its hash (the package README) or pass nonce={…}. */}
|
|
11
|
+
<ThemeScript />
|
|
12
|
+
</head>
|
|
13
|
+
<body>{children}</body>
|
|
14
|
+
</html>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// app/(site)/layout.tsx — the shell every public page shares: header, main and footer, with the navigation from site-nav.ts. A server component: only SiteRouter and the phone menu run in the browser.
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
import {
|
|
4
|
+
LinkButton,
|
|
5
|
+
SiteFooter,
|
|
6
|
+
SiteFooterGroup,
|
|
7
|
+
SiteHeader,
|
|
8
|
+
SiteMain,
|
|
9
|
+
SiteMenu,
|
|
10
|
+
SiteNav,
|
|
11
|
+
SiteShell,
|
|
12
|
+
ThemeSwitcher,
|
|
13
|
+
} from '@setsquare/ui';
|
|
14
|
+
import { COMPANY, NAV, PRODUCT_NAME } from './site-nav';
|
|
15
|
+
import { SiteRouter } from './site-router';
|
|
16
|
+
|
|
17
|
+
// The root layout (app/layout.tsx, from root-layout.tsx) owns <html> and <head>, and ThemeScript there puts the
|
|
18
|
+
// stored theme on <html> before the first paint. The ThemeSwitcher in the footer uses the same options.
|
|
19
|
+
export default function SiteLayout({ children }: { children: ReactNode }) {
|
|
20
|
+
const lockup = (
|
|
21
|
+
<a
|
|
22
|
+
href="/"
|
|
23
|
+
className="inline-flex min-h-target min-w-target items-center rounded-md text-base font-bold text-foreground no-underline"
|
|
24
|
+
>
|
|
25
|
+
{PRODUCT_NAME}
|
|
26
|
+
</a>
|
|
27
|
+
);
|
|
28
|
+
return (
|
|
29
|
+
<SiteRouter>
|
|
30
|
+
<SiteShell>
|
|
31
|
+
<SiteHeader
|
|
32
|
+
lockup={lockup}
|
|
33
|
+
nav={<SiteNav items={NAV} />}
|
|
34
|
+
actions={
|
|
35
|
+
<LinkButton href="/start" variant="primary">
|
|
36
|
+
Get started
|
|
37
|
+
</LinkButton>
|
|
38
|
+
}
|
|
39
|
+
menu={<SiteMenu items={NAV} />}
|
|
40
|
+
/>
|
|
41
|
+
<SiteMain>{children}</SiteMain>
|
|
42
|
+
<SiteFooter
|
|
43
|
+
lockup={lockup}
|
|
44
|
+
legal={
|
|
45
|
+
<div className="flex flex-wrap items-center justify-between gap-4">
|
|
46
|
+
<p>© {PRODUCT_NAME}</p>
|
|
47
|
+
<ThemeSwitcher />
|
|
48
|
+
</div>
|
|
49
|
+
}
|
|
50
|
+
>
|
|
51
|
+
{/* The group SiteMenu links to before hydration: it lists the whole navigation. */}
|
|
52
|
+
<SiteFooterGroup id="site-nav" title="Product" items={NAV} />
|
|
53
|
+
<SiteFooterGroup title="Company" items={COMPANY} />
|
|
54
|
+
</SiteFooter>
|
|
55
|
+
</SiteShell>
|
|
56
|
+
</SiteRouter>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// app/(site)/site-nav.ts — the public site's navigation, once. The header, the phone menu and the footer all read
|
|
2
|
+
// this list, so the order is the same everywhere.
|
|
3
|
+
import type { SiteNavItem } from '@setsquare/ui';
|
|
4
|
+
|
|
5
|
+
export const PRODUCT_NAME = 'Product';
|
|
6
|
+
|
|
7
|
+
export const NAV: SiteNavItem[] = [
|
|
8
|
+
{ href: '/', label: 'Home' },
|
|
9
|
+
{ href: '/pricing', label: 'Pricing' },
|
|
10
|
+
{ href: '/docs', label: 'Docs' },
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
export const COMPANY: SiteNavItem[] = [
|
|
14
|
+
{ href: '/about', label: 'About' },
|
|
15
|
+
{ href: '/privacy', label: 'Privacy' },
|
|
16
|
+
];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// app/(site)/not-found.tsx — a public page that doesn't exist, in the site shell.
|
|
2
|
+
import { LinkButton } from '@setsquare/ui';
|
|
3
|
+
|
|
4
|
+
export default function NotFound() {
|
|
5
|
+
return (
|
|
6
|
+
<section className="section-band">
|
|
7
|
+
<div className="site-container-narrow">
|
|
8
|
+
<p className="eyebrow">Page not found</p>
|
|
9
|
+
<h1 className="text-display-page mt-3">We can’t find that page</h1>
|
|
10
|
+
<p className="text-lead mt-4 text-muted-foreground">
|
|
11
|
+
It may have moved, or the address may have a typo in it.
|
|
12
|
+
</p>
|
|
13
|
+
<div className="mt-8">
|
|
14
|
+
<LinkButton href="/" variant="primary" visualSize="lg">
|
|
15
|
+
Go to the home page
|
|
16
|
+
</LinkButton>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
</section>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// app/(site)/site-router.tsx — React Aria's links go through Next's router, and the site's navigation learns the current page from the path.
|
|
2
|
+
'use client';
|
|
3
|
+
|
|
4
|
+
import type { ReactNode } from 'react';
|
|
5
|
+
import { usePathname, useRouter } from 'next/navigation';
|
|
6
|
+
import { RouterProvider } from 'react-aria-components';
|
|
7
|
+
import { SitePathProvider } from '@setsquare/ui';
|
|
8
|
+
|
|
9
|
+
export function SiteRouter({ children }: { children: ReactNode }) {
|
|
10
|
+
const router = useRouter();
|
|
11
|
+
const pathname = usePathname();
|
|
12
|
+
return (
|
|
13
|
+
<RouterProvider navigate={(href) => router.push(href)}>
|
|
14
|
+
<SitePathProvider path={pathname}>{children}</SitePathProvider>
|
|
15
|
+
</RouterProvider>
|
|
16
|
+
);
|
|
17
|
+
}
|