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