thikachi-ui 0.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/.editorconfig ADDED
@@ -0,0 +1,12 @@
1
+ # EditorConfig is awesome: https://EditorConfig.org
2
+
3
+ # top-most EditorConfig file
4
+ root = true
5
+
6
+ [*]
7
+ indent_style = space
8
+ indent_size = 2
9
+ end_of_line = lf
10
+ charset = utf-8
11
+ trim_trailing_whitespace = true
12
+ insert_final_newline = true
package/.eslintrc.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "env": {
3
+ "browser": true,
4
+ "es2021": true
5
+ },
6
+ "extends": [
7
+ "eslint:recommended",
8
+ "plugin:@typescript-eslint/recommended",
9
+ "plugin:react/recommended",
10
+ "plugin:storybook/recommended"
11
+ ],
12
+ "parser": "@typescript-eslint/parser",
13
+ "parserOptions": {
14
+ "ecmaVersion": "latest",
15
+ "sourceType": "module"
16
+ },
17
+ "plugins": [
18
+ "@typescript-eslint",
19
+ "react"
20
+ ],
21
+ "rules": {
22
+ "indent": [
23
+ "error",
24
+ 2
25
+ ],
26
+ "linebreak-style": [
27
+ "error",
28
+ "unix"
29
+ ],
30
+ "quotes": [
31
+ "error",
32
+ "single"
33
+ ],
34
+ "semi": [
35
+ "error",
36
+ "always"
37
+ ]
38
+ }
39
+ }
@@ -0,0 +1,11 @@
1
+ import type { StorybookConfig } from "@storybook/react-vite";
2
+
3
+ const config: StorybookConfig = {
4
+ stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
5
+ addons: ["@storybook/addon-essentials"],
6
+ framework: {
7
+ name: "@storybook/react-vite",
8
+ options: {},
9
+ },
10
+ };
11
+ export default config;
@@ -0,0 +1,20 @@
1
+ import { withThemeByClassName } from "@storybook/addon-themes";
2
+ import type { Preview, ReactRenderer } from "@storybook/react";
3
+ import "../src/index.css";
4
+
5
+ const preview: Preview = {
6
+ parameters: {
7
+ layout: "centered",
8
+ },
9
+ decorators: [
10
+ withThemeByClassName<ReactRenderer>({
11
+ themes: {
12
+ light: "",
13
+ dark: "dark",
14
+ },
15
+ defaultTheme: "dark",
16
+ }),
17
+ ],
18
+ };
19
+
20
+ export default preview;
package/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # React + TypeScript + Vite
2
+
3
+ This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4
+
5
+ Currently, two official plugins are available:
6
+
7
+ - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
8
+ - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9
+
10
+ ## React Compiler
11
+
12
+ The React Compiler is currently not compatible with SWC. See [this issue](https://github.com/vitejs/vite-plugin-react/issues/428) for tracking the progress.
13
+
14
+ ## Expanding the ESLint configuration
15
+
16
+ If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
17
+
18
+ ```js
19
+ export default defineConfig([
20
+ globalIgnores(['dist']),
21
+ {
22
+ files: ['**/*.{ts,tsx}'],
23
+ extends: [
24
+ // Other configs...
25
+
26
+ // Remove tseslint.configs.recommended and replace with this
27
+ tseslint.configs.recommendedTypeChecked,
28
+ // Alternatively, use this for stricter rules
29
+ tseslint.configs.strictTypeChecked,
30
+ // Optionally, add this for stylistic rules
31
+ tseslint.configs.stylisticTypeChecked,
32
+
33
+ // Other configs...
34
+ ],
35
+ languageOptions: {
36
+ parserOptions: {
37
+ project: ['./tsconfig.node.json', './tsconfig.app.json'],
38
+ tsconfigRootDir: import.meta.dirname,
39
+ },
40
+ // other options...
41
+ },
42
+ },
43
+ ])
44
+ ```
45
+
46
+ You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
47
+
48
+ ```js
49
+ // eslint.config.js
50
+ import reactX from 'eslint-plugin-react-x'
51
+ import reactDom from 'eslint-plugin-react-dom'
52
+
53
+ export default defineConfig([
54
+ globalIgnores(['dist']),
55
+ {
56
+ files: ['**/*.{ts,tsx}'],
57
+ extends: [
58
+ // Other configs...
59
+ // Enable lint rules for React
60
+ reactX.configs['recommended-typescript'],
61
+ // Enable lint rules for React DOM
62
+ reactDom.configs.recommended,
63
+ ],
64
+ languageOptions: {
65
+ parserOptions: {
66
+ project: ['./tsconfig.node.json', './tsconfig.app.json'],
67
+ tsconfigRootDir: import.meta.dirname,
68
+ },
69
+ // other options...
70
+ },
71
+ },
72
+ ])
73
+ ```
@@ -0,0 +1,23 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "new-york",
4
+ "rsc": false,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "",
8
+ "css": "src/index.css",
9
+ "baseColor": "neutral",
10
+ "cssVariables": true,
11
+ "prefix": ""
12
+ },
13
+ "iconLibrary": "lucide",
14
+ "rtl": false,
15
+ "aliases": {
16
+ "components": "@/components",
17
+ "utils": "@/lib/utils",
18
+ "ui": "@/components/ui",
19
+ "lib": "@/lib",
20
+ "hooks": "@/hooks"
21
+ },
22
+ "registries": {}
23
+ }
@@ -0,0 +1,23 @@
1
+ import js from '@eslint/js'
2
+ import globals from 'globals'
3
+ import reactHooks from 'eslint-plugin-react-hooks'
4
+ import reactRefresh from 'eslint-plugin-react-refresh'
5
+ import tseslint from 'typescript-eslint'
6
+ import { defineConfig, globalIgnores } from 'eslint/config'
7
+
8
+ export default defineConfig([
9
+ globalIgnores(['dist']),
10
+ {
11
+ files: ['**/*.{ts,tsx}'],
12
+ extends: [
13
+ js.configs.recommended,
14
+ tseslint.configs.recommended,
15
+ reactHooks.configs.flat.recommended,
16
+ reactRefresh.configs.vite,
17
+ ],
18
+ languageOptions: {
19
+ ecmaVersion: 2020,
20
+ globals: globals.browser,
21
+ },
22
+ },
23
+ ])
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "thikachi-ui",
3
+ "private": false,
4
+ "version": "0.0.0",
5
+ "scripts": {
6
+ "dev": "storybook dev -p 6006",
7
+ "build": "tsup",
8
+ "build:storybook": "storybook build"
9
+ },
10
+ "dependencies": {
11
+ "@tailwindcss/vite": "^4.1.18",
12
+ "class-variance-authority": "^0.7.1",
13
+ "clsx": "^2.1.1",
14
+ "lucide-react": "^0.563.0",
15
+ "radix-ui": "^1.4.3",
16
+ "react": "^19.2.0",
17
+ "react-dom": "^19.2.0",
18
+ "tailwind-merge": "^3.4.0",
19
+ "tailwindcss": "^4.1.18"
20
+ },
21
+ "devDependencies": {
22
+ "@storybook/addon-essentials": "8.2.6",
23
+ "@storybook/addon-themes": "^8.2.6",
24
+ "@storybook/blocks": "8.2.6",
25
+ "@storybook/react": "8.2.6",
26
+ "@storybook/react-vite": "8.2.6",
27
+ "@storybook/test": "8.2.6",
28
+ "@types/node": "^24.10.1",
29
+ "@types/react": "^19.2.5",
30
+ "@types/react-dom": "^19.2.3",
31
+ "@typescript-eslint/eslint-plugin": "^8.54.0",
32
+ "@typescript-eslint/parser": "^8.54.0",
33
+ "@vitejs/plugin-react-swc": "^4.2.2",
34
+ "eslint": "^8.57.1",
35
+ "eslint-plugin-react": "^7.37.5",
36
+ "eslint-plugin-storybook": "^10.2.4",
37
+ "globals": "^16.5.0",
38
+ "storybook": "8.2.6",
39
+ "tsup": "^8.5.1",
40
+ "tw-animate-css": "^1.4.0",
41
+ "typescript": "~5.9.3",
42
+ "vite": "^7.2.4"
43
+ }
44
+ }
@@ -0,0 +1,107 @@
1
+ import * as React from "react"
2
+ import { Avatar as AvatarPrimitive } from "radix-ui"
3
+
4
+ import { cn } from "@/lib/utils"
5
+
6
+ function Avatar({
7
+ className,
8
+ size = "default",
9
+ ...props
10
+ }: React.ComponentProps<typeof AvatarPrimitive.Root> & {
11
+ size?: "default" | "sm" | "lg"
12
+ }) {
13
+ return (
14
+ <AvatarPrimitive.Root
15
+ data-slot="avatar"
16
+ data-size={size}
17
+ className={cn(
18
+ "group/avatar relative flex size-8 shrink-0 overflow-hidden rounded-full select-none data-[size=lg]:size-10 data-[size=sm]:size-6",
19
+ className
20
+ )}
21
+ {...props}
22
+ />
23
+ )
24
+ }
25
+
26
+ function AvatarImage({
27
+ className,
28
+ ...props
29
+ }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
30
+ return (
31
+ <AvatarPrimitive.Image
32
+ data-slot="avatar-image"
33
+ className={cn("aspect-square size-full", className)}
34
+ {...props}
35
+ />
36
+ )
37
+ }
38
+
39
+ function AvatarFallback({
40
+ className,
41
+ ...props
42
+ }: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
43
+ return (
44
+ <AvatarPrimitive.Fallback
45
+ data-slot="avatar-fallback"
46
+ className={cn(
47
+ "bg-muted text-muted-foreground flex size-full items-center justify-center rounded-full text-sm group-data-[size=sm]/avatar:text-xs",
48
+ className
49
+ )}
50
+ {...props}
51
+ />
52
+ )
53
+ }
54
+
55
+ function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
56
+ return (
57
+ <span
58
+ data-slot="avatar-badge"
59
+ className={cn(
60
+ "bg-primary text-primary-foreground ring-background absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full ring-2 select-none",
61
+ "group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
62
+ "group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
63
+ "group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
64
+ className
65
+ )}
66
+ {...props}
67
+ />
68
+ )
69
+ }
70
+
71
+ function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
72
+ return (
73
+ <div
74
+ data-slot="avatar-group"
75
+ className={cn(
76
+ "*:data-[slot=avatar]:ring-background group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2",
77
+ className
78
+ )}
79
+ {...props}
80
+ />
81
+ )
82
+ }
83
+
84
+ function AvatarGroupCount({
85
+ className,
86
+ ...props
87
+ }: React.ComponentProps<"div">) {
88
+ return (
89
+ <div
90
+ data-slot="avatar-group-count"
91
+ className={cn(
92
+ "bg-muted text-muted-foreground ring-background relative flex size-8 shrink-0 items-center justify-center rounded-full text-sm ring-2 group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
93
+ className
94
+ )}
95
+ {...props}
96
+ />
97
+ )
98
+ }
99
+
100
+ export {
101
+ Avatar,
102
+ AvatarImage,
103
+ AvatarFallback,
104
+ AvatarBadge,
105
+ AvatarGroup,
106
+ AvatarGroupCount,
107
+ }
@@ -0,0 +1,64 @@
1
+ import * as React from "react"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+ import { Slot } from "radix-ui"
4
+
5
+ import { cn } from "@/lib/utils"
6
+
7
+ const buttonVariants = cva(
8
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
13
+ destructive:
14
+ "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
15
+ outline:
16
+ "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
17
+ secondary:
18
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80",
19
+ ghost:
20
+ "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
21
+ link: "text-primary underline-offset-4 hover:underline",
22
+ },
23
+ size: {
24
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
25
+ xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
26
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
27
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
28
+ icon: "size-9",
29
+ "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
30
+ "icon-sm": "size-8",
31
+ "icon-lg": "size-10",
32
+ },
33
+ },
34
+ defaultVariants: {
35
+ variant: "default",
36
+ size: "default",
37
+ },
38
+ }
39
+ )
40
+
41
+ function Button({
42
+ className,
43
+ variant = "default",
44
+ size = "default",
45
+ asChild = false,
46
+ ...props
47
+ }: React.ComponentProps<"button"> &
48
+ VariantProps<typeof buttonVariants> & {
49
+ asChild?: boolean
50
+ }) {
51
+ const Comp = asChild ? Slot.Root : "button"
52
+
53
+ return (
54
+ <Comp
55
+ data-slot="button"
56
+ data-variant={variant}
57
+ data-size={size}
58
+ className={cn(buttonVariants({ variant, size, className }))}
59
+ {...props}
60
+ />
61
+ )
62
+ }
63
+
64
+ export { Button, buttonVariants }
@@ -0,0 +1,257 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
5
+ import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui"
6
+
7
+ import { cn } from "@/lib/utils"
8
+
9
+ function DropdownMenu({
10
+ ...props
11
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
12
+ return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />
13
+ }
14
+
15
+ function DropdownMenuPortal({
16
+ ...props
17
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
18
+ return (
19
+ <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
20
+ )
21
+ }
22
+
23
+ function DropdownMenuTrigger({
24
+ ...props
25
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
26
+ return (
27
+ <DropdownMenuPrimitive.Trigger
28
+ data-slot="dropdown-menu-trigger"
29
+ {...props}
30
+ />
31
+ )
32
+ }
33
+
34
+ function DropdownMenuContent({
35
+ className,
36
+ sideOffset = 4,
37
+ ...props
38
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
39
+ return (
40
+ <DropdownMenuPrimitive.Portal>
41
+ <DropdownMenuPrimitive.Content
42
+ data-slot="dropdown-menu-content"
43
+ sideOffset={sideOffset}
44
+ className={cn(
45
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 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 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
46
+ className
47
+ )}
48
+ {...props}
49
+ />
50
+ </DropdownMenuPrimitive.Portal>
51
+ )
52
+ }
53
+
54
+ function DropdownMenuGroup({
55
+ ...props
56
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
57
+ return (
58
+ <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
59
+ )
60
+ }
61
+
62
+ function DropdownMenuItem({
63
+ className,
64
+ inset,
65
+ variant = "default",
66
+ ...props
67
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
68
+ inset?: boolean
69
+ variant?: "default" | "destructive"
70
+ }) {
71
+ return (
72
+ <DropdownMenuPrimitive.Item
73
+ data-slot="dropdown-menu-item"
74
+ data-inset={inset}
75
+ data-variant={variant}
76
+ className={cn(
77
+ "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
78
+ className
79
+ )}
80
+ {...props}
81
+ />
82
+ )
83
+ }
84
+
85
+ function DropdownMenuCheckboxItem({
86
+ className,
87
+ children,
88
+ checked,
89
+ ...props
90
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
91
+ return (
92
+ <DropdownMenuPrimitive.CheckboxItem
93
+ data-slot="dropdown-menu-checkbox-item"
94
+ className={cn(
95
+ "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
96
+ className
97
+ )}
98
+ checked={checked}
99
+ {...props}
100
+ >
101
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
102
+ <DropdownMenuPrimitive.ItemIndicator>
103
+ <CheckIcon className="size-4" />
104
+ </DropdownMenuPrimitive.ItemIndicator>
105
+ </span>
106
+ {children}
107
+ </DropdownMenuPrimitive.CheckboxItem>
108
+ )
109
+ }
110
+
111
+ function DropdownMenuRadioGroup({
112
+ ...props
113
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
114
+ return (
115
+ <DropdownMenuPrimitive.RadioGroup
116
+ data-slot="dropdown-menu-radio-group"
117
+ {...props}
118
+ />
119
+ )
120
+ }
121
+
122
+ function DropdownMenuRadioItem({
123
+ className,
124
+ children,
125
+ ...props
126
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
127
+ return (
128
+ <DropdownMenuPrimitive.RadioItem
129
+ data-slot="dropdown-menu-radio-item"
130
+ className={cn(
131
+ "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
132
+ className
133
+ )}
134
+ {...props}
135
+ >
136
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
137
+ <DropdownMenuPrimitive.ItemIndicator>
138
+ <CircleIcon className="size-2 fill-current" />
139
+ </DropdownMenuPrimitive.ItemIndicator>
140
+ </span>
141
+ {children}
142
+ </DropdownMenuPrimitive.RadioItem>
143
+ )
144
+ }
145
+
146
+ function DropdownMenuLabel({
147
+ className,
148
+ inset,
149
+ ...props
150
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
151
+ inset?: boolean
152
+ }) {
153
+ return (
154
+ <DropdownMenuPrimitive.Label
155
+ data-slot="dropdown-menu-label"
156
+ data-inset={inset}
157
+ className={cn(
158
+ "px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
159
+ className
160
+ )}
161
+ {...props}
162
+ />
163
+ )
164
+ }
165
+
166
+ function DropdownMenuSeparator({
167
+ className,
168
+ ...props
169
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
170
+ return (
171
+ <DropdownMenuPrimitive.Separator
172
+ data-slot="dropdown-menu-separator"
173
+ className={cn("bg-border -mx-1 my-1 h-px", className)}
174
+ {...props}
175
+ />
176
+ )
177
+ }
178
+
179
+ function DropdownMenuShortcut({
180
+ className,
181
+ ...props
182
+ }: React.ComponentProps<"span">) {
183
+ return (
184
+ <span
185
+ data-slot="dropdown-menu-shortcut"
186
+ className={cn(
187
+ "text-muted-foreground ml-auto text-xs tracking-widest",
188
+ className
189
+ )}
190
+ {...props}
191
+ />
192
+ )
193
+ }
194
+
195
+ function DropdownMenuSub({
196
+ ...props
197
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
198
+ return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />
199
+ }
200
+
201
+ function DropdownMenuSubTrigger({
202
+ className,
203
+ inset,
204
+ children,
205
+ ...props
206
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
207
+ inset?: boolean
208
+ }) {
209
+ return (
210
+ <DropdownMenuPrimitive.SubTrigger
211
+ data-slot="dropdown-menu-sub-trigger"
212
+ data-inset={inset}
213
+ className={cn(
214
+ "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
215
+ className
216
+ )}
217
+ {...props}
218
+ >
219
+ {children}
220
+ <ChevronRightIcon className="ml-auto size-4" />
221
+ </DropdownMenuPrimitive.SubTrigger>
222
+ )
223
+ }
224
+
225
+ function DropdownMenuSubContent({
226
+ className,
227
+ ...props
228
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
229
+ return (
230
+ <DropdownMenuPrimitive.SubContent
231
+ data-slot="dropdown-menu-sub-content"
232
+ className={cn(
233
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 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 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
234
+ className
235
+ )}
236
+ {...props}
237
+ />
238
+ )
239
+ }
240
+
241
+ export {
242
+ DropdownMenu,
243
+ DropdownMenuPortal,
244
+ DropdownMenuTrigger,
245
+ DropdownMenuContent,
246
+ DropdownMenuGroup,
247
+ DropdownMenuLabel,
248
+ DropdownMenuItem,
249
+ DropdownMenuCheckboxItem,
250
+ DropdownMenuRadioGroup,
251
+ DropdownMenuRadioItem,
252
+ DropdownMenuSeparator,
253
+ DropdownMenuShortcut,
254
+ DropdownMenuSub,
255
+ DropdownMenuSubTrigger,
256
+ DropdownMenuSubContent,
257
+ }
@@ -0,0 +1,21 @@
1
+ import * as React from "react"
2
+
3
+ import { cn } from "@/lib/utils"
4
+
5
+ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
6
+ return (
7
+ <input
8
+ type={type}
9
+ data-slot="input"
10
+ className={cn(
11
+ "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
12
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
13
+ "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
14
+ className
15
+ )}
16
+ {...props}
17
+ />
18
+ )
19
+ }
20
+
21
+ export { Input }
package/src/index.css ADDED
@@ -0,0 +1,124 @@
1
+ @import "tailwindcss";
2
+ @import "tw-animate-css";
3
+ @config "../tailwind.config.js";
4
+
5
+ @custom-variant dark (&:is(.dark *));
6
+
7
+ @theme inline {
8
+ --radius-sm: calc(var(--radius) - 4px);
9
+ --radius-md: calc(var(--radius) - 2px);
10
+ --radius-lg: var(--radius);
11
+ --radius-xl: calc(var(--radius) + 4px);
12
+ --radius-2xl: calc(var(--radius) + 8px);
13
+ --radius-3xl: calc(var(--radius) + 12px);
14
+ --radius-4xl: calc(var(--radius) + 16px);
15
+ --color-background: var(--background);
16
+ --color-foreground: var(--foreground);
17
+ --color-card: var(--card);
18
+ --color-card-foreground: var(--card-foreground);
19
+ --color-popover: var(--popover);
20
+ --color-popover-foreground: var(--popover-foreground);
21
+ --color-primary: var(--primary);
22
+ --color-primary-foreground: var(--primary-foreground);
23
+ --color-secondary: var(--secondary);
24
+ --color-secondary-foreground: var(--secondary-foreground);
25
+ --color-muted: var(--muted);
26
+ --color-muted-foreground: var(--muted-foreground);
27
+ --color-accent: var(--accent);
28
+ --color-accent-foreground: var(--accent-foreground);
29
+ --color-destructive: var(--destructive);
30
+ --color-border: var(--border);
31
+ --color-input: var(--input);
32
+ --color-ring: var(--ring);
33
+ --color-chart-1: var(--chart-1);
34
+ --color-chart-2: var(--chart-2);
35
+ --color-chart-3: var(--chart-3);
36
+ --color-chart-4: var(--chart-4);
37
+ --color-chart-5: var(--chart-5);
38
+ --color-sidebar: var(--sidebar);
39
+ --color-sidebar-foreground: var(--sidebar-foreground);
40
+ --color-sidebar-primary: var(--sidebar-primary);
41
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
42
+ --color-sidebar-accent: var(--sidebar-accent);
43
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
44
+ --color-sidebar-border: var(--sidebar-border);
45
+ --color-sidebar-ring: var(--sidebar-ring);
46
+ }
47
+
48
+ :root {
49
+ --radius: 0.625rem;
50
+ --background: oklch(1 0 0);
51
+ --foreground: oklch(0.145 0 0);
52
+ --card: oklch(1 0 0);
53
+ --card-foreground: oklch(0.145 0 0);
54
+ --popover: oklch(1 0 0);
55
+ --popover-foreground: oklch(0.145 0 0);
56
+ --primary: oklch(0.205 0 0);
57
+ --primary-foreground: oklch(0.985 0 0);
58
+ --secondary: oklch(0.97 0 0);
59
+ --secondary-foreground: oklch(0.205 0 0);
60
+ --muted: oklch(0.97 0 0);
61
+ --muted-foreground: oklch(0.556 0 0);
62
+ --accent: oklch(0.97 0 0);
63
+ --accent-foreground: oklch(0.205 0 0);
64
+ --destructive: oklch(0.577 0.245 27.325);
65
+ --border: oklch(0.922 0 0);
66
+ --input: oklch(0.922 0 0);
67
+ --ring: oklch(0.708 0 0);
68
+ --chart-1: oklch(0.646 0.222 41.116);
69
+ --chart-2: oklch(0.6 0.118 184.704);
70
+ --chart-3: oklch(0.398 0.07 227.392);
71
+ --chart-4: oklch(0.828 0.189 84.429);
72
+ --chart-5: oklch(0.769 0.188 70.08);
73
+ --sidebar: oklch(0.985 0 0);
74
+ --sidebar-foreground: oklch(0.145 0 0);
75
+ --sidebar-primary: oklch(0.205 0 0);
76
+ --sidebar-primary-foreground: oklch(0.985 0 0);
77
+ --sidebar-accent: oklch(0.97 0 0);
78
+ --sidebar-accent-foreground: oklch(0.205 0 0);
79
+ --sidebar-border: oklch(0.922 0 0);
80
+ --sidebar-ring: oklch(0.708 0 0);
81
+ }
82
+
83
+ .dark {
84
+ --background: oklch(0.145 0 0);
85
+ --foreground: oklch(0.985 0 0);
86
+ --card: oklch(0.205 0 0);
87
+ --card-foreground: oklch(0.985 0 0);
88
+ --popover: oklch(0.205 0 0);
89
+ --popover-foreground: oklch(0.985 0 0);
90
+ --primary: oklch(0.922 0 0);
91
+ --primary-foreground: oklch(0.205 0 0);
92
+ --secondary: oklch(0.269 0 0);
93
+ --secondary-foreground: oklch(0.985 0 0);
94
+ --muted: oklch(0.269 0 0);
95
+ --muted-foreground: oklch(0.708 0 0);
96
+ --accent: oklch(0.269 0 0);
97
+ --accent-foreground: oklch(0.985 0 0);
98
+ --destructive: oklch(0.704 0.191 22.216);
99
+ --border: oklch(1 0 0 / 10%);
100
+ --input: oklch(1 0 0 / 15%);
101
+ --ring: oklch(0.556 0 0);
102
+ --chart-1: oklch(0.488 0.243 264.376);
103
+ --chart-2: oklch(0.696 0.17 162.48);
104
+ --chart-3: oklch(0.769 0.188 70.08);
105
+ --chart-4: oklch(0.627 0.265 303.9);
106
+ --chart-5: oklch(0.645 0.246 16.439);
107
+ --sidebar: oklch(0.205 0 0);
108
+ --sidebar-foreground: oklch(0.985 0 0);
109
+ --sidebar-primary: oklch(0.488 0.243 264.376);
110
+ --sidebar-primary-foreground: oklch(0.985 0 0);
111
+ --sidebar-accent: oklch(0.269 0 0);
112
+ --sidebar-accent-foreground: oklch(0.985 0 0);
113
+ --sidebar-border: oklch(1 0 0 / 10%);
114
+ --sidebar-ring: oklch(0.556 0 0);
115
+ }
116
+
117
+ @layer base {
118
+ * {
119
+ @apply border-border outline-ring/50;
120
+ }
121
+ body {
122
+ @apply bg-background text-foreground;
123
+ }
124
+ }
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ import "./index.css";
2
+
3
+ export * from "./components/Avatar";
4
+ export * from "./components/Button";
5
+ export * from "./components/DropdownMenu";
6
+ export * from "./components/Input";
7
+
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx"
2
+ import { twMerge } from "tailwind-merge"
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }
@@ -0,0 +1,24 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+
3
+ import { Avatar, AvatarFallback, AvatarImage } from "@/components/Avatar";
4
+
5
+ const meta = {
6
+ title: "Components/Avatar",
7
+ component: Avatar,
8
+ tags: ["autodocs"],
9
+ } satisfies Meta<typeof Avatar>;
10
+
11
+ export default meta;
12
+ type Story = StoryObj<typeof meta>;
13
+
14
+ export const Default: Story = {
15
+ render: () => (
16
+ <Avatar>
17
+ <AvatarImage
18
+ src="https://github.com/thiagokachi.png"
19
+ alt="@thiagokachi"
20
+ />
21
+ <AvatarFallback>TK</AvatarFallback>
22
+ </Avatar>
23
+ ),
24
+ };
@@ -0,0 +1,48 @@
1
+ import { Button } from "@/components/Button";
2
+ import type { Meta, StoryObj } from "@storybook/react";
3
+ import { fn } from "@storybook/test";
4
+
5
+ const meta = {
6
+ title: "Components/Button",
7
+ component: Button,
8
+ tags: ["autodocs"],
9
+ args: { onClick: fn() },
10
+ } satisfies Meta<typeof Button>;
11
+
12
+ export default meta;
13
+ type Story = StoryObj<typeof meta>;
14
+
15
+ export const Default: Story = {
16
+ args: {
17
+ children: "Default Button",
18
+ variant: "default",
19
+ },
20
+ };
21
+
22
+ export const Destructive: Story = {
23
+ args: {
24
+ children: "Button",
25
+ variant: "destructive",
26
+ },
27
+ };
28
+
29
+ export const Outline: Story = {
30
+ args: {
31
+ children: "Outline Button",
32
+ variant: "outline",
33
+ },
34
+ };
35
+
36
+ export const Ghost: Story = {
37
+ args: {
38
+ children: "Ghost Button",
39
+ variant: "ghost",
40
+ },
41
+ };
42
+
43
+ export const Link: Story = {
44
+ args: {
45
+ children: "Link Button",
46
+ variant: "link",
47
+ },
48
+ };
@@ -0,0 +1,137 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import {
3
+ Cloud,
4
+ CreditCard,
5
+ Github,
6
+ Keyboard,
7
+ LifeBuoy,
8
+ LogOut,
9
+ Mail,
10
+ MessageSquare,
11
+ Plus,
12
+ PlusCircle,
13
+ Settings,
14
+ User,
15
+ UserPlus,
16
+ Users,
17
+ } from "lucide-react";
18
+
19
+ import { Button } from "@/components/Button";
20
+ import {
21
+ DropdownMenu,
22
+ DropdownMenuContent,
23
+ DropdownMenuGroup,
24
+ DropdownMenuItem,
25
+ DropdownMenuLabel,
26
+ DropdownMenuPortal,
27
+ DropdownMenuSeparator,
28
+ DropdownMenuShortcut,
29
+ DropdownMenuSub,
30
+ DropdownMenuSubContent,
31
+ DropdownMenuSubTrigger,
32
+ DropdownMenuTrigger,
33
+ } from "@/components/DropdownMenu";
34
+
35
+ const meta = {
36
+ title: "Components/DropdownMenu",
37
+ component: DropdownMenu,
38
+ tags: ["autodocs"],
39
+ parameters: {
40
+ layout: "fullscreen",
41
+ },
42
+ } satisfies Meta<typeof DropdownMenu>;
43
+
44
+ export default meta;
45
+ type Story = StoryObj<typeof meta>;
46
+
47
+ export const Default: Story = {
48
+ render: () => (
49
+ <div className="w-full flex justify-center mt-10">
50
+ <DropdownMenu>
51
+ <DropdownMenuTrigger asChild>
52
+ <Button variant="outline">Open</Button>
53
+ </DropdownMenuTrigger>
54
+ <DropdownMenuContent className="w-56">
55
+ <DropdownMenuLabel>My Account</DropdownMenuLabel>
56
+ <DropdownMenuSeparator />
57
+ <DropdownMenuGroup>
58
+ <DropdownMenuItem>
59
+ <User className="mr-2 h-4 w-4" />
60
+ <span>Profile</span>
61
+ <DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
62
+ </DropdownMenuItem>
63
+ <DropdownMenuItem>
64
+ <CreditCard className="mr-2 h-4 w-4" />
65
+ <span>Billing</span>
66
+ <DropdownMenuShortcut>⌘B</DropdownMenuShortcut>
67
+ </DropdownMenuItem>
68
+ <DropdownMenuItem>
69
+ <Settings className="mr-2 h-4 w-4" />
70
+ <span>Settings</span>
71
+ <DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
72
+ </DropdownMenuItem>
73
+ <DropdownMenuItem>
74
+ <Keyboard className="mr-2 h-4 w-4" />
75
+ <span>Keyboard shortcuts</span>
76
+ <DropdownMenuShortcut>⌘K</DropdownMenuShortcut>
77
+ </DropdownMenuItem>
78
+ </DropdownMenuGroup>
79
+ <DropdownMenuSeparator />
80
+ <DropdownMenuGroup>
81
+ <DropdownMenuItem>
82
+ <Users className="mr-2 h-4 w-4" />
83
+ <span>Team</span>
84
+ </DropdownMenuItem>
85
+ <DropdownMenuSub>
86
+ <DropdownMenuSubTrigger>
87
+ <UserPlus className="mr-2 h-4 w-4" />
88
+ <span>Invite users</span>
89
+ </DropdownMenuSubTrigger>
90
+ <DropdownMenuPortal>
91
+ <DropdownMenuSubContent>
92
+ <DropdownMenuItem>
93
+ <Mail className="mr-2 h-4 w-4" />
94
+ <span>Email</span>
95
+ </DropdownMenuItem>
96
+ <DropdownMenuItem>
97
+ <MessageSquare className="mr-2 h-4 w-4" />
98
+ <span>Message</span>
99
+ </DropdownMenuItem>
100
+ <DropdownMenuSeparator />
101
+ <DropdownMenuItem>
102
+ <PlusCircle className="mr-2 h-4 w-4" />
103
+ <span>More...</span>
104
+ </DropdownMenuItem>
105
+ </DropdownMenuSubContent>
106
+ </DropdownMenuPortal>
107
+ </DropdownMenuSub>
108
+ <DropdownMenuItem>
109
+ <Plus className="mr-2 h-4 w-4" />
110
+ <span>New Team</span>
111
+ <DropdownMenuShortcut>⌘+T</DropdownMenuShortcut>
112
+ </DropdownMenuItem>
113
+ </DropdownMenuGroup>
114
+ <DropdownMenuSeparator />
115
+ <DropdownMenuItem>
116
+ <Github className="mr-2 h-4 w-4" />
117
+ <span>GitHub</span>
118
+ </DropdownMenuItem>
119
+ <DropdownMenuItem>
120
+ <LifeBuoy className="mr-2 h-4 w-4" />
121
+ <span>Support</span>
122
+ </DropdownMenuItem>
123
+ <DropdownMenuItem disabled>
124
+ <Cloud className="mr-2 h-4 w-4" />
125
+ <span>API</span>
126
+ </DropdownMenuItem>
127
+ <DropdownMenuSeparator />
128
+ <DropdownMenuItem>
129
+ <LogOut className="mr-2 h-4 w-4" />
130
+ <span>Log out</span>
131
+ <DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
132
+ </DropdownMenuItem>
133
+ </DropdownMenuContent>
134
+ </DropdownMenu>
135
+ </div>
136
+ ),
137
+ };
@@ -0,0 +1,20 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { fn } from "@storybook/test";
3
+
4
+ import { Input } from "@/components/Input";
5
+
6
+ const meta = {
7
+ title: "Components/Input",
8
+ component: Input,
9
+ tags: ["autodocs"],
10
+ args: { onChange: fn() },
11
+ } satisfies Meta<typeof Input>;
12
+
13
+ export default meta;
14
+ type Story = StoryObj<typeof meta>;
15
+
16
+ export const Default: Story = {
17
+ args: {
18
+ placeholder: "Username",
19
+ },
20
+ };
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,12 @@
1
+ const content = ["./src/**/*.{ts,tsx}"];
2
+
3
+ if (process.env.NODE_ENV === "production") {
4
+ content.push("!./src/**/*.stories.{ts,tsx}");
5
+ }
6
+
7
+ /** @type {import('tailwindcss').Config} */
8
+ module.exports = {
9
+ darkMode: ["class"],
10
+ safelist: ["dark"],
11
+ content,
12
+ };
@@ -0,0 +1,33 @@
1
+ {
2
+ "compilerOptions": {
3
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
4
+ "target": "ES2022",
5
+ "useDefineForClassFields": true,
6
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
7
+ "module": "ESNext",
8
+ "types": ["vite/client"],
9
+ "skipLibCheck": true,
10
+
11
+ /* Bundler mode */
12
+ "moduleResolution": "bundler",
13
+ "allowImportingTsExtensions": true,
14
+ "verbatimModuleSyntax": true,
15
+ "moduleDetection": "force",
16
+ "noEmit": true,
17
+ "jsx": "react-jsx",
18
+
19
+ /* Linting */
20
+ "strict": true,
21
+ "noUnusedLocals": true,
22
+ "noUnusedParameters": true,
23
+ "erasableSyntaxOnly": true,
24
+ "noFallthroughCasesInSwitch": true,
25
+ "noUncheckedSideEffectImports": true,
26
+
27
+ "baseUrl": ".",
28
+ "paths": {
29
+ "@/*": ["./src/*"]
30
+ }
31
+ },
32
+ "include": ["src"]
33
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "jsx": "react-jsx",
4
+ "target": "es2022",
5
+ "esModuleInterop": true,
6
+ "strict": true,
7
+ "module": "NodeNext",
8
+ "sourceMap": false,
9
+ "declaration": true,
10
+ "lib": ["es2022", "dom", "dom.iterable"],
11
+ "baseUrl": ".",
12
+ "paths": {
13
+ "@/*": [
14
+ "./src/*"
15
+ ]
16
+ }
17
+ },
18
+ "include": ["src", "tailwind.config.ts"]
19
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "files": [],
3
+ "references": [
4
+ { "path": "./tsconfig.app.json" },
5
+ { "path": "./tsconfig.node.json" },
6
+ ],
7
+ "compilerOptions": {
8
+ "baseUrl": ".",
9
+ "paths": {
10
+ "@/*": ["./src/*"]
11
+ }
12
+ }
13
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
4
+ "target": "ES2023",
5
+ "lib": ["ES2023"],
6
+ "module": "ESNext",
7
+ "types": ["node"],
8
+ "skipLibCheck": true,
9
+
10
+ /* Bundler mode */
11
+ "moduleResolution": "bundler",
12
+ "allowImportingTsExtensions": true,
13
+ "verbatimModuleSyntax": true,
14
+ "moduleDetection": "force",
15
+ "noEmit": true,
16
+
17
+ /* Linting */
18
+ "strict": true,
19
+ "noUnusedLocals": true,
20
+ "noUnusedParameters": true,
21
+ "erasableSyntaxOnly": true,
22
+ "noFallthroughCasesInSwitch": true,
23
+ "noUncheckedSideEffectImports": true
24
+ },
25
+ "include": ["vite.config.ts"]
26
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,12 @@
1
+ import { defineConfig } from "tsup";
2
+
3
+ export default defineConfig({
4
+ entry: ["src/index.ts"],
5
+ minify: true,
6
+ sourcemap: false,
7
+ clean: true,
8
+ format: ["esm", "cjs"],
9
+ dts: true,
10
+ tsconfig: "tsconfig.build.json",
11
+ external: ["tw-animate-css"],
12
+ });
package/vite.config.ts ADDED
@@ -0,0 +1,14 @@
1
+ import tailwindcss from "@tailwindcss/vite";
2
+ import react from "@vitejs/plugin-react-swc";
3
+ import path from "path";
4
+ import { defineConfig } from "vite";
5
+
6
+ // https://vite.dev/config/
7
+ export default defineConfig({
8
+ plugins: [react(), tailwindcss()],
9
+ resolve: {
10
+ alias: {
11
+ "@": path.resolve(__dirname, "./src"),
12
+ },
13
+ },
14
+ });