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
|
@@ -3,66 +3,111 @@ import { ChevronRightIcon } from "lucide-react"
|
|
|
3
3
|
|
|
4
4
|
import { cn } from "@/lib/utils"
|
|
5
5
|
|
|
6
|
-
export type BreadcrumbItem = {
|
|
7
|
-
key: string
|
|
8
|
-
label: React.ReactNode
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
export type BreadcrumbItem = {
|
|
7
|
+
key: string
|
|
8
|
+
label: React.ReactNode
|
|
9
|
+
icon?: React.ReactNode
|
|
10
|
+
href?: string
|
|
11
|
+
current?: boolean
|
|
12
|
+
currentLabel?: React.AriaAttributes["aria-current"]
|
|
13
|
+
onSelect?: () => void
|
|
14
|
+
}
|
|
13
15
|
|
|
14
16
|
export type BreadcrumbsProps = React.ComponentProps<"nav"> & {
|
|
15
17
|
items: BreadcrumbItem[]
|
|
18
|
+
maxItems?: number
|
|
19
|
+
collapseLabel?: React.ReactNode
|
|
16
20
|
separator?: React.ReactNode
|
|
17
21
|
renderLink?: (props: React.ComponentProps<"a"> & { item: BreadcrumbItem; [key: `data-${string}`]: string | boolean | undefined }) => React.ReactNode
|
|
18
22
|
}
|
|
19
23
|
|
|
24
|
+
function collapseBreadcrumbItems(items: BreadcrumbItem[], maxItems: number, collapseLabel: React.ReactNode): BreadcrumbItem[] {
|
|
25
|
+
if (items.length <= maxItems || maxItems < 3) return items
|
|
26
|
+
|
|
27
|
+
const firstItem = items[0]
|
|
28
|
+
const trailingCount = Math.max(maxItems - 2, 1)
|
|
29
|
+
const trailingItems = items.slice(-trailingCount)
|
|
30
|
+
|
|
31
|
+
return [
|
|
32
|
+
firstItem,
|
|
33
|
+
{
|
|
34
|
+
key: "__collapsed__",
|
|
35
|
+
label: collapseLabel,
|
|
36
|
+
},
|
|
37
|
+
...trailingItems,
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
|
|
20
41
|
function Breadcrumbs({
|
|
21
42
|
className,
|
|
22
43
|
items,
|
|
44
|
+
maxItems,
|
|
45
|
+
collapseLabel = "…",
|
|
23
46
|
separator = <ChevronRightIcon className="size-3.5" />,
|
|
24
47
|
renderLink,
|
|
25
48
|
...props
|
|
26
49
|
}: BreadcrumbsProps) {
|
|
27
|
-
|
|
50
|
+
const resolvedItems = React.useMemo(
|
|
51
|
+
() => (typeof maxItems === "number" ? collapseBreadcrumbItems(items, maxItems, collapseLabel) : items),
|
|
52
|
+
[collapseLabel, items, maxItems]
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return (
|
|
28
56
|
<nav
|
|
29
57
|
data-slot="breadcrumbs"
|
|
30
58
|
aria-label="Breadcrumb"
|
|
31
59
|
className={cn("flex min-w-0 items-center gap-1 text-sm text-muted-foreground", className)}
|
|
32
60
|
{...props}
|
|
33
61
|
>
|
|
34
|
-
{
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
|
|
62
|
+
{resolvedItems.map((item, index) => {
|
|
63
|
+
const isCollapsed = item.key === "__collapsed__"
|
|
64
|
+
const isLast = index === resolvedItems.length - 1
|
|
65
|
+
const isCurrent = item.current || isLast
|
|
66
|
+
|
|
38
67
|
return (
|
|
39
68
|
<React.Fragment key={item.key}>
|
|
40
69
|
{index > 0 && <span className="shrink-0 opacity-60">{separator}</span>}
|
|
41
|
-
{
|
|
70
|
+
{isCollapsed ? (
|
|
71
|
+
<span data-slot="breadcrumbs-collapsed" className="shrink-0 rounded-full border border-border/65 px-2 py-0.5 text-xs text-muted-foreground">
|
|
72
|
+
{item.label}
|
|
73
|
+
</span>
|
|
74
|
+
) : item.href && !isCurrent ? (
|
|
42
75
|
renderLink ? renderLink({
|
|
43
76
|
item,
|
|
44
77
|
href: item.href,
|
|
78
|
+
"data-slot": "breadcrumbs-link",
|
|
45
79
|
className: "truncate transition-colors hover:text-foreground",
|
|
46
80
|
onClick: () => item.onSelect?.(),
|
|
47
|
-
children:
|
|
81
|
+
children: (
|
|
82
|
+
<span className="inline-flex min-w-0 items-center gap-1.5">
|
|
83
|
+
{item.icon ? <span className="shrink-0">{item.icon}</span> : null}
|
|
84
|
+
<span className="truncate">{item.label}</span>
|
|
85
|
+
</span>
|
|
86
|
+
),
|
|
48
87
|
}) : (
|
|
49
88
|
<a
|
|
50
89
|
href={item.href}
|
|
90
|
+
data-slot="breadcrumbs-link"
|
|
51
91
|
className="truncate transition-colors hover:text-foreground"
|
|
52
92
|
onClick={() => item.onSelect?.()}
|
|
53
93
|
>
|
|
54
|
-
|
|
94
|
+
<span className="inline-flex min-w-0 items-center gap-1.5">
|
|
95
|
+
{item.icon ? <span className="shrink-0">{item.icon}</span> : null}
|
|
96
|
+
<span className="truncate">{item.label}</span>
|
|
97
|
+
</span>
|
|
55
98
|
</a>
|
|
56
99
|
)
|
|
57
100
|
) : (
|
|
58
101
|
<span
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
102
|
+
data-slot="breadcrumbs-current"
|
|
103
|
+
aria-current={isCurrent ? (item.currentLabel ?? "page") : undefined}
|
|
104
|
+
className={cn("inline-flex min-w-0 items-center gap-1.5 truncate", isCurrent && "font-medium text-foreground")}
|
|
105
|
+
>
|
|
106
|
+
{item.icon ? <span className="shrink-0">{item.icon}</span> : null}
|
|
107
|
+
<span className="truncate">{item.label}</span>
|
|
108
|
+
</span>
|
|
109
|
+
)}
|
|
110
|
+
</React.Fragment>
|
|
66
111
|
)
|
|
67
112
|
})}
|
|
68
113
|
</nav>
|
|
@@ -1,147 +1,335 @@
|
|
|
1
1
|
import * as React from "react"
|
|
2
2
|
|
|
3
3
|
import { Badge } from "@/components/ui/badge"
|
|
4
|
+
import { Tooltip } from "@/components/ui/tooltip"
|
|
4
5
|
import { cn } from "@/lib/utils"
|
|
5
|
-
|
|
6
|
-
export type SidebarNavItem = {
|
|
7
|
-
key: string
|
|
8
|
-
label: React.ReactNode
|
|
9
|
-
icon?: React.ReactNode
|
|
10
|
-
badge?: React.ReactNode
|
|
11
|
-
href?: string
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
6
|
+
|
|
7
|
+
export type SidebarNavItem = {
|
|
8
|
+
key: string
|
|
9
|
+
label: React.ReactNode
|
|
10
|
+
icon?: React.ReactNode
|
|
11
|
+
badge?: React.ReactNode
|
|
12
|
+
href?: string
|
|
13
|
+
items?: SidebarNavItem[]
|
|
14
|
+
active?: boolean
|
|
15
|
+
current?: React.AriaAttributes["aria-current"]
|
|
16
|
+
disabled?: boolean
|
|
17
|
+
hidden?: boolean
|
|
18
|
+
sectionLabel?: React.ReactNode
|
|
19
|
+
defaultExpanded?: boolean
|
|
20
|
+
tooltip?: React.ReactNode
|
|
21
|
+
onSelect?: () => void
|
|
22
|
+
}
|
|
23
|
+
|
|
18
24
|
export type SidebarNavProps = React.ComponentProps<"nav"> & {
|
|
19
25
|
items: SidebarNavItem[]
|
|
20
26
|
collapsed?: boolean
|
|
27
|
+
tooltipOnCollapsed?: boolean
|
|
21
28
|
itemClassName?: string
|
|
22
29
|
activeItemClassName?: string
|
|
23
30
|
renderItem?: (item: SidebarNavItem, element: React.ReactNode) => React.ReactNode
|
|
24
31
|
renderLink?: (props: React.ComponentProps<"a"> & { item: SidebarNavItem; [key: `data-${string}`]: string | boolean | undefined }) => React.ReactNode
|
|
25
32
|
}
|
|
26
|
-
|
|
27
|
-
function
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
33
|
+
|
|
34
|
+
function hasVisibleChildren(item: SidebarNavItem) {
|
|
35
|
+
return item.items?.some((child) => !child.hidden) ?? false
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function isActiveItem(item: SidebarNavItem): boolean {
|
|
39
|
+
if (item.active) return true
|
|
40
|
+
return item.items?.some((child) => isActiveItem(child)) ?? false
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function SidebarLeaf({
|
|
44
|
+
item,
|
|
45
|
+
collapsed,
|
|
46
|
+
depth,
|
|
47
|
+
itemClassName,
|
|
48
|
+
activeItemClassName,
|
|
49
|
+
renderLink,
|
|
50
|
+
}: {
|
|
51
|
+
item: SidebarNavItem
|
|
52
|
+
collapsed: boolean
|
|
53
|
+
depth: number
|
|
54
|
+
itemClassName?: string
|
|
55
|
+
activeItemClassName?: string
|
|
56
|
+
renderLink?: SidebarNavProps["renderLink"]
|
|
57
|
+
}) {
|
|
58
|
+
const currentValue: React.AriaAttributes["aria-current"] = item.current ?? (item.active ? "page" : undefined)
|
|
59
|
+
const content = (
|
|
60
|
+
<>
|
|
61
|
+
{item.icon && <span className="shrink-0 [&_svg]:size-4">{item.icon}</span>}
|
|
62
|
+
{!collapsed && <span className="min-w-0 flex-1 truncate">{item.label}</span>}
|
|
63
|
+
{!collapsed && item.badge && (
|
|
64
|
+
<Badge variant="secondary" className="ml-auto h-5 shrink-0 px-1.5">
|
|
65
|
+
{item.badge}
|
|
66
|
+
</Badge>
|
|
67
|
+
)}
|
|
68
|
+
</>
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
const commonClassName = cn(
|
|
72
|
+
"group flex min-h-8 items-center gap-2 rounded-lg border border-transparent px-2 text-sm font-medium transition-[background-color,border-color,color,box-shadow] data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50",
|
|
73
|
+
collapsed && "justify-center px-0",
|
|
74
|
+
depth > 0 && !collapsed && "pl-3",
|
|
75
|
+
itemClassName,
|
|
76
|
+
item.active && activeItemClassName
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
const wrapCollapsed = (node: React.ReactNode) =>
|
|
80
|
+
collapsed ? (
|
|
81
|
+
<Tooltip content={item.tooltip ?? item.label} side="right">
|
|
82
|
+
<span className="block">{node}</span>
|
|
83
|
+
</Tooltip>
|
|
84
|
+
) : (
|
|
85
|
+
node
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
const internal = item.href && item.href.startsWith("/")
|
|
89
|
+
|
|
90
|
+
if (internal) {
|
|
91
|
+
if (renderLink) {
|
|
92
|
+
return wrapCollapsed(
|
|
93
|
+
renderLink({
|
|
94
|
+
item,
|
|
95
|
+
href: item.href,
|
|
96
|
+
"data-slot": "sidebar-nav-item",
|
|
97
|
+
"data-depth": String(depth),
|
|
98
|
+
"data-active": item.active || undefined,
|
|
99
|
+
"data-disabled": item.disabled || undefined,
|
|
100
|
+
"aria-current": currentValue,
|
|
101
|
+
className: commonClassName,
|
|
102
|
+
onClick: (event) => {
|
|
103
|
+
if (item.disabled) {
|
|
104
|
+
event.preventDefault()
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
item.onSelect?.()
|
|
108
|
+
},
|
|
109
|
+
children: content,
|
|
110
|
+
})
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return wrapCollapsed(
|
|
115
|
+
<a
|
|
116
|
+
href={item.href}
|
|
117
|
+
data-slot="sidebar-nav-item"
|
|
118
|
+
data-depth={depth}
|
|
119
|
+
data-active={item.active || undefined}
|
|
120
|
+
data-disabled={item.disabled || undefined}
|
|
121
|
+
aria-current={currentValue}
|
|
122
|
+
className={commonClassName}
|
|
123
|
+
onClick={(event) => {
|
|
124
|
+
if (item.disabled) {
|
|
125
|
+
event.preventDefault()
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
item.onSelect?.()
|
|
129
|
+
}}
|
|
130
|
+
>
|
|
131
|
+
{content}
|
|
132
|
+
</a>
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (item.href) {
|
|
137
|
+
return wrapCollapsed(
|
|
138
|
+
<button
|
|
139
|
+
type="button"
|
|
140
|
+
data-slot="sidebar-nav-item"
|
|
141
|
+
data-depth={depth}
|
|
142
|
+
data-active={item.active || undefined}
|
|
143
|
+
data-disabled={item.disabled || undefined}
|
|
144
|
+
aria-current={currentValue}
|
|
145
|
+
className={cn("w-full text-left", commonClassName)}
|
|
146
|
+
onClick={() => {
|
|
147
|
+
if (item.disabled) return
|
|
148
|
+
const href = item.href
|
|
149
|
+
if (!href) return
|
|
150
|
+
item.onSelect?.()
|
|
151
|
+
if (href.startsWith("http")) {
|
|
152
|
+
window.open(href, "_blank", "noopener,noreferrer")
|
|
153
|
+
return
|
|
154
|
+
}
|
|
155
|
+
window.location.assign(href)
|
|
156
|
+
}}
|
|
157
|
+
>
|
|
158
|
+
{content}
|
|
159
|
+
</button>
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return wrapCollapsed(
|
|
164
|
+
<button
|
|
165
|
+
type="button"
|
|
166
|
+
data-slot="sidebar-nav-item"
|
|
167
|
+
data-depth={depth}
|
|
168
|
+
data-active={item.active || undefined}
|
|
169
|
+
data-disabled={item.disabled || undefined}
|
|
170
|
+
aria-current={currentValue}
|
|
171
|
+
disabled={item.disabled}
|
|
172
|
+
className={cn("w-full text-left", commonClassName)}
|
|
173
|
+
onClick={item.onSelect}
|
|
174
|
+
>
|
|
175
|
+
{content}
|
|
176
|
+
</button>
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function SidebarTree({
|
|
181
|
+
items,
|
|
182
|
+
collapsed,
|
|
183
|
+
depth,
|
|
31
184
|
itemClassName,
|
|
32
185
|
activeItemClassName,
|
|
33
186
|
renderItem,
|
|
34
187
|
renderLink,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const element = item.href && isInternalLink ? (
|
|
69
|
-
renderLink ? (
|
|
70
|
-
<React.Fragment key={item.key}>
|
|
71
|
-
{renderLink({
|
|
72
|
-
item,
|
|
73
|
-
href: item.href,
|
|
74
|
-
"data-active": item.active || undefined,
|
|
75
|
-
"data-disabled": item.disabled || undefined,
|
|
76
|
-
"aria-current": item.active ? "page" : undefined,
|
|
77
|
-
className: commonClassName,
|
|
78
|
-
onClick: (event) => {
|
|
79
|
-
if (item.disabled) event.preventDefault()
|
|
80
|
-
item.onSelect?.()
|
|
81
|
-
},
|
|
82
|
-
children: content,
|
|
83
|
-
})}
|
|
84
|
-
</React.Fragment>
|
|
85
|
-
) : (
|
|
86
|
-
<a
|
|
87
|
-
key={item.key}
|
|
88
|
-
href={item.href}
|
|
89
|
-
data-active={item.active || undefined}
|
|
90
|
-
data-disabled={item.disabled || undefined}
|
|
91
|
-
aria-current={item.active ? "page" : undefined}
|
|
92
|
-
className={commonClassName}
|
|
93
|
-
onClick={(event) => {
|
|
94
|
-
if (item.disabled) event.preventDefault()
|
|
95
|
-
item.onSelect?.()
|
|
96
|
-
}}
|
|
188
|
+
}: {
|
|
189
|
+
items: SidebarNavItem[]
|
|
190
|
+
collapsed: boolean
|
|
191
|
+
depth: number
|
|
192
|
+
itemClassName?: string
|
|
193
|
+
activeItemClassName?: string
|
|
194
|
+
renderItem?: SidebarNavProps["renderItem"]
|
|
195
|
+
renderLink?: SidebarNavProps["renderLink"]
|
|
196
|
+
}) {
|
|
197
|
+
return items.map((item) => {
|
|
198
|
+
if (item.hidden) return null
|
|
199
|
+
|
|
200
|
+
const showSectionLabel = !collapsed && depth === 0 && item.sectionLabel
|
|
201
|
+
const hasChildren = hasVisibleChildren(item)
|
|
202
|
+
|
|
203
|
+
if (!hasChildren) {
|
|
204
|
+
const element = (
|
|
205
|
+
<SidebarLeaf
|
|
206
|
+
item={item}
|
|
207
|
+
collapsed={collapsed}
|
|
208
|
+
depth={depth}
|
|
209
|
+
itemClassName={itemClassName}
|
|
210
|
+
activeItemClassName={activeItemClassName}
|
|
211
|
+
renderLink={renderLink}
|
|
212
|
+
/>
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
return (
|
|
216
|
+
<React.Fragment key={item.key}>
|
|
217
|
+
{showSectionLabel ? (
|
|
218
|
+
<div
|
|
219
|
+
data-slot="sidebar-nav-group-label"
|
|
220
|
+
className="px-2 pb-1 pt-3 text-[11px] font-semibold uppercase tracking-[0.14em] text-muted-foreground first:pt-0"
|
|
97
221
|
>
|
|
98
|
-
{
|
|
99
|
-
</
|
|
222
|
+
{item.sectionLabel}
|
|
223
|
+
</div>
|
|
224
|
+
) : null}
|
|
225
|
+
{renderItem ? renderItem(item, element) : element}
|
|
226
|
+
</React.Fragment>
|
|
227
|
+
)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const isExpanded = item.defaultExpanded ?? isActiveItem(item)
|
|
231
|
+
const trigger = (
|
|
232
|
+
<summary
|
|
233
|
+
data-slot="sidebar-nav-group-trigger"
|
|
234
|
+
className={cn(
|
|
235
|
+
"flex min-h-8 list-none items-center gap-2 rounded-lg border border-transparent text-sm font-medium transition-[background-color,border-color,color,box-shadow]",
|
|
236
|
+
collapsed ? "justify-center px-0" : "px-2"
|
|
237
|
+
)}
|
|
238
|
+
>
|
|
239
|
+
{item.icon ? (
|
|
240
|
+
collapsed ? (
|
|
241
|
+
<Tooltip content={item.tooltip ?? item.label} side="right">
|
|
242
|
+
<span className="shrink-0 [&_svg]:size-4">{item.icon}</span>
|
|
243
|
+
</Tooltip>
|
|
244
|
+
) : (
|
|
245
|
+
<span className="shrink-0 [&_svg]:size-4">{item.icon}</span>
|
|
100
246
|
)
|
|
101
|
-
) :
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
247
|
+
) : null}
|
|
248
|
+
{!collapsed && <span className="min-w-0 flex-1 truncate">{item.label}</span>}
|
|
249
|
+
{!collapsed && item.badge ? (
|
|
250
|
+
<Badge variant="secondary" className="h-5 shrink-0 px-1.5">
|
|
251
|
+
{item.badge}
|
|
252
|
+
</Badge>
|
|
253
|
+
) : null}
|
|
254
|
+
{!collapsed && (
|
|
255
|
+
<span
|
|
256
|
+
data-slot="sidebar-nav-group-chevron"
|
|
257
|
+
className="ml-auto text-xs text-muted-foreground transition-transform group-open/sidebar-nav-details:rotate-90"
|
|
258
|
+
>
|
|
259
|
+
›
|
|
260
|
+
</span>
|
|
261
|
+
)}
|
|
262
|
+
</summary>
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
const groupElement = (
|
|
266
|
+
<div data-slot="sidebar-nav-group" data-depth={depth}>
|
|
267
|
+
{showSectionLabel ? (
|
|
268
|
+
<div
|
|
269
|
+
data-slot="sidebar-nav-group-label"
|
|
270
|
+
className="px-2 pb-1 pt-3 text-[11px] font-semibold uppercase tracking-[0.14em] text-muted-foreground first:pt-0"
|
|
271
|
+
>
|
|
272
|
+
{item.sectionLabel}
|
|
273
|
+
</div>
|
|
274
|
+
) : null}
|
|
275
|
+
<details
|
|
276
|
+
data-slot="sidebar-nav-group-details"
|
|
277
|
+
open={isExpanded}
|
|
278
|
+
className="group/sidebar-nav-details"
|
|
279
|
+
>
|
|
280
|
+
{trigger}
|
|
281
|
+
<div
|
|
282
|
+
data-slot="sidebar-nav-group-content"
|
|
283
|
+
className={cn("mt-1 grid gap-1", !collapsed && "ml-3 border-l border-border/55 pl-2")}
|
|
120
284
|
>
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
285
|
+
<SidebarTree
|
|
286
|
+
items={item.items ?? []}
|
|
287
|
+
collapsed={collapsed}
|
|
288
|
+
depth={depth + 1}
|
|
289
|
+
itemClassName={itemClassName}
|
|
290
|
+
activeItemClassName={activeItemClassName}
|
|
291
|
+
renderItem={renderItem}
|
|
292
|
+
renderLink={renderLink}
|
|
293
|
+
/>
|
|
294
|
+
</div>
|
|
295
|
+
</details>
|
|
296
|
+
</div>
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
return renderItem ? <React.Fragment key={item.key}>{renderItem(item, groupElement)}</React.Fragment> : <React.Fragment key={item.key}>{groupElement}</React.Fragment>
|
|
300
|
+
})
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function SidebarNav({
|
|
304
|
+
className,
|
|
305
|
+
items,
|
|
306
|
+
collapsed = false,
|
|
307
|
+
itemClassName,
|
|
308
|
+
activeItemClassName,
|
|
309
|
+
renderItem,
|
|
310
|
+
renderLink,
|
|
311
|
+
...props
|
|
312
|
+
}: SidebarNavProps) {
|
|
313
|
+
const visibleItems = items.filter((item) => !item.hidden)
|
|
314
|
+
|
|
315
|
+
return (
|
|
316
|
+
<nav
|
|
317
|
+
data-slot="sidebar-nav"
|
|
318
|
+
data-collapsed={collapsed || undefined}
|
|
319
|
+
className={cn("grid gap-1", className)}
|
|
320
|
+
{...props}
|
|
321
|
+
>
|
|
322
|
+
<SidebarTree
|
|
323
|
+
items={visibleItems}
|
|
324
|
+
collapsed={collapsed}
|
|
325
|
+
depth={0}
|
|
326
|
+
itemClassName={itemClassName}
|
|
327
|
+
activeItemClassName={activeItemClassName}
|
|
328
|
+
renderItem={renderItem}
|
|
329
|
+
renderLink={renderLink}
|
|
330
|
+
/>
|
|
331
|
+
</nav>
|
|
332
|
+
)
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export { SidebarNav }
|
|
@@ -27,17 +27,28 @@ function ConfirmDialog({
|
|
|
27
27
|
onConfirm,
|
|
28
28
|
onOpenChange,
|
|
29
29
|
...props
|
|
30
|
-
}: ConfirmDialogProps) {
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
30
|
+
}: ConfirmDialogProps) {
|
|
31
|
+
const handleOpenChange = (open: boolean) => {
|
|
32
|
+
if (isLoading && open === false) {
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
onOpenChange?.(open)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const handleCancel = () => {
|
|
40
|
+
onCancel?.()
|
|
41
|
+
if (!isLoading) {
|
|
42
|
+
onOpenChange?.(false)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<ModalShell
|
|
48
|
+
showCloseButton={!isLoading}
|
|
49
|
+
onOpenChange={handleOpenChange}
|
|
50
|
+
footer={
|
|
51
|
+
<DialogActions>
|
|
41
52
|
<DialogActionButton
|
|
42
53
|
type="button"
|
|
43
54
|
variant="outline"
|
|
@@ -46,15 +57,15 @@ function ConfirmDialog({
|
|
|
46
57
|
>
|
|
47
58
|
{cancelText}
|
|
48
59
|
</DialogActionButton>
|
|
49
|
-
<DialogActionButton
|
|
50
|
-
type="button"
|
|
51
|
-
variant={confirmVariant}
|
|
52
|
-
disabled={confirmDisabled}
|
|
53
|
-
isLoading={isLoading}
|
|
54
|
-
onClick={onConfirm}
|
|
55
|
-
>
|
|
56
|
-
{confirmText}
|
|
57
|
-
</DialogActionButton>
|
|
60
|
+
<DialogActionButton
|
|
61
|
+
type="button"
|
|
62
|
+
variant={confirmVariant}
|
|
63
|
+
disabled={confirmDisabled || isLoading}
|
|
64
|
+
isLoading={isLoading}
|
|
65
|
+
onClick={onConfirm}
|
|
66
|
+
>
|
|
67
|
+
{confirmText}
|
|
68
|
+
</DialogActionButton>
|
|
58
69
|
</DialogActions>
|
|
59
70
|
}
|
|
60
71
|
{...props}
|