@sntlr/registry-shell 1.0.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/LICENSE +21 -0
- package/README.md +200 -0
- package/dist/adapter/custom.d.ts +47 -0
- package/dist/adapter/custom.js +53 -0
- package/dist/adapter/custom.js.map +1 -0
- package/dist/adapter/default.d.ts +40 -0
- package/dist/adapter/default.js +202 -0
- package/dist/adapter/default.js.map +1 -0
- package/dist/cli/build.d.ts +1 -0
- package/dist/cli/build.js +31 -0
- package/dist/cli/build.js.map +1 -0
- package/dist/cli/dev.d.ts +1 -0
- package/dist/cli/dev.js +26 -0
- package/dist/cli/dev.js.map +1 -0
- package/dist/cli/index.d.ts +12 -0
- package/dist/cli/index.js +49 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +1 -0
- package/dist/cli/init.js +70 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/shared.d.ts +33 -0
- package/dist/cli/shared.js +278 -0
- package/dist/cli/shared.js.map +1 -0
- package/dist/cli/start.d.ts +1 -0
- package/dist/cli/start.js +24 -0
- package/dist/cli/start.js.map +1 -0
- package/dist/config-loader.d.ts +49 -0
- package/dist/config-loader.js +140 -0
- package/dist/define-config.d.ts +188 -0
- package/dist/define-config.js +21 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +9 -0
- package/package.json +124 -0
- package/src/adapter/custom.ts +90 -0
- package/src/adapter/default.ts +241 -0
- package/src/cli/build.ts +38 -0
- package/src/cli/dev.ts +38 -0
- package/src/cli/index.ts +52 -0
- package/src/cli/init.ts +76 -0
- package/src/cli/shared.ts +306 -0
- package/src/cli/start.ts +28 -0
- package/src/config-loader.ts +190 -0
- package/src/define-config.ts +206 -0
- package/src/index.ts +17 -0
- package/src/next-app/app/[...asset]/route.ts +81 -0
- package/src/next-app/app/_user-global.css +6 -0
- package/src/next-app/app/_user-sources.css +9 -0
- package/src/next-app/app/a11y/[name]/route.ts +19 -0
- package/src/next-app/app/api/search-index/route.ts +19 -0
- package/src/next-app/app/components/[name]/page.tsx +61 -0
- package/src/next-app/app/components/layout.tsx +18 -0
- package/src/next-app/app/docs/[slug]/page.tsx +53 -0
- package/src/next-app/app/docs/layout.tsx +18 -0
- package/src/next-app/app/globals.css +329 -0
- package/src/next-app/app/layout.tsx +102 -0
- package/src/next-app/app/page.tsx +9 -0
- package/src/next-app/app/preview-snapshot/[name]/page.tsx +20 -0
- package/src/next-app/app/preview-snapshot/layout.tsx +17 -0
- package/src/next-app/app/props/[name]/route.ts +19 -0
- package/src/next-app/app/r/[name]/route.ts +14 -0
- package/src/next-app/app/tests/[name]/route.ts +19 -0
- package/src/next-app/components/a11y-info.tsx +287 -0
- package/src/next-app/components/a11y-provider.tsx +39 -0
- package/src/next-app/components/component-breadcrumb.tsx +55 -0
- package/src/next-app/components/component-icon.tsx +140 -0
- package/src/next-app/components/component-preview.tsx +13 -0
- package/src/next-app/components/component-tabs.tsx +209 -0
- package/src/next-app/components/docs-toc.tsx +86 -0
- package/src/next-app/components/global-mobile-sidebar.tsx +35 -0
- package/src/next-app/components/header.tsx +188 -0
- package/src/next-app/components/heading-anchor.tsx +52 -0
- package/src/next-app/components/homepage-demo.tsx +180 -0
- package/src/next-app/components/locale-toggle.tsx +35 -0
- package/src/next-app/components/localized-mdx-client.tsx +14 -0
- package/src/next-app/components/localized-mdx.tsx +27 -0
- package/src/next-app/components/mobile-sidebar.tsx +22 -0
- package/src/next-app/components/nav-data-provider.tsx +37 -0
- package/src/next-app/components/navigation-progress.tsx +62 -0
- package/src/next-app/components/preview-canvas.tsx +368 -0
- package/src/next-app/components/preview-controls.tsx +94 -0
- package/src/next-app/components/preview-layout.tsx +218 -0
- package/src/next-app/components/props-table.tsx +134 -0
- package/src/next-app/components/resizable-preview.tsx +101 -0
- package/src/next-app/components/search.tsx +177 -0
- package/src/next-app/components/settings-modal.tsx +98 -0
- package/src/next-app/components/shell-ui/accordion.tsx +70 -0
- package/src/next-app/components/shell-ui/backdrop.tsx +29 -0
- package/src/next-app/components/shell-ui/badge.tsx +55 -0
- package/src/next-app/components/shell-ui/breadcrumb.tsx +120 -0
- package/src/next-app/components/shell-ui/button.tsx +64 -0
- package/src/next-app/components/shell-ui/card.tsx +127 -0
- package/src/next-app/components/shell-ui/checkbox.tsx +33 -0
- package/src/next-app/components/shell-ui/dialog.tsx +171 -0
- package/src/next-app/components/shell-ui/empty-state.tsx +66 -0
- package/src/next-app/components/shell-ui/input.tsx +27 -0
- package/src/next-app/components/shell-ui/kbd.tsx +30 -0
- package/src/next-app/components/shell-ui/label.tsx +25 -0
- package/src/next-app/components/shell-ui/select.tsx +204 -0
- package/src/next-app/components/shell-ui/separator.tsx +32 -0
- package/src/next-app/components/shell-ui/skeleton.tsx +18 -0
- package/src/next-app/components/shell-ui/table.tsx +124 -0
- package/src/next-app/components/shell-ui/tabs.tsx +102 -0
- package/src/next-app/components/shell-ui/toggle.tsx +56 -0
- package/src/next-app/components/sidebar-layout.tsx +37 -0
- package/src/next-app/components/sidebar-provider.tsx +75 -0
- package/src/next-app/components/sidebar.tsx +222 -0
- package/src/next-app/components/snapshot-preview.tsx +28 -0
- package/src/next-app/components/test-info.tsx +155 -0
- package/src/next-app/components/theme-provider.tsx +16 -0
- package/src/next-app/components/theme-toggle.tsx +21 -0
- package/src/next-app/components/translated-text.tsx +8 -0
- package/src/next-app/fallback/homepage.tsx +112 -0
- package/src/next-app/fallback/previews.ts +17 -0
- package/src/next-app/hooks/use-active-section.ts +23 -0
- package/src/next-app/hooks/use-controls.ts +72 -0
- package/src/next-app/hooks/use-mobile.ts +19 -0
- package/src/next-app/lib/branding.ts +52 -0
- package/src/next-app/lib/components-nav.ts +8 -0
- package/src/next-app/lib/docs.ts +16 -0
- package/src/next-app/lib/github.ts +38 -0
- package/src/next-app/lib/i18n.tsx +630 -0
- package/src/next-app/lib/locales.ts +17 -0
- package/src/next-app/lib/preview-loader.ts +7 -0
- package/src/next-app/lib/registry-adapter.ts +199 -0
- package/src/next-app/lib/utils.ts +6 -0
- package/src/next-app/next-env.d.ts +6 -0
- package/src/next-app/next.config.ts +101 -0
- package/src/next-app/postcss.config.mjs +7 -0
- package/src/next-app/public/favicon.ico +0 -0
- package/src/next-app/public/favicon_dark.svg +3 -0
- package/src/next-app/public/favicon_light.svg +3 -0
- package/src/next-app/registry.config.ts +50 -0
- package/src/next-app/tsconfig.json +29 -0
- package/src/next-app/user-aliases.d.ts +17 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
|
|
5
|
+
import { Select as SelectPrimitive } from "radix-ui"
|
|
6
|
+
|
|
7
|
+
import { cn } from "@shell/lib/utils"
|
|
8
|
+
|
|
9
|
+
/** Root component that manages the state of the select dropdown. */
|
|
10
|
+
function Select({
|
|
11
|
+
...props
|
|
12
|
+
}: React.ComponentProps<typeof SelectPrimitive.Root>) {
|
|
13
|
+
return <SelectPrimitive.Root data-slot="select" {...props} />
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Groups related select items together under an optional label. */
|
|
17
|
+
function SelectGroup({
|
|
18
|
+
...props
|
|
19
|
+
}: React.ComponentProps<typeof SelectPrimitive.Group>) {
|
|
20
|
+
return <SelectPrimitive.Group data-slot="select-group" {...props} />
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Displays the currently selected value or a placeholder when no value is selected. */
|
|
24
|
+
function SelectValue({
|
|
25
|
+
...props
|
|
26
|
+
}: React.ComponentProps<typeof SelectPrimitive.Value>) {
|
|
27
|
+
return <SelectPrimitive.Value data-slot="select-value" {...props} />
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Button that toggles the select dropdown open or closed.
|
|
32
|
+
* @param props.size - Controls the trigger height: `"default"` (36px) or `"sm"` (32px).
|
|
33
|
+
*/
|
|
34
|
+
function SelectTrigger({
|
|
35
|
+
className,
|
|
36
|
+
size = "default",
|
|
37
|
+
children,
|
|
38
|
+
...props
|
|
39
|
+
}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
|
|
40
|
+
/** Controls the trigger height. */
|
|
41
|
+
size?: "sm" | "default"
|
|
42
|
+
}) {
|
|
43
|
+
return (
|
|
44
|
+
<SelectPrimitive.Trigger
|
|
45
|
+
data-slot="select-trigger"
|
|
46
|
+
data-size={size}
|
|
47
|
+
className={cn(
|
|
48
|
+
"flex w-fit items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs cursor-pointer transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
|
|
49
|
+
className
|
|
50
|
+
)}
|
|
51
|
+
{...props}
|
|
52
|
+
>
|
|
53
|
+
{children}
|
|
54
|
+
<SelectPrimitive.Icon asChild>
|
|
55
|
+
<ChevronDownIcon className="size-4 opacity-50" />
|
|
56
|
+
</SelectPrimitive.Icon>
|
|
57
|
+
</SelectPrimitive.Trigger>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Dropdown panel that renders the list of select options inside a portal. */
|
|
62
|
+
function SelectContent({
|
|
63
|
+
className,
|
|
64
|
+
children,
|
|
65
|
+
position = "item-aligned",
|
|
66
|
+
align = "center",
|
|
67
|
+
...props
|
|
68
|
+
}: React.ComponentProps<typeof SelectPrimitive.Content>) {
|
|
69
|
+
return (
|
|
70
|
+
<SelectPrimitive.Portal>
|
|
71
|
+
<SelectPrimitive.Content
|
|
72
|
+
data-slot="select-content"
|
|
73
|
+
className={cn(
|
|
74
|
+
"relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
|
75
|
+
position === "popper" &&
|
|
76
|
+
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
77
|
+
className
|
|
78
|
+
)}
|
|
79
|
+
position={position}
|
|
80
|
+
align={align}
|
|
81
|
+
{...props}
|
|
82
|
+
>
|
|
83
|
+
<SelectScrollUpButton />
|
|
84
|
+
<SelectPrimitive.Viewport
|
|
85
|
+
className={cn(
|
|
86
|
+
"p-1",
|
|
87
|
+
position === "popper" &&
|
|
88
|
+
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
|
|
89
|
+
)}
|
|
90
|
+
>
|
|
91
|
+
{children}
|
|
92
|
+
</SelectPrimitive.Viewport>
|
|
93
|
+
<SelectScrollDownButton />
|
|
94
|
+
</SelectPrimitive.Content>
|
|
95
|
+
</SelectPrimitive.Portal>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Renders a non-interactive label for a group of select items. */
|
|
100
|
+
function SelectLabel({
|
|
101
|
+
className,
|
|
102
|
+
...props
|
|
103
|
+
}: React.ComponentProps<typeof SelectPrimitive.Label>) {
|
|
104
|
+
return (
|
|
105
|
+
<SelectPrimitive.Label
|
|
106
|
+
data-slot="select-label"
|
|
107
|
+
className={cn("px-2 py-1.5 text-xs text-muted-foreground", className)}
|
|
108
|
+
{...props}
|
|
109
|
+
/>
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** A selectable option within the select dropdown, displaying a check icon when active. */
|
|
114
|
+
function SelectItem({
|
|
115
|
+
className,
|
|
116
|
+
children,
|
|
117
|
+
...props
|
|
118
|
+
}: React.ComponentProps<typeof SelectPrimitive.Item>) {
|
|
119
|
+
return (
|
|
120
|
+
<SelectPrimitive.Item
|
|
121
|
+
data-slot="select-item"
|
|
122
|
+
className={cn(
|
|
123
|
+
"relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
|
124
|
+
className
|
|
125
|
+
)}
|
|
126
|
+
{...props}
|
|
127
|
+
>
|
|
128
|
+
<span
|
|
129
|
+
data-slot="select-item-indicator"
|
|
130
|
+
className="absolute right-2 flex size-3.5 items-center justify-center"
|
|
131
|
+
>
|
|
132
|
+
<SelectPrimitive.ItemIndicator>
|
|
133
|
+
<CheckIcon className="size-4" />
|
|
134
|
+
</SelectPrimitive.ItemIndicator>
|
|
135
|
+
</span>
|
|
136
|
+
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
|
137
|
+
</SelectPrimitive.Item>
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** A visual divider between groups of select items. */
|
|
142
|
+
function SelectSeparator({
|
|
143
|
+
className,
|
|
144
|
+
...props
|
|
145
|
+
}: React.ComponentProps<typeof SelectPrimitive.Separator>) {
|
|
146
|
+
return (
|
|
147
|
+
<SelectPrimitive.Separator
|
|
148
|
+
data-slot="select-separator"
|
|
149
|
+
className={cn("pointer-events-none -mx-1 my-1 h-px bg-border", className)}
|
|
150
|
+
{...props}
|
|
151
|
+
/>
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Button shown at the top of the dropdown to scroll up when content overflows. */
|
|
156
|
+
function SelectScrollUpButton({
|
|
157
|
+
className,
|
|
158
|
+
...props
|
|
159
|
+
}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
|
|
160
|
+
return (
|
|
161
|
+
<SelectPrimitive.ScrollUpButton
|
|
162
|
+
data-slot="select-scroll-up-button"
|
|
163
|
+
className={cn(
|
|
164
|
+
"flex cursor-default items-center justify-center py-1",
|
|
165
|
+
className
|
|
166
|
+
)}
|
|
167
|
+
{...props}
|
|
168
|
+
>
|
|
169
|
+
<ChevronUpIcon className="size-4" />
|
|
170
|
+
</SelectPrimitive.ScrollUpButton>
|
|
171
|
+
)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Button shown at the bottom of the dropdown to scroll down when content overflows. */
|
|
175
|
+
function SelectScrollDownButton({
|
|
176
|
+
className,
|
|
177
|
+
...props
|
|
178
|
+
}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
|
|
179
|
+
return (
|
|
180
|
+
<SelectPrimitive.ScrollDownButton
|
|
181
|
+
data-slot="select-scroll-down-button"
|
|
182
|
+
className={cn(
|
|
183
|
+
"flex cursor-default items-center justify-center py-1",
|
|
184
|
+
className
|
|
185
|
+
)}
|
|
186
|
+
{...props}
|
|
187
|
+
>
|
|
188
|
+
<ChevronDownIcon className="size-4" />
|
|
189
|
+
</SelectPrimitive.ScrollDownButton>
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export {
|
|
194
|
+
Select,
|
|
195
|
+
SelectContent,
|
|
196
|
+
SelectGroup,
|
|
197
|
+
SelectItem,
|
|
198
|
+
SelectLabel,
|
|
199
|
+
SelectScrollDownButton,
|
|
200
|
+
SelectScrollUpButton,
|
|
201
|
+
SelectSeparator,
|
|
202
|
+
SelectTrigger,
|
|
203
|
+
SelectValue,
|
|
204
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Separator as SeparatorPrimitive } from "radix-ui"
|
|
5
|
+
|
|
6
|
+
import { cn } from "@shell/lib/utils"
|
|
7
|
+
|
|
8
|
+
/** A visual divider between content sections. */
|
|
9
|
+
function Separator({
|
|
10
|
+
/** Additional CSS classes to apply to the separator. */
|
|
11
|
+
className,
|
|
12
|
+
/** The orientation of the separator. @default "horizontal" */
|
|
13
|
+
orientation = "horizontal",
|
|
14
|
+
/** Whether the separator is purely decorative (hidden from assistive technology). @default true */
|
|
15
|
+
decorative = true,
|
|
16
|
+
...props
|
|
17
|
+
}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
|
|
18
|
+
return (
|
|
19
|
+
<SeparatorPrimitive.Root
|
|
20
|
+
data-slot="separator"
|
|
21
|
+
decorative={decorative}
|
|
22
|
+
orientation={orientation}
|
|
23
|
+
className={cn(
|
|
24
|
+
"shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
|
|
25
|
+
className
|
|
26
|
+
)}
|
|
27
|
+
{...props}
|
|
28
|
+
/>
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export { Separator }
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { cn } from "@shell/lib/utils"
|
|
2
|
+
|
|
3
|
+
/** A pulsing placeholder used to indicate loading content. */
|
|
4
|
+
function Skeleton({
|
|
5
|
+
/** Additional CSS classes to apply to the skeleton. */
|
|
6
|
+
className,
|
|
7
|
+
...props
|
|
8
|
+
}: React.ComponentProps<"div">) {
|
|
9
|
+
return (
|
|
10
|
+
<div
|
|
11
|
+
data-slot="skeleton"
|
|
12
|
+
className={cn("animate-pulse rounded-md bg-accent", className)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { Skeleton }
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@shell/lib/utils"
|
|
6
|
+
|
|
7
|
+
/** A responsive HTML table wrapper with horizontal scroll support. */
|
|
8
|
+
function Table({ className, ...props }: React.ComponentProps<"table">) {
|
|
9
|
+
return (
|
|
10
|
+
<div
|
|
11
|
+
data-slot="table-container"
|
|
12
|
+
className="relative w-full overflow-x-auto"
|
|
13
|
+
>
|
|
14
|
+
<table
|
|
15
|
+
data-slot="table"
|
|
16
|
+
className={cn("w-full caption-bottom text-sm", className)}
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
</div>
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Table header section. Rows inside receive a bottom border. */
|
|
24
|
+
function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
|
|
25
|
+
return (
|
|
26
|
+
<thead
|
|
27
|
+
data-slot="table-header"
|
|
28
|
+
className={cn("[&_tr]:border-b", className)}
|
|
29
|
+
{...props}
|
|
30
|
+
/>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Table body section. */
|
|
35
|
+
function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
|
|
36
|
+
return (
|
|
37
|
+
<tbody
|
|
38
|
+
data-slot="table-body"
|
|
39
|
+
className={cn("[&_tr:last-child]:border-0", className)}
|
|
40
|
+
{...props}
|
|
41
|
+
/>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Table footer section with a muted background. */
|
|
46
|
+
function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
|
|
47
|
+
return (
|
|
48
|
+
<tfoot
|
|
49
|
+
data-slot="table-footer"
|
|
50
|
+
className={cn(
|
|
51
|
+
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
|
|
52
|
+
className
|
|
53
|
+
)}
|
|
54
|
+
{...props}
|
|
55
|
+
/>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** A table row with hover and selection state styling. */
|
|
60
|
+
function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
|
|
61
|
+
return (
|
|
62
|
+
<tr
|
|
63
|
+
data-slot="table-row"
|
|
64
|
+
className={cn(
|
|
65
|
+
"border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted",
|
|
66
|
+
className
|
|
67
|
+
)}
|
|
68
|
+
{...props}
|
|
69
|
+
/>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** A table header cell with medium font weight. */
|
|
74
|
+
function TableHead({ className, ...props }: React.ComponentProps<"th">) {
|
|
75
|
+
return (
|
|
76
|
+
<th
|
|
77
|
+
data-slot="table-head"
|
|
78
|
+
className={cn(
|
|
79
|
+
"h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
|
80
|
+
className
|
|
81
|
+
)}
|
|
82
|
+
{...props}
|
|
83
|
+
/>
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** A table data cell. */
|
|
88
|
+
function TableCell({ className, ...props }: React.ComponentProps<"td">) {
|
|
89
|
+
return (
|
|
90
|
+
<td
|
|
91
|
+
data-slot="table-cell"
|
|
92
|
+
className={cn(
|
|
93
|
+
"p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
|
94
|
+
className
|
|
95
|
+
)}
|
|
96
|
+
{...props}
|
|
97
|
+
/>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** A table caption rendered below the table. */
|
|
102
|
+
function TableCaption({
|
|
103
|
+
className,
|
|
104
|
+
...props
|
|
105
|
+
}: React.ComponentProps<"caption">) {
|
|
106
|
+
return (
|
|
107
|
+
<caption
|
|
108
|
+
data-slot="table-caption"
|
|
109
|
+
className={cn("mt-4 text-sm text-muted-foreground", className)}
|
|
110
|
+
{...props}
|
|
111
|
+
/>
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export {
|
|
116
|
+
Table,
|
|
117
|
+
TableHeader,
|
|
118
|
+
TableBody,
|
|
119
|
+
TableFooter,
|
|
120
|
+
TableHead,
|
|
121
|
+
TableRow,
|
|
122
|
+
TableCell,
|
|
123
|
+
TableCaption,
|
|
124
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
5
|
+
import { Tabs as TabsPrimitive } from "radix-ui"
|
|
6
|
+
|
|
7
|
+
import { cn } from "@shell/lib/utils"
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Root component that manages tab state and layout direction.
|
|
11
|
+
* @param props.orientation - Sets the layout axis: `"horizontal"` (default) or `"vertical"`.
|
|
12
|
+
*/
|
|
13
|
+
function Tabs({
|
|
14
|
+
className,
|
|
15
|
+
orientation = "horizontal",
|
|
16
|
+
...props
|
|
17
|
+
}: React.ComponentProps<typeof TabsPrimitive.Root>) {
|
|
18
|
+
return (
|
|
19
|
+
<TabsPrimitive.Root
|
|
20
|
+
data-slot="tabs"
|
|
21
|
+
data-orientation={orientation}
|
|
22
|
+
orientation={orientation}
|
|
23
|
+
className={cn(
|
|
24
|
+
"group/tabs flex gap-2 data-[orientation=horizontal]:flex-col",
|
|
25
|
+
className
|
|
26
|
+
)}
|
|
27
|
+
{...props}
|
|
28
|
+
/>
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const tabsListVariants = cva(
|
|
33
|
+
"group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-[orientation=horizontal]/tabs:h-9 group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col data-[variant=line]:rounded-none",
|
|
34
|
+
{
|
|
35
|
+
variants: {
|
|
36
|
+
variant: {
|
|
37
|
+
default: "bg-muted",
|
|
38
|
+
line: "gap-1 bg-transparent",
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
defaultVariants: {
|
|
42
|
+
variant: "default",
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Container for tab triggers that controls their visual grouping.
|
|
49
|
+
* @param props.variant - Visual style: `"default"` renders a muted background, `"line"` uses an underline indicator.
|
|
50
|
+
*/
|
|
51
|
+
function TabsList({
|
|
52
|
+
className,
|
|
53
|
+
/** Visual style variant: `"default"` for a muted background or `"line"` for an underline indicator. */
|
|
54
|
+
variant = "default",
|
|
55
|
+
...props
|
|
56
|
+
}: React.ComponentProps<typeof TabsPrimitive.List> &
|
|
57
|
+
VariantProps<typeof tabsListVariants>) {
|
|
58
|
+
return (
|
|
59
|
+
<TabsPrimitive.List
|
|
60
|
+
data-slot="tabs-list"
|
|
61
|
+
data-variant={variant}
|
|
62
|
+
className={cn(tabsListVariants({ variant }), className)}
|
|
63
|
+
{...props}
|
|
64
|
+
/>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** A button that activates its associated tab panel when clicked. */
|
|
69
|
+
function TabsTrigger({
|
|
70
|
+
className,
|
|
71
|
+
...props
|
|
72
|
+
}: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
|
|
73
|
+
return (
|
|
74
|
+
<TabsPrimitive.Trigger
|
|
75
|
+
data-slot="tabs-trigger"
|
|
76
|
+
className={cn(
|
|
77
|
+
"relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap cursor-pointer text-foreground/60 transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 group-data-[variant=default]/tabs-list:data-[state=active]:shadow-sm group-data-[variant=line]/tabs-list:data-[state=active]:shadow-none dark:text-muted-foreground dark:hover:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
78
|
+
"group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:border-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent",
|
|
79
|
+
"data-[state=active]:bg-background data-[state=active]:text-foreground dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 dark:data-[state=active]:text-foreground",
|
|
80
|
+
"after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100",
|
|
81
|
+
className
|
|
82
|
+
)}
|
|
83
|
+
{...props}
|
|
84
|
+
/>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Panel that displays content associated with the currently active tab. */
|
|
89
|
+
function TabsContent({
|
|
90
|
+
className,
|
|
91
|
+
...props
|
|
92
|
+
}: React.ComponentProps<typeof TabsPrimitive.Content>) {
|
|
93
|
+
return (
|
|
94
|
+
<TabsPrimitive.Content
|
|
95
|
+
data-slot="tabs-content"
|
|
96
|
+
className={cn("flex-1 outline-none", className)}
|
|
97
|
+
{...props}
|
|
98
|
+
/>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants }
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
5
|
+
import { Toggle as TogglePrimitive } from "radix-ui"
|
|
6
|
+
|
|
7
|
+
import { cn } from "@shell/lib/utils"
|
|
8
|
+
|
|
9
|
+
const toggleVariants = cva(
|
|
10
|
+
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap cursor-pointer transition-[color,box-shadow] outline-none hover:bg-muted hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
11
|
+
{
|
|
12
|
+
variants: {
|
|
13
|
+
variant: {
|
|
14
|
+
default: "bg-transparent",
|
|
15
|
+
outline:
|
|
16
|
+
"border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground",
|
|
17
|
+
},
|
|
18
|
+
size: {
|
|
19
|
+
default: "h-9 min-w-9 px-2",
|
|
20
|
+
sm: "h-8 min-w-8 px-1.5",
|
|
21
|
+
lg: "h-10 min-w-10 px-2.5",
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
defaultVariants: {
|
|
25
|
+
variant: "default",
|
|
26
|
+
size: "default",
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
/** A two-state button that can be toggled on or off. */
|
|
32
|
+
function Toggle({
|
|
33
|
+
/** Additional CSS classes to apply to the toggle. */
|
|
34
|
+
className,
|
|
35
|
+
/** The visual style variant of the toggle. @default "default" */
|
|
36
|
+
variant,
|
|
37
|
+
/** The size of the toggle. @default "default" */
|
|
38
|
+
size,
|
|
39
|
+
...props
|
|
40
|
+
}: React.ComponentProps<typeof TogglePrimitive.Root> &
|
|
41
|
+
VariantProps<typeof toggleVariants> & {
|
|
42
|
+
/** The visual style variant of the toggle. */
|
|
43
|
+
variant?: "default" | "outline"
|
|
44
|
+
/** The size of the toggle. */
|
|
45
|
+
size?: "default" | "sm" | "lg"
|
|
46
|
+
}) {
|
|
47
|
+
return (
|
|
48
|
+
<TogglePrimitive.Root
|
|
49
|
+
data-slot="toggle"
|
|
50
|
+
className={cn(toggleVariants({ variant, size, className }))}
|
|
51
|
+
{...props}
|
|
52
|
+
/>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export { Toggle, toggleVariants }
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { Sidebar } from "@shell/components/sidebar"
|
|
4
|
+
import { useMobileSidebar } from "@shell/components/sidebar-provider"
|
|
5
|
+
import { useActiveSection } from "@shell/hooks/use-active-section"
|
|
6
|
+
import type { DocMeta } from "@shell/lib/docs"
|
|
7
|
+
import type { ComponentMeta } from "@shell/lib/components-nav"
|
|
8
|
+
|
|
9
|
+
export function SidebarLayout({
|
|
10
|
+
docs,
|
|
11
|
+
components,
|
|
12
|
+
children,
|
|
13
|
+
}: {
|
|
14
|
+
docs: DocMeta[]
|
|
15
|
+
components: ComponentMeta[]
|
|
16
|
+
children: React.ReactNode
|
|
17
|
+
}) {
|
|
18
|
+
const { open, close, collapsed } = useMobileSidebar()
|
|
19
|
+
const activeSection = useActiveSection(components)
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div className="flex">
|
|
23
|
+
{/* Desktop-only — the mobile floating card is mounted once from the root
|
|
24
|
+
layout so the hamburger menu works on every page (homepage included). */}
|
|
25
|
+
<Sidebar
|
|
26
|
+
docs={docs}
|
|
27
|
+
components={components}
|
|
28
|
+
open={open}
|
|
29
|
+
onClose={close}
|
|
30
|
+
collapsed={collapsed}
|
|
31
|
+
activeSection={activeSection}
|
|
32
|
+
display="desktop"
|
|
33
|
+
/>
|
|
34
|
+
<main id="main-content" tabIndex={-1} className="flex-1 min-w-0 outline-none">{children}</main>
|
|
35
|
+
</div>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { createContext, useCallback, useContext, useEffect, useLayoutEffect, useState, type ReactNode } from "react"
|
|
4
|
+
|
|
5
|
+
interface SidebarContextValue {
|
|
6
|
+
open: boolean
|
|
7
|
+
toggle: () => void
|
|
8
|
+
close: () => void
|
|
9
|
+
collapsed: boolean
|
|
10
|
+
setCollapsed: (v: boolean) => void
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const SidebarContext = createContext<SidebarContextValue>({
|
|
14
|
+
open: false,
|
|
15
|
+
toggle: () => {},
|
|
16
|
+
close: () => {},
|
|
17
|
+
collapsed: false,
|
|
18
|
+
setCollapsed: () => {},
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Runs synchronously after DOM commit and *before* paint, so we can swap
|
|
23
|
+
* client-only state into the tree post-hydration without ever rendering a
|
|
24
|
+
* mismatched DOM. On the server it's a no-op alias to `useEffect`.
|
|
25
|
+
*/
|
|
26
|
+
const useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect
|
|
27
|
+
|
|
28
|
+
export function SidebarProvider({ children }: { children: ReactNode }) {
|
|
29
|
+
// Always start with the SSR-safe defaults so the server HTML and the first
|
|
30
|
+
// client render are identical. Reading from sessionStorage / matchMedia in
|
|
31
|
+
// a `useState` initializer caused a hydration mismatch on the Header (which
|
|
32
|
+
// builds className strings from `collapsed`), and React's mismatch recovery
|
|
33
|
+
// discards the affected subtree — which restarts the bento card CSS
|
|
34
|
+
// animations on the homepage. We restore the persisted state in a layout
|
|
35
|
+
// effect that runs before the first paint, so users never see a flash.
|
|
36
|
+
const [open, setOpen] = useState(false)
|
|
37
|
+
const [collapsed, setCollapsedState] = useState(false)
|
|
38
|
+
|
|
39
|
+
useIsomorphicLayoutEffect(() => {
|
|
40
|
+
// Sidebar nav: only restore on desktop (mobile always starts closed).
|
|
41
|
+
const isMobile = window.matchMedia("(max-width: 767px)").matches
|
|
42
|
+
if (!isMobile) {
|
|
43
|
+
const storedOpen = sessionStorage.getItem("sidebar-nav-open") === "true"
|
|
44
|
+
if (storedOpen) setOpen(true)
|
|
45
|
+
}
|
|
46
|
+
// Preview fullscreen.
|
|
47
|
+
const storedCollapsed = sessionStorage.getItem("preview-fullscreen") === "true"
|
|
48
|
+
if (storedCollapsed) setCollapsedState(true)
|
|
49
|
+
}, [])
|
|
50
|
+
|
|
51
|
+
const toggle = useCallback(() => {
|
|
52
|
+
setOpen((o) => {
|
|
53
|
+
sessionStorage.setItem("sidebar-nav-open", String(!o))
|
|
54
|
+
return !o
|
|
55
|
+
})
|
|
56
|
+
}, [])
|
|
57
|
+
const close = useCallback(() => {
|
|
58
|
+
setOpen(false)
|
|
59
|
+
sessionStorage.setItem("sidebar-nav-open", "false")
|
|
60
|
+
}, [])
|
|
61
|
+
const setCollapsed = useCallback((v: boolean) => {
|
|
62
|
+
setCollapsedState(v)
|
|
63
|
+
sessionStorage.setItem("preview-fullscreen", String(v))
|
|
64
|
+
}, [])
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<SidebarContext.Provider value={{ open, toggle, close, collapsed, setCollapsed }}>
|
|
68
|
+
{children}
|
|
69
|
+
</SidebarContext.Provider>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function useMobileSidebar() {
|
|
74
|
+
return useContext(SidebarContext)
|
|
75
|
+
}
|