fulldev-ui 0.9.0 → 0.9.2
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/CHANGELOG.md +14 -0
- package/package.json +11 -11
- package/public/r/blocks.json +2 -0
- package/public/r/components.json +4 -0
- package/public/r/header-1.json +1 -1
- package/public/r/registry.json +6 -0
- package/public/r/sidebar-1.json +1 -1
- package/public/r/sidebar.json +1 -1
- package/registry.json +6 -0
- package/src/components/blocks/header-1.astro +3 -2
- package/src/components/blocks/sidebar-1.astro +59 -15
- package/src/components/ui/sidebar/sidebar-provider.astro +12 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Fulldev UI
|
|
2
2
|
|
|
3
|
+
## 0.9.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`c052c13`](https://github.com/fulldotdev/ui/commit/c052c13c356bae933ffd838de1a95a0af7e97e21) Thanks [@silveltman](https://github.com/silveltman)! - Add active navigation item support to the Header 1 block.
|
|
8
|
+
|
|
9
|
+
- [`49e1bfc`](https://github.com/fulldotdev/ui/commit/49e1bfc3d1bbb7a6cb126094385704fdcc0a49d6) Thanks [@silveltman](https://github.com/silveltman)! - Update registry build and styling dependencies.
|
|
10
|
+
|
|
11
|
+
## 0.9.1
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- [#178](https://github.com/fulldotdev/ui/pull/178) [`e2c8f47`](https://github.com/fulldotdev/ui/commit/e2c8f476f3333d09eb21ff4fa251d84326664be3) Thanks [@silveltman](https://github.com/silveltman)! - Fix vertical alignment in the header block.
|
|
16
|
+
|
|
3
17
|
## 0.9.0
|
|
4
18
|
|
|
5
19
|
### Minor Changes
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fulldev-ui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.2",
|
|
5
5
|
"description": "Open-source Astro UI components and blocks",
|
|
6
6
|
"homepage": "https://ui.full.dev",
|
|
7
7
|
"bugs": "https://github.com/fulldotdev/ui/issues",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@astrojs/mdx": "^5.0.6",
|
|
52
|
-
"@astrojs/sitemap": "^3.7.
|
|
52
|
+
"@astrojs/sitemap": "^3.7.3",
|
|
53
53
|
"@data-slot/accordion": "^0.2.166",
|
|
54
54
|
"@data-slot/alert-dialog": "^0.2.166",
|
|
55
55
|
"@data-slot/collapsible": "^0.2.166",
|
|
@@ -68,10 +68,10 @@
|
|
|
68
68
|
"@data-slot/tabs": "^0.2.166",
|
|
69
69
|
"@data-slot/toggle": "^0.2.166",
|
|
70
70
|
"@data-slot/tooltip": "^0.2.166",
|
|
71
|
-
"@fontsource-variable/geist": "^5.2.
|
|
72
|
-
"@fontsource-variable/geist-mono": "^5.2.
|
|
71
|
+
"@fontsource-variable/geist": "^5.2.9",
|
|
72
|
+
"@fontsource-variable/geist-mono": "^5.2.8",
|
|
73
73
|
"@lexingtonthemes/seo": "^0.2.0",
|
|
74
|
-
"@tailwindcss/vite": "^4.3.
|
|
74
|
+
"@tailwindcss/vite": "^4.3.2",
|
|
75
75
|
"astro": "^6.3.3",
|
|
76
76
|
"astro-favicons": "^3.1.6",
|
|
77
77
|
"astro-live-code": "^0.0.6",
|
|
@@ -79,11 +79,11 @@
|
|
|
79
79
|
"class-variance-authority": "^0.7.1",
|
|
80
80
|
"clsx": "^2.1.1",
|
|
81
81
|
"embla-carousel": "^8.6.0",
|
|
82
|
-
"lucide-static": "^1.
|
|
83
|
-
"shadcn": "^4.
|
|
84
|
-
"simple-icons": "^16.
|
|
82
|
+
"lucide-static": "^1.22.0",
|
|
83
|
+
"shadcn": "^4.12.0",
|
|
84
|
+
"simple-icons": "^16.24.1",
|
|
85
85
|
"tailwind-merge": "^3.6.0",
|
|
86
|
-
"tailwindcss": "^4.3.
|
|
86
|
+
"tailwindcss": "^4.3.2",
|
|
87
87
|
"tw-animate-css": "^1.4.0",
|
|
88
88
|
"typescript": "^6.0.3"
|
|
89
89
|
},
|
|
@@ -92,10 +92,10 @@
|
|
|
92
92
|
"@changesets/changelog-github": "^0.7.0",
|
|
93
93
|
"@changesets/cli": "^2.31.0",
|
|
94
94
|
"@ianvs/prettier-plugin-sort-imports": "^4.7.1",
|
|
95
|
-
"prettier": "3.
|
|
95
|
+
"prettier": "3.9.3",
|
|
96
96
|
"prettier-plugin-astro": "0.14.1",
|
|
97
97
|
"prettier-plugin-tailwindcss": "^0.8.0",
|
|
98
|
-
"sharp": "^0.
|
|
98
|
+
"sharp": "^0.35.2"
|
|
99
99
|
},
|
|
100
100
|
"scripts": {
|
|
101
101
|
"astro": "astro",
|
package/public/r/blocks.json
CHANGED
package/public/r/components.json
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"@fulldev/empty",
|
|
21
21
|
"@fulldev/field",
|
|
22
22
|
"@fulldev/header",
|
|
23
|
+
"@fulldev/hover-card",
|
|
23
24
|
"@fulldev/input",
|
|
24
25
|
"@fulldev/input-group",
|
|
25
26
|
"@fulldev/item",
|
|
@@ -28,6 +29,7 @@
|
|
|
28
29
|
"@fulldev/marquee",
|
|
29
30
|
"@fulldev/native-select",
|
|
30
31
|
"@fulldev/navigation-menu",
|
|
32
|
+
"@fulldev/popover",
|
|
31
33
|
"@fulldev/price",
|
|
32
34
|
"@fulldev/radio-group",
|
|
33
35
|
"@fulldev/rating",
|
|
@@ -44,7 +46,9 @@
|
|
|
44
46
|
"@fulldev/tabs",
|
|
45
47
|
"@fulldev/textarea",
|
|
46
48
|
"@fulldev/theme-toggle",
|
|
49
|
+
"@fulldev/tile",
|
|
47
50
|
"@fulldev/toc",
|
|
51
|
+
"@fulldev/toggle",
|
|
48
52
|
"@fulldev/tooltip",
|
|
49
53
|
"@fulldev/typography",
|
|
50
54
|
"@fulldev/video",
|
package/public/r/header-1.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"files": [
|
|
13
13
|
{
|
|
14
14
|
"path": "src/components/blocks/header-1.astro",
|
|
15
|
-
"content": "---\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\nimport { Header, HeaderContainer, HeaderGroup } from \"@/components/ui/header\"\nimport { Icon } from \"@/components/ui/icon\"\nimport { Logo, LogoImage, LogoText } from \"@/components/ui/logo\"\nimport {\n NavigationMenu,\n NavigationMenuItem,\n NavigationMenuLink,\n NavigationMenuList,\n} from \"@/components/ui/navigation-menu\"\nimport {\n Sheet,\n SheetClose,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n SheetTrigger,\n} from \"@/components/ui/sheet\"\n\ntype Props = {\n class?: string\n logo: {\n label: string\n href: string\n src?: any\n srcLight?: any\n srcDark?: any\n alt?: string\n }\n navigation: {\n label: string\n href: string\n links?: {\n label: string\n href: string\n }[]\n }[]\n buttons: {\n label: string\n href: string\n icon?: string\n variant?: \"default\" | \"outline\" | \"secondary\" | \"ghost\"\n }[]\n}\n\nconst { class: className, logo, navigation, buttons } = Astro.props\n---\n\n<Header class={cn(\"\", className)}>\n <HeaderContainer class=\"justify-between gap-4\">\n <HeaderGroup>\n <Logo href={logo.href}>\n <LogoImage\n src={logo.src}\n srcLight={logo.srcLight}\n srcDark={logo.srcDark}\n alt={logo.alt}\n />\n {logo.label && <LogoText>{logo.label}</LogoText>}\n </Logo>\n </HeaderGroup>\n <NavigationMenu class=\"hidden flex-none justify-start lg:flex\">\n <NavigationMenuList class=\"justify-start\">\n {\n navigation.map((item, index) => (\n <NavigationMenuItem value={`nav-${index}`}>\n <NavigationMenuLink href={item.href}>\n {item.label}\n </NavigationMenuLink>\n </NavigationMenuItem>\n ))\n }\n </NavigationMenuList>\n </NavigationMenu>\n <HeaderGroup class=\"ml-auto hidden flex-1 justify-end lg:flex\">\n <slot />\n {\n buttons.map(({ label, icon, ...button }) => (\n <Button\n as=\"a\"\n size={icon && !label ? \"icon-sm\" : \"sm\"}\n aria-label={label}\n target={button.href.startsWith(\"http\") ? \"_blank\" : undefined}\n rel={button.href.startsWith(\"http\") ? \"noreferrer\" : undefined}\n {...button}\n >\n {icon && <Icon name={icon} />}\n {label}\n </Button>\n ))\n }\n </HeaderGroup>\n <Sheet class=\"lg:hidden\">\n <SheetTrigger\n variant=\"ghost\"\n size=\"icon-sm\"\n class=\"ml-auto\"\n aria-label=\"Open menu\"\n >\n <Icon name=\"menu\" />\n </SheetTrigger>\n <SheetContent side=\"right\" class=\"p-0\">\n <SheetHeader class=\"sr-only\">\n <SheetTitle>Menu</SheetTitle>\n <SheetDescription>Site navigation and actions.</SheetDescription>\n </SheetHeader>\n <div class=\"flex h-full flex-col gap-6 px-4 py-12\">\n <nav aria-label=\"Main navigation\" class=\"flex flex-col gap-1\">\n {\n navigation.map((item) => (\n <SheetClose variant=\"ghost\" class=\"justify-start\">\n {item.label}\n </SheetClose>\n ))\n }\n </nav>\n <div class=\"mt-auto flex flex-col gap-2\">\n <slot />\n </div>\n </div>\n </SheetContent>\n </Sheet>\n </HeaderContainer>\n</Header>\n",
|
|
15
|
+
"content": "---\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\nimport { Header, HeaderContainer, HeaderGroup } from \"@/components/ui/header\"\nimport { Icon } from \"@/components/ui/icon\"\nimport { Logo, LogoImage, LogoText } from \"@/components/ui/logo\"\nimport {\n NavigationMenu,\n NavigationMenuItem,\n NavigationMenuLink,\n NavigationMenuList,\n} from \"@/components/ui/navigation-menu\"\nimport {\n Sheet,\n SheetClose,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n SheetTrigger,\n} from \"@/components/ui/sheet\"\n\ntype Props = {\n class?: string\n logo: {\n label: string\n href: string\n src?: any\n srcLight?: any\n srcDark?: any\n alt?: string\n }\n navigation: {\n label: string\n href: string\n active?: boolean\n links?: {\n label: string\n href: string\n }[]\n }[]\n buttons: {\n label: string\n href: string\n icon?: string\n variant?: \"default\" | \"outline\" | \"secondary\" | \"ghost\"\n }[]\n}\n\nconst { class: className, logo, navigation, buttons } = Astro.props\n---\n\n<Header class={cn(\"\", className)}>\n <HeaderContainer class=\"items-center justify-between gap-4\">\n <HeaderGroup>\n <Logo href={logo.href}>\n <LogoImage\n src={logo.src}\n srcLight={logo.srcLight}\n srcDark={logo.srcDark}\n alt={logo.alt}\n />\n {logo.label && <LogoText>{logo.label}</LogoText>}\n </Logo>\n </HeaderGroup>\n <NavigationMenu class=\"hidden flex-none justify-start lg:flex\">\n <NavigationMenuList class=\"justify-start\">\n {\n navigation.map((item, index) => (\n <NavigationMenuItem value={`nav-${index}`}>\n <NavigationMenuLink href={item.href} active={item.active}>\n {item.label}\n </NavigationMenuLink>\n </NavigationMenuItem>\n ))\n }\n </NavigationMenuList>\n </NavigationMenu>\n <HeaderGroup class=\"ml-auto hidden flex-1 justify-end lg:flex\">\n <slot />\n {\n buttons.map(({ label, icon, ...button }) => (\n <Button\n as=\"a\"\n size={icon && !label ? \"icon-sm\" : \"sm\"}\n aria-label={label}\n target={button.href.startsWith(\"http\") ? \"_blank\" : undefined}\n rel={button.href.startsWith(\"http\") ? \"noreferrer\" : undefined}\n {...button}\n >\n {icon && <Icon name={icon} />}\n {label}\n </Button>\n ))\n }\n </HeaderGroup>\n <Sheet class=\"lg:hidden\">\n <SheetTrigger\n variant=\"ghost\"\n size=\"icon-sm\"\n class=\"ml-auto\"\n aria-label=\"Open menu\"\n >\n <Icon name=\"menu\" />\n </SheetTrigger>\n <SheetContent side=\"right\" class=\"p-0\">\n <SheetHeader class=\"sr-only\">\n <SheetTitle>Menu</SheetTitle>\n <SheetDescription>Site navigation and actions.</SheetDescription>\n </SheetHeader>\n <div class=\"flex h-full flex-col gap-6 px-4 py-12\">\n <nav aria-label=\"Main navigation\" class=\"flex flex-col gap-1\">\n {\n navigation.map((item) => (\n <SheetClose variant=\"ghost\" class=\"justify-start\">\n {item.label}\n </SheetClose>\n ))\n }\n </nav>\n <div class=\"mt-auto flex flex-col gap-2\">\n <slot />\n </div>\n </div>\n </SheetContent>\n </Sheet>\n </HeaderContainer>\n</Header>\n",
|
|
16
16
|
"type": "registry:block"
|
|
17
17
|
}
|
|
18
18
|
],
|
package/public/r/registry.json
CHANGED
|
@@ -1687,6 +1687,7 @@
|
|
|
1687
1687
|
"@fulldev/empty",
|
|
1688
1688
|
"@fulldev/field",
|
|
1689
1689
|
"@fulldev/header",
|
|
1690
|
+
"@fulldev/hover-card",
|
|
1690
1691
|
"@fulldev/input",
|
|
1691
1692
|
"@fulldev/input-group",
|
|
1692
1693
|
"@fulldev/item",
|
|
@@ -1695,6 +1696,7 @@
|
|
|
1695
1696
|
"@fulldev/marquee",
|
|
1696
1697
|
"@fulldev/native-select",
|
|
1697
1698
|
"@fulldev/navigation-menu",
|
|
1699
|
+
"@fulldev/popover",
|
|
1698
1700
|
"@fulldev/price",
|
|
1699
1701
|
"@fulldev/radio-group",
|
|
1700
1702
|
"@fulldev/rating",
|
|
@@ -1711,7 +1713,9 @@
|
|
|
1711
1713
|
"@fulldev/tabs",
|
|
1712
1714
|
"@fulldev/textarea",
|
|
1713
1715
|
"@fulldev/theme-toggle",
|
|
1716
|
+
"@fulldev/tile",
|
|
1714
1717
|
"@fulldev/toc",
|
|
1718
|
+
"@fulldev/toggle",
|
|
1715
1719
|
"@fulldev/tooltip",
|
|
1716
1720
|
"@fulldev/typography",
|
|
1717
1721
|
"@fulldev/video",
|
|
@@ -1738,6 +1742,8 @@
|
|
|
1738
1742
|
"@fulldev/content-2",
|
|
1739
1743
|
"@fulldev/content-3",
|
|
1740
1744
|
"@fulldev/content-4",
|
|
1745
|
+
"@fulldev/content-5",
|
|
1746
|
+
"@fulldev/content-6",
|
|
1741
1747
|
"@fulldev/cta-1",
|
|
1742
1748
|
"@fulldev/cta-2",
|
|
1743
1749
|
"@fulldev/cta-3",
|
package/public/r/sidebar-1.json
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"files": [
|
|
21
21
|
{
|
|
22
22
|
"path": "src/components/blocks/sidebar-1.astro",
|
|
23
|
-
"content": "---\nimport { cn } from \"@/lib/utils\"\nimport {\n Breadcrumb,\n BreadcrumbEllipsis,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n} from \"@/components/ui/breadcrumb\"\nimport { Button } from \"@/components/ui/button\"\nimport {\n Command,\n CommandDialog,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n CommandShortcut,\n} from \"@/components/ui/command\"\nimport { Dialog, DialogTrigger } from \"@/components/ui/dialog\"\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\"\nimport { Header, HeaderContainer, HeaderGroup } from \"@/components/ui/header\"\nimport { Icon } from \"@/components/ui/icon\"\nimport {\n InputGroup,\n InputGroupAddon,\n InputGroupInput,\n} from \"@/components/ui/input-group\"\nimport { Kbd } from \"@/components/ui/kbd\"\nimport { LogoImage, LogoText } from \"@/components/ui/logo\"\nimport { Separator } from \"@/components/ui/separator\"\nimport {\n Sidebar,\n SidebarContent,\n SidebarGroup,\n SidebarGroupLabel,\n SidebarHeader,\n SidebarInset,\n SidebarMenu,\n SidebarMenuButton,\n SidebarMenuItem,\n SidebarProvider,\n SidebarTrigger,\n} from \"@/components/ui/sidebar\"\nimport { ThemeToggle } from \"@/components/ui/theme-toggle\"\n\ntype Props = {\n class?: string\n logo: {\n label: string\n href: string\n src?: any\n srcLight?: any\n srcDark?: any\n alt?: string\n }\n search: {\n label: string\n empty: string\n items: {\n label: string\n href: string\n title: string\n path: string\n description: string\n group: string\n }[]\n }\n breadcrumb: {\n items: {\n label: string\n href: string\n }[]\n menu: {\n label: string\n href: string\n }[]\n }\n navigation: {\n label: string\n href: string\n links?: {\n label: string\n href: string\n }[]\n }[]\n githubRepo: string\n}\n\nconst {\n class: className,\n logo,\n search,\n breadcrumb,\n navigation,\n githubRepo,\n}: Props = Astro.props\n\nconst normalizePath = (path: string) => {\n if (path === \"/\") return path\n\n return path.replace(/\\/$/, \"\")\n}\nconst currentPath = normalizePath(Astro.url.pathname)\nconst searchGroups = search.items.reduce<\n { label: string; items: typeof search.items }[]\n>((groups, item) => {\n const group = groups.find((entry) => entry.label === item.group)\n\n if (group) {\n group.items.push(item)\n } else {\n groups.push({ label: item.group, items: [item] })\n }\n\n return groups\n}, [])\n\nlet githubStars = \"Stars\"\n\ntry {\n const response = await fetch(`https://api.github.com/repos/${githubRepo}`)\n const data = await response.json()\n\n if (response.ok && typeof data.stargazers_count === \"number\") {\n githubStars = new Intl.NumberFormat().format(data.stargazers_count)\n }\n} catch {\n // Keep the fallback label when GitHub is unavailable.\n}\n---\n\n<SidebarProvider>\n <Sidebar class={cn(\"\", className)} collapsible=\"offcanvas\" variant=\"inset\">\n <SidebarHeader class=\"flex-col\">\n <SidebarMenuButton size=\"lg\" variant=\"ghost\" href={logo.href}>\n <LogoImage\n src={logo.src}\n srcLight={logo.srcLight}\n srcDark={logo.srcDark}\n alt={logo.alt}\n />\n <LogoText class=\"font-semibold\">{logo.label}</LogoText>\n </SidebarMenuButton>\n <Dialog class=\"w-full\" data-search-dialog>\n <DialogTrigger\n as=\"div\"\n role=\"button\"\n tabindex=\"0\"\n class=\"h-auto w-full cursor-pointer border-0 bg-transparent p-0 shadow-none hover:bg-transparent\"\n aria-keyshortcuts=\"Meta+K Control+K\"\n >\n <InputGroup class=\"h-8 shadow-none\">\n <InputGroupInput\n placeholder={search.label}\n readonly\n tabindex=\"-1\"\n aria-label={search.label}\n class=\"cursor-pointer\"\n />\n <InputGroupAddon>\n <Icon name=\"search\" />\n </InputGroupAddon>\n <InputGroupAddon align=\"inline-end\">\n <Kbd data-search-modifier>⌘</Kbd>\n <Kbd>K</Kbd>\n </InputGroupAddon>\n </InputGroup>\n </DialogTrigger>\n\n <CommandDialog title={search.label} description={search.label}>\n <Command\n class=\"[&_[data-slot=command-item][data-selected]]:bg-accent [&_[data-slot=command-item][data-selected]]:text-accent-foreground\"\n >\n <CommandInput placeholder={search.label} />\n <CommandList>\n <CommandEmpty>{search.empty}</CommandEmpty>\n\n {\n searchGroups.map((group) => (\n <CommandGroup heading={group.label}>\n {group.items.map((item) => (\n <CommandItem\n href={item.href}\n value={item.path}\n label={item.label}\n keywords={[item.title, item.path]}\n >\n {item.label}\n <CommandShortcut>{item.href}</CommandShortcut>\n </CommandItem>\n ))}\n </CommandGroup>\n ))\n }\n </CommandList>\n </Command>\n </CommandDialog>\n </Dialog>\n </SidebarHeader>\n <SidebarContent\n class=\"mask-[linear-gradient(to_bottom,transparent,black_1rem,black_calc(100%-1rem),transparent)] [-webkit-mask-image:linear-gradient(to_bottom,transparent,black_1rem,black_calc(100%-1rem),transparent)] group-data-[collapsible=icon]:mask-none group-data-[collapsible=icon]:[-webkit-mask-image:none]\"\n >\n {\n navigation.map((group) => (\n <SidebarGroup>\n <SidebarGroupLabel>{group.label}</SidebarGroupLabel>\n <SidebarMenu>\n {(group.links ?? [group]).map((item) => (\n <SidebarMenuItem>\n <SidebarMenuButton\n href={item.href}\n isActive={normalizePath(item.href) === currentPath}\n >\n <span>{item.label}</span>\n </SidebarMenuButton>\n </SidebarMenuItem>\n ))}\n </SidebarMenu>\n </SidebarGroup>\n ))\n }\n </SidebarContent>\n </Sidebar>\n <SidebarInset>\n <Header class=\"h-16\">\n <HeaderContainer class=\"max-w-none justify-between\">\n <HeaderGroup>\n <SidebarTrigger />\n <Separator orientation=\"vertical\" class=\"my-auto mr-2 h-4\" />\n <Breadcrumb>\n <BreadcrumbList>\n {\n breadcrumb.items.map((item, index) => {\n const isLast = index === breadcrumb.items.length - 1\n const isHome = item.href === \"/\"\n const hasMenu =\n !isHome &&\n breadcrumb.menu.some(\n (menuItem) =>\n normalizePath(menuItem.href) ===\n normalizePath(item.href)\n )\n\n return (\n <>\n {index > 0 && (\n <BreadcrumbSeparator>/</BreadcrumbSeparator>\n )}\n <BreadcrumbItem>\n {hasMenu ? (\n <DropdownMenu>\n <DropdownMenuTrigger\n aria-label={`${item.label} pages`}\n aria-current={isLast ? \"page\" : undefined}\n class={cn(\n \"hover:text-foreground inline-flex items-center gap-1 transition-colors\",\n isLast && \"text-foreground\"\n )}\n >\n {item.label}\n <Icon\n name=\"chevron-down\"\n class=\"size-3.5 opacity-70\"\n aria-hidden=\"true\"\n />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\">\n <DropdownMenuGroup>\n {breadcrumb.menu.map((menuItem) => (\n <a\n href={menuItem.href}\n aria-current={\n normalizePath(menuItem.href) ===\n currentPath\n ? \"page\"\n : undefined\n }\n class=\"hover:bg-accent hover:text-accent-foreground aria-[current=page]:text-foreground flex items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none\"\n >\n {menuItem.label}\n </a>\n ))}\n </DropdownMenuGroup>\n </DropdownMenuContent>\n </DropdownMenu>\n ) : isLast ? (\n <BreadcrumbPage>{item.label}</BreadcrumbPage>\n ) : (\n <BreadcrumbLink href={item.href}>\n {item.label}\n </BreadcrumbLink>\n )}\n </BreadcrumbItem>\n </>\n )\n })\n }\n {\n breadcrumb.menu.length > 0 && currentPath === \"/\" && (\n <>\n <BreadcrumbSeparator>/</BreadcrumbSeparator>\n <BreadcrumbItem>\n <DropdownMenu>\n <DropdownMenuTrigger\n aria-label=\"Open page menu\"\n class=\"hover:text-foreground inline-flex items-center leading-none transition-colors\"\n >\n <BreadcrumbEllipsis />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\">\n <DropdownMenuGroup>\n {breadcrumb.menu.map((item) => (\n <a\n href={item.href}\n class=\"hover:bg-accent hover:text-accent-foreground flex items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none\"\n >\n {item.label}\n </a>\n ))}\n </DropdownMenuGroup>\n </DropdownMenuContent>\n </DropdownMenu>\n </BreadcrumbItem>\n </>\n )\n }\n </BreadcrumbList>\n </Breadcrumb>\n </HeaderGroup>\n <HeaderGroup>\n <Button\n as=\"a\"\n href={`https://github.com/${githubRepo}`}\n size=\"sm\"\n variant=\"ghost\"\n target=\"_blank\"\n rel=\"noreferrer\"\n aria-label={`View ${githubRepo} on GitHub, ${githubStars}`}\n >\n <Icon name=\"github\" />\n <span>{githubStars}</span>\n </Button>\n <ThemeToggle />\n </HeaderGroup>\n </HeaderContainer>\n </Header>\n <slot />\n </SidebarInset>\n</SidebarProvider>\n\n<script>\n const isMac = /Mac|iPhone|iPad|iPod/i.test(\n navigator.platform || navigator.userAgent\n )\n\n document.querySelectorAll(\"[data-search-modifier]\").forEach((shortcut) => {\n shortcut.textContent = isMac ? \"⌘\" : \"Ctrl\"\n })\n\n document.addEventListener(\"keydown\", (event) => {\n if (event.key.toLowerCase() !== \"k\") return\n if (event.shiftKey || event.altKey) return\n\n const dialog = document.querySelector(\"[data-search-dialog]\")\n const isSearchShortcut = isMac\n ? event.metaKey && !event.ctrlKey\n : event.ctrlKey && !event.metaKey\n\n if (!dialog || dialog.hasAttribute(\"data-open\") || !isSearchShortcut) return\n\n event.preventDefault()\n dialog.dispatchEvent(\n new CustomEvent(\"dialog:set\", { detail: { open: true } })\n )\n })\n</script>\n",
|
|
23
|
+
"content": "---\nimport { cn } from \"@/lib/utils\"\nimport {\n Breadcrumb,\n BreadcrumbEllipsis,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n} from \"@/components/ui/breadcrumb\"\nimport { Button } from \"@/components/ui/button\"\nimport {\n Command,\n CommandDialog,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n CommandShortcut,\n} from \"@/components/ui/command\"\nimport { Dialog, DialogTrigger } from \"@/components/ui/dialog\"\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\"\nimport { Header, HeaderContainer, HeaderGroup } from \"@/components/ui/header\"\nimport { Icon } from \"@/components/ui/icon\"\nimport {\n InputGroup,\n InputGroupAddon,\n InputGroupInput,\n} from \"@/components/ui/input-group\"\nimport { Kbd } from \"@/components/ui/kbd\"\nimport { LogoImage, LogoText } from \"@/components/ui/logo\"\nimport { Separator } from \"@/components/ui/separator\"\nimport {\n Sidebar,\n SidebarContent,\n SidebarGroup,\n SidebarGroupLabel,\n SidebarHeader,\n SidebarInset,\n SidebarMenu,\n SidebarMenuButton,\n SidebarMenuItem,\n SidebarProvider,\n SidebarTrigger,\n} from \"@/components/ui/sidebar\"\nimport { ThemeToggle } from \"@/components/ui/theme-toggle\"\n\ntype Props = {\n class?: string\n logo: {\n label: string\n href: string\n src?: any\n srcLight?: any\n srcDark?: any\n alt?: string\n }\n search: {\n label: string\n empty: string\n items: {\n label: string\n href: string\n title: string\n path: string\n description: string\n group: string\n }[]\n }\n breadcrumb: {\n items: {\n label: string\n href: string\n }[]\n menu: {\n label: string\n href: string\n }[]\n }\n navigation: {\n label: string\n href: string\n links?: {\n label: string\n href: string\n }[]\n }[]\n githubRepo: string\n}\n\nconst {\n class: className,\n logo,\n search,\n breadcrumb,\n navigation,\n githubRepo,\n}: Props = Astro.props\n\nconst normalizePath = (path: string) => {\n if (path === \"/\") return path\n\n return path.replace(/\\/$/, \"\")\n}\nconst currentPath = normalizePath(Astro.url.pathname)\nconst searchGroups = search.items.reduce<\n { label: string; items: typeof search.items }[]\n>((groups, item) => {\n const group = groups.find((entry) => entry.label === item.group)\n\n if (group) {\n group.items.push(item)\n } else {\n groups.push({ label: item.group, items: [item] })\n }\n\n return groups\n}, [])\n\nconst githubStars = \"560+\"\n---\n\n<SidebarProvider>\n <Sidebar class={cn(\"\", className)} collapsible=\"offcanvas\" variant=\"inset\">\n <SidebarHeader class=\"flex-col\">\n <SidebarMenuButton size=\"lg\" variant=\"ghost\" href={logo.href}>\n <LogoImage\n src={logo.src}\n srcLight={logo.srcLight}\n srcDark={logo.srcDark}\n alt={logo.alt}\n />\n <LogoText class=\"font-semibold\">{logo.label}</LogoText>\n </SidebarMenuButton>\n <Dialog class=\"w-full\" data-search-dialog>\n <DialogTrigger\n as=\"div\"\n role=\"button\"\n tabindex=\"0\"\n class=\"h-auto w-full cursor-pointer border-0 bg-transparent p-0 shadow-none hover:bg-transparent\"\n aria-keyshortcuts=\"Meta+K Control+K\"\n >\n <InputGroup class=\"h-8 shadow-none\">\n <InputGroupInput\n placeholder={search.label}\n readonly\n tabindex=\"-1\"\n aria-label={search.label}\n class=\"cursor-pointer\"\n />\n <InputGroupAddon>\n <Icon name=\"search\" />\n </InputGroupAddon>\n <InputGroupAddon align=\"inline-end\">\n <Kbd data-search-modifier>⌘</Kbd>\n <Kbd>K</Kbd>\n </InputGroupAddon>\n </InputGroup>\n </DialogTrigger>\n\n <CommandDialog title={search.label} description={search.label}>\n <Command\n class=\"[&_[data-slot=command-item][data-selected]]:bg-accent [&_[data-slot=command-item][data-selected]]:text-accent-foreground\"\n >\n <CommandInput placeholder={search.label} />\n <CommandList>\n <CommandEmpty>{search.empty}</CommandEmpty>\n\n {\n searchGroups.map((group) => (\n <CommandGroup heading={group.label}>\n {group.items.map((item) => (\n <CommandItem\n href={item.href}\n value={item.path}\n label={item.label}\n keywords={[item.title, item.path]}\n >\n {item.label}\n <CommandShortcut>{item.href}</CommandShortcut>\n </CommandItem>\n ))}\n </CommandGroup>\n ))\n }\n </CommandList>\n </Command>\n </CommandDialog>\n </Dialog>\n </SidebarHeader>\n <SidebarContent\n class=\"mask-[linear-gradient(to_bottom,transparent,black_1rem,black_calc(100%-1rem),transparent)] [-webkit-mask-image:linear-gradient(to_bottom,transparent,black_1rem,black_calc(100%-1rem),transparent)] group-data-[collapsible=icon]:mask-none group-data-[collapsible=icon]:[-webkit-mask-image:none]\"\n >\n {\n navigation.map((group) => (\n <SidebarGroup>\n <SidebarGroupLabel>{group.label}</SidebarGroupLabel>\n <SidebarMenu>\n {(group.links ?? [group]).map((item) => (\n <SidebarMenuItem>\n <SidebarMenuButton\n href={item.href}\n isActive={normalizePath(item.href) === currentPath}\n >\n <span>{item.label}</span>\n </SidebarMenuButton>\n </SidebarMenuItem>\n ))}\n </SidebarMenu>\n </SidebarGroup>\n ))\n }\n </SidebarContent>\n </Sidebar>\n <SidebarInset>\n <Header class=\"h-16\">\n <HeaderContainer class=\"max-w-none justify-between\">\n <HeaderGroup class=\"flex-1 md:flex-none\">\n <SidebarTrigger />\n <Separator orientation=\"vertical\" class=\"my-auto mr-2 h-4\" />\n <Dialog class=\"block min-w-0 flex-1 md:hidden\" data-search-dialog>\n <DialogTrigger\n as=\"div\"\n role=\"button\"\n tabindex=\"0\"\n class=\"h-auto w-full cursor-pointer border-0 bg-transparent p-0 shadow-none hover:bg-transparent\"\n aria-keyshortcuts=\"Meta+K Control+K\"\n >\n <InputGroup class=\"h-8 shadow-none\">\n <InputGroupInput\n placeholder={search.label}\n readonly\n tabindex=\"-1\"\n aria-label={search.label}\n class=\"cursor-pointer\"\n />\n <InputGroupAddon>\n <Icon name=\"search\" />\n </InputGroupAddon>\n </InputGroup>\n </DialogTrigger>\n\n <CommandDialog title={search.label} description={search.label}>\n <Command\n class=\"[&_[data-slot=command-item][data-selected]]:bg-accent [&_[data-slot=command-item][data-selected]]:text-accent-foreground\"\n >\n <CommandInput placeholder={search.label} />\n <CommandList>\n <CommandEmpty>{search.empty}</CommandEmpty>\n\n {\n searchGroups.map((group) => (\n <CommandGroup heading={group.label}>\n {group.items.map((item) => (\n <CommandItem\n href={item.href}\n value={item.path}\n label={item.label}\n keywords={[item.title, item.path]}\n >\n {item.label}\n <CommandShortcut>{item.href}</CommandShortcut>\n </CommandItem>\n ))}\n </CommandGroup>\n ))\n }\n </CommandList>\n </Command>\n </CommandDialog>\n </Dialog>\n <Breadcrumb class=\"hidden md:block\">\n <BreadcrumbList>\n {\n breadcrumb.items.map((item, index) => {\n const isLast = index === breadcrumb.items.length - 1\n const isHome = item.href === \"/\"\n const hasMenu =\n !isHome &&\n breadcrumb.menu.some(\n (menuItem) =>\n normalizePath(menuItem.href) ===\n normalizePath(item.href)\n )\n\n return (\n <>\n {index > 0 && (\n <BreadcrumbSeparator>/</BreadcrumbSeparator>\n )}\n <BreadcrumbItem>\n {hasMenu ? (\n <DropdownMenu>\n <DropdownMenuTrigger\n aria-label={`${item.label} pages`}\n aria-current={isLast ? \"page\" : undefined}\n class={cn(\n \"hover:text-foreground inline-flex items-center gap-1 transition-colors\",\n isLast && \"text-foreground\"\n )}\n >\n {item.label}\n <Icon\n name=\"chevron-down\"\n class=\"size-3.5 opacity-70\"\n aria-hidden=\"true\"\n />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\">\n <DropdownMenuGroup>\n {breadcrumb.menu.map((menuItem) => (\n <a\n href={menuItem.href}\n aria-current={\n normalizePath(menuItem.href) ===\n currentPath\n ? \"page\"\n : undefined\n }\n class=\"hover:bg-accent hover:text-accent-foreground aria-[current=page]:text-foreground flex items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none\"\n >\n {menuItem.label}\n </a>\n ))}\n </DropdownMenuGroup>\n </DropdownMenuContent>\n </DropdownMenu>\n ) : isLast ? (\n <BreadcrumbPage>{item.label}</BreadcrumbPage>\n ) : (\n <BreadcrumbLink href={item.href}>\n {item.label}\n </BreadcrumbLink>\n )}\n </BreadcrumbItem>\n </>\n )\n })\n }\n {\n breadcrumb.menu.length > 0 && currentPath === \"/\" && (\n <>\n <BreadcrumbSeparator>/</BreadcrumbSeparator>\n <BreadcrumbItem>\n <DropdownMenu>\n <DropdownMenuTrigger\n aria-label=\"Open page menu\"\n class=\"hover:text-foreground inline-flex items-center leading-none transition-colors\"\n >\n <BreadcrumbEllipsis />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\">\n <DropdownMenuGroup>\n {breadcrumb.menu.map((item) => (\n <a\n href={item.href}\n class=\"hover:bg-accent hover:text-accent-foreground flex items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none\"\n >\n {item.label}\n </a>\n ))}\n </DropdownMenuGroup>\n </DropdownMenuContent>\n </DropdownMenu>\n </BreadcrumbItem>\n </>\n )\n }\n </BreadcrumbList>\n </Breadcrumb>\n </HeaderGroup>\n <HeaderGroup>\n <Button\n as=\"a\"\n href={`https://github.com/${githubRepo}`}\n size=\"sm\"\n variant=\"ghost\"\n target=\"_blank\"\n rel=\"noreferrer\"\n aria-label={`View ${githubRepo} on GitHub, ${githubStars}`}\n >\n <Icon name=\"github\" />\n <span>{githubStars}</span>\n </Button>\n <ThemeToggle />\n </HeaderGroup>\n </HeaderContainer>\n </Header>\n <slot />\n </SidebarInset>\n</SidebarProvider>\n\n<script>\n const isMac = /Mac|iPhone|iPad|iPod/i.test(\n navigator.platform || navigator.userAgent\n )\n\n document.querySelectorAll(\"[data-search-modifier]\").forEach((shortcut) => {\n shortcut.textContent = isMac ? \"⌘\" : \"Ctrl\"\n })\n\n document.addEventListener(\"keydown\", (event) => {\n if (event.key.toLowerCase() !== \"k\") return\n if (event.shiftKey || event.altKey) return\n\n const dialogs = Array.from(\n document.querySelectorAll(\"[data-search-dialog]\")\n )\n const dialog =\n dialogs.find((dialog) => dialog.getClientRects().length > 0) ?? dialogs[0]\n const isSearchShortcut = isMac\n ? event.metaKey && !event.ctrlKey\n : event.ctrlKey && !event.metaKey\n\n if (!dialog || dialog.hasAttribute(\"data-open\") || !isSearchShortcut) return\n\n event.preventDefault()\n dialog.dispatchEvent(\n new CustomEvent(\"dialog:set\", { detail: { open: true } })\n )\n })\n</script>\n",
|
|
24
24
|
"type": "registry:block"
|
|
25
25
|
}
|
|
26
26
|
],
|
package/public/r/sidebar.json
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"files": [
|
|
15
15
|
{
|
|
16
16
|
"path": "src/components/ui/sidebar/sidebar-provider.astro",
|
|
17
|
-
"content": "---\nimport type { HTMLAttributes } from \"astro/types\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst SIDEBAR_WIDTH = \"16rem\"\nconst SIDEBAR_WIDTH_MOBILE = \"18rem\"\nconst SIDEBAR_WIDTH_ICON = \"3rem\"\n\ntype Props = HTMLAttributes<\"div\"> & {\n defaultOpen?: boolean\n open?: boolean\n}\n\nconst {\n class: className,\n style,\n defaultOpen = true,\n open,\n ...props\n} = Astro.props\n\nconst resolvedStyle = [\n `--sidebar-width: ${SIDEBAR_WIDTH}`,\n `--sidebar-width-mobile: ${SIDEBAR_WIDTH_MOBILE}`,\n `--sidebar-width-icon: ${SIDEBAR_WIDTH_ICON}`,\n typeof style === \"string\" ? style : undefined,\n]\n .filter(Boolean)\n .join(\"; \")\n---\n\n<div\n data-slot=\"sidebar-wrapper\"\n data-default-open={defaultOpen ? \"true\" : \"false\"}\n data-open={open === undefined ? undefined : open ? \"true\" : \"false\"}\n style={resolvedStyle}\n class={cn(\n \"group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full\",\n className\n )}\n {...props}\n>\n <slot />\n <script is:inline>\n ;(() => {\n window.__fulldevSyncSidebarProvider ??= (provider, open) => {\n const state = open ? \"expanded\" : \"collapsed\"\n const sidebar = provider.querySelector(\n \"[data-slot='sidebar']:not([data-mobile='true'])\"\n )\n const collapsible = sidebar?.dataset.sidebarCollapsible ?? \"offcanvas\"\n\n provider.dataset.open = open ? \"true\" : \"false\"\n provider.dataset.state = state\n\n if (sidebar) {\n sidebar.dataset.state = collapsible === \"none\" ? \"expanded\" : state\n sidebar.dataset.collapsible =\n collapsible === \"none\" ? \"none\" : open ? \"\" : collapsible\n }\n\n provider\n .querySelectorAll(\"[data-sidebar='trigger'], [data-sidebar='rail']\")\n .forEach((control) => {\n control.setAttribute(\"aria-expanded\", String(open))\n })\n\n provider\n .querySelectorAll(\"[data-sidebar='menu-tooltip']\")\n .forEach((tooltip) => {\n tooltip.hidden = state !== \"collapsed\"\n })\n }\n\n const provider = document.currentScript?.closest(\n \"[data-slot='sidebar-wrapper']\"\n )\n if (!(provider instanceof HTMLElement)) return\n\n const controlledOpen = provider.dataset.open\n const cookieValue = document.cookie\n .split(\"; \")\n .find((entry) => entry.startsWith(\"sidebar_state=\"))\n ?.slice(\"sidebar_state=\".length)\n\n const isOpen =\n controlledOpen === \"true\" || controlledOpen === \"false\"\n ? controlledOpen === \"true\"\n : cookieValue === \"true\" || cookieValue === \"false\"\n ? cookieValue === \"true\"\n : provider.dataset.defaultOpen !== \"false\"\n\n provider.dataset.openMobile = \"false\"\n window.__fulldevSyncSidebarProvider(provider, isOpen)\n })()\n </script>\n</div>\n\n<script is:inline>\n ;(() => {\n const scrollTop = Number(sessionStorage.getItem(\"sidebar_scroll\") || 0)\n\n document.querySelectorAll(\"[data-sidebar='content']\").forEach((content) => {\n content.scrollTop = scrollTop\n })\n })()\n</script>\n\n<script is:inline>\n ;(() => {\n if (window.__fulldevSidebarInitialized) return\n window.__fulldevSidebarInitialized = true\n\n const SIDEBAR_COOKIE_NAME = \"sidebar_state\"\n const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7\n\n const toggleSidebar = (provider) => {\n if (!window.matchMedia(\"(min-width: 768px)\").matches) {\n const openMobile = provider.dataset.openMobile !== \"true\"\n provider.dataset.openMobile = openMobile ? \"true\" : \"false\"\n provider\n .querySelector(\"[data-sidebar='sheet']\")\n ?.dispatchEvent(\n new CustomEvent(\"dialog:set\", { detail: { open: openMobile } })\n )\n return\n }\n\n const open = provider.dataset.open === \"false\"\n document.cookie = `${SIDEBAR_COOKIE_NAME}=${open}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`\n window.__fulldevSyncSidebarProvider?.(provider, open)\n }\n\n document.addEventListener(\"click\", (event) => {\n const control = event.target?.closest?.(\n \"[data-sidebar='trigger'], [data-sidebar='rail']\"\n )\n if (!control || control.matches(\":disabled, [aria-disabled='true']\")) {\n return\n }\n\n const provider = control.closest(\"[data-slot='sidebar-wrapper']\")\n if (!provider) return\n\n toggleSidebar(provider)\n })\n\n document.addEventListener(\"keydown\", (event) => {\n if (event.key.toLowerCase() !== \"b\") return\n if (!event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {\n return\n }\n if (event.defaultPrevented) return\n\n const target = event.target\n const isTyping =\n target instanceof HTMLElement &&\n (target.isContentEditable ||\n target.matches(\"input, textarea, select, [role='textbox']\"))\n if (isTyping) return\n\n const provider = document.querySelector(\"[data-slot='sidebar-wrapper']\")\n if (!(provider instanceof HTMLElement)) return\n\n event.preventDefault()\n toggleSidebar(provider)\n })\n\n document.addEventListener(\n \"scroll\",\n (event) => {\n if (event.target?.dataset?.sidebar === \"content\") {\n sessionStorage.setItem(\"sidebar_scroll\", event.target.scrollTop)\n }\n },\n true\n )\n })()\n</script>\n",
|
|
17
|
+
"content": "---\nimport type { HTMLAttributes } from \"astro/types\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst SIDEBAR_WIDTH = \"16rem\"\nconst SIDEBAR_WIDTH_MOBILE = \"18rem\"\nconst SIDEBAR_WIDTH_ICON = \"3rem\"\n\ntype Props = HTMLAttributes<\"div\"> & {\n defaultOpen?: boolean\n open?: boolean\n}\n\nconst {\n class: className,\n style,\n defaultOpen = true,\n open,\n ...props\n} = Astro.props\n\nconst resolvedStyle = [\n `--sidebar-width: ${SIDEBAR_WIDTH}`,\n `--sidebar-width-mobile: ${SIDEBAR_WIDTH_MOBILE}`,\n `--sidebar-width-icon: ${SIDEBAR_WIDTH_ICON}`,\n typeof style === \"string\" ? style : undefined,\n]\n .filter(Boolean)\n .join(\"; \")\n---\n\n<div\n data-slot=\"sidebar-wrapper\"\n data-default-open={defaultOpen ? \"true\" : \"false\"}\n data-open={open === undefined ? undefined : open ? \"true\" : \"false\"}\n data-sidebar-initializing=\"true\"\n style={resolvedStyle}\n class={cn(\n \"group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full\",\n className\n )}\n {...props}\n>\n <slot />\n <style is:inline>\n [data-slot=\"sidebar-wrapper\"][data-sidebar-initializing=\"true\"]\n [data-slot=\"sidebar-gap\"],\n [data-slot=\"sidebar-wrapper\"][data-sidebar-initializing=\"true\"]\n [data-slot=\"sidebar-container\"] {\n transition: none !important;\n }\n </style>\n <script is:inline>\n ;(() => {\n window.__fulldevSyncSidebarProvider ??= (provider, open) => {\n const state = open ? \"expanded\" : \"collapsed\"\n const sidebar = provider.querySelector(\n \"[data-slot='sidebar']:not([data-mobile='true'])\"\n )\n const collapsible = sidebar?.dataset.sidebarCollapsible ?? \"offcanvas\"\n\n provider.dataset.open = open ? \"true\" : \"false\"\n provider.dataset.state = state\n\n if (sidebar) {\n sidebar.dataset.state = collapsible === \"none\" ? \"expanded\" : state\n sidebar.dataset.collapsible =\n collapsible === \"none\" ? \"none\" : open ? \"\" : collapsible\n }\n\n provider\n .querySelectorAll(\"[data-sidebar='trigger'], [data-sidebar='rail']\")\n .forEach((control) => {\n control.setAttribute(\"aria-expanded\", String(open))\n })\n\n provider\n .querySelectorAll(\"[data-sidebar='menu-tooltip']\")\n .forEach((tooltip) => {\n tooltip.hidden = state !== \"collapsed\"\n })\n }\n\n const provider = document.currentScript?.closest(\n \"[data-slot='sidebar-wrapper']\"\n )\n if (!(provider instanceof HTMLElement)) return\n\n const controlledOpen = provider.dataset.open\n const cookieValue = document.cookie\n .split(\"; \")\n .find((entry) => entry.startsWith(\"sidebar_state=\"))\n ?.slice(\"sidebar_state=\".length)\n\n const isOpen =\n controlledOpen === \"true\" || controlledOpen === \"false\"\n ? controlledOpen === \"true\"\n : cookieValue === \"true\" || cookieValue === \"false\"\n ? cookieValue === \"true\"\n : provider.dataset.defaultOpen !== \"false\"\n\n provider.dataset.openMobile = \"false\"\n window.__fulldevSyncSidebarProvider(provider, isOpen)\n requestAnimationFrame(() => {\n provider.removeAttribute(\"data-sidebar-initializing\")\n })\n })()\n </script>\n</div>\n\n<script is:inline>\n ;(() => {\n const scrollTop = Number(sessionStorage.getItem(\"sidebar_scroll\") || 0)\n\n document.querySelectorAll(\"[data-sidebar='content']\").forEach((content) => {\n content.scrollTop = scrollTop\n })\n })()\n</script>\n\n<script is:inline>\n ;(() => {\n if (window.__fulldevSidebarInitialized) return\n window.__fulldevSidebarInitialized = true\n\n const SIDEBAR_COOKIE_NAME = \"sidebar_state\"\n const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7\n\n const toggleSidebar = (provider) => {\n if (!window.matchMedia(\"(min-width: 768px)\").matches) {\n const openMobile = provider.dataset.openMobile !== \"true\"\n provider.dataset.openMobile = openMobile ? \"true\" : \"false\"\n provider\n .querySelector(\"[data-sidebar='sheet']\")\n ?.dispatchEvent(\n new CustomEvent(\"dialog:set\", { detail: { open: openMobile } })\n )\n return\n }\n\n const open = provider.dataset.open === \"false\"\n document.cookie = `${SIDEBAR_COOKIE_NAME}=${open}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`\n window.__fulldevSyncSidebarProvider?.(provider, open)\n }\n\n document.addEventListener(\"click\", (event) => {\n const control = event.target?.closest?.(\n \"[data-sidebar='trigger'], [data-sidebar='rail']\"\n )\n if (!control || control.matches(\":disabled, [aria-disabled='true']\")) {\n return\n }\n\n const provider = control.closest(\"[data-slot='sidebar-wrapper']\")\n if (!provider) return\n\n toggleSidebar(provider)\n })\n\n document.addEventListener(\"keydown\", (event) => {\n if (event.key.toLowerCase() !== \"b\") return\n if (!event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {\n return\n }\n if (event.defaultPrevented) return\n\n const target = event.target\n const isTyping =\n target instanceof HTMLElement &&\n (target.isContentEditable ||\n target.matches(\"input, textarea, select, [role='textbox']\"))\n if (isTyping) return\n\n const provider = document.querySelector(\"[data-slot='sidebar-wrapper']\")\n if (!(provider instanceof HTMLElement)) return\n\n event.preventDefault()\n toggleSidebar(provider)\n })\n\n document.addEventListener(\n \"scroll\",\n (event) => {\n if (event.target?.dataset?.sidebar === \"content\") {\n sessionStorage.setItem(\"sidebar_scroll\", event.target.scrollTop)\n }\n },\n true\n )\n })()\n</script>\n",
|
|
18
18
|
"type": "registry:ui"
|
|
19
19
|
},
|
|
20
20
|
{
|
package/registry.json
CHANGED
|
@@ -1687,6 +1687,7 @@
|
|
|
1687
1687
|
"@fulldev/empty",
|
|
1688
1688
|
"@fulldev/field",
|
|
1689
1689
|
"@fulldev/header",
|
|
1690
|
+
"@fulldev/hover-card",
|
|
1690
1691
|
"@fulldev/input",
|
|
1691
1692
|
"@fulldev/input-group",
|
|
1692
1693
|
"@fulldev/item",
|
|
@@ -1695,6 +1696,7 @@
|
|
|
1695
1696
|
"@fulldev/marquee",
|
|
1696
1697
|
"@fulldev/native-select",
|
|
1697
1698
|
"@fulldev/navigation-menu",
|
|
1699
|
+
"@fulldev/popover",
|
|
1698
1700
|
"@fulldev/price",
|
|
1699
1701
|
"@fulldev/radio-group",
|
|
1700
1702
|
"@fulldev/rating",
|
|
@@ -1711,7 +1713,9 @@
|
|
|
1711
1713
|
"@fulldev/tabs",
|
|
1712
1714
|
"@fulldev/textarea",
|
|
1713
1715
|
"@fulldev/theme-toggle",
|
|
1716
|
+
"@fulldev/tile",
|
|
1714
1717
|
"@fulldev/toc",
|
|
1718
|
+
"@fulldev/toggle",
|
|
1715
1719
|
"@fulldev/tooltip",
|
|
1716
1720
|
"@fulldev/typography",
|
|
1717
1721
|
"@fulldev/video",
|
|
@@ -1738,6 +1742,8 @@
|
|
|
1738
1742
|
"@fulldev/content-2",
|
|
1739
1743
|
"@fulldev/content-3",
|
|
1740
1744
|
"@fulldev/content-4",
|
|
1745
|
+
"@fulldev/content-5",
|
|
1746
|
+
"@fulldev/content-6",
|
|
1741
1747
|
"@fulldev/cta-1",
|
|
1742
1748
|
"@fulldev/cta-2",
|
|
1743
1749
|
"@fulldev/cta-3",
|
|
@@ -33,6 +33,7 @@ type Props = {
|
|
|
33
33
|
navigation: {
|
|
34
34
|
label: string
|
|
35
35
|
href: string
|
|
36
|
+
active?: boolean
|
|
36
37
|
links?: {
|
|
37
38
|
label: string
|
|
38
39
|
href: string
|
|
@@ -50,7 +51,7 @@ const { class: className, logo, navigation, buttons } = Astro.props
|
|
|
50
51
|
---
|
|
51
52
|
|
|
52
53
|
<Header class={cn("", className)}>
|
|
53
|
-
<HeaderContainer class="justify-between gap-4">
|
|
54
|
+
<HeaderContainer class="items-center justify-between gap-4">
|
|
54
55
|
<HeaderGroup>
|
|
55
56
|
<Logo href={logo.href}>
|
|
56
57
|
<LogoImage
|
|
@@ -67,7 +68,7 @@ const { class: className, logo, navigation, buttons } = Astro.props
|
|
|
67
68
|
{
|
|
68
69
|
navigation.map((item, index) => (
|
|
69
70
|
<NavigationMenuItem value={`nav-${index}`}>
|
|
70
|
-
<NavigationMenuLink href={item.href}>
|
|
71
|
+
<NavigationMenuLink href={item.href} active={item.active}>
|
|
71
72
|
{item.label}
|
|
72
73
|
</NavigationMenuLink>
|
|
73
74
|
</NavigationMenuItem>
|
|
@@ -124,18 +124,7 @@ const searchGroups = search.items.reduce<
|
|
|
124
124
|
return groups
|
|
125
125
|
}, [])
|
|
126
126
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
try {
|
|
130
|
-
const response = await fetch(`https://api.github.com/repos/${githubRepo}`)
|
|
131
|
-
const data = await response.json()
|
|
132
|
-
|
|
133
|
-
if (response.ok && typeof data.stargazers_count === "number") {
|
|
134
|
-
githubStars = new Intl.NumberFormat().format(data.stargazers_count)
|
|
135
|
-
}
|
|
136
|
-
} catch {
|
|
137
|
-
// Keep the fallback label when GitHub is unavailable.
|
|
138
|
-
}
|
|
127
|
+
const githubStars = "560+"
|
|
139
128
|
---
|
|
140
129
|
|
|
141
130
|
<SidebarProvider>
|
|
@@ -233,10 +222,61 @@ try {
|
|
|
233
222
|
<SidebarInset>
|
|
234
223
|
<Header class="h-16">
|
|
235
224
|
<HeaderContainer class="max-w-none justify-between">
|
|
236
|
-
<HeaderGroup>
|
|
225
|
+
<HeaderGroup class="flex-1 md:flex-none">
|
|
237
226
|
<SidebarTrigger />
|
|
238
227
|
<Separator orientation="vertical" class="my-auto mr-2 h-4" />
|
|
239
|
-
<
|
|
228
|
+
<Dialog class="block min-w-0 flex-1 md:hidden" data-search-dialog>
|
|
229
|
+
<DialogTrigger
|
|
230
|
+
as="div"
|
|
231
|
+
role="button"
|
|
232
|
+
tabindex="0"
|
|
233
|
+
class="h-auto w-full cursor-pointer border-0 bg-transparent p-0 shadow-none hover:bg-transparent"
|
|
234
|
+
aria-keyshortcuts="Meta+K Control+K"
|
|
235
|
+
>
|
|
236
|
+
<InputGroup class="h-8 shadow-none">
|
|
237
|
+
<InputGroupInput
|
|
238
|
+
placeholder={search.label}
|
|
239
|
+
readonly
|
|
240
|
+
tabindex="-1"
|
|
241
|
+
aria-label={search.label}
|
|
242
|
+
class="cursor-pointer"
|
|
243
|
+
/>
|
|
244
|
+
<InputGroupAddon>
|
|
245
|
+
<Icon name="search" />
|
|
246
|
+
</InputGroupAddon>
|
|
247
|
+
</InputGroup>
|
|
248
|
+
</DialogTrigger>
|
|
249
|
+
|
|
250
|
+
<CommandDialog title={search.label} description={search.label}>
|
|
251
|
+
<Command
|
|
252
|
+
class="[&_[data-slot=command-item][data-selected]]:bg-accent [&_[data-slot=command-item][data-selected]]:text-accent-foreground"
|
|
253
|
+
>
|
|
254
|
+
<CommandInput placeholder={search.label} />
|
|
255
|
+
<CommandList>
|
|
256
|
+
<CommandEmpty>{search.empty}</CommandEmpty>
|
|
257
|
+
|
|
258
|
+
{
|
|
259
|
+
searchGroups.map((group) => (
|
|
260
|
+
<CommandGroup heading={group.label}>
|
|
261
|
+
{group.items.map((item) => (
|
|
262
|
+
<CommandItem
|
|
263
|
+
href={item.href}
|
|
264
|
+
value={item.path}
|
|
265
|
+
label={item.label}
|
|
266
|
+
keywords={[item.title, item.path]}
|
|
267
|
+
>
|
|
268
|
+
{item.label}
|
|
269
|
+
<CommandShortcut>{item.href}</CommandShortcut>
|
|
270
|
+
</CommandItem>
|
|
271
|
+
))}
|
|
272
|
+
</CommandGroup>
|
|
273
|
+
))
|
|
274
|
+
}
|
|
275
|
+
</CommandList>
|
|
276
|
+
</Command>
|
|
277
|
+
</CommandDialog>
|
|
278
|
+
</Dialog>
|
|
279
|
+
<Breadcrumb class="hidden md:block">
|
|
240
280
|
<BreadcrumbList>
|
|
241
281
|
{
|
|
242
282
|
breadcrumb.items.map((item, index) => {
|
|
@@ -370,7 +410,11 @@ try {
|
|
|
370
410
|
if (event.key.toLowerCase() !== "k") return
|
|
371
411
|
if (event.shiftKey || event.altKey) return
|
|
372
412
|
|
|
373
|
-
const
|
|
413
|
+
const dialogs = Array.from(
|
|
414
|
+
document.querySelectorAll("[data-search-dialog]")
|
|
415
|
+
)
|
|
416
|
+
const dialog =
|
|
417
|
+
dialogs.find((dialog) => dialog.getClientRects().length > 0) ?? dialogs[0]
|
|
374
418
|
const isSearchShortcut = isMac
|
|
375
419
|
? event.metaKey && !event.ctrlKey
|
|
376
420
|
: event.ctrlKey && !event.metaKey
|
|
@@ -34,6 +34,7 @@ const resolvedStyle = [
|
|
|
34
34
|
data-slot="sidebar-wrapper"
|
|
35
35
|
data-default-open={defaultOpen ? "true" : "false"}
|
|
36
36
|
data-open={open === undefined ? undefined : open ? "true" : "false"}
|
|
37
|
+
data-sidebar-initializing="true"
|
|
37
38
|
style={resolvedStyle}
|
|
38
39
|
class={cn(
|
|
39
40
|
"group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full",
|
|
@@ -42,6 +43,14 @@ const resolvedStyle = [
|
|
|
42
43
|
{...props}
|
|
43
44
|
>
|
|
44
45
|
<slot />
|
|
46
|
+
<style is:inline>
|
|
47
|
+
[data-slot="sidebar-wrapper"][data-sidebar-initializing="true"]
|
|
48
|
+
[data-slot="sidebar-gap"],
|
|
49
|
+
[data-slot="sidebar-wrapper"][data-sidebar-initializing="true"]
|
|
50
|
+
[data-slot="sidebar-container"] {
|
|
51
|
+
transition: none !important;
|
|
52
|
+
}
|
|
53
|
+
</style>
|
|
45
54
|
<script is:inline>
|
|
46
55
|
;(() => {
|
|
47
56
|
window.__fulldevSyncSidebarProvider ??= (provider, open) => {
|
|
@@ -93,6 +102,9 @@ const resolvedStyle = [
|
|
|
93
102
|
|
|
94
103
|
provider.dataset.openMobile = "false"
|
|
95
104
|
window.__fulldevSyncSidebarProvider(provider, isOpen)
|
|
105
|
+
requestAnimationFrame(() => {
|
|
106
|
+
provider.removeAttribute("data-sidebar-initializing")
|
|
107
|
+
})
|
|
96
108
|
})()
|
|
97
109
|
</script>
|
|
98
110
|
</div>
|