@almanaclabs/blaze-ui 0.3.1 → 0.4.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/README.md +23 -0
- package/dist/components/accordion/accordion.js +53 -0
- package/dist/components/alert/alert.js +43 -0
- package/dist/components/alert-dialog/alert-dialog.js +94 -0
- package/dist/components/aspect-ratio/aspect-ratio.js +13 -0
- package/dist/components/attachment/attachment.js +104 -0
- package/dist/components/avatar/avatar.js +50 -0
- package/dist/components/badge/badge.js +29 -0
- package/dist/components/breadcrumb/breadcrumb.js +72 -0
- package/dist/components/bubble/bubble.js +73 -0
- package/dist/components/button/button.js +46 -0
- package/dist/components/button-group/button-group.js +41 -0
- package/dist/components/calendar/calendar.js +92 -0
- package/dist/components/card/card.js +56 -0
- package/dist/components/carousel/carousel.js +110 -0
- package/dist/components/chart/chart.js +131 -0
- package/dist/components/checkbox/checkbox.js +20 -0
- package/dist/components/collapsible/collapsible.js +23 -0
- package/dist/components/combobox/combobox.js +165 -0
- package/dist/components/data-table/data-table.js +30 -0
- package/dist/components/date-picker/date-picker.js +39 -0
- package/dist/components/dialog/dialog.js +92 -0
- package/dist/components/drawer/drawer.js +121 -0
- package/dist/components/dropdown-menu/dropdown-menu.js +136 -0
- package/dist/components/empty/empty.js +59 -0
- package/dist/components/field/field.js +107 -0
- package/dist/components/hover-card/hover-card.js +36 -0
- package/dist/components/input/input.js +15 -0
- package/dist/components/input-group/input-group.js +77 -0
- package/dist/components/item/item.js +121 -0
- package/dist/components/label/label.js +13 -0
- package/dist/components/marker/marker.js +43 -0
- package/dist/components/message/message.js +49 -0
- package/dist/components/native-select/native-select.js +39 -0
- package/dist/components/pagination/pagination.js +80 -0
- package/dist/components/popover/popover.js +54 -0
- package/dist/components/progress/progress.js +45 -0
- package/dist/components/questionnaire/questionnaire-primitive.js +353 -0
- package/dist/components/questionnaire/questionnaire.js +169 -0
- package/dist/components/radio-group/radio-group.js +26 -0
- package/dist/components/scroll-area/scroll-area.js +37 -0
- package/dist/components/select/select.js +98 -0
- package/dist/components/separator/separator.js +15 -0
- package/dist/components/sheet/sheet.js +89 -0
- package/dist/components/sidebar/sidebar.js +358 -0
- package/dist/components/skeleton/skeleton.js +12 -0
- package/dist/components/slider/slider.js +37 -0
- package/dist/components/spinner/spinner.js +15 -0
- package/dist/components/switch/switch.js +19 -0
- package/dist/components/table/table.js +66 -0
- package/dist/components/tabs/tabs.js +46 -0
- package/dist/components/textarea/textarea.js +13 -0
- package/dist/components/toast/toast.js +116 -0
- package/dist/components/toggle/toggle.js +35 -0
- package/dist/components/toggle-group/toggle-group.js +52 -0
- package/dist/components/tooltip/tooltip.js +41 -0
- package/dist/hooks/use-mobile.js +14 -0
- package/dist/index.js +56 -3699
- package/dist/lib/utils.js +8 -0
- package/dist/recipes/recipe-runtime.d.ts +6 -0
- package/dist/recipes/recipe-runtime.d.ts.map +1 -0
- package/package.json +2 -1
- package/recipes/README.md +22 -0
- package/recipes/manifest.json +12 -0
- package/recipes/workspace-shell/README.md +41 -0
- package/recipes/workspace-shell/workspace-shell.tsx +386 -0
- package/src/styles/globals.css +5 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recipe-runtime.d.ts","sourceRoot":"","sources":["../../src/recipes/recipe-runtime.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,sBAAsB,CAAA;AACpC,cAAc,sBAAsB,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@almanaclabs/blaze-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Shared React components and design tokens for Blaze",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
],
|
|
9
9
|
"files": [
|
|
10
10
|
"dist",
|
|
11
|
+
"recipes",
|
|
11
12
|
"src/styles",
|
|
12
13
|
"README.md"
|
|
13
14
|
],
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Recipes
|
|
2
|
+
|
|
3
|
+
Recipes are executable reference implementations for designers and AI agents. They
|
|
4
|
+
ship with the npm package and show how Blaze UI primitives combine into recognizable
|
|
5
|
+
product surfaces without becoming part of the package's runtime API.
|
|
6
|
+
|
|
7
|
+
Use a recipe when the page shares its anatomy and interaction model. Copy or adapt
|
|
8
|
+
the composition into the application, replace fictional data, and connect links and
|
|
9
|
+
actions through the application's framework conventions.
|
|
10
|
+
|
|
11
|
+
Installed recipes are available under
|
|
12
|
+
`node_modules/@almanaclabs/blaze-ui/recipes`. Start with `manifest.json` when
|
|
13
|
+
discovering recipes programmatically.
|
|
14
|
+
|
|
15
|
+
Preserve the recipe's semantic landmarks, responsive behavior, Blaze UI primitives,
|
|
16
|
+
and semantic tokens. Do not copy fixture data or introduce application routing,
|
|
17
|
+
authorization, serializers, or business rules into this directory.
|
|
18
|
+
|
|
19
|
+
## Available recipes
|
|
20
|
+
|
|
21
|
+
- [Workspace shell](workspace-shell/README.md): collapsible navigation, workspace
|
|
22
|
+
identity, page header, sub-navigation, account access, and a page-content slot.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"recipes": [
|
|
4
|
+
{
|
|
5
|
+
"name": "workspace-shell",
|
|
6
|
+
"description": "Authenticated Blaze workspace chrome with access-mode theming, navigation, and an empty page-content slot.",
|
|
7
|
+
"dependencies": ["@almanaclabs/blaze-ui", "@tabler/icons-react"],
|
|
8
|
+
"entry": "workspace-shell/workspace-shell.tsx",
|
|
9
|
+
"instructions": "workspace-shell/README.md"
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Workspace shell recipe
|
|
2
|
+
|
|
3
|
+
Use this recipe for authenticated pages that sit inside a selected customer
|
|
4
|
+
workspace. It establishes the persistent application chrome; the page owns only the
|
|
5
|
+
content rendered inside the shell.
|
|
6
|
+
|
|
7
|
+
## Preserve
|
|
8
|
+
|
|
9
|
+
- One `SidebarProvider` wrapping the sidebar and inset.
|
|
10
|
+
- Primary and settings navigation landmarks with an active item marked by
|
|
11
|
+
`aria-current="page"`.
|
|
12
|
+
- A single page heading in the sticky header.
|
|
13
|
+
- Optional page-level navigation centered in the header on desktop and available in
|
|
14
|
+
the normal document order.
|
|
15
|
+
- The sidebar's icon-collapse and mobile-sheet behavior.
|
|
16
|
+
- Semantic Blaze UI colors and spacing.
|
|
17
|
+
- Access-mode theming: AM mode always uses the light sidebar; client mode always uses
|
|
18
|
+
the dark sidebar.
|
|
19
|
+
|
|
20
|
+
## Adapt
|
|
21
|
+
|
|
22
|
+
- Replace the fictional workspace and user with serialized application data.
|
|
23
|
+
- Declare `@tabler/icons-react` as an application dependency because the copied
|
|
24
|
+
recipe imports its navigation icons directly.
|
|
25
|
+
- Replace `href` values through the application's routing layer. In Inertia
|
|
26
|
+
applications, render `Link` through the Blaze UI component's `render` prop rather
|
|
27
|
+
than adding a parallel client-side router.
|
|
28
|
+
- Derive the active item and access mode from server-authorized page data.
|
|
29
|
+
- Fill the empty content area with the prototype's page-specific design. The shell
|
|
30
|
+
recipe deliberately provides no example page content.
|
|
31
|
+
|
|
32
|
+
## Avoid
|
|
33
|
+
|
|
34
|
+
- Importing this recipe as a runtime package component.
|
|
35
|
+
- Moving routing, authorization, access switching, or data fetching into the recipe.
|
|
36
|
+
- Rebuilding the sidebar, header, or responsive behavior with arbitrary CSS.
|
|
37
|
+
- Using production customer names, emails, logos, or metrics in Storybook.
|
|
38
|
+
|
|
39
|
+
Start from `workspace-shell.tsx`. Use the hosted Storybook
|
|
40
|
+
`Recipes/Workspace shell` entry to review expanded, collapsed, AM-mode, client-mode,
|
|
41
|
+
and mobile states.
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
import type { ComponentProps, ComponentType, ReactNode } from 'react'
|
|
2
|
+
import {
|
|
3
|
+
IconArrowsUpDown,
|
|
4
|
+
IconBolt,
|
|
5
|
+
IconBrowser,
|
|
6
|
+
IconChartBar,
|
|
7
|
+
IconHome,
|
|
8
|
+
IconLogout,
|
|
9
|
+
IconSearch,
|
|
10
|
+
IconSettings,
|
|
11
|
+
IconStar,
|
|
12
|
+
IconTags,
|
|
13
|
+
IconTarget,
|
|
14
|
+
IconUser,
|
|
15
|
+
IconUsers,
|
|
16
|
+
IconWorld,
|
|
17
|
+
} from '@tabler/icons-react'
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
Avatar,
|
|
21
|
+
AvatarFallback,
|
|
22
|
+
Button,
|
|
23
|
+
DropdownMenu,
|
|
24
|
+
DropdownMenuContent,
|
|
25
|
+
DropdownMenuGroup,
|
|
26
|
+
DropdownMenuItem,
|
|
27
|
+
DropdownMenuLabel,
|
|
28
|
+
DropdownMenuSeparator,
|
|
29
|
+
DropdownMenuTrigger,
|
|
30
|
+
Sidebar,
|
|
31
|
+
SidebarContent,
|
|
32
|
+
SidebarFooter,
|
|
33
|
+
SidebarGroup,
|
|
34
|
+
SidebarGroupContent,
|
|
35
|
+
SidebarGroupLabel,
|
|
36
|
+
SidebarHeader,
|
|
37
|
+
SidebarInset,
|
|
38
|
+
SidebarMenu,
|
|
39
|
+
SidebarMenuButton,
|
|
40
|
+
SidebarMenuItem,
|
|
41
|
+
SidebarProvider,
|
|
42
|
+
SidebarRail,
|
|
43
|
+
SidebarTrigger,
|
|
44
|
+
TooltipProvider,
|
|
45
|
+
} from '@almanaclabs/blaze-ui'
|
|
46
|
+
|
|
47
|
+
type NavigationIcon = ComponentType<{
|
|
48
|
+
className?: string
|
|
49
|
+
'aria-hidden'?: boolean | 'true' | 'false'
|
|
50
|
+
}>
|
|
51
|
+
|
|
52
|
+
export type WorkspaceShellNavigationItem = {
|
|
53
|
+
key: string
|
|
54
|
+
label: string
|
|
55
|
+
href: string
|
|
56
|
+
icon: NavigationIcon
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type WorkspaceShellSubNavigationItem = {
|
|
60
|
+
key: string
|
|
61
|
+
label: string
|
|
62
|
+
href: string
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export type WorkspaceShellRecipeProps = {
|
|
66
|
+
title: string
|
|
67
|
+
workspaceName: string
|
|
68
|
+
userEmail: string
|
|
69
|
+
activeNavigationKey: string
|
|
70
|
+
activeSubNavigationKey?: string
|
|
71
|
+
accessMode?: 'am' | 'client'
|
|
72
|
+
children?: ReactNode
|
|
73
|
+
defaultSidebarOpen?: boolean
|
|
74
|
+
subNavigation?: readonly WorkspaceShellSubNavigationItem[]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const primaryNavigation: readonly WorkspaceShellNavigationItem[] = [
|
|
78
|
+
{ key: 'home', label: 'Home', href: '#home', icon: IconHome },
|
|
79
|
+
{
|
|
80
|
+
key: 'analytics',
|
|
81
|
+
label: 'Analytics',
|
|
82
|
+
href: '#analytics',
|
|
83
|
+
icon: IconChartBar,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
key: 'integrations',
|
|
87
|
+
label: 'Integrations',
|
|
88
|
+
href: '#integrations',
|
|
89
|
+
icon: IconBolt,
|
|
90
|
+
},
|
|
91
|
+
{ key: 'brand-kit', label: 'Brand Kit', href: '#brand-kit', icon: IconTags },
|
|
92
|
+
{
|
|
93
|
+
key: 'scorecard',
|
|
94
|
+
label: 'Scorecard',
|
|
95
|
+
href: '#scorecard',
|
|
96
|
+
icon: IconTarget,
|
|
97
|
+
},
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
const channelNavigation: readonly WorkspaceShellNavigationItem[] = [
|
|
101
|
+
{
|
|
102
|
+
key: 'google-ads',
|
|
103
|
+
label: 'Google Ads',
|
|
104
|
+
href: '#google-ads',
|
|
105
|
+
icon: IconSearch,
|
|
106
|
+
},
|
|
107
|
+
{ key: 'seo-aeo', label: 'SEO/AEO', href: '#seo-aeo', icon: IconWorld },
|
|
108
|
+
{ key: 'website', label: 'Website', href: '#website', icon: IconBrowser },
|
|
109
|
+
{
|
|
110
|
+
key: 'reputation',
|
|
111
|
+
label: 'Reputation',
|
|
112
|
+
href: '#reputation',
|
|
113
|
+
icon: IconStar,
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
key: 'inbound-sdr',
|
|
117
|
+
label: 'Inbound SDR',
|
|
118
|
+
href: '#inbound-sdr',
|
|
119
|
+
icon: IconUsers,
|
|
120
|
+
},
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
function initials(value: string) {
|
|
124
|
+
return value
|
|
125
|
+
.split(/\s+|@/)
|
|
126
|
+
.filter(Boolean)
|
|
127
|
+
.slice(0, 2)
|
|
128
|
+
.map((part) => part[0]?.toUpperCase())
|
|
129
|
+
.join('')
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function RecipeLink({
|
|
133
|
+
href,
|
|
134
|
+
children,
|
|
135
|
+
...props
|
|
136
|
+
}: ComponentProps<'a'> & { href: string }) {
|
|
137
|
+
return (
|
|
138
|
+
<a href={href} onClick={(event) => event.preventDefault()} {...props}>
|
|
139
|
+
{children}
|
|
140
|
+
</a>
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function WorkspaceSelector({ workspaceName }: { workspaceName: string }) {
|
|
145
|
+
return (
|
|
146
|
+
<SidebarMenu>
|
|
147
|
+
<SidebarMenuItem>
|
|
148
|
+
<DropdownMenu>
|
|
149
|
+
<DropdownMenuTrigger
|
|
150
|
+
render={
|
|
151
|
+
<SidebarMenuButton
|
|
152
|
+
size="lg"
|
|
153
|
+
aria-label={`Open workspace menu for ${workspaceName}`}
|
|
154
|
+
className="data-[popup-open]:bg-sidebar-accent data-[popup-open]:text-sidebar-accent-foreground"
|
|
155
|
+
/>
|
|
156
|
+
}
|
|
157
|
+
>
|
|
158
|
+
<span className="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary font-semibold text-sidebar-primary-foreground">
|
|
159
|
+
{initials(workspaceName).slice(0, 1)}
|
|
160
|
+
</span>
|
|
161
|
+
<span className="grid flex-1 text-left text-sm leading-tight">
|
|
162
|
+
<span className="truncate font-medium">{workspaceName}</span>
|
|
163
|
+
<span className="truncate text-xs text-sidebar-foreground/65">
|
|
164
|
+
Client workspace
|
|
165
|
+
</span>
|
|
166
|
+
</span>
|
|
167
|
+
<IconArrowsUpDown className="ml-auto" aria-hidden="true" />
|
|
168
|
+
</DropdownMenuTrigger>
|
|
169
|
+
<DropdownMenuContent align="start" side="right" className="min-w-56">
|
|
170
|
+
<DropdownMenuGroup>
|
|
171
|
+
<DropdownMenuLabel>Workspace</DropdownMenuLabel>
|
|
172
|
+
<DropdownMenuItem>
|
|
173
|
+
<IconUsers aria-hidden="true" />
|
|
174
|
+
All workspaces
|
|
175
|
+
</DropdownMenuItem>
|
|
176
|
+
</DropdownMenuGroup>
|
|
177
|
+
<DropdownMenuSeparator />
|
|
178
|
+
<DropdownMenuItem>
|
|
179
|
+
<IconLogout aria-hidden="true" />
|
|
180
|
+
Sign out
|
|
181
|
+
</DropdownMenuItem>
|
|
182
|
+
</DropdownMenuContent>
|
|
183
|
+
</DropdownMenu>
|
|
184
|
+
</SidebarMenuItem>
|
|
185
|
+
</SidebarMenu>
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function NavigationGroup({
|
|
190
|
+
items,
|
|
191
|
+
activeKey,
|
|
192
|
+
label,
|
|
193
|
+
}: {
|
|
194
|
+
items: readonly WorkspaceShellNavigationItem[]
|
|
195
|
+
activeKey: string
|
|
196
|
+
label?: string
|
|
197
|
+
}) {
|
|
198
|
+
return (
|
|
199
|
+
<SidebarGroup>
|
|
200
|
+
{label && <SidebarGroupLabel>{label}</SidebarGroupLabel>}
|
|
201
|
+
<SidebarGroupContent>
|
|
202
|
+
<SidebarMenu>
|
|
203
|
+
{items.map((item) => (
|
|
204
|
+
<SidebarMenuItem key={item.key}>
|
|
205
|
+
<SidebarMenuButton
|
|
206
|
+
tooltip={item.label}
|
|
207
|
+
isActive={item.key === activeKey}
|
|
208
|
+
render={
|
|
209
|
+
<RecipeLink
|
|
210
|
+
href={item.href}
|
|
211
|
+
aria-current={item.key === activeKey ? 'page' : undefined}
|
|
212
|
+
/>
|
|
213
|
+
}
|
|
214
|
+
>
|
|
215
|
+
<item.icon aria-hidden="true" />
|
|
216
|
+
<span>{item.label}</span>
|
|
217
|
+
</SidebarMenuButton>
|
|
218
|
+
</SidebarMenuItem>
|
|
219
|
+
))}
|
|
220
|
+
</SidebarMenu>
|
|
221
|
+
</SidebarGroupContent>
|
|
222
|
+
</SidebarGroup>
|
|
223
|
+
)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function AccountMenu({ userEmail }: { userEmail: string }) {
|
|
227
|
+
return (
|
|
228
|
+
<DropdownMenu>
|
|
229
|
+
<DropdownMenuTrigger
|
|
230
|
+
render={
|
|
231
|
+
<Button
|
|
232
|
+
variant="ghost"
|
|
233
|
+
size="icon"
|
|
234
|
+
aria-label={`Open account menu. Signed in as ${userEmail}`}
|
|
235
|
+
className="rounded-full"
|
|
236
|
+
/>
|
|
237
|
+
}
|
|
238
|
+
>
|
|
239
|
+
<Avatar size="sm">
|
|
240
|
+
<AvatarFallback>{initials(userEmail)}</AvatarFallback>
|
|
241
|
+
</Avatar>
|
|
242
|
+
</DropdownMenuTrigger>
|
|
243
|
+
<DropdownMenuContent align="end" className="min-w-56">
|
|
244
|
+
<DropdownMenuGroup>
|
|
245
|
+
<DropdownMenuLabel>{userEmail}</DropdownMenuLabel>
|
|
246
|
+
</DropdownMenuGroup>
|
|
247
|
+
<DropdownMenuSeparator />
|
|
248
|
+
<DropdownMenuGroup>
|
|
249
|
+
<DropdownMenuItem>
|
|
250
|
+
<IconUser aria-hidden="true" />
|
|
251
|
+
Account settings
|
|
252
|
+
</DropdownMenuItem>
|
|
253
|
+
</DropdownMenuGroup>
|
|
254
|
+
<DropdownMenuSeparator />
|
|
255
|
+
<DropdownMenuItem>
|
|
256
|
+
<IconLogout aria-hidden="true" />
|
|
257
|
+
Sign out
|
|
258
|
+
</DropdownMenuItem>
|
|
259
|
+
</DropdownMenuContent>
|
|
260
|
+
</DropdownMenu>
|
|
261
|
+
)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export function WorkspaceShellRecipe({
|
|
265
|
+
title,
|
|
266
|
+
workspaceName,
|
|
267
|
+
userEmail,
|
|
268
|
+
activeNavigationKey,
|
|
269
|
+
activeSubNavigationKey,
|
|
270
|
+
children,
|
|
271
|
+
accessMode = 'client',
|
|
272
|
+
defaultSidebarOpen = true,
|
|
273
|
+
subNavigation = [],
|
|
274
|
+
}: WorkspaceShellRecipeProps) {
|
|
275
|
+
const darkSidebar = accessMode === 'client'
|
|
276
|
+
const sidebarTheme = darkSidebar ? 'dark' : 'light'
|
|
277
|
+
|
|
278
|
+
return (
|
|
279
|
+
<TooltipProvider>
|
|
280
|
+
<SidebarProvider
|
|
281
|
+
defaultOpen={defaultSidebarOpen}
|
|
282
|
+
className="min-h-[720px]"
|
|
283
|
+
>
|
|
284
|
+
<Sidebar collapsible="icon">
|
|
285
|
+
<div
|
|
286
|
+
className={`${darkSidebar ? 'dark' : ''} flex min-h-0 flex-1 flex-col bg-sidebar text-sidebar-foreground`}
|
|
287
|
+
data-sidebar-theme={sidebarTheme}
|
|
288
|
+
>
|
|
289
|
+
<SidebarHeader className="border-b border-sidebar-border p-2">
|
|
290
|
+
<WorkspaceSelector workspaceName={workspaceName} />
|
|
291
|
+
</SidebarHeader>
|
|
292
|
+
<SidebarContent>
|
|
293
|
+
<nav aria-label="Primary">
|
|
294
|
+
<NavigationGroup
|
|
295
|
+
items={primaryNavigation}
|
|
296
|
+
activeKey={activeNavigationKey}
|
|
297
|
+
/>
|
|
298
|
+
<NavigationGroup
|
|
299
|
+
items={channelNavigation}
|
|
300
|
+
activeKey={activeNavigationKey}
|
|
301
|
+
label="Channels"
|
|
302
|
+
/>
|
|
303
|
+
</nav>
|
|
304
|
+
</SidebarContent>
|
|
305
|
+
<SidebarFooter className="p-2">
|
|
306
|
+
<nav aria-label="Settings">
|
|
307
|
+
<SidebarMenu>
|
|
308
|
+
<SidebarMenuItem>
|
|
309
|
+
<SidebarMenuButton
|
|
310
|
+
tooltip="Settings"
|
|
311
|
+
isActive={activeNavigationKey === 'settings'}
|
|
312
|
+
render={
|
|
313
|
+
<RecipeLink
|
|
314
|
+
href="#settings"
|
|
315
|
+
aria-current={
|
|
316
|
+
activeNavigationKey === 'settings'
|
|
317
|
+
? 'page'
|
|
318
|
+
: undefined
|
|
319
|
+
}
|
|
320
|
+
/>
|
|
321
|
+
}
|
|
322
|
+
>
|
|
323
|
+
<IconSettings aria-hidden="true" />
|
|
324
|
+
<span>Settings</span>
|
|
325
|
+
</SidebarMenuButton>
|
|
326
|
+
</SidebarMenuItem>
|
|
327
|
+
</SidebarMenu>
|
|
328
|
+
</nav>
|
|
329
|
+
</SidebarFooter>
|
|
330
|
+
</div>
|
|
331
|
+
<SidebarRail />
|
|
332
|
+
</Sidebar>
|
|
333
|
+
|
|
334
|
+
<SidebarInset className="min-w-0">
|
|
335
|
+
<header className="sticky top-0 z-20 shrink-0 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/80">
|
|
336
|
+
<div className="grid min-h-16 grid-cols-[minmax(0,1fr)_auto] items-center gap-3 px-4 transition-[min-height] group-has-data-[collapsible=icon]/sidebar-wrapper:min-h-12 md:grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] md:px-6">
|
|
337
|
+
<div className="col-start-1 row-start-1 flex min-w-0 items-center gap-2">
|
|
338
|
+
<SidebarTrigger className="-ml-2 shrink-0" />
|
|
339
|
+
<h1 className="truncate text-lg font-semibold tracking-tight">
|
|
340
|
+
{title}
|
|
341
|
+
</h1>
|
|
342
|
+
</div>
|
|
343
|
+
|
|
344
|
+
{subNavigation.length > 0 && (
|
|
345
|
+
<nav
|
|
346
|
+
aria-label="Page"
|
|
347
|
+
className="col-span-2 col-start-1 row-start-2 -mx-4 overflow-x-auto border-t px-4 md:col-span-1 md:col-start-2 md:row-start-1 md:mx-0 md:overflow-visible md:border-0 md:px-0"
|
|
348
|
+
>
|
|
349
|
+
<ul className="flex min-w-max items-center justify-center gap-1">
|
|
350
|
+
{subNavigation.map((item) => {
|
|
351
|
+
const active = item.key === activeSubNavigationKey
|
|
352
|
+
|
|
353
|
+
return (
|
|
354
|
+
<li key={item.key}>
|
|
355
|
+
<Button
|
|
356
|
+
aria-current={active ? 'page' : undefined}
|
|
357
|
+
nativeButton={false}
|
|
358
|
+
render={<RecipeLink href={item.href} />}
|
|
359
|
+
role="link"
|
|
360
|
+
size="sm"
|
|
361
|
+
variant={active ? 'secondary' : 'ghost'}
|
|
362
|
+
>
|
|
363
|
+
{item.label}
|
|
364
|
+
</Button>
|
|
365
|
+
</li>
|
|
366
|
+
)
|
|
367
|
+
})}
|
|
368
|
+
</ul>
|
|
369
|
+
</nav>
|
|
370
|
+
)}
|
|
371
|
+
|
|
372
|
+
<div className="col-start-2 row-start-1 flex items-center gap-2 justify-self-end md:col-start-3">
|
|
373
|
+
<Button variant="outline" size="sm">
|
|
374
|
+
{accessMode === 'client' ? 'Client' : 'AM'}
|
|
375
|
+
</Button>
|
|
376
|
+
<AccountMenu userEmail={userEmail} />
|
|
377
|
+
</div>
|
|
378
|
+
</div>
|
|
379
|
+
</header>
|
|
380
|
+
|
|
381
|
+
<div className="flex-1 p-4 md:p-6">{children}</div>
|
|
382
|
+
</SidebarInset>
|
|
383
|
+
</SidebarProvider>
|
|
384
|
+
</TooltipProvider>
|
|
385
|
+
)
|
|
386
|
+
}
|
package/src/styles/globals.css
CHANGED
|
@@ -168,6 +168,11 @@
|
|
|
168
168
|
background-color: var(--background);
|
|
169
169
|
color: var(--foreground);
|
|
170
170
|
}
|
|
171
|
+
|
|
172
|
+
button:not(:disabled):not([aria-disabled='true']),
|
|
173
|
+
[role='button']:not(:disabled):not([aria-disabled='true']) {
|
|
174
|
+
cursor: pointer;
|
|
175
|
+
}
|
|
171
176
|
}
|
|
172
177
|
|
|
173
178
|
@media (prefers-reduced-motion: reduce) {
|