azamat-ui-kit-cli 0.3.13 → 0.3.14
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/index.cjs +807 -107
- package/package.json +1 -1
- package/vendor/src/components/data-table/data-table-pagination.tsx +1 -1
- package/vendor/src/components/data-table/data-table-toolbar.tsx +13 -12
- package/vendor/src/components/data-table/data-table.tsx +14 -14
- package/vendor/src/components/display/smart-card.tsx +17 -14
- package/vendor/src/components/form/form-input.tsx +3 -1
- package/vendor/src/components/form/form-textarea.tsx +15 -12
- package/vendor/src/components/inputs/async-select.tsx +106 -47
- package/vendor/src/components/inputs/clearable-input.tsx +1 -0
- package/vendor/src/components/inputs/input-chrome.tsx +1 -1
- package/vendor/src/components/inputs/input-decorator.tsx +16 -8
- package/vendor/src/components/inputs/simple-select.tsx +28 -28
- package/vendor/src/components/layout/app-sidebar.tsx +454 -154
- package/vendor/src/components/layout/breadcrumbs.tsx +67 -22
- package/vendor/src/components/layout/sidebar-nav.tsx +316 -128
- package/vendor/src/components/overlay/confirm-dialog.tsx +31 -20
- package/vendor/src/components/ui/badge.tsx +33 -32
- package/vendor/src/components/ui/button.tsx +15 -17
- package/vendor/src/components/ui/card.tsx +26 -25
- package/vendor/src/components/ui/dialog.tsx +6 -3
- package/vendor/src/components/ui/dropdown-menu.tsx +9 -9
- package/vendor/src/components/ui/input-primitive.tsx +1 -1
- package/vendor/src/components/ui/input.tsx +105 -2
- package/vendor/src/components/ui/popover.tsx +1 -1
- package/vendor/src/components/ui/select.tsx +3 -3
- package/vendor/src/components/ui/table.tsx +4 -4
- package/vendor/src/components/ui/tabs.tsx +2 -2
- package/vendor/src/families/member-metadata.ts +3 -3
- package/vendor/templates/styles/globals.css +706 -6
|
@@ -1,179 +1,479 @@
|
|
|
1
1
|
import * as React from "react"
|
|
2
2
|
|
|
3
|
+
import { Tooltip } from "@/components/ui/tooltip"
|
|
3
4
|
import { cn } from "@/lib/utils"
|
|
4
|
-
|
|
5
|
-
export type AppSidebarNavItem = {
|
|
6
|
-
key: string
|
|
7
|
-
label: React.ReactNode
|
|
8
|
-
icon?: React.ReactNode
|
|
9
|
-
href?: string
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
5
|
+
|
|
6
|
+
export type AppSidebarNavItem = {
|
|
7
|
+
key: string
|
|
8
|
+
label: React.ReactNode
|
|
9
|
+
icon?: React.ReactNode
|
|
10
|
+
href?: string
|
|
11
|
+
items?: AppSidebarNavItem[]
|
|
12
|
+
active?: boolean
|
|
13
|
+
disabled?: boolean
|
|
14
|
+
badge?: React.ReactNode
|
|
15
|
+
hidden?: boolean
|
|
16
|
+
sectionLabel?: React.ReactNode
|
|
17
|
+
defaultExpanded?: boolean
|
|
18
|
+
current?: React.AriaAttributes["aria-current"]
|
|
19
|
+
tooltip?: React.ReactNode
|
|
20
|
+
onSelect?: () => void
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type AppSidebarFooterAccount = {
|
|
24
|
+
label: React.ReactNode
|
|
25
|
+
description?: React.ReactNode
|
|
26
|
+
avatar?: React.ReactNode
|
|
27
|
+
href?: string
|
|
28
|
+
tooltip?: React.ReactNode
|
|
29
|
+
onSelect?: () => void
|
|
30
|
+
}
|
|
31
|
+
|
|
17
32
|
export type AppSidebarProps = React.ComponentProps<"aside"> & {
|
|
18
33
|
header?: React.ReactNode
|
|
19
34
|
footer?: React.ReactNode
|
|
20
35
|
items?: AppSidebarNavItem[]
|
|
21
36
|
collapsed?: boolean
|
|
37
|
+
collapsedRail?: React.ReactNode
|
|
38
|
+
railItems?: AppSidebarNavItem[]
|
|
39
|
+
footerAccount?: AppSidebarFooterAccount
|
|
40
|
+
secondaryActions?: AppSidebarNavItem[]
|
|
41
|
+
footerSecondary?: React.ReactNode
|
|
42
|
+
tooltipOnCollapsed?: boolean
|
|
22
43
|
onItemSelect?: (item: AppSidebarNavItem) => void
|
|
23
44
|
renderItem?: (item: AppSidebarNavItem, state: { collapsed: boolean }) => React.ReactNode
|
|
24
45
|
renderLink?: (props: React.ComponentProps<"a"> & { item: AppSidebarNavItem; [key: `data-${string}`]: string | boolean | undefined }) => React.ReactNode
|
|
25
46
|
}
|
|
26
|
-
|
|
27
|
-
function
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
47
|
+
|
|
48
|
+
function hasVisibleSidebarChildren(item: AppSidebarNavItem) {
|
|
49
|
+
return item.items?.some((child) => !child.hidden) ?? false
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function isSidebarItemActive(item: AppSidebarNavItem): boolean {
|
|
53
|
+
if (item.active) return true
|
|
54
|
+
return item.items?.some((child) => isSidebarItemActive(child)) ?? false
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function SidebarLeafItem({
|
|
58
|
+
item,
|
|
59
|
+
collapsed,
|
|
60
|
+
depth,
|
|
61
|
+
onItemSelect,
|
|
62
|
+
renderLink,
|
|
63
|
+
}: {
|
|
64
|
+
item: AppSidebarNavItem
|
|
65
|
+
collapsed: boolean
|
|
66
|
+
depth: number
|
|
67
|
+
onItemSelect?: (item: AppSidebarNavItem) => void
|
|
68
|
+
renderLink?: AppSidebarProps["renderLink"]
|
|
69
|
+
}) {
|
|
70
|
+
const currentValue: React.AriaAttributes["aria-current"] = item.current ?? (item.active ? "page" : undefined)
|
|
71
|
+
const commonProps = {
|
|
72
|
+
"aria-current": currentValue,
|
|
73
|
+
"aria-disabled": item.disabled || undefined,
|
|
74
|
+
"data-slot": "app-sidebar-item" as const,
|
|
75
|
+
"data-active": item.active || undefined,
|
|
76
|
+
"data-disabled": item.disabled || undefined,
|
|
77
|
+
"data-depth": String(depth),
|
|
78
|
+
className: cn(
|
|
79
|
+
"flex min-h-9 items-center gap-2 rounded-lg border border-transparent px-2.5 text-sm font-medium outline-none transition-[background-color,border-color,color,box-shadow] data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50",
|
|
80
|
+
collapsed && "justify-center px-2"
|
|
81
|
+
),
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const content = (
|
|
85
|
+
<>
|
|
86
|
+
{item.icon && <span className="shrink-0">{item.icon}</span>}
|
|
87
|
+
{!collapsed && <span className="min-w-0 flex-1 truncate">{item.label}</span>}
|
|
88
|
+
{!collapsed && item.badge && <span className="shrink-0">{item.badge}</span>}
|
|
89
|
+
</>
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
const wrapCollapsedContent = (node: React.ReactNode) =>
|
|
93
|
+
collapsed ? (
|
|
94
|
+
<Tooltip content={item.tooltip ?? item.label} side="right">
|
|
95
|
+
<span className="block">{node}</span>
|
|
96
|
+
</Tooltip>
|
|
97
|
+
) : (
|
|
98
|
+
node
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
if (item.href?.startsWith("/")) {
|
|
102
|
+
if (renderLink) {
|
|
103
|
+
return wrapCollapsedContent(
|
|
104
|
+
<>
|
|
105
|
+
{renderLink({
|
|
106
|
+
item,
|
|
107
|
+
href: item.href,
|
|
108
|
+
...commonProps,
|
|
109
|
+
onClick: (event) => {
|
|
110
|
+
if (item.disabled) {
|
|
111
|
+
event.preventDefault()
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
item.onSelect?.()
|
|
115
|
+
onItemSelect?.(item)
|
|
116
|
+
},
|
|
117
|
+
children: content,
|
|
118
|
+
})}
|
|
119
|
+
</>
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return wrapCollapsedContent(
|
|
124
|
+
<a
|
|
125
|
+
href={item.href}
|
|
126
|
+
{...commonProps}
|
|
127
|
+
onClick={(event) => {
|
|
128
|
+
if (item.disabled) {
|
|
129
|
+
event.preventDefault()
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
item.onSelect?.()
|
|
133
|
+
onItemSelect?.(item)
|
|
134
|
+
}}
|
|
135
|
+
>
|
|
136
|
+
{content}
|
|
137
|
+
</a>
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (item.href) {
|
|
142
|
+
return wrapCollapsedContent(
|
|
143
|
+
<button
|
|
144
|
+
type="button"
|
|
145
|
+
{...commonProps}
|
|
146
|
+
className={cn(commonProps.className, "w-full")}
|
|
147
|
+
onClick={() => {
|
|
148
|
+
if (item.disabled) return
|
|
149
|
+
const href = item.href
|
|
150
|
+
if (!href) return
|
|
151
|
+
item.onSelect?.()
|
|
152
|
+
onItemSelect?.(item)
|
|
153
|
+
if (href.startsWith("http")) {
|
|
154
|
+
window.open(href, "_blank", "noopener,noreferrer")
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
window.location.assign(href)
|
|
158
|
+
}}
|
|
159
|
+
>
|
|
160
|
+
{content}
|
|
161
|
+
</button>
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return wrapCollapsedContent(
|
|
166
|
+
<button
|
|
167
|
+
type="button"
|
|
168
|
+
disabled={item.disabled}
|
|
169
|
+
{...commonProps}
|
|
170
|
+
onClick={() => {
|
|
171
|
+
item.onSelect?.()
|
|
172
|
+
onItemSelect?.(item)
|
|
173
|
+
}}
|
|
174
|
+
>
|
|
175
|
+
{content}
|
|
176
|
+
</button>
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function SidebarTree({
|
|
181
|
+
items,
|
|
182
|
+
collapsed,
|
|
183
|
+
depth,
|
|
184
|
+
onItemSelect,
|
|
185
|
+
renderLink,
|
|
186
|
+
}: {
|
|
187
|
+
items: AppSidebarNavItem[]
|
|
188
|
+
collapsed: boolean
|
|
189
|
+
depth: number
|
|
190
|
+
onItemSelect?: (item: AppSidebarNavItem) => void
|
|
191
|
+
renderLink?: AppSidebarProps["renderLink"]
|
|
192
|
+
}) {
|
|
193
|
+
return items.map((item) => {
|
|
194
|
+
if (item.hidden) return null
|
|
195
|
+
|
|
196
|
+
const hasChildren = hasVisibleSidebarChildren(item)
|
|
197
|
+
const active = isSidebarItemActive(item)
|
|
198
|
+
const showSectionLabel = !collapsed && depth === 0 && item.sectionLabel
|
|
199
|
+
|
|
200
|
+
if (!hasChildren) {
|
|
201
|
+
return (
|
|
202
|
+
<React.Fragment key={item.key}>
|
|
203
|
+
{showSectionLabel ? (
|
|
204
|
+
<div
|
|
205
|
+
data-slot="app-sidebar-group-label"
|
|
206
|
+
className="px-2.5 pb-1 pt-3 text-[11px] font-semibold uppercase tracking-[0.14em] text-muted-foreground first:pt-0"
|
|
207
|
+
>
|
|
208
|
+
{item.sectionLabel}
|
|
209
|
+
</div>
|
|
210
|
+
) : null}
|
|
211
|
+
<SidebarLeafItem
|
|
212
|
+
item={item}
|
|
213
|
+
collapsed={collapsed}
|
|
214
|
+
depth={depth}
|
|
215
|
+
onItemSelect={onItemSelect}
|
|
216
|
+
renderLink={renderLink}
|
|
217
|
+
/>
|
|
218
|
+
</React.Fragment>
|
|
219
|
+
)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const defaultExpanded = item.defaultExpanded ?? active
|
|
223
|
+
|
|
224
|
+
return (
|
|
225
|
+
<div key={item.key} data-slot="app-sidebar-group" data-depth={depth}>
|
|
226
|
+
{showSectionLabel && (
|
|
227
|
+
<div
|
|
228
|
+
data-slot="app-sidebar-group-label"
|
|
229
|
+
className="px-2.5 pb-1 pt-3 text-[11px] font-semibold uppercase tracking-[0.14em] text-muted-foreground first:pt-0"
|
|
230
|
+
>
|
|
231
|
+
{item.sectionLabel}
|
|
232
|
+
</div>
|
|
233
|
+
)}
|
|
234
|
+
<details data-slot="app-sidebar-group-details" open={defaultExpanded} className="group/app-sidebar-details">
|
|
235
|
+
<summary
|
|
236
|
+
data-slot="app-sidebar-group-trigger"
|
|
237
|
+
className={cn(
|
|
238
|
+
"flex min-h-9 list-none items-center gap-2 rounded-lg border border-transparent text-sm font-medium outline-none transition-[background-color,border-color,color,box-shadow]",
|
|
239
|
+
collapsed ? "justify-center px-2" : "px-2.5"
|
|
240
|
+
)}
|
|
241
|
+
>
|
|
242
|
+
{item.icon ? (
|
|
243
|
+
collapsed ? (
|
|
244
|
+
<Tooltip content={item.tooltip ?? item.label} side="right">
|
|
245
|
+
<span className="shrink-0">{item.icon}</span>
|
|
246
|
+
</Tooltip>
|
|
247
|
+
) : (
|
|
248
|
+
<span className="shrink-0">{item.icon}</span>
|
|
249
|
+
)
|
|
250
|
+
) : null}
|
|
251
|
+
{!collapsed && <span className="min-w-0 flex-1 truncate">{item.label}</span>}
|
|
252
|
+
{!collapsed && item.badge && <span className="shrink-0">{item.badge}</span>}
|
|
253
|
+
{!collapsed && <span data-slot="app-sidebar-group-chevron" className="ml-auto text-xs text-muted-foreground transition-transform group-open/app-sidebar-details:rotate-90">›</span>}
|
|
254
|
+
</summary>
|
|
255
|
+
<div data-slot="app-sidebar-group-content" className={cn("mt-1 space-y-1", !collapsed && "pl-3")}>
|
|
256
|
+
<SidebarTree
|
|
257
|
+
items={item.items ?? []}
|
|
258
|
+
collapsed={collapsed}
|
|
259
|
+
depth={depth + 1}
|
|
260
|
+
onItemSelect={onItemSelect}
|
|
261
|
+
renderLink={renderLink}
|
|
262
|
+
/>
|
|
263
|
+
</div>
|
|
264
|
+
</details>
|
|
265
|
+
</div>
|
|
266
|
+
)
|
|
267
|
+
})
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function SidebarActionButton({
|
|
271
|
+
item,
|
|
272
|
+
collapsed,
|
|
273
|
+
onItemSelect,
|
|
274
|
+
}: {
|
|
275
|
+
item: AppSidebarNavItem
|
|
276
|
+
collapsed: boolean
|
|
277
|
+
onItemSelect?: (item: AppSidebarNavItem) => void
|
|
278
|
+
}) {
|
|
279
|
+
const content = (
|
|
280
|
+
<button
|
|
281
|
+
type="button"
|
|
282
|
+
data-slot="app-sidebar-action"
|
|
283
|
+
data-active={item.active || undefined}
|
|
284
|
+
data-disabled={item.disabled || undefined}
|
|
285
|
+
className={cn(
|
|
286
|
+
"flex min-h-9 items-center gap-2 rounded-lg border border-transparent px-2.5 text-sm font-medium outline-none transition-[background-color,border-color,color,box-shadow] data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50",
|
|
287
|
+
collapsed && "justify-center px-2"
|
|
288
|
+
)}
|
|
289
|
+
disabled={item.disabled}
|
|
290
|
+
onClick={() => {
|
|
291
|
+
if (item.disabled) return
|
|
292
|
+
item.onSelect?.()
|
|
293
|
+
onItemSelect?.(item)
|
|
294
|
+
}}
|
|
295
|
+
>
|
|
296
|
+
{item.icon ? <span className="shrink-0">{item.icon}</span> : null}
|
|
297
|
+
{!collapsed ? <span className="min-w-0 flex-1 truncate">{item.label}</span> : null}
|
|
298
|
+
{!collapsed && item.badge ? <span className="shrink-0">{item.badge}</span> : null}
|
|
299
|
+
</button>
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
return collapsed ? (
|
|
303
|
+
<Tooltip content={item.tooltip ?? item.label} side="right">
|
|
304
|
+
<span className="block">{content}</span>
|
|
305
|
+
</Tooltip>
|
|
306
|
+
) : (
|
|
307
|
+
content
|
|
308
|
+
)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function SidebarFooterAccount({
|
|
312
|
+
account,
|
|
313
|
+
collapsed,
|
|
314
|
+
}: {
|
|
315
|
+
account: AppSidebarFooterAccount
|
|
316
|
+
collapsed: boolean
|
|
317
|
+
}) {
|
|
318
|
+
const body = (
|
|
319
|
+
<button
|
|
320
|
+
type="button"
|
|
321
|
+
data-slot="app-sidebar-account"
|
|
322
|
+
className={cn(
|
|
323
|
+
"flex w-full items-center gap-3 rounded-[min(var(--radius-xl),16px)] border border-transparent text-left transition-[background-color,border-color,color,box-shadow]",
|
|
324
|
+
collapsed ? "justify-center px-2 py-2.5" : "px-3 py-2.5"
|
|
325
|
+
)}
|
|
326
|
+
onClick={() => {
|
|
327
|
+
account.onSelect?.()
|
|
328
|
+
if (!account.href) return
|
|
329
|
+
if (account.href.startsWith("http")) {
|
|
330
|
+
window.open(account.href, "_blank", "noopener,noreferrer")
|
|
331
|
+
return
|
|
332
|
+
}
|
|
333
|
+
window.location.assign(account.href)
|
|
334
|
+
}}
|
|
335
|
+
>
|
|
336
|
+
{account.avatar ? (
|
|
337
|
+
<span
|
|
338
|
+
data-slot="app-sidebar-account-avatar"
|
|
339
|
+
className="inline-flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-full border border-border/65 bg-muted/45 text-sm font-semibold"
|
|
340
|
+
>
|
|
341
|
+
{account.avatar}
|
|
342
|
+
</span>
|
|
343
|
+
) : null}
|
|
344
|
+
{!collapsed ? (
|
|
345
|
+
<span className="min-w-0 flex-1">
|
|
346
|
+
<span data-slot="app-sidebar-account-label" className="block truncate text-sm font-semibold text-foreground">
|
|
347
|
+
{account.label}
|
|
348
|
+
</span>
|
|
349
|
+
{account.description ? (
|
|
350
|
+
<span data-slot="app-sidebar-account-description" className="block truncate text-xs text-muted-foreground">
|
|
351
|
+
{account.description}
|
|
352
|
+
</span>
|
|
353
|
+
) : null}
|
|
354
|
+
</span>
|
|
355
|
+
) : null}
|
|
356
|
+
</button>
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
return collapsed ? (
|
|
360
|
+
<Tooltip content={account.tooltip ?? account.label} side="right">
|
|
361
|
+
<span className="block">{body}</span>
|
|
362
|
+
</Tooltip>
|
|
363
|
+
) : (
|
|
364
|
+
body
|
|
365
|
+
)
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function AppSidebar({
|
|
369
|
+
className,
|
|
370
|
+
header,
|
|
371
|
+
footer,
|
|
372
|
+
items = [],
|
|
32
373
|
collapsed = false,
|
|
374
|
+
collapsedRail,
|
|
375
|
+
railItems = [],
|
|
376
|
+
footerAccount,
|
|
377
|
+
secondaryActions = [],
|
|
378
|
+
footerSecondary,
|
|
379
|
+
tooltipOnCollapsed = true,
|
|
33
380
|
onItemSelect,
|
|
34
381
|
renderItem,
|
|
35
382
|
renderLink,
|
|
36
383
|
children,
|
|
37
384
|
...props
|
|
38
385
|
}: AppSidebarProps) {
|
|
39
|
-
const visibleItems = items.filter((item) => !item.hidden)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
{
|
|
47
|
-
|
|
48
|
-
{
|
|
49
|
-
|
|
386
|
+
const visibleItems = items.filter((item) => !item.hidden)
|
|
387
|
+
const visibleRailItems = railItems.filter((item) => !item.hidden)
|
|
388
|
+
const visibleSecondaryActions = secondaryActions.filter((item) => !item.hidden)
|
|
389
|
+
|
|
390
|
+
return (
|
|
391
|
+
<aside
|
|
392
|
+
data-slot="app-sidebar"
|
|
393
|
+
data-collapsed={collapsed || undefined}
|
|
394
|
+
className={cn("flex h-full min-h-0 flex-col", className)}
|
|
395
|
+
{...props}
|
|
396
|
+
>
|
|
397
|
+
{header && <div data-slot="app-sidebar-header" className="shrink-0 border-b p-3">{header}</div>}
|
|
398
|
+
|
|
50
399
|
<nav data-slot="app-sidebar-nav" className="min-h-0 flex-1 space-y-1 overflow-y-auto p-2">
|
|
51
400
|
{children ??
|
|
52
|
-
visibleItems.map((item) =>
|
|
53
|
-
renderItem
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
"data-active": item.active || undefined,
|
|
65
|
-
"data-disabled": item.disabled || undefined,
|
|
66
|
-
className: cn(
|
|
67
|
-
"flex min-h-9 items-center gap-2 rounded-lg px-2.5 text-sm font-medium outline-none transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 focus-visible:ring-sidebar-ring data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50",
|
|
68
|
-
collapsed && "justify-center px-2"
|
|
69
|
-
),
|
|
70
|
-
onClick: (event) => {
|
|
71
|
-
if (item.disabled) {
|
|
72
|
-
event.preventDefault()
|
|
73
|
-
return
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
item.onSelect?.()
|
|
77
|
-
onItemSelect?.(item)
|
|
78
|
-
},
|
|
79
|
-
children: (
|
|
80
|
-
<>
|
|
81
|
-
{item.icon && <span className="shrink-0">{item.icon}</span>}
|
|
82
|
-
{!collapsed && <span className="min-w-0 flex-1 truncate">{item.label}</span>}
|
|
83
|
-
{!collapsed && item.badge && <span className="shrink-0">{item.badge}</span>}
|
|
84
|
-
</>
|
|
85
|
-
),
|
|
86
|
-
})}
|
|
87
|
-
</React.Fragment>
|
|
88
|
-
) : (
|
|
89
|
-
<a
|
|
90
|
-
key={item.key}
|
|
91
|
-
href={item.href}
|
|
92
|
-
aria-current={item.active ? "page" : undefined}
|
|
93
|
-
aria-disabled={item.disabled || undefined}
|
|
94
|
-
data-active={item.active || undefined}
|
|
95
|
-
data-disabled={item.disabled || undefined}
|
|
96
|
-
className={cn(
|
|
97
|
-
"flex min-h-9 items-center gap-2 rounded-lg px-2.5 text-sm font-medium outline-none transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 focus-visible:ring-sidebar-ring data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50",
|
|
98
|
-
collapsed && "justify-center px-2"
|
|
99
|
-
)}
|
|
100
|
-
onClick={(event) => {
|
|
101
|
-
if (item.disabled) {
|
|
102
|
-
event.preventDefault()
|
|
103
|
-
return
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
item.onSelect?.()
|
|
107
|
-
onItemSelect?.(item)
|
|
108
|
-
}}
|
|
109
|
-
>
|
|
110
|
-
{item.icon && <span className="shrink-0">{item.icon}</span>}
|
|
111
|
-
{!collapsed && <span className="min-w-0 flex-1 truncate">{item.label}</span>}
|
|
112
|
-
{!collapsed && item.badge && <span className="shrink-0">{item.badge}</span>}
|
|
113
|
-
</a>
|
|
114
|
-
)
|
|
115
|
-
) : item.href ? (
|
|
116
|
-
<button
|
|
117
|
-
key={item.key}
|
|
118
|
-
type="button"
|
|
119
|
-
aria-current={item.active ? "page" : undefined}
|
|
120
|
-
aria-disabled={item.disabled || undefined}
|
|
121
|
-
data-active={item.active || undefined}
|
|
122
|
-
data-disabled={item.disabled || undefined}
|
|
123
|
-
className={cn(
|
|
124
|
-
"flex min-h-9 w-full items-center gap-2 rounded-lg px-2.5 text-sm font-medium outline-none transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 focus-visible:ring-sidebar-ring data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50",
|
|
125
|
-
collapsed && "justify-center px-2"
|
|
126
|
-
)}
|
|
127
|
-
onClick={() => {
|
|
128
|
-
if (item.disabled) return
|
|
129
|
-
const href = item.href
|
|
130
|
-
if (!href) return
|
|
131
|
-
|
|
132
|
-
item.onSelect?.()
|
|
133
|
-
onItemSelect?.(item)
|
|
134
|
-
|
|
135
|
-
if (href.startsWith("http")) {
|
|
136
|
-
window.open(href, "_blank", "noopener,noreferrer")
|
|
137
|
-
return
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
window.location.assign(href)
|
|
141
|
-
}}
|
|
142
|
-
>
|
|
143
|
-
{item.icon && <span className="shrink-0">{item.icon}</span>}
|
|
144
|
-
{!collapsed && <span className="min-w-0 flex-1 truncate">{item.label}</span>}
|
|
145
|
-
{!collapsed && item.badge && <span className="shrink-0">{item.badge}</span>}
|
|
146
|
-
</button>
|
|
147
|
-
) : (
|
|
148
|
-
<button
|
|
149
|
-
key={item.key}
|
|
150
|
-
type="button"
|
|
151
|
-
disabled={item.disabled}
|
|
152
|
-
aria-current={item.active ? "page" : undefined}
|
|
153
|
-
aria-disabled={item.disabled || undefined}
|
|
154
|
-
data-active={item.active || undefined}
|
|
155
|
-
data-disabled={item.disabled || undefined}
|
|
156
|
-
className={cn(
|
|
157
|
-
"flex min-h-9 items-center gap-2 rounded-lg px-2.5 text-sm font-medium outline-none transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 focus-visible:ring-sidebar-ring data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50",
|
|
158
|
-
collapsed && "justify-center px-2"
|
|
159
|
-
)}
|
|
160
|
-
onClick={() => {
|
|
161
|
-
item.onSelect?.()
|
|
162
|
-
onItemSelect?.(item)
|
|
163
|
-
}}
|
|
164
|
-
>
|
|
165
|
-
{item.icon && <span className="shrink-0">{item.icon}</span>}
|
|
166
|
-
{!collapsed && <span className="min-w-0 flex-1 truncate">{item.label}</span>}
|
|
167
|
-
{!collapsed && item.badge && <span className="shrink-0">{item.badge}</span>}
|
|
168
|
-
</button>
|
|
169
|
-
)
|
|
401
|
+
visibleItems.map((item) => {
|
|
402
|
+
if (!renderItem) return null
|
|
403
|
+
|
|
404
|
+
const renderedItem = renderItem(item, { collapsed })
|
|
405
|
+
if (!collapsed || !tooltipOnCollapsed) {
|
|
406
|
+
return <React.Fragment key={item.key}>{renderedItem}</React.Fragment>
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return (
|
|
410
|
+
<Tooltip key={item.key} content={item.tooltip ?? item.label} side="right">
|
|
411
|
+
<span className="block">{renderedItem}</span>
|
|
412
|
+
</Tooltip>
|
|
170
413
|
)
|
|
171
|
-
)}
|
|
414
|
+
})}
|
|
415
|
+
{!children && !renderItem && (
|
|
416
|
+
<SidebarTree
|
|
417
|
+
items={visibleItems}
|
|
418
|
+
collapsed={collapsed}
|
|
419
|
+
depth={0}
|
|
420
|
+
onItemSelect={onItemSelect}
|
|
421
|
+
renderLink={renderLink}
|
|
422
|
+
/>
|
|
423
|
+
)}
|
|
172
424
|
</nav>
|
|
173
|
-
|
|
174
|
-
{footer &&
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
425
|
+
|
|
426
|
+
{(footerAccount || footerSecondary || footer || visibleSecondaryActions.length > 0 || (collapsed && (collapsedRail || visibleRailItems.length > 0))) && (
|
|
427
|
+
<div data-slot="app-sidebar-footer" className="shrink-0 border-t p-3">
|
|
428
|
+
{collapsed ? (
|
|
429
|
+
<>
|
|
430
|
+
{visibleRailItems.length > 0 ? (
|
|
431
|
+
<div data-slot="app-sidebar-rail-actions" className="grid gap-2">
|
|
432
|
+
{visibleRailItems.map((item) => (
|
|
433
|
+
<SidebarActionButton
|
|
434
|
+
key={item.key}
|
|
435
|
+
item={item}
|
|
436
|
+
collapsed
|
|
437
|
+
onItemSelect={onItemSelect}
|
|
438
|
+
/>
|
|
439
|
+
))}
|
|
440
|
+
</div>
|
|
441
|
+
) : null}
|
|
442
|
+
{collapsedRail ? <div data-slot="app-sidebar-rail">{collapsedRail}</div> : null}
|
|
443
|
+
</>
|
|
444
|
+
) : null}
|
|
445
|
+
{!collapsed && footerAccount ? (
|
|
446
|
+
<div data-slot="app-sidebar-account-wrap" className="mb-3">
|
|
447
|
+
<SidebarFooterAccount account={footerAccount} collapsed={false} />
|
|
448
|
+
</div>
|
|
449
|
+
) : null}
|
|
450
|
+
{!collapsed && visibleSecondaryActions.length > 0 ? (
|
|
451
|
+
<div data-slot="app-sidebar-secondary-actions" className="mb-3 grid gap-2">
|
|
452
|
+
{visibleSecondaryActions.map((item) => (
|
|
453
|
+
<SidebarActionButton
|
|
454
|
+
key={item.key}
|
|
455
|
+
item={item}
|
|
456
|
+
collapsed={false}
|
|
457
|
+
onItemSelect={onItemSelect}
|
|
458
|
+
/>
|
|
459
|
+
))}
|
|
460
|
+
</div>
|
|
461
|
+
) : null}
|
|
462
|
+
{!collapsed && footerSecondary ? (
|
|
463
|
+
<div data-slot="app-sidebar-footer-secondary" className="mb-3">
|
|
464
|
+
{footerSecondary}
|
|
465
|
+
</div>
|
|
466
|
+
) : null}
|
|
467
|
+
{collapsed && footerAccount ? (
|
|
468
|
+
<div data-slot="app-sidebar-account-wrap">
|
|
469
|
+
<SidebarFooterAccount account={footerAccount} collapsed />
|
|
470
|
+
</div>
|
|
471
|
+
) : null}
|
|
472
|
+
{!collapsed && footer}
|
|
473
|
+
</div>
|
|
474
|
+
)}
|
|
475
|
+
</aside>
|
|
476
|
+
)
|
|
477
|
+
}
|
|
178
478
|
|
|
179
479
|
export { AppSidebar }
|