diff --git a/package-lock.json b/package-lock.json index 24c3db01..4e11dd38 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,9 +11,12 @@ "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-menubar": "^1.1.1", + "@radix-ui/react-separator": "^1.1.0", + "@radix-ui/react-slot": "^1.1.0", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "eslint-config-airbnb-typescript": "^18.0.0", + "lucide-react": "^0.424.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.26.0", @@ -1544,6 +1547,29 @@ } } }, + "node_modules/@radix-ui/react-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.0.tgz", + "integrity": "sha512-3uBAs+egzvJBDZAzvb/n4NxxOYpnspmWxO2u5NbZ8Y6FM/NdrGSF9bop3Cf6F6C71z1rTSn8KV0Fo2ZVd79lGA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", @@ -5143,6 +5169,15 @@ "yallist": "^3.0.2" } }, + "node_modules/lucide-react": { + "version": "0.424.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.424.0.tgz", + "integrity": "sha512-x2Nj2aytk1iOyHqt4hKenfVlySq0rYxNeEf8hE0o+Yh0iE36Rqz0rkngVdv2uQtjZ70LAE73eeplhhptYt9x4Q==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", diff --git a/package.json b/package.json index e353973f..61348cbe 100644 --- a/package.json +++ b/package.json @@ -14,9 +14,12 @@ "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-menubar": "^1.1.1", + "@radix-ui/react-separator": "^1.1.0", + "@radix-ui/react-slot": "^1.1.0", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "eslint-config-airbnb-typescript": "^18.0.0", + "lucide-react": "^0.424.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.26.0", diff --git a/src/components/button/button-variants.tsx b/src/components/button/button-variants.tsx new file mode 100644 index 00000000..e7b106ae --- /dev/null +++ b/src/components/button/button-variants.tsx @@ -0,0 +1,31 @@ +import { cva } from 'class-variance-authority'; + +export const buttonVariants = cva( + 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50', + { + variants: { + variant: { + default: + 'bg-primary text-primary-foreground shadow hover:bg-primary/90', + destructive: + 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90', + outline: + 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground', + secondary: + 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80', + ghost: 'hover:bg-accent hover:text-accent-foreground', + link: 'text-primary underline-offset-4 hover:underline', + }, + size: { + default: 'h-9 px-4 py-2', + sm: 'h-8 rounded-md px-3 text-xs', + lg: 'h-10 rounded-md px-8', + icon: 'h-9 w-9', + }, + }, + defaultVariants: { + variant: 'default', + size: 'default', + }, + } +); diff --git a/src/components/button/button.tsx b/src/components/button/button.tsx new file mode 100644 index 00000000..f6c1dbc7 --- /dev/null +++ b/src/components/button/button.tsx @@ -0,0 +1,28 @@ +import * as React from 'react'; +import { Slot } from '@radix-ui/react-slot'; +import { type VariantProps } from 'class-variance-authority'; + +import { cn } from '@/lib/utils'; +import { buttonVariants } from './button-variants'; + +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 }; diff --git a/src/components/card/card.tsx b/src/components/card/card.tsx new file mode 100644 index 00000000..c22c0b46 --- /dev/null +++ b/src/components/card/card.tsx @@ -0,0 +1,83 @@ +import * as React from 'react'; + +import { cn } from '@/lib/utils'; + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +Card.displayName = 'Card'; + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +CardHeader.displayName = 'CardHeader'; + +const CardTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)); +CardTitle.displayName = 'CardTitle'; + +const CardDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)); +CardDescription.displayName = 'CardDescription'; + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)); +CardContent.displayName = 'CardContent'; + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +CardFooter.displayName = 'CardFooter'; + +export { + Card, + CardHeader, + CardFooter, + CardTitle, + CardDescription, + CardContent, +}; diff --git a/src/components/separator/separator.tsx b/src/components/separator/separator.tsx new file mode 100644 index 00000000..25959909 --- /dev/null +++ b/src/components/separator/separator.tsx @@ -0,0 +1,31 @@ +import * as React from 'react'; +import * as SeparatorPrimitive from '@radix-ui/react-separator'; + +import { cn } from '@/lib/utils'; + +const Separator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>( + ( + { className, orientation = 'horizontal', decorative = true, ...props }, + ref + ) => ( + + ) +); +Separator.displayName = SeparatorPrimitive.Root.displayName; + +export { Separator }; diff --git a/src/pages/editor-page/editor-page.tsx b/src/pages/editor-page/editor-page.tsx index 93f5ef5c..9cdb859a 100644 --- a/src/pages/editor-page/editor-page.tsx +++ b/src/pages/editor-page/editor-page.tsx @@ -1,10 +1,12 @@ import React from 'react'; import { TopNavbar } from './top-navbar/top-navbar'; +import { Toolbar } from './toolbar/toolbar'; export const EditorPage: React.FC = () => { return (
+
); }; diff --git a/src/pages/editor-page/toolbar/toolbar-button.tsx b/src/pages/editor-page/toolbar/toolbar-button.tsx new file mode 100644 index 00000000..ba90c215 --- /dev/null +++ b/src/pages/editor-page/toolbar/toolbar-button.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { Button, ButtonProps } from '@/components/button/button'; + +export const ToolbarButton: React.FC = (props) => { + return ( +