diff --git a/.storybook/main.ts b/.storybook/main.ts index f5fe1c0..098c0fa 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -31,6 +31,8 @@ const config: StorybookConfig = { resolve: { alias: [ { find: /^@\/components\/(.*)/, replacement: path.resolve(__dirname, "../lib/components/$1") }, + { find: /^@\/utils\/(.*)/, replacement: path.resolve(__dirname, "../lib/utils/$1") }, + { find: /^@\/ui\/(.*)/, replacement: path.resolve(__dirname, "../lib/components/ui/$1") }, { find: /^@\/assets\/(.*)/, replacement: path.resolve(__dirname, "../lib/assets/$1") } ] } diff --git a/components.json b/components.json index f505345..463900b 100644 --- a/components.json +++ b/components.json @@ -12,7 +12,7 @@ }, "aliases": { "components": "@/components", - "utils": "lib/utils/utils", + "utils": "@/utils/utils", "ui": "@/components/ui" } } \ No newline at end of file diff --git a/lib/components/button/Button.tsx b/lib/components/button/Button.tsx index 6a68dc8..2f27f5f 100644 --- a/lib/components/button/Button.tsx +++ b/lib/components/button/Button.tsx @@ -1,59 +1,48 @@ -import { twMerge } from "tailwind-merge"; -import { - primary as primaryStyles, - secondary as secondaryStyles, -} from "./Button.styles"; - +import { Button as ShadcnButton } from "@/ui/button"; // import { primary as primaryStyles, secondary as secondaryStyles } from "@/components/button/Button.styles"; export interface ButtonProps { /** * Is this the principal call to action on the page? */ - primary?: boolean; - /** - * What background color to use - */ - backgroundColor?: string; + variant?: + | "default" + | "destructive" + | "outline" + | "secondary" + | "ghost" + | "link"; /** * How large should the button be? */ - size?: "small" | "medium" | "large"; + size?: "sm" | "default" | "lg" | "icon"; /** - * Button contents + * Button contents. Can be a string or a React node (e.g. an icon) */ - label: string; + label: string | React.ReactNode; /** * Optional click handler */ onClick?: () => void; + + /** + * Optional tailwindcss classes + */ + className?: string; } /** - * Primary UI component for user interaction + * Button component */ export const Button = ({ - primary = false, - size = "medium", + variant = "default", + size = "default", label, ...props }: ButtonProps) => { - const classes = []; - const mode = primary ? primaryStyles : secondaryStyles; - classes.push(mode); - if (size === "small") { - classes.push("text-sm"); - } - if (size === "medium") { - classes.push("text-base"); - } - if (size === "large") { - classes.push("text-xl"); - } - return ( - + + {label} + ); }; diff --git a/lib/components/ui/button.tsx b/lib/components/ui/button.tsx new file mode 100644 index 0000000..7cd9121 --- /dev/null +++ b/lib/components/ui/button.tsx @@ -0,0 +1,58 @@ +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; + +import { cn } from "@/utils/utils"; + +const buttonVariants = cva( + "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:ring-offset-slate-950 dark:focus-visible:ring-slate-300", + { + variants: { + variant: { + default: + "bg-slate-900 text-slate-50 hover:bg-slate-900/90 dark:bg-slate-50 dark:text-slate-900 dark:hover:bg-slate-50/90", + destructive: + "bg-red-500 text-slate-50 hover:bg-red-500/90 dark:bg-red-900 dark:text-slate-50 dark:hover:bg-red-900/90", + outline: + "border border-slate-200 bg-white hover:bg-slate-100 hover:text-slate-900 dark:border-slate-800 dark:bg-slate-950 dark:hover:bg-slate-800 dark:hover:text-slate-50", + secondary: + "bg-slate-100 text-slate-900 hover:bg-slate-100/80 dark:bg-slate-800 dark:text-slate-50 dark:hover:bg-slate-800/80", + ghost: + "hover:bg-slate-100 hover:text-slate-900 dark:hover:bg-slate-800 dark:hover:text-slate-50", + link: "text-slate-900 underline-offset-4 hover:underline dark:text-slate-50", + }, + size: { + default: "h-10 px-4 py-2", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8", + icon: "h-10 w-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + }, +); + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean; +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button"; + return ( + + ); + }, +); +Button.displayName = "Button"; + +export { Button, buttonVariants }; diff --git a/lib/tailwind/config.ts b/lib/tailwind/config.ts index 8756ca9..6393e3e 100644 --- a/lib/tailwind/config.ts +++ b/lib/tailwind/config.ts @@ -8,50 +8,36 @@ const theme = { extend: { colors: { brand: { - 100: "#E1F5FE", - 200: "#B3E5FC", - 300: "#81D4FA", - 400: "#4FC3F7", - 500: "#29B6F6", - 600: "#03A9F4", - 700: "#039BE5", - 800: "#0288D1", - 900: "#01579B", + 100: "#e0f2fe", + 200: "#bae6fd", + 300: "#7dd3fc", + 400: "#38bdf8", + 500: "#0ea5e9", + 600: "#0284c7", + 700: "#0369a1", + 800: "#075985", + 900: "#0c4a6e", + 950: "#082f49", }, accent: { - 100: "#FF80AB", - 200: "#FF4081", - 300: "#F50057", - 400: "#C51162", - 500: "#880E4F", - 600: "#AD1457", - 700: "#C2185B", - 800: "#D81B60", - 900: "#E91E63", - }, - text: { - primary: "#FFF", + primary: "#f8fafc", secondary: "#757575", - disabled: "#BDBDBD", - hint: "#9E9E9E", - inverted: "#000000", // Changed from white to black - }, - supporting: { - success: "#4CAF50", // Green - error: "#F44336", // Red - warning: "#FFC107", // Yellow - info: "#2196F3", // Blue + inverted: "#000000", + success: "#065f46", // Green + error: "#991b1b", // Red + warning: "#ca8a04", // Yellow + info: "#075985", // Blue }, neutral: { - 100: "#F5F5F5", - 200: "#EEEEEE", - 300: "#E0E0E0", - 400: "#BDBDBD", - 500: "#9E9E9E", - 600: "#757575", - 700: "#616161", - 800: "#424242", - 900: "#212121", + 100: "#f1f5f9", + 200: "#e2e8f0", + 300: "#cbd5e1", + 400: "#94a3b8", + 500: "#64748b", + 600: "#475569", + 700: "#334155", + 800: "#1e293b", + 900: "#0f172a", }, }, }, diff --git a/package-lock.json b/package-lock.json index aebe5c9..241c4e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,9 @@ "name": "@maany_shr/rage-ui-kit", "version": "1.0.0-alpha", "dependencies": { + "@heroicons/react": "^2.1.3", + "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-slot": "^1.0.2", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", "lucide-react": "^0.372.0", @@ -2220,7 +2223,6 @@ "version": "7.24.4", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.4.tgz", "integrity": "sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==", - "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -2853,6 +2855,14 @@ "integrity": "sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ==", "dev": true }, + "node_modules/@heroicons/react": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.1.3.tgz", + "integrity": "sha512-fEcPfo4oN345SoqdlCDdSa4ivjaKbk0jTd+oubcgNxnNgAfzysfwWfQUr+51wigiWHQQRiZNd1Ao0M5Y3M2EGg==", + "peerDependencies": { + "react": ">= 16" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -3263,7 +3273,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==", - "dev": true, "dependencies": { "@babel/runtime": "^7.13.10" }, @@ -3277,11 +3286,18 @@ } } }, + "node_modules/@radix-ui/react-icons": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.0.tgz", + "integrity": "sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==", + "peerDependencies": { + "react": "^16.x || ^17.x || ^18.x" + } + }, "node_modules/@radix-ui/react-slot": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", - "dev": true, "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.1" @@ -5465,7 +5481,7 @@ "version": "15.7.11", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==", - "dev": true + "devOptional": true }, "node_modules/@types/qs": { "version": "6.9.15", @@ -5483,7 +5499,7 @@ "version": "18.2.48", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.48.tgz", "integrity": "sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w==", - "dev": true, + "devOptional": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -5509,7 +5525,7 @@ "version": "0.16.8", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", - "dev": true + "devOptional": true }, "node_modules/@types/semver": { "version": "7.5.6", @@ -7503,7 +7519,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true + "devOptional": true }, "node_modules/data-urls": { "version": "5.0.0", @@ -12525,8 +12541,7 @@ "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/regenerator-transform": { "version": "0.15.2", diff --git a/package.json b/package.json index 8d4843a..68910bd 100644 --- a/package.json +++ b/package.json @@ -92,6 +92,9 @@ "vitest": "^1.2.2" }, "dependencies": { + "@heroicons/react": "^2.1.3", + "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-slot": "^1.0.2", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", "lucide-react": "^0.372.0", diff --git a/stories/components/Button.stories.ts b/stories/components/Button.stories.tsx similarity index 70% rename from stories/components/Button.stories.ts rename to stories/components/Button.stories.tsx index e87ed70..9adeb07 100644 --- a/stories/components/Button.stories.ts +++ b/stories/components/Button.stories.tsx @@ -1,6 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import { Button } from "@/components/button"; +import { Satellite } from "lucide-react"; // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export const meta = { @@ -12,10 +13,6 @@ const meta = { }, // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs tags: ["autodocs"], - // More on argTypes: https://storybook.js.org/docs/api/argtypes - argTypes: { - backgroundColor: { control: "color" }, - }, } satisfies Meta; export default meta; @@ -23,28 +20,22 @@ type Story = StoryObj; // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args export const Primary: Story = { - args: { - primary: true, - label: "Button", - }, -}; - -export const Secondary: Story = { args: { label: "Button", }, }; -export const Large: Story = { +export const TextIcon: Story = { args: { - size: "large", - label: "Button", + label: "🚀", + size: "icon", }, }; -export const Small: Story = { +export const ReactIcon: Story = { args: { - size: "small", - label: "Button", + label: , + size: "icon", + variant: "outline", }, }; diff --git a/tsconfig.json b/tsconfig.json index 6b893d7..170601b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,7 +23,9 @@ "types": ["@testing-library/jest-dom", "vite/client"], "paths": { "@/components/*": ["./lib/components/*"], + "@/utils/*": ["./lib/utils/*"], "@/assets/*": ["./lib/assets/*"], + "@/ui/*": ["./lib/components/ui/*"], } }, "include": ["app", "lib", "tests", "stories"], diff --git a/vite.config.ts b/vite.config.ts index 4e033ad..1fa283b 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -19,6 +19,14 @@ export default defineConfig({ find: /^@\/assets\/(.*)/, replacement: path.resolve(__dirname, "lib/assets/$1"), }, + { + find: /^@\/ui\/(.*)/, + replacement: path.resolve(__dirname, "lib/ui/$1"), + }, + { + find: /^@\/utils\/(.*)/, + replacement: path.resolve(__dirname, "lib/utils/$1"), + }, ], }, test: { @@ -31,6 +39,14 @@ export default defineConfig({ find: /^@\/assets\/(.*)/, replacement: path.resolve(__dirname, "lib/assets/$1"), }, + { + find: /^@\/ui\/(.*)/, + replacement: path.resolve(__dirname, "lib/ui/$1"), + }, + { + find: /^@\/utils\/(.*)/, + replacement: path.resolve(__dirname, "lib/utils/$1"), + }, ], clearMocks: true, globals: true,