@almanaclabs/blaze-ui 0.4.0 → 0.6.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/dist/components/accordion/accordion.js +2 -2
- package/dist/components/alert/alert.d.ts +1 -1
- package/dist/components/alert/alert.js +4 -4
- package/dist/components/alert-dialog/alert-dialog.d.ts.map +1 -1
- package/dist/components/alert-dialog/alert-dialog.js +3 -3
- package/dist/components/attachment/attachment.d.ts +2 -2
- package/dist/components/attachment/attachment.js +3 -3
- package/dist/components/avatar/avatar.d.ts +6 -4
- package/dist/components/avatar/avatar.d.ts.map +1 -1
- package/dist/components/avatar/avatar.js +10 -9
- package/dist/components/badge/badge.d.ts +3 -2
- package/dist/components/badge/badge.d.ts.map +1 -1
- package/dist/components/badge/badge.js +30 -14
- package/dist/components/breadcrumb/breadcrumb.d.ts.map +1 -1
- package/dist/components/breadcrumb/breadcrumb.js +2 -2
- package/dist/components/bubble/bubble.d.ts +1 -1
- package/dist/components/bubble/bubble.js +1 -1
- package/dist/components/button/button.d.ts +5 -3
- package/dist/components/button/button.d.ts.map +1 -1
- package/dist/components/button/button.js +46 -27
- package/dist/components/button/index.d.ts +1 -1
- package/dist/components/button/index.d.ts.map +1 -1
- package/dist/components/button-group/button-group.js +4 -4
- package/dist/components/calendar/calendar.d.ts.map +1 -1
- package/dist/components/calendar/calendar.js +18 -18
- package/dist/components/card/card.js +4 -4
- package/dist/components/carousel/carousel.d.ts.map +1 -1
- package/dist/components/carousel/carousel.js +31 -31
- package/dist/components/chart/chart.js +34 -34
- package/dist/components/checkbox/checkbox.js +1 -1
- package/dist/components/combobox/combobox.d.ts.map +1 -1
- package/dist/components/combobox/combobox.js +44 -44
- package/dist/components/data-table/data-table.js +1 -1
- package/dist/components/date-picker/date-picker.js +10 -10
- package/dist/components/dialog/dialog.js +3 -3
- package/dist/components/drawer/drawer.js +37 -37
- package/dist/components/dropdown-menu/dropdown-menu.js +7 -7
- package/dist/components/empty/empty.d.ts.map +1 -1
- package/dist/components/empty/empty.js +4 -4
- package/dist/components/field/field.d.ts.map +1 -1
- package/dist/components/field/field.js +28 -28
- package/dist/components/identity-mark/identity-mark.d.ts +5 -0
- package/dist/components/identity-mark/identity-mark.d.ts.map +1 -0
- package/dist/components/identity-mark/identity-mark.js +20 -0
- package/dist/components/identity-mark/index.d.ts +2 -0
- package/dist/components/identity-mark/index.d.ts.map +1 -0
- package/dist/components/input/input.d.ts +9 -2
- package/dist/components/input/input.d.ts.map +1 -1
- package/dist/components/input/input.js +23 -10
- package/dist/components/input-group/input-group.d.ts +11 -3
- package/dist/components/input-group/input-group.d.ts.map +1 -1
- package/dist/components/input-group/input-group.js +56 -38
- package/dist/components/item/item.d.ts +2 -2
- package/dist/components/item/item.js +2 -2
- package/dist/components/label/label.js +2 -2
- package/dist/components/marker/marker.d.ts +1 -1
- package/dist/components/marker/marker.js +3 -3
- package/dist/components/message/message.js +1 -1
- package/dist/components/metric-overview/index.d.ts +2 -0
- package/dist/components/metric-overview/index.d.ts.map +1 -0
- package/dist/components/metric-overview/metric-overview.d.ts +12 -0
- package/dist/components/metric-overview/metric-overview.d.ts.map +1 -0
- package/dist/components/metric-overview/metric-overview.js +42 -0
- package/dist/components/native-select/native-select.d.ts +2 -1
- package/dist/components/native-select/native-select.d.ts.map +1 -1
- package/dist/components/native-select/native-select.js +22 -21
- package/dist/components/navigation-action/index.d.ts +2 -0
- package/dist/components/navigation-action/index.d.ts.map +1 -0
- package/dist/components/navigation-action/navigation-action.d.ts +6 -0
- package/dist/components/navigation-action/navigation-action.d.ts.map +1 -0
- package/dist/components/navigation-action/navigation-action.js +18 -0
- package/dist/components/pagination/pagination.d.ts.map +1 -1
- package/dist/components/pagination/pagination.js +6 -6
- package/dist/components/popover/popover.js +1 -1
- package/dist/components/progress/progress.js +2 -2
- package/dist/components/questionnaire/questionnaire-primitive.js +71 -71
- package/dist/components/questionnaire/questionnaire.d.ts.map +1 -1
- package/dist/components/questionnaire/questionnaire.js +7 -7
- package/dist/components/scroll-area/scroll-area.js +1 -1
- package/dist/components/select/select.d.ts +2 -1
- package/dist/components/select/select.d.ts.map +1 -1
- package/dist/components/select/select.js +50 -49
- package/dist/components/sheet/sheet.js +2 -2
- package/dist/components/sidebar/sidebar.d.ts +13 -2
- package/dist/components/sidebar/sidebar.d.ts.map +1 -1
- package/dist/components/sidebar/sidebar.js +156 -95
- package/dist/components/spinner/spinner.js +1 -1
- package/dist/components/table/table.d.ts +4 -1
- package/dist/components/table/table.d.ts.map +1 -1
- package/dist/components/table/table.js +11 -9
- package/dist/components/tabs/tabs.d.ts +1 -1
- package/dist/components/tabs/tabs.js +2 -2
- package/dist/components/textarea/textarea.js +2 -2
- package/dist/components/toast/toast.d.ts.map +1 -1
- package/dist/components/toast/toast.js +6 -6
- package/dist/components/toggle/toggle.d.ts +1 -1
- package/dist/components/toggle/toggle.js +1 -1
- package/dist/components/toggle-group/toggle-group.js +10 -10
- package/dist/components/tooltip/tooltip.js +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +58 -55
- package/dist/lib/control-size.d.ts +9 -0
- package/dist/lib/control-size.d.ts.map +1 -0
- package/dist/lib/control-size.js +10 -0
- package/dist/recipes/recipe-runtime.d.ts +12 -0
- package/dist/recipes/recipe-runtime.d.ts.map +1 -1
- package/package.json +1 -1
- package/recipes/README.md +2 -0
- package/recipes/guided-workflow/README.md +51 -0
- package/recipes/guided-workflow/guided-workflow.tsx +495 -0
- package/recipes/manifest.json +8 -1
- package/recipes/workspace-shell/README.md +79 -25
- package/recipes/workspace-shell/assets/google-search-ads.svg +6 -0
- package/recipes/workspace-shell/assets/home.svg +5 -0
- package/recipes/workspace-shell/assets/radiant-health.png +0 -0
- package/recipes/workspace-shell/workspace-shell.css +55 -0
- package/recipes/workspace-shell/workspace-shell.tsx +463 -220
- package/src/styles/fonts/Sohne-Book.woff2 +0 -0
- package/src/styles/fonts/Sohne-Medium.woff2 +0 -0
- package/src/styles/fonts/Sohne-Semi.woff2 +0 -0
- package/src/styles/globals.css +235 -56
|
@@ -1,19 +1,24 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useState } from 'react'
|
|
1
4
|
import type { ComponentProps, ComponentType, ReactNode } from 'react'
|
|
2
5
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
IconAd2,
|
|
7
|
+
IconChevronDown,
|
|
8
|
+
IconFileAnalytics,
|
|
9
|
+
IconIdBadge,
|
|
10
|
+
IconLayoutSidebarLeftCollapse,
|
|
11
|
+
IconLayoutSidebarLeftExpand,
|
|
8
12
|
IconLogout,
|
|
9
|
-
|
|
13
|
+
IconMessageStar,
|
|
14
|
+
IconMessages,
|
|
15
|
+
IconPlugConnected,
|
|
16
|
+
IconScoreboard,
|
|
10
17
|
IconSettings,
|
|
11
|
-
IconStar,
|
|
12
|
-
IconTags,
|
|
13
|
-
IconTarget,
|
|
14
18
|
IconUser,
|
|
15
19
|
IconUsers,
|
|
16
|
-
|
|
20
|
+
IconWorldSearch,
|
|
21
|
+
IconWorldWww,
|
|
17
22
|
} from '@tabler/icons-react'
|
|
18
23
|
|
|
19
24
|
import {
|
|
@@ -27,27 +32,66 @@ import {
|
|
|
27
32
|
DropdownMenuLabel,
|
|
28
33
|
DropdownMenuSeparator,
|
|
29
34
|
DropdownMenuTrigger,
|
|
35
|
+
IdentityMark,
|
|
36
|
+
IdentityMarkImage,
|
|
30
37
|
Sidebar,
|
|
31
38
|
SidebarContent,
|
|
32
39
|
SidebarFooter,
|
|
33
40
|
SidebarGroup,
|
|
34
41
|
SidebarGroupContent,
|
|
35
42
|
SidebarGroupLabel,
|
|
36
|
-
SidebarHeader,
|
|
37
43
|
SidebarInset,
|
|
38
44
|
SidebarMenu,
|
|
39
45
|
SidebarMenuButton,
|
|
40
46
|
SidebarMenuItem,
|
|
41
47
|
SidebarProvider,
|
|
42
48
|
SidebarRail,
|
|
43
|
-
SidebarTrigger,
|
|
44
49
|
TooltipProvider,
|
|
50
|
+
cn,
|
|
51
|
+
useSidebar,
|
|
45
52
|
} from '@almanaclabs/blaze-ui'
|
|
46
53
|
|
|
47
|
-
|
|
54
|
+
import './workspace-shell.css'
|
|
55
|
+
|
|
56
|
+
const googleSearchAdsLogo = new URL(
|
|
57
|
+
'./assets/google-search-ads.svg',
|
|
58
|
+
import.meta.url,
|
|
59
|
+
).href
|
|
60
|
+
const radiantHealthLogo = new URL(
|
|
61
|
+
'./assets/radiant-health.png',
|
|
62
|
+
import.meta.url,
|
|
63
|
+
).href
|
|
64
|
+
|
|
65
|
+
type NavigationIconProps = {
|
|
48
66
|
className?: string
|
|
49
67
|
'aria-hidden'?: boolean | 'true' | 'false'
|
|
50
|
-
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
type NavigationIcon = ComponentType<NavigationIconProps>
|
|
71
|
+
|
|
72
|
+
function HomeNavigationIcon({
|
|
73
|
+
className,
|
|
74
|
+
'aria-hidden': ariaHidden,
|
|
75
|
+
}: NavigationIconProps) {
|
|
76
|
+
return (
|
|
77
|
+
<svg
|
|
78
|
+
viewBox="0 0 15 15"
|
|
79
|
+
fill="none"
|
|
80
|
+
aria-hidden={ariaHidden}
|
|
81
|
+
focusable="false"
|
|
82
|
+
className={cn('shrink-0', className)}
|
|
83
|
+
>
|
|
84
|
+
<path
|
|
85
|
+
d="M4.6875 10.6641H10.3125M7.06536 2.00698L2.19036 5.30377C1.99251 5.43757 1.875 5.65416 1.875 5.88503V12.0551C1.875 12.646 2.37868 13.125 3 13.125H12C12.6213 13.125 13.125 12.646 13.125 12.0551V5.88503C13.125 5.65416 13.0075 5.43757 12.8096 5.30377L7.93464 2.00698C7.67442 1.83101 7.32558 1.83101 7.06536 2.00698Z"
|
|
86
|
+
stroke="currentColor"
|
|
87
|
+
strokeLinecap="round"
|
|
88
|
+
strokeLinejoin="round"
|
|
89
|
+
vectorEffect="non-scaling-stroke"
|
|
90
|
+
style={{ strokeWidth: 'var(--icon-stroke-15)' }}
|
|
91
|
+
/>
|
|
92
|
+
</svg>
|
|
93
|
+
)
|
|
94
|
+
}
|
|
51
95
|
|
|
52
96
|
export type WorkspaceShellNavigationItem = {
|
|
53
97
|
key: string
|
|
@@ -56,67 +100,113 @@ export type WorkspaceShellNavigationItem = {
|
|
|
56
100
|
icon: NavigationIcon
|
|
57
101
|
}
|
|
58
102
|
|
|
59
|
-
export type
|
|
103
|
+
export type WorkspaceShellNavigationGroup = {
|
|
60
104
|
key: string
|
|
61
105
|
label: string
|
|
62
|
-
|
|
106
|
+
items: readonly WorkspaceShellNavigationItem[]
|
|
63
107
|
}
|
|
64
108
|
|
|
65
109
|
export type WorkspaceShellRecipeProps = {
|
|
66
|
-
title: string
|
|
67
|
-
workspaceName: string
|
|
68
|
-
userEmail: string
|
|
69
110
|
activeNavigationKey: string
|
|
70
|
-
|
|
71
|
-
accessMode?: 'am' | 'client'
|
|
111
|
+
channelTitle: string
|
|
72
112
|
children?: ReactNode
|
|
113
|
+
channelMark?: ReactNode
|
|
73
114
|
defaultSidebarOpen?: boolean
|
|
74
|
-
|
|
115
|
+
defaultSidebarWidth?: number
|
|
116
|
+
footer?: ReactNode
|
|
117
|
+
headerAction?: ReactNode
|
|
118
|
+
mode?: 'standard' | 'focused'
|
|
119
|
+
maxSidebarWidth?: number
|
|
120
|
+
minSidebarWidth?: number
|
|
121
|
+
navigationGroups?: readonly WorkspaceShellNavigationGroup[]
|
|
122
|
+
pageHeader?: ReactNode
|
|
123
|
+
onSidebarWidthChange?: (width: number) => void
|
|
124
|
+
userAvatar?: ReactNode
|
|
125
|
+
userName: string
|
|
126
|
+
workspaceMark?: ReactNode
|
|
127
|
+
workspaceName: string
|
|
75
128
|
}
|
|
76
129
|
|
|
77
|
-
const
|
|
78
|
-
|
|
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
|
-
]
|
|
130
|
+
const DEFAULT_SIDEBAR_WIDTH = 220
|
|
131
|
+
const MIN_SIDEBAR_WIDTH = 200
|
|
132
|
+
const MAX_SIDEBAR_WIDTH = 320
|
|
99
133
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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 },
|
|
134
|
+
function clampSidebarWidth(width: number, min: number, max: number) {
|
|
135
|
+
return Math.min(Math.max(width, min), max)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const defaultNavigationGroups: readonly WorkspaceShellNavigationGroup[] = [
|
|
109
139
|
{
|
|
110
|
-
key: '
|
|
111
|
-
label: '
|
|
112
|
-
|
|
113
|
-
|
|
140
|
+
key: 'primary',
|
|
141
|
+
label: 'Manage',
|
|
142
|
+
items: [
|
|
143
|
+
{
|
|
144
|
+
key: 'home',
|
|
145
|
+
label: 'Home',
|
|
146
|
+
href: '#home',
|
|
147
|
+
icon: HomeNavigationIcon,
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
key: 'analytics',
|
|
151
|
+
label: 'Analytics',
|
|
152
|
+
href: '#analytics',
|
|
153
|
+
icon: IconFileAnalytics,
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
key: 'integrations',
|
|
157
|
+
label: 'Integrations',
|
|
158
|
+
href: '#integrations',
|
|
159
|
+
icon: IconPlugConnected,
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
key: 'brand-kit',
|
|
163
|
+
label: 'Brand Kit',
|
|
164
|
+
href: '#brand-kit',
|
|
165
|
+
icon: IconIdBadge,
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
key: 'scorecard',
|
|
169
|
+
label: 'Scorecard',
|
|
170
|
+
href: '#scorecard',
|
|
171
|
+
icon: IconScoreboard,
|
|
172
|
+
},
|
|
173
|
+
],
|
|
114
174
|
},
|
|
115
175
|
{
|
|
116
|
-
key: '
|
|
117
|
-
label: '
|
|
118
|
-
|
|
119
|
-
|
|
176
|
+
key: 'channels',
|
|
177
|
+
label: 'Channels',
|
|
178
|
+
items: [
|
|
179
|
+
{
|
|
180
|
+
key: 'google-ads',
|
|
181
|
+
label: 'Google Ads',
|
|
182
|
+
href: '#google-ads',
|
|
183
|
+
icon: IconAd2,
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
key: 'seo-aeo',
|
|
187
|
+
label: 'SEO/AEO',
|
|
188
|
+
href: '#seo-aeo',
|
|
189
|
+
icon: IconWorldSearch,
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
key: 'website',
|
|
193
|
+
label: 'Website',
|
|
194
|
+
href: '#website',
|
|
195
|
+
icon: IconWorldWww,
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
key: 'reputation',
|
|
199
|
+
label: 'Reputation',
|
|
200
|
+
href: '#reputation',
|
|
201
|
+
icon: IconMessageStar,
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
key: 'inbound-sdr',
|
|
205
|
+
label: 'Inbound SDR',
|
|
206
|
+
href: '#inbound-sdr',
|
|
207
|
+
icon: IconMessages,
|
|
208
|
+
},
|
|
209
|
+
],
|
|
120
210
|
},
|
|
121
211
|
]
|
|
122
212
|
|
|
@@ -141,66 +231,139 @@ function RecipeLink({
|
|
|
141
231
|
)
|
|
142
232
|
}
|
|
143
233
|
|
|
144
|
-
function
|
|
234
|
+
function DefaultWorkspaceMark() {
|
|
145
235
|
return (
|
|
146
|
-
<
|
|
147
|
-
<
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
236
|
+
<IdentityMark data-identity="workspace" className="bg-[#45164a]">
|
|
237
|
+
<IdentityMarkImage
|
|
238
|
+
data-identity-artwork="workspace"
|
|
239
|
+
src={radiantHealthLogo}
|
|
240
|
+
alt=""
|
|
241
|
+
aria-hidden="true"
|
|
242
|
+
className="rounded-[4px] object-cover"
|
|
243
|
+
/>
|
|
244
|
+
</IdentityMark>
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function DefaultChannelMark() {
|
|
249
|
+
return (
|
|
250
|
+
<IdentityMark data-identity="channel">
|
|
251
|
+
<IdentityMarkImage
|
|
252
|
+
data-identity-artwork="channel"
|
|
253
|
+
src={googleSearchAdsLogo}
|
|
254
|
+
alt=""
|
|
255
|
+
aria-hidden="true"
|
|
256
|
+
/>
|
|
257
|
+
</IdentityMark>
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function WorkspaceSidebarTrigger() {
|
|
262
|
+
const { state, toggleSidebar } = useSidebar()
|
|
263
|
+
|
|
264
|
+
if (state === 'collapsed') {
|
|
265
|
+
return null
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return (
|
|
269
|
+
<Button
|
|
270
|
+
type="button"
|
|
271
|
+
variant="tertiary"
|
|
272
|
+
size="icon-sm"
|
|
273
|
+
aria-label="Collapse workspace navigation"
|
|
274
|
+
onClick={toggleSidebar}
|
|
275
|
+
className="mr-px shrink-0 text-foreground group-data-[collapsible=icon]:hidden"
|
|
276
|
+
>
|
|
277
|
+
<IconLayoutSidebarLeftCollapse aria-hidden="true" />
|
|
278
|
+
</Button>
|
|
279
|
+
)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function WorkspaceFrameSidebarTrigger() {
|
|
283
|
+
const { isMobile, state, toggleSidebar } = useSidebar()
|
|
284
|
+
|
|
285
|
+
if (!isMobile && state !== 'collapsed') {
|
|
286
|
+
return null
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return (
|
|
290
|
+
<Button
|
|
291
|
+
type="button"
|
|
292
|
+
variant="tertiary"
|
|
293
|
+
size="icon-sm"
|
|
294
|
+
aria-label={
|
|
295
|
+
isMobile ? 'Open workspace navigation' : 'Expand workspace navigation'
|
|
296
|
+
}
|
|
297
|
+
onClick={toggleSidebar}
|
|
298
|
+
className="shrink-0 text-foreground"
|
|
299
|
+
>
|
|
300
|
+
<IconLayoutSidebarLeftExpand aria-hidden="true" />
|
|
301
|
+
</Button>
|
|
302
|
+
)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function WorkspaceMenu({
|
|
306
|
+
workspaceMark,
|
|
307
|
+
workspaceName,
|
|
308
|
+
}: {
|
|
309
|
+
workspaceMark: ReactNode
|
|
310
|
+
workspaceName: string
|
|
311
|
+
}) {
|
|
312
|
+
return (
|
|
313
|
+
<DropdownMenu>
|
|
314
|
+
<DropdownMenuTrigger
|
|
315
|
+
render={
|
|
316
|
+
<Button
|
|
317
|
+
type="button"
|
|
318
|
+
variant="tertiary"
|
|
319
|
+
size="md"
|
|
320
|
+
aria-label={`Open workspace menu for ${workspaceName}`}
|
|
321
|
+
className="min-w-0 flex-1 justify-start gap-1.5 px-[10px] text-left group-data-[collapsible=icon]:size-6 group-data-[collapsible=icon]:flex-none group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:p-0"
|
|
322
|
+
/>
|
|
323
|
+
}
|
|
324
|
+
>
|
|
325
|
+
{workspaceMark}
|
|
326
|
+
<span className="truncate text-sm font-medium leading-[1.4] tracking-[0.01em] group-data-[collapsible=icon]:hidden">
|
|
327
|
+
{workspaceName}
|
|
328
|
+
</span>
|
|
329
|
+
<IconChevronDown
|
|
330
|
+
className="icon-optical-16 shrink-0 group-data-[collapsible=icon]:hidden"
|
|
331
|
+
aria-hidden="true"
|
|
332
|
+
/>
|
|
333
|
+
</DropdownMenuTrigger>
|
|
334
|
+
<DropdownMenuContent align="start" side="right" className="min-w-56">
|
|
335
|
+
<DropdownMenuGroup>
|
|
336
|
+
<DropdownMenuLabel>Workspace</DropdownMenuLabel>
|
|
337
|
+
<DropdownMenuItem>
|
|
338
|
+
<IconUsers aria-hidden="true" />
|
|
339
|
+
All workspaces
|
|
340
|
+
</DropdownMenuItem>
|
|
341
|
+
</DropdownMenuGroup>
|
|
342
|
+
<DropdownMenuSeparator />
|
|
343
|
+
<DropdownMenuItem>
|
|
344
|
+
<IconLogout aria-hidden="true" />
|
|
345
|
+
Sign out
|
|
346
|
+
</DropdownMenuItem>
|
|
347
|
+
</DropdownMenuContent>
|
|
348
|
+
</DropdownMenu>
|
|
186
349
|
)
|
|
187
350
|
}
|
|
188
351
|
|
|
189
352
|
function NavigationGroup({
|
|
190
|
-
|
|
353
|
+
group,
|
|
191
354
|
activeKey,
|
|
192
|
-
label,
|
|
193
355
|
}: {
|
|
194
|
-
|
|
356
|
+
group: WorkspaceShellNavigationGroup
|
|
195
357
|
activeKey: string
|
|
196
|
-
label?: string
|
|
197
358
|
}) {
|
|
198
359
|
return (
|
|
199
|
-
<SidebarGroup>
|
|
200
|
-
|
|
360
|
+
<SidebarGroup className="gap-0.5 p-0">
|
|
361
|
+
<SidebarGroupLabel className="h-[27px] rounded-[2px] px-[10px] text-xs font-medium tracking-[0.02em] text-foreground group-data-[collapsible=icon]:mt-0! group-data-[collapsible=icon]:opacity-0">
|
|
362
|
+
{group.label}
|
|
363
|
+
</SidebarGroupLabel>
|
|
201
364
|
<SidebarGroupContent>
|
|
202
|
-
<SidebarMenu>
|
|
203
|
-
{items.map((item) => (
|
|
365
|
+
<SidebarMenu className="gap-px">
|
|
366
|
+
{group.items.map((item) => (
|
|
204
367
|
<SidebarMenuItem key={item.key}>
|
|
205
368
|
<SidebarMenuButton
|
|
206
369
|
tooltip={item.label}
|
|
@@ -208,12 +371,18 @@ function NavigationGroup({
|
|
|
208
371
|
render={
|
|
209
372
|
<RecipeLink
|
|
210
373
|
href={item.href}
|
|
374
|
+
aria-label={item.label}
|
|
211
375
|
aria-current={item.key === activeKey ? 'page' : undefined}
|
|
212
376
|
/>
|
|
213
377
|
}
|
|
214
378
|
>
|
|
215
|
-
<item.icon
|
|
216
|
-
|
|
379
|
+
<item.icon
|
|
380
|
+
className="icon-optical-15 text-foreground"
|
|
381
|
+
aria-hidden="true"
|
|
382
|
+
/>
|
|
383
|
+
<span className="workspace-navigation-label group-data-[collapsible=icon]:translate-x-1 group-data-[collapsible=icon]:opacity-0">
|
|
384
|
+
{item.label}
|
|
385
|
+
</span>
|
|
217
386
|
</SidebarMenuButton>
|
|
218
387
|
</SidebarMenuItem>
|
|
219
388
|
))}
|
|
@@ -223,29 +392,34 @@ function NavigationGroup({
|
|
|
223
392
|
)
|
|
224
393
|
}
|
|
225
394
|
|
|
226
|
-
function AccountMenu({
|
|
395
|
+
function AccountMenu({
|
|
396
|
+
userAvatar,
|
|
397
|
+
userName,
|
|
398
|
+
}: {
|
|
399
|
+
userAvatar: ReactNode
|
|
400
|
+
userName: string
|
|
401
|
+
}) {
|
|
227
402
|
return (
|
|
228
403
|
<DropdownMenu>
|
|
229
404
|
<DropdownMenuTrigger
|
|
230
405
|
render={
|
|
231
406
|
<Button
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
407
|
+
type="button"
|
|
408
|
+
variant="tertiary"
|
|
409
|
+
size="md"
|
|
410
|
+
aria-label={`Open account menu for ${userName}`}
|
|
411
|
+
className="min-w-0 flex-1 justify-start gap-1.5 px-[10px] text-left font-normal group-data-[collapsible=icon]:size-5 group-data-[collapsible=icon]:flex-none group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:p-0"
|
|
236
412
|
/>
|
|
237
413
|
}
|
|
238
414
|
>
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
415
|
+
{userAvatar}
|
|
416
|
+
<span className="truncate text-sm font-normal leading-[1.4] tracking-[0.01em] group-data-[collapsible=icon]:hidden">
|
|
417
|
+
{userName}
|
|
418
|
+
</span>
|
|
242
419
|
</DropdownMenuTrigger>
|
|
243
|
-
<DropdownMenuContent align="
|
|
244
|
-
<DropdownMenuGroup>
|
|
245
|
-
<DropdownMenuLabel>{userEmail}</DropdownMenuLabel>
|
|
246
|
-
</DropdownMenuGroup>
|
|
247
|
-
<DropdownMenuSeparator />
|
|
420
|
+
<DropdownMenuContent align="start" side="right" className="min-w-56">
|
|
248
421
|
<DropdownMenuGroup>
|
|
422
|
+
<DropdownMenuLabel>{userName}</DropdownMenuLabel>
|
|
249
423
|
<DropdownMenuItem>
|
|
250
424
|
<IconUser aria-hidden="true" />
|
|
251
425
|
Account settings
|
|
@@ -262,123 +436,192 @@ function AccountMenu({ userEmail }: { userEmail: string }) {
|
|
|
262
436
|
}
|
|
263
437
|
|
|
264
438
|
export function WorkspaceShellRecipe({
|
|
265
|
-
title,
|
|
266
|
-
workspaceName,
|
|
267
|
-
userEmail,
|
|
268
439
|
activeNavigationKey,
|
|
269
|
-
|
|
440
|
+
channelTitle,
|
|
270
441
|
children,
|
|
271
|
-
|
|
442
|
+
channelMark = <DefaultChannelMark />,
|
|
272
443
|
defaultSidebarOpen = true,
|
|
273
|
-
|
|
444
|
+
defaultSidebarWidth = DEFAULT_SIDEBAR_WIDTH,
|
|
445
|
+
footer,
|
|
446
|
+
headerAction,
|
|
447
|
+
mode = 'standard',
|
|
448
|
+
maxSidebarWidth = MAX_SIDEBAR_WIDTH,
|
|
449
|
+
minSidebarWidth = MIN_SIDEBAR_WIDTH,
|
|
450
|
+
navigationGroups = defaultNavigationGroups,
|
|
451
|
+
pageHeader,
|
|
452
|
+
onSidebarWidthChange,
|
|
453
|
+
userAvatar,
|
|
454
|
+
userName,
|
|
455
|
+
workspaceMark = <DefaultWorkspaceMark />,
|
|
456
|
+
workspaceName,
|
|
274
457
|
}: WorkspaceShellRecipeProps) {
|
|
275
|
-
const
|
|
276
|
-
const
|
|
458
|
+
const resolvedMinSidebarWidth = Math.min(minSidebarWidth, maxSidebarWidth)
|
|
459
|
+
const resolvedMaxSidebarWidth = Math.max(minSidebarWidth, maxSidebarWidth)
|
|
460
|
+
const [sidebarWidth, setSidebarWidth] = useState(() =>
|
|
461
|
+
clampSidebarWidth(
|
|
462
|
+
defaultSidebarWidth,
|
|
463
|
+
resolvedMinSidebarWidth,
|
|
464
|
+
resolvedMaxSidebarWidth,
|
|
465
|
+
),
|
|
466
|
+
)
|
|
467
|
+
const [isSidebarResizing, setIsSidebarResizing] = useState(false)
|
|
468
|
+
|
|
469
|
+
useEffect(() => {
|
|
470
|
+
setSidebarWidth((currentWidth) =>
|
|
471
|
+
clampSidebarWidth(
|
|
472
|
+
currentWidth,
|
|
473
|
+
resolvedMinSidebarWidth,
|
|
474
|
+
resolvedMaxSidebarWidth,
|
|
475
|
+
),
|
|
476
|
+
)
|
|
477
|
+
}, [resolvedMaxSidebarWidth, resolvedMinSidebarWidth])
|
|
478
|
+
|
|
479
|
+
const updateSidebarWidth = useCallback(
|
|
480
|
+
(nextWidth: number) => {
|
|
481
|
+
const boundedWidth = clampSidebarWidth(
|
|
482
|
+
Math.round(nextWidth),
|
|
483
|
+
resolvedMinSidebarWidth,
|
|
484
|
+
resolvedMaxSidebarWidth,
|
|
485
|
+
)
|
|
486
|
+
if (boundedWidth === sidebarWidth) {
|
|
487
|
+
return
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
setSidebarWidth(boundedWidth)
|
|
491
|
+
onSidebarWidthChange?.(boundedWidth)
|
|
492
|
+
},
|
|
493
|
+
[
|
|
494
|
+
onSidebarWidthChange,
|
|
495
|
+
resolvedMaxSidebarWidth,
|
|
496
|
+
resolvedMinSidebarWidth,
|
|
497
|
+
sidebarWidth,
|
|
498
|
+
],
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
const resolvedUserAvatar = userAvatar ?? (
|
|
502
|
+
<Avatar size="xs" shape="rounded">
|
|
503
|
+
<AvatarFallback>{initials(userName)}</AvatarFallback>
|
|
504
|
+
</Avatar>
|
|
505
|
+
)
|
|
277
506
|
|
|
278
507
|
return (
|
|
279
508
|
<TooltipProvider>
|
|
280
509
|
<SidebarProvider
|
|
281
510
|
defaultOpen={defaultSidebarOpen}
|
|
282
|
-
|
|
511
|
+
data-sidebar-resizing={isSidebarResizing || undefined}
|
|
512
|
+
className="workspace-shell-motion h-svh min-h-[720px] overflow-hidden bg-[#f5f5f5]"
|
|
513
|
+
style={
|
|
514
|
+
{
|
|
515
|
+
'--sidebar-width': `${sidebarWidth}px`,
|
|
516
|
+
'--sidebar-width-icon': '3.4375rem',
|
|
517
|
+
} as ComponentProps<typeof SidebarProvider>['style']
|
|
518
|
+
}
|
|
283
519
|
>
|
|
284
|
-
<Sidebar
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
>
|
|
289
|
-
<
|
|
290
|
-
<
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
520
|
+
<Sidebar
|
|
521
|
+
collapsible="icon"
|
|
522
|
+
className="group-data-[side=left]:border-r-0"
|
|
523
|
+
>
|
|
524
|
+
<div className="flex min-h-0 flex-1 flex-col bg-sidebar text-sidebar-foreground">
|
|
525
|
+
<SidebarGroup className="h-[76px] shrink-0 flex-row items-center gap-2 p-0 px-[10px] group-data-[collapsible=icon]:px-[15px]">
|
|
526
|
+
<WorkspaceMenu
|
|
527
|
+
workspaceMark={workspaceMark}
|
|
528
|
+
workspaceName={workspaceName}
|
|
529
|
+
/>
|
|
530
|
+
<WorkspaceSidebarTrigger />
|
|
531
|
+
</SidebarGroup>
|
|
532
|
+
|
|
533
|
+
<SidebarContent className="pt-2">
|
|
534
|
+
<nav
|
|
535
|
+
aria-label="Workspace"
|
|
536
|
+
className="flex flex-col gap-6 px-[10px]"
|
|
537
|
+
>
|
|
538
|
+
{navigationGroups.map((group) => (
|
|
539
|
+
<NavigationGroup
|
|
540
|
+
key={group.key}
|
|
541
|
+
group={group}
|
|
542
|
+
activeKey={activeNavigationKey}
|
|
543
|
+
/>
|
|
544
|
+
))}
|
|
303
545
|
</nav>
|
|
304
546
|
</SidebarContent>
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
}
|
|
322
|
-
>
|
|
323
|
-
<IconSettings aria-hidden="true" />
|
|
324
|
-
<span>Settings</span>
|
|
325
|
-
</SidebarMenuButton>
|
|
326
|
-
</SidebarMenuItem>
|
|
327
|
-
</SidebarMenu>
|
|
328
|
-
</nav>
|
|
547
|
+
|
|
548
|
+
<SidebarFooter className="h-14 shrink-0 flex-row items-center gap-2 px-[10px] py-0 group-data-[collapsible=icon]:h-[90px] group-data-[collapsible=icon]:flex-col-reverse group-data-[collapsible=icon]:gap-[10px] group-data-[collapsible=icon]:px-0 group-data-[collapsible=icon]:py-[22px]">
|
|
549
|
+
<AccountMenu
|
|
550
|
+
userAvatar={resolvedUserAvatar}
|
|
551
|
+
userName={userName}
|
|
552
|
+
/>
|
|
553
|
+
<Button
|
|
554
|
+
nativeButton={false}
|
|
555
|
+
render={<RecipeLink href="#settings" />}
|
|
556
|
+
variant="tertiary"
|
|
557
|
+
size="icon-sm"
|
|
558
|
+
aria-label="Settings"
|
|
559
|
+
className="mr-px shrink-0 text-foreground group-data-[collapsible=icon]:mr-0"
|
|
560
|
+
>
|
|
561
|
+
<IconSettings aria-hidden="true" />
|
|
562
|
+
</Button>
|
|
329
563
|
</SidebarFooter>
|
|
330
564
|
</div>
|
|
331
|
-
<SidebarRail
|
|
565
|
+
<SidebarRail
|
|
566
|
+
mode="resize"
|
|
567
|
+
value={sidebarWidth}
|
|
568
|
+
min={resolvedMinSidebarWidth}
|
|
569
|
+
max={resolvedMaxSidebarWidth}
|
|
570
|
+
onValueChange={updateSidebarWidth}
|
|
571
|
+
onResizeStart={() => setIsSidebarResizing(true)}
|
|
572
|
+
onResizeEnd={() => setIsSidebarResizing(false)}
|
|
573
|
+
aria-label="Resize workspace navigation"
|
|
574
|
+
className="inset-y-2.5"
|
|
575
|
+
/>
|
|
332
576
|
</Sidebar>
|
|
333
577
|
|
|
334
|
-
<SidebarInset className="min-w-0">
|
|
335
|
-
<
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
578
|
+
<SidebarInset className="min-h-0 min-w-0 bg-[#f5f5f5] p-0 md:p-2.5 md:pl-0">
|
|
579
|
+
<section
|
|
580
|
+
data-slot="workspace-frame"
|
|
581
|
+
data-mode={mode}
|
|
582
|
+
aria-label={channelTitle}
|
|
583
|
+
className="flex min-h-0 flex-1 flex-col overflow-hidden border bg-background md:rounded-[10px]"
|
|
584
|
+
>
|
|
585
|
+
<header className="flex h-14 shrink-0 items-center gap-1 px-4 md:px-5">
|
|
586
|
+
<WorkspaceFrameSidebarTrigger />
|
|
587
|
+
<div className="flex min-w-0 items-center gap-2">
|
|
588
|
+
{channelMark}
|
|
589
|
+
<span className="truncate text-sm font-medium tracking-normal">
|
|
590
|
+
{channelTitle}
|
|
591
|
+
</span>
|
|
342
592
|
</div>
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
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>
|
|
593
|
+
{headerAction && (
|
|
594
|
+
<div className="ml-auto flex shrink-0 items-center">
|
|
595
|
+
{headerAction}
|
|
596
|
+
</div>
|
|
370
597
|
)}
|
|
598
|
+
</header>
|
|
371
599
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
</Button>
|
|
376
|
-
<AccountMenu userEmail={userEmail} />
|
|
600
|
+
{pageHeader && (
|
|
601
|
+
<div data-slot="workspace-page-header" className="shrink-0">
|
|
602
|
+
{pageHeader}
|
|
377
603
|
</div>
|
|
604
|
+
)}
|
|
605
|
+
|
|
606
|
+
<div
|
|
607
|
+
data-slot="workspace-content"
|
|
608
|
+
className={cn(
|
|
609
|
+
'min-h-0 flex-1',
|
|
610
|
+
mode === 'focused' ? 'overflow-hidden' : 'overflow-auto',
|
|
611
|
+
)}
|
|
612
|
+
>
|
|
613
|
+
{children}
|
|
378
614
|
</div>
|
|
379
|
-
</header>
|
|
380
615
|
|
|
381
|
-
|
|
616
|
+
{footer && (
|
|
617
|
+
<footer
|
|
618
|
+
data-slot="workspace-footer"
|
|
619
|
+
className="min-h-[88px] shrink-0 border-t bg-background"
|
|
620
|
+
>
|
|
621
|
+
{footer}
|
|
622
|
+
</footer>
|
|
623
|
+
)}
|
|
624
|
+
</section>
|
|
382
625
|
</SidebarInset>
|
|
383
626
|
</SidebarProvider>
|
|
384
627
|
</TooltipProvider>
|