@sykoramaros/marosh-components 0.1.17 → 0.2.3
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/cli/index.cjs +513 -537
- package/dist/index.d.ts +27 -46
- package/dist/index.js +23 -23
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5283 -5401
- package/dist/index.mjs.map +1 -1
- package/package.json +43 -27
- package/src/components/basicComponents/CustomButton.stories.tsx +60 -0
- package/src/components/basicComponents/Footer.stories.tsx +47 -0
- package/src/components/basicComponents/Footer.tsx +13 -5
- package/src/components/basicComponents/LanguageSwitcher.stories.tsx +52 -0
- package/src/components/basicComponents/LanguageSwitcher.tsx +84 -0
- package/src/components/basicComponents/MainNav.stories.tsx +34 -0
- package/src/components/basicComponents/MainNav.tsx +24 -6
- package/src/components/basicComponents/MainSidebarLayout.stories.tsx +77 -0
- package/src/components/basicComponents/MainSidebarLayout.tsx +32 -27
- package/src/components/basicComponents/Switcher.stories.tsx +42 -0
- package/src/components/basicComponents/Switcher.tsx +2 -0
- package/src/components/ui/dropdown-menu.tsx +171 -0
- package/src/components/ui/sidebar.tsx +1 -0
- package/src/hooks/use-mobile.ts +5 -6
- package/src/index.ts +6 -7
- package/src/providers/BaseUrlProvider.tsx +1 -1
- package/src/providers/ThemeContextProvider.tsx +14 -2
- package/src/vite-env.d.ts +1 -0
- package/src/App.css +0 -42
- package/src/App.tsx +0 -44
- package/src/config/projectData.ts +0 -122
- package/src/main.tsx +0 -23
- package/src/providers/ProjectDataProvider.tsx +0 -26
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sykoramaros/marosh-components",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.3",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"module": "./dist/index.mjs",
|
|
@@ -23,13 +23,15 @@
|
|
|
23
23
|
"README.md"
|
|
24
24
|
],
|
|
25
25
|
"scripts": {
|
|
26
|
-
"dev": "
|
|
26
|
+
"dev": "storybook dev -p 6006",
|
|
27
27
|
"build": "bun run build:lib && bun run build:cli",
|
|
28
28
|
"build:lib": "bun run vite build",
|
|
29
29
|
"build:cli": "bun run tsup",
|
|
30
30
|
"lint": "bun run eslint .",
|
|
31
31
|
"preview": "bun run vite preview",
|
|
32
|
-
"prepublishOnly": "bun run build"
|
|
32
|
+
"prepublishOnly": "bun run build",
|
|
33
|
+
"storybook": "storybook dev -p 6006",
|
|
34
|
+
"build-storybook": "storybook build"
|
|
33
35
|
},
|
|
34
36
|
"peerDependencies": {
|
|
35
37
|
"react": "^18.0.0 || ^19.0.0",
|
|
@@ -37,46 +39,60 @@
|
|
|
37
39
|
},
|
|
38
40
|
"dependencies": {
|
|
39
41
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
42
|
+
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
40
43
|
"@radix-ui/react-navigation-menu": "^1.2.14",
|
|
41
44
|
"@radix-ui/react-separator": "^1.1.8",
|
|
42
45
|
"@radix-ui/react-slot": "^1.2.4",
|
|
43
46
|
"@radix-ui/react-switch": "^1.2.6",
|
|
44
47
|
"@radix-ui/react-tooltip": "^1.2.8",
|
|
45
|
-
"@tanstack/react-router": "^1.145.6",
|
|
46
|
-
"@tanstack/router-cli": "^1.145.6",
|
|
47
|
-
"@tanstack/router-plugin": "^1.145.6",
|
|
48
|
-
"ajv": "^8.17.1",
|
|
49
48
|
"class-variance-authority": "^0.7.1",
|
|
50
49
|
"clsx": "^2.1.1",
|
|
51
50
|
"lucide-react": "^0.562.0",
|
|
52
|
-
"tailwind-merge": "^3.
|
|
51
|
+
"tailwind-merge": "^3.6.0"
|
|
53
52
|
},
|
|
54
53
|
"devDependencies": {
|
|
55
|
-
"@eslint/js": "^9.39.
|
|
56
|
-
"@inquirer/prompts": "^8.
|
|
57
|
-
"@
|
|
54
|
+
"@eslint/js": "^9.39.4",
|
|
55
|
+
"@inquirer/prompts": "^8.4.3",
|
|
56
|
+
"@tanstack/react-router": "^1.169.2",
|
|
57
|
+
"@tanstack/router-cli": "^1.166.43",
|
|
58
|
+
"@tanstack/router-plugin": "^1.167.35",
|
|
59
|
+
"ajv": "^8.20.0",
|
|
60
|
+
"@tailwindcss/vite": "^4.3.0",
|
|
58
61
|
"@types/fs-extra": "^11.0.4",
|
|
59
|
-
"@types/node": "^25.0
|
|
60
|
-
"@types/react": "^19.2.
|
|
62
|
+
"@types/node": "^25.8.0",
|
|
63
|
+
"@types/react": "^19.2.14",
|
|
61
64
|
"@types/react-dom": "^19.2.3",
|
|
62
|
-
"@vitejs/plugin-react": "^5.
|
|
63
|
-
"autoprefixer": "^10.
|
|
64
|
-
"commander": "^14.0.
|
|
65
|
-
"eslint": "^9.39.
|
|
66
|
-
"eslint-plugin-react-hooks": "^7.
|
|
67
|
-
"eslint-plugin-react-refresh": "^0.4.
|
|
68
|
-
"fs-extra": "^11.3.
|
|
65
|
+
"@vitejs/plugin-react": "^5.2.0",
|
|
66
|
+
"autoprefixer": "^10.5.0",
|
|
67
|
+
"commander": "^14.0.3",
|
|
68
|
+
"eslint": "^9.39.4",
|
|
69
|
+
"eslint-plugin-react-hooks": "^7.1.1",
|
|
70
|
+
"eslint-plugin-react-refresh": "^0.4.26",
|
|
71
|
+
"fs-extra": "^11.3.5",
|
|
69
72
|
"globals": "^16.5.0",
|
|
70
|
-
"postcss": "^8.5.
|
|
71
|
-
"react": "^19.2.
|
|
72
|
-
"react-dom": "^19.2.
|
|
73
|
-
"tailwindcss": "^4.
|
|
73
|
+
"postcss": "^8.5.14",
|
|
74
|
+
"react": "^19.2.6",
|
|
75
|
+
"react-dom": "^19.2.6",
|
|
76
|
+
"tailwindcss": "^4.3.0",
|
|
74
77
|
"tsup": "^8.5.1",
|
|
75
78
|
"tw-animate-css": "^1.4.0",
|
|
76
79
|
"typescript": "~5.9.3",
|
|
77
|
-
"typescript-eslint": "^8.
|
|
78
|
-
"vite": "^7.
|
|
79
|
-
"vite-plugin-dts": "^4.5.4"
|
|
80
|
+
"typescript-eslint": "^8.59.3",
|
|
81
|
+
"vite": "^7.3.3",
|
|
82
|
+
"vite-plugin-dts": "^4.5.4",
|
|
83
|
+
"storybook": "^10.4.0",
|
|
84
|
+
"@storybook/tanstack-react": "^10.4.0",
|
|
85
|
+
"@chromatic-com/storybook": "^5.2.1",
|
|
86
|
+
"@storybook/addon-vitest": "^10.4.0",
|
|
87
|
+
"@storybook/addon-a11y": "^10.4.0",
|
|
88
|
+
"@storybook/addon-docs": "^10.4.0",
|
|
89
|
+
"@storybook/addon-mcp": "^0.6.0",
|
|
90
|
+
"@tanstack/react-query": "^5.100.10",
|
|
91
|
+
"eslint-plugin-storybook": "^10.4.0",
|
|
92
|
+
"vitest": "^4.1.6",
|
|
93
|
+
"playwright": "^1.60.0",
|
|
94
|
+
"@vitest/browser-playwright": "^4.1.6",
|
|
95
|
+
"@vitest/coverage-v8": "^4.1.6"
|
|
80
96
|
},
|
|
81
97
|
"keywords": [
|
|
82
98
|
"react",
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/tanstack-react"
|
|
2
|
+
import { CustomButton } from "./CustomButton"
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof CustomButton> = {
|
|
5
|
+
title: "Components/CustomButton",
|
|
6
|
+
component: CustomButton,
|
|
7
|
+
tags: ["autodocs"],
|
|
8
|
+
argTypes: {
|
|
9
|
+
variant: {
|
|
10
|
+
control: "select",
|
|
11
|
+
options: ["default", "destructive", "outline", "secondary", "ghost", "link"],
|
|
12
|
+
},
|
|
13
|
+
size: {
|
|
14
|
+
control: "select",
|
|
15
|
+
options: ["default", "sm", "lg", "icon"],
|
|
16
|
+
},
|
|
17
|
+
disabled: { control: "boolean" },
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default meta
|
|
22
|
+
type Story = StoryObj<typeof CustomButton>
|
|
23
|
+
|
|
24
|
+
export const Default: Story = {
|
|
25
|
+
args: { children: "Button", variant: "default", size: "default" },
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const Destructive: Story = {
|
|
29
|
+
args: { children: "Delete", variant: "destructive" },
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const Outline: Story = {
|
|
33
|
+
args: { children: "Outline", variant: "outline" },
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const Secondary: Story = {
|
|
37
|
+
args: { children: "Secondary", variant: "secondary" },
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const Ghost: Story = {
|
|
41
|
+
args: { children: "Ghost", variant: "ghost" },
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const Disabled: Story = {
|
|
45
|
+
args: { children: "Disabled", variant: "default", disabled: true },
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const AllVariants: Story = {
|
|
49
|
+
render: () => (
|
|
50
|
+
<div className="flex flex-wrap gap-3">
|
|
51
|
+
{(["default", "destructive", "outline", "secondary", "ghost", "link"] as const).map(
|
|
52
|
+
(variant) => (
|
|
53
|
+
<CustomButton key={variant} variant={variant}>
|
|
54
|
+
{variant}
|
|
55
|
+
</CustomButton>
|
|
56
|
+
),
|
|
57
|
+
)}
|
|
58
|
+
</div>
|
|
59
|
+
),
|
|
60
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/tanstack-react"
|
|
2
|
+
import { Link } from "@tanstack/react-router"
|
|
3
|
+
import { Facebook, Gitlab, Linkedin } from "lucide-react"
|
|
4
|
+
import { Footer } from "./Footer"
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof Footer> = {
|
|
7
|
+
title: "Components/Footer",
|
|
8
|
+
component: Footer,
|
|
9
|
+
tags: ["autodocs"],
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default meta
|
|
13
|
+
type Story = StoryObj<typeof Footer>
|
|
14
|
+
|
|
15
|
+
const links = [
|
|
16
|
+
{ title: "Home", url: "/" },
|
|
17
|
+
{ title: "About", url: "/about" },
|
|
18
|
+
{ title: "Contact", url: "/contact" },
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
const socialProfiles = [
|
|
22
|
+
{ title: "Facebook", url: "https://facebook.com", icon: Facebook },
|
|
23
|
+
{ title: "GitLab", url: "https://gitlab.com", icon: Gitlab },
|
|
24
|
+
{ title: "LinkedIn", url: "https://linkedin.com", icon: Linkedin },
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
export const Default: Story = {
|
|
28
|
+
args: {
|
|
29
|
+
links,
|
|
30
|
+
socialProfiles,
|
|
31
|
+
copyright: `Copyright © ${new Date().getFullYear()}`,
|
|
32
|
+
LinkComponent: Link,
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const NoCopyright: Story = {
|
|
37
|
+
args: { links, socialProfiles, LinkComponent: Link },
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const NoSocial: Story = {
|
|
41
|
+
args: {
|
|
42
|
+
links,
|
|
43
|
+
socialProfiles: [],
|
|
44
|
+
copyright: `Copyright © ${new Date().getFullYear()}`,
|
|
45
|
+
LinkComponent: Link,
|
|
46
|
+
},
|
|
47
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Link } from "@tanstack/react-router"
|
|
2
1
|
import { LucideIcon } from "lucide-react"
|
|
3
2
|
import { cn } from "@/lib/utils"
|
|
3
|
+
import type { LinkComponentProps } from "@/components/basicComponents/MainNav"
|
|
4
4
|
|
|
5
5
|
export interface FooterLinks {
|
|
6
6
|
title: string
|
|
@@ -18,13 +18,21 @@ export interface FooterItems extends React.ComponentPropsWithoutRef<"footer"> {
|
|
|
18
18
|
copyright?: string
|
|
19
19
|
socialProfiles: FooterSocial[]
|
|
20
20
|
className?: string
|
|
21
|
+
LinkComponent?: React.ComponentType<LinkComponentProps>
|
|
21
22
|
}
|
|
22
23
|
|
|
24
|
+
const DefaultLink = ({ to, className, children, target }: LinkComponentProps) => (
|
|
25
|
+
<a href={to} className={className} target={target}>
|
|
26
|
+
{children}
|
|
27
|
+
</a>
|
|
28
|
+
)
|
|
29
|
+
|
|
23
30
|
export const Footer = ({
|
|
24
31
|
links,
|
|
25
32
|
copyright,
|
|
26
33
|
socialProfiles,
|
|
27
34
|
className,
|
|
35
|
+
LinkComponent = DefaultLink,
|
|
28
36
|
...props
|
|
29
37
|
}: FooterItems) => {
|
|
30
38
|
return (
|
|
@@ -34,27 +42,27 @@ export const Footer = ({
|
|
|
34
42
|
{socialProfiles.map((item) => {
|
|
35
43
|
const Icon = typeof item.icon === "string" ? null : item.icon
|
|
36
44
|
return (
|
|
37
|
-
<
|
|
45
|
+
<LinkComponent
|
|
38
46
|
to={item.url}
|
|
39
47
|
className="rounded-xl"
|
|
40
48
|
key={item.title}
|
|
41
49
|
target="_blank"
|
|
42
50
|
>
|
|
43
51
|
{Icon && <Icon />}
|
|
44
|
-
</
|
|
52
|
+
</LinkComponent>
|
|
45
53
|
)
|
|
46
54
|
})}
|
|
47
55
|
</div>
|
|
48
56
|
<div className="flex justify-center gap-5 sm:gap-8 xl:gap-13">
|
|
49
57
|
{links.map((item) => {
|
|
50
58
|
return (
|
|
51
|
-
<
|
|
59
|
+
<LinkComponent
|
|
52
60
|
key={item.title}
|
|
53
61
|
to={item.url}
|
|
54
62
|
className="no-underline! text-foreground!"
|
|
55
63
|
>
|
|
56
64
|
{item.title}
|
|
57
|
-
</
|
|
65
|
+
</LinkComponent>
|
|
58
66
|
)
|
|
59
67
|
})}
|
|
60
68
|
</div>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/tanstack-react"
|
|
2
|
+
import { useState } from "react"
|
|
3
|
+
import { LanguageSwitcher } from "./LanguageSwitcher"
|
|
4
|
+
|
|
5
|
+
const LANGUAGES = [
|
|
6
|
+
{ code: "en", label: "English", flag: "🇺🇸" },
|
|
7
|
+
{ code: "fr", label: "Français", flag: "🇫🇷" },
|
|
8
|
+
{ code: "es", label: "Español", flag: "🇪🇸" },
|
|
9
|
+
{ code: "cs", label: "Čeština", flag: "🇨🇿" },
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
const meta: Meta<typeof LanguageSwitcher> = {
|
|
13
|
+
title: "Components/LanguageSwitcher",
|
|
14
|
+
component: LanguageSwitcher,
|
|
15
|
+
tags: ["autodocs"],
|
|
16
|
+
parameters: {
|
|
17
|
+
layout: "centered",
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default meta
|
|
22
|
+
type Story = StoryObj<typeof LanguageSwitcher>
|
|
23
|
+
|
|
24
|
+
export const Default: Story = {
|
|
25
|
+
render: () => {
|
|
26
|
+
const [lang, setLang] = useState("en")
|
|
27
|
+
return <LanguageSwitcher languages={LANGUAGES} value={lang} onChange={setLang} />
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const Bubble: Story = {
|
|
32
|
+
render: () => {
|
|
33
|
+
const [lang, setLang] = useState("en")
|
|
34
|
+
return <LanguageSwitcher languages={LANGUAGES} value={lang} onChange={setLang} variant="bubble" />
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const TwoLanguages: Story = {
|
|
39
|
+
render: () => {
|
|
40
|
+
const [lang, setLang] = useState("en")
|
|
41
|
+
return (
|
|
42
|
+
<LanguageSwitcher
|
|
43
|
+
languages={[
|
|
44
|
+
{ code: "en", label: "English", flag: "🇺🇸" },
|
|
45
|
+
{ code: "cs", label: "Čeština", flag: "🇨🇿" },
|
|
46
|
+
]}
|
|
47
|
+
value={lang}
|
|
48
|
+
onChange={setLang}
|
|
49
|
+
/>
|
|
50
|
+
)
|
|
51
|
+
},
|
|
52
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { ChevronDown } from "lucide-react"
|
|
2
|
+
import { cn } from "@/lib/utils"
|
|
3
|
+
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
|
|
4
|
+
|
|
5
|
+
export interface Language {
|
|
6
|
+
code: string
|
|
7
|
+
label: string
|
|
8
|
+
flag: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface LanguageSwitcherProps {
|
|
12
|
+
languages: Language[]
|
|
13
|
+
value: string
|
|
14
|
+
onChange: (code: string) => void
|
|
15
|
+
variant?: "default" | "bubble"
|
|
16
|
+
className?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function LanguageSwitcher({
|
|
20
|
+
languages,
|
|
21
|
+
value,
|
|
22
|
+
onChange,
|
|
23
|
+
variant = "default",
|
|
24
|
+
className,
|
|
25
|
+
}: LanguageSwitcherProps) {
|
|
26
|
+
const current = languages.find((l) => l.code === value) ?? languages[0]
|
|
27
|
+
const isBubble = variant === "bubble"
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<DropdownMenuPrimitive.Root>
|
|
31
|
+
<DropdownMenuPrimitive.Trigger
|
|
32
|
+
className={cn(
|
|
33
|
+
"flex items-center transition-colors focus:outline-none",
|
|
34
|
+
isBubble
|
|
35
|
+
? "gap-1 rounded-lg border border-border bg-background px-2 py-0.5 text-sm font-medium shadow-sm hover:bg-accent"
|
|
36
|
+
: "gap-1.5 rounded-md px-2.5 py-1.5 text-sm font-medium hover:bg-accent",
|
|
37
|
+
className
|
|
38
|
+
)}
|
|
39
|
+
>
|
|
40
|
+
<span className={cn("leading-none", isBubble ? "text-lg" : "text-base")}>{current.flag}</span>
|
|
41
|
+
{!isBubble && <span>{current.code.toUpperCase()}</span>}
|
|
42
|
+
<ChevronDown className={cn("opacity-60", isBubble ? "h-3 w-3 opacity-50" : "h-3.5 w-3.5")} />
|
|
43
|
+
</DropdownMenuPrimitive.Trigger>
|
|
44
|
+
|
|
45
|
+
<DropdownMenuPrimitive.Portal>
|
|
46
|
+
<DropdownMenuPrimitive.Content
|
|
47
|
+
align="end"
|
|
48
|
+
sideOffset={isBubble ? 10 : 4}
|
|
49
|
+
className={cn(
|
|
50
|
+
"relative z-50 overflow-visible p-1.5",
|
|
51
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out",
|
|
52
|
+
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
53
|
+
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
54
|
+
"data-[side=bottom]:slide-in-from-top-2",
|
|
55
|
+
isBubble
|
|
56
|
+
? "group min-w-37.5 rounded-2xl border border-border bg-white shadow-xl"
|
|
57
|
+
: "min-w-35 rounded-md border bg-popover text-popover-foreground shadow-md"
|
|
58
|
+
)}
|
|
59
|
+
>
|
|
60
|
+
{isBubble && (
|
|
61
|
+
<span className="absolute right-4 h-3.5 w-3.5 rotate-45 border-border bg-white group-data-[side=bottom]:-top-1.75 group-data-[side=bottom]:rounded-tl-sm group-data-[side=bottom]:border-l group-data-[side=bottom]:border-t group-data-[side=top]:-bottom-1.75 group-data-[side=top]:rounded-br-sm group-data-[side=top]:border-r group-data-[side=top]:border-b" />
|
|
62
|
+
)}
|
|
63
|
+
|
|
64
|
+
{languages.map((lang) => (
|
|
65
|
+
<DropdownMenuPrimitive.Item
|
|
66
|
+
key={lang.code}
|
|
67
|
+
onSelect={() => onChange(lang.code)}
|
|
68
|
+
className={cn(
|
|
69
|
+
"flex cursor-pointer select-none items-center outline-none transition-colors focus:bg-accent",
|
|
70
|
+
isBubble
|
|
71
|
+
? "gap-3 rounded-xl px-3 py-2 text-sm"
|
|
72
|
+
: "gap-2.5 rounded-sm px-2 py-1.5 text-sm",
|
|
73
|
+
lang.code === value && (isBubble ? "bg-accent font-medium" : "bg-accent")
|
|
74
|
+
)}
|
|
75
|
+
>
|
|
76
|
+
<span className={cn("leading-none", isBubble ? "text-xl" : "text-base")}>{lang.flag}</span>
|
|
77
|
+
<span>{lang.label}</span>
|
|
78
|
+
</DropdownMenuPrimitive.Item>
|
|
79
|
+
))}
|
|
80
|
+
</DropdownMenuPrimitive.Content>
|
|
81
|
+
</DropdownMenuPrimitive.Portal>
|
|
82
|
+
</DropdownMenuPrimitive.Root>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/tanstack-react"
|
|
2
|
+
import { Link } from "@tanstack/react-router"
|
|
3
|
+
import { Home, Info, MessageSquareMore } from "lucide-react"
|
|
4
|
+
import { MainNav } from "./MainNav"
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof MainNav> = {
|
|
7
|
+
title: "Components/MainNav",
|
|
8
|
+
component: MainNav,
|
|
9
|
+
tags: ["autodocs"],
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default meta
|
|
13
|
+
type Story = StoryObj<typeof MainNav>
|
|
14
|
+
|
|
15
|
+
const navItems = [
|
|
16
|
+
{ title: "Home", url: "/", icon: Home },
|
|
17
|
+
{ title: "About", url: "/about", icon: Info },
|
|
18
|
+
{ title: "Contact", url: "/contact", icon: MessageSquareMore },
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
export const Default: Story = {
|
|
22
|
+
args: { navItems, LinkComponent: Link },
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const NoIcons: Story = {
|
|
26
|
+
args: {
|
|
27
|
+
navItems: navItems.map(({ title, url }) => ({ title, url })),
|
|
28
|
+
LinkComponent: Link,
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const Empty: Story = {
|
|
33
|
+
args: { navItems: [], LinkComponent: Link },
|
|
34
|
+
}
|
|
@@ -9,7 +9,6 @@ import { Sheet, SheetTrigger, SheetContent } from "@/components/ui/sheet"
|
|
|
9
9
|
import { Button } from "@/components/ui/button"
|
|
10
10
|
import { Menu, type LucideIcon } from "lucide-react"
|
|
11
11
|
import { cn } from "@/lib/utils"
|
|
12
|
-
import { Link } from "@tanstack/react-router"
|
|
13
12
|
|
|
14
13
|
export interface NavItem {
|
|
15
14
|
title: string
|
|
@@ -17,11 +16,30 @@ export interface NavItem {
|
|
|
17
16
|
icon?: LucideIcon | React.ComponentType
|
|
18
17
|
}
|
|
19
18
|
|
|
19
|
+
export interface LinkComponentProps {
|
|
20
|
+
to: string
|
|
21
|
+
className?: string
|
|
22
|
+
children?: React.ReactNode
|
|
23
|
+
target?: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const DefaultLink = ({ to, className, children, target }: LinkComponentProps) => (
|
|
27
|
+
<a href={to} className={className} target={target}>
|
|
28
|
+
{children}
|
|
29
|
+
</a>
|
|
30
|
+
)
|
|
31
|
+
|
|
20
32
|
export interface MainNavProps extends React.ComponentPropsWithoutRef<"header"> {
|
|
21
33
|
navItems: NavItem[]
|
|
34
|
+
LinkComponent?: React.ComponentType<LinkComponentProps>
|
|
22
35
|
}
|
|
23
36
|
|
|
24
|
-
export const MainNav = ({
|
|
37
|
+
export const MainNav = ({
|
|
38
|
+
navItems,
|
|
39
|
+
className,
|
|
40
|
+
LinkComponent = DefaultLink,
|
|
41
|
+
...props
|
|
42
|
+
}: MainNavProps) => {
|
|
25
43
|
return (
|
|
26
44
|
<header className={cn(className)} {...props}>
|
|
27
45
|
<nav className="hidden sm:flex sticky top-0 z-10 items-center px-4 h-14 bg-background">
|
|
@@ -33,13 +51,13 @@ export const MainNav = ({ navItems, className, ...props }: MainNavProps) => {
|
|
|
33
51
|
asChild
|
|
34
52
|
className={navigationMenuTriggerStyle()}
|
|
35
53
|
>
|
|
36
|
-
<
|
|
54
|
+
<LinkComponent
|
|
37
55
|
to={item.url}
|
|
38
56
|
className="flex items-center no-underline! text-foreground!"
|
|
39
57
|
>
|
|
40
58
|
{item.icon && <item.icon />}
|
|
41
59
|
<span>{item.title}</span>
|
|
42
|
-
</
|
|
60
|
+
</LinkComponent>
|
|
43
61
|
</NavigationMenuLink>
|
|
44
62
|
</NavigationMenuItem>
|
|
45
63
|
))}
|
|
@@ -56,14 +74,14 @@ export const MainNav = ({ navItems, className, ...props }: MainNavProps) => {
|
|
|
56
74
|
<SheetContent side="top" className="p-8">
|
|
57
75
|
<div className="flex flex-col gap-4">
|
|
58
76
|
{navItems.map((item) => (
|
|
59
|
-
<
|
|
77
|
+
<LinkComponent
|
|
60
78
|
key={item.title}
|
|
61
79
|
to={item.url}
|
|
62
80
|
className="flex items-center gap-2 no-underline! text-foreground!"
|
|
63
81
|
>
|
|
64
82
|
{item.icon && <item.icon />}
|
|
65
83
|
<span className="text-base font-medium">{item.title}</span>
|
|
66
|
-
</
|
|
84
|
+
</LinkComponent>
|
|
67
85
|
))}
|
|
68
86
|
</div>
|
|
69
87
|
</SheetContent>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/tanstack-react"
|
|
2
|
+
import { Link } from "@tanstack/react-router"
|
|
3
|
+
import { Home, Info, MessageSquareMore, Facebook, Gitlab, Linkedin } from "lucide-react"
|
|
4
|
+
import { MainSidebarLayout } from "./MainSidebarLayout"
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof MainSidebarLayout> = {
|
|
7
|
+
title: "Components/MainSidebarLayout",
|
|
8
|
+
component: MainSidebarLayout,
|
|
9
|
+
tags: ["autodocs"],
|
|
10
|
+
parameters: {
|
|
11
|
+
layout: "fullscreen",
|
|
12
|
+
},
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default meta
|
|
16
|
+
type Story = StoryObj<typeof MainSidebarLayout>
|
|
17
|
+
|
|
18
|
+
const navItems = [
|
|
19
|
+
{ title: "Home", url: "/", icon: Home },
|
|
20
|
+
{ title: "About", url: "/about", icon: Info },
|
|
21
|
+
{ title: "Contact", url: "/contact", icon: MessageSquareMore },
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
const footerItems = {
|
|
25
|
+
links: [
|
|
26
|
+
{ title: "Home", url: "/" },
|
|
27
|
+
{ title: "About", url: "/about" },
|
|
28
|
+
],
|
|
29
|
+
socialProfiles: [
|
|
30
|
+
{ title: "Facebook", url: "https://facebook.com", icon: Facebook },
|
|
31
|
+
{ title: "GitLab", url: "https://gitlab.com", icon: Gitlab },
|
|
32
|
+
{ title: "LinkedIn", url: "https://linkedin.com", icon: Linkedin },
|
|
33
|
+
],
|
|
34
|
+
copyright: `Copyright © ${new Date().getFullYear()}`,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const WithNav: Story = {
|
|
38
|
+
args: {
|
|
39
|
+
useMainNav: true,
|
|
40
|
+
navItems,
|
|
41
|
+
LinkComponent: Link,
|
|
42
|
+
children: <div className="p-4">Page content</div>,
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const WithSidebar: Story = {
|
|
47
|
+
args: {
|
|
48
|
+
useSidebar: true,
|
|
49
|
+
appName: "My App",
|
|
50
|
+
navItems,
|
|
51
|
+
LinkComponent: Link,
|
|
52
|
+
children: <div className="p-4">Page content</div>,
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const WithSidebarAndFooter: Story = {
|
|
57
|
+
args: {
|
|
58
|
+
useSidebar: true,
|
|
59
|
+
useFooter: true,
|
|
60
|
+
appName: "My App",
|
|
61
|
+
navItems,
|
|
62
|
+
footerItems,
|
|
63
|
+
LinkComponent: Link,
|
|
64
|
+
children: <div className="p-4">Page content</div>,
|
|
65
|
+
},
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const NavAndFooter: Story = {
|
|
69
|
+
args: {
|
|
70
|
+
useMainNav: true,
|
|
71
|
+
useFooter: true,
|
|
72
|
+
navItems,
|
|
73
|
+
footerItems,
|
|
74
|
+
LinkComponent: Link,
|
|
75
|
+
children: <div className="p-4">Page content</div>,
|
|
76
|
+
},
|
|
77
|
+
}
|