From f3b998f6b8f7bbfd67491df3705358d2e2394786 Mon Sep 17 00:00:00 2001 From: Chandini Date: Thu, 11 Sep 2025 09:34:34 +0530 Subject: [PATCH] frontend changes --- next.config.ts | 19 +- package-lock.json | 91 +++ package.json | 7 + src/app/globals.css | 59 ++ src/components/canvas.tsx | 197 +++--- src/components/component-palette.tsx | 309 ++++------ src/components/component-renderer.tsx | 838 +++++++++++++++++++++----- src/components/dual-canvas-editor.tsx | 32 +- src/components/main-dashboard.tsx | 2 +- src/components/properties-panel.tsx | 507 +++++++--------- src/components/ui/calendar.tsx | 213 +++++++ src/components/ui/carousel.tsx | 262 ++++++++ src/components/ui/context-menu.tsx | 200 ++++++ src/components/ui/drawer.tsx | 115 ++++ src/components/ui/menubar.tsx | 256 ++++++++ src/components/wireframe-canvas.tsx | 60 +- src/components/wireframe-renderer.tsx | 49 +- src/lib/store.ts | 14 +- src/lib/wireframe-converter.ts | 90 +++ src/lib/wireframe-integration.tsx | 2 +- 20 files changed, 2530 insertions(+), 792 deletions(-) create mode 100644 src/components/ui/calendar.tsx create mode 100644 src/components/ui/carousel.tsx create mode 100644 src/components/ui/context-menu.tsx create mode 100644 src/components/ui/drawer.tsx create mode 100644 src/components/ui/menubar.tsx create mode 100644 src/lib/wireframe-converter.ts diff --git a/next.config.ts b/next.config.ts index e9ffa30..9533e62 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,7 +1,24 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - /* config options here */ + transpilePackages: ['@tldraw/tldraw'], + webpack: (config, { isServer }) => { + // Fix tldraw duplication issues + config.resolve.alias = { + ...config.resolve.alias, + '@tldraw/utils': require.resolve('@tldraw/utils'), + '@tldraw/state': require.resolve('@tldraw/state'), + '@tldraw/state-react': require.resolve('@tldraw/state-react'), + '@tldraw/store': require.resolve('@tldraw/store'), + '@tldraw/validate': require.resolve('@tldraw/validate'), + '@tldraw/tlschema': require.resolve('@tldraw/tlschema'), + '@tldraw/editor': require.resolve('@tldraw/editor'), + 'tldraw': require.resolve('tldraw'), + '@tldraw/tldraw': require.resolve('@tldraw/tldraw'), + }; + + return config; + }, }; export default nextConfig; diff --git a/package-lock.json b/package-lock.json index 15b928d..a366f24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,9 +15,12 @@ "@next/font": "^14.2.15", "@radix-ui/react-avatar": "^1.1.10", "@radix-ui/react-checkbox": "^1.3.2", + "@radix-ui/react-context-menu": "^2.2.16", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.15", "@radix-ui/react-label": "^2.1.7", + "@radix-ui/react-menubar": "^1.1.16", + "@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-progress": "^1.1.7", "@radix-ui/react-radio-group": "^1.3.7", "@radix-ui/react-scroll-area": "^1.2.10", @@ -30,14 +33,18 @@ "axios": "^1.11.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "date-fns": "^4.1.0", + "embla-carousel-react": "^8.6.0", "lucide-react": "^0.539.0", "next": "15.4.6", "react": "19.1.0", + "react-day-picker": "^9.9.0", "react-dom": "19.1.0", "react-resizable-panels": "^3.0.5", "socket.io-client": "^4.8.1", "svg-path-parser": "^1.1.0", "tailwind-merge": "^3.3.1", + "vaul": "^1.1.2", "zustand": "^5.0.8" }, "devDependencies": { @@ -90,6 +97,12 @@ "anthropic-ai-sdk": "bin/cli" } }, + "node_modules/@date-fns/tz": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@date-fns/tz/-/tz-1.4.1.tgz", + "integrity": "sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==", + "license": "MIT" + }, "node_modules/@dnd-kit/accessibility": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz", @@ -4854,6 +4867,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/date-fns-jalali": { + "version": "4.1.0-0", + "resolved": "https://registry.npmjs.org/date-fns-jalali/-/date-fns-jalali-4.1.0-0.tgz", + "integrity": "sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==", + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", @@ -4967,6 +4996,34 @@ "node": ">= 0.4" } }, + "node_modules/embla-carousel": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", + "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==", + "license": "MIT" + }, + "node_modules/embla-carousel-react": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel-react/-/embla-carousel-react-8.6.0.tgz", + "integrity": "sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA==", + "license": "MIT", + "dependencies": { + "embla-carousel": "8.6.0", + "embla-carousel-reactive-utils": "8.6.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/embla-carousel-reactive-utils": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel-reactive-utils/-/embla-carousel-reactive-utils-8.6.0.tgz", + "integrity": "sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A==", + "license": "MIT", + "peerDependencies": { + "embla-carousel": "8.6.0" + } + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -8008,6 +8065,27 @@ "node": ">=0.10.0" } }, + "node_modules/react-day-picker": { + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-9.9.0.tgz", + "integrity": "sha512-NtkJbuX6cl/VaGNb3sVVhmMA6LSMnL5G3xNL+61IyoZj0mUZFWTg4hmj7PHjIQ8MXN9dHWhUHFoJWG6y60DKSg==", + "license": "MIT", + "dependencies": { + "@date-fns/tz": "^1.4.1", + "date-fns": "^4.1.0", + "date-fns-jalali": "^4.1.0-0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/gpbl" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/react-dom": { "version": "19.1.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", @@ -9204,6 +9282,19 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/vaul": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vaul/-/vaul-1.1.2.tgz", + "integrity": "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-dialog": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" + } + }, "node_modules/w3c-keyname": { "version": "2.2.8", "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", diff --git a/package.json b/package.json index 61fe4cf..3f9f38a 100644 --- a/package.json +++ b/package.json @@ -16,9 +16,12 @@ "@next/font": "^14.2.15", "@radix-ui/react-avatar": "^1.1.10", "@radix-ui/react-checkbox": "^1.3.2", + "@radix-ui/react-context-menu": "^2.2.16", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.15", "@radix-ui/react-label": "^2.1.7", + "@radix-ui/react-menubar": "^1.1.16", + "@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-progress": "^1.1.7", "@radix-ui/react-radio-group": "^1.3.7", "@radix-ui/react-scroll-area": "^1.2.10", @@ -31,14 +34,18 @@ "axios": "^1.11.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "date-fns": "^4.1.0", + "embla-carousel-react": "^8.6.0", "lucide-react": "^0.539.0", "next": "15.4.6", "react": "19.1.0", + "react-day-picker": "^9.9.0", "react-dom": "19.1.0", "react-resizable-panels": "^3.0.5", "socket.io-client": "^4.8.1", "svg-path-parser": "^1.1.0", "tailwind-merge": "^3.3.1", + "vaul": "^1.1.2", "zustand": "^5.0.8" }, "devDependencies": { diff --git a/src/app/globals.css b/src/app/globals.css index dc98be7..b245970 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -120,3 +120,62 @@ @apply bg-background text-foreground; } } + +/* Added custom styles for drag-and-drop editor */ +.drag-overlay { + @apply opacity-50 rotate-3 scale-105; +} + +.drop-zone-active { + @apply ring-2 ring-accent bg-accent/5; +} + +.canvas-grid { + background-image: linear-gradient(rgba(99, 102, 241, 0.1) 1px, transparent 1px), + linear-gradient(90deg, rgba(99, 102, 241, 0.1) 1px, transparent 1px); + background-size: 20px 20px; +} + +.resizer { + @apply bg-border hover:bg-accent transition-colors; +} + +.resizer:hover { + @apply bg-accent; +} + +/* Enhanced scrolling for panels */ +[data-slot="scroll-area-viewport"] { + scrollbar-width: thin; + scrollbar-color: hsl(var(--border)) transparent; +} + +[data-slot="scroll-area-viewport"]::-webkit-scrollbar { + width: 6px; +} + +[data-slot="scroll-area-viewport"]::-webkit-scrollbar-track { + background: transparent; +} + +[data-slot="scroll-area-viewport"]::-webkit-scrollbar-thumb { + background-color: hsl(var(--border)); + border-radius: 3px; +} + +[data-slot="scroll-area-viewport"]::-webkit-scrollbar-thumb:hover { + background-color: hsl(var(--accent)); +} + +/* Smooth scrolling for panels */ +.component-panel-scroll { + scroll-behavior: smooth; + overflow-y: auto; + overflow-x: hidden; +} + +.properties-panel-scroll { + scroll-behavior: smooth; + overflow-y: auto; + overflow-x: hidden; +} diff --git a/src/components/canvas.tsx b/src/components/canvas.tsx index 04b1962..4946041 100644 --- a/src/components/canvas.tsx +++ b/src/components/canvas.tsx @@ -1,143 +1,82 @@ "use client" -import { useRef, useState } from "react" +import { useDroppable } from "@dnd-kit/core" +import { useEffect, useRef, useState } from "react" import { useEditorStore } from "@/lib/store" -import { ComponentRenderer } from "./component-renderer" -import { cn } from "@/lib/utils" +import { ComponentRenderer } from "@/components/component-renderer" +import { MousePointer } from "lucide-react" export function Canvas() { - const canvasRef = useRef(null) - const [isDragging, setIsDragging] = useState(false) - const [dragStart, setDragStart] = useState<{ x: number; y: number } | null>(null) - const [selectedComponents, setSelectedComponents] = useState>(new Set()) - - const { - components, - selectedComponent, - selectComponent, - moveComponent, - updateComponent - } = useEditorStore() + const { components, selectComponent, moveComponent } = useEditorStore() + const containerRef = useRef(null) + const [dragState, setDragState] = useState<{ + id: string + offsetX: number + offsetY: number + } | null>(null) - const handleMouseDown = (e: React.MouseEvent) => { - if (e.target === canvasRef.current) { - // Clicked on empty canvas, deselect all - selectComponent(null) - setSelectedComponents(new Set()) - } - } - - const handleComponentClick = (componentId: string, e: React.MouseEvent) => { - e.stopPropagation() - - const component = components.find(c => c.id === componentId) - if (component) { - selectComponent(component) - setSelectedComponents(new Set([componentId])) - } - } - - const handleComponentMouseDown = (componentId: string, e: React.MouseEvent) => { - e.stopPropagation() - - const component = components.find(c => c.id === componentId) - if (component) { - selectComponent(component) - setSelectedComponents(new Set([componentId])) - - // Start dragging - setIsDragging(true) - setDragStart({ x: e.clientX, y: e.clientY }) - } - } - - const handleMouseMove = (e: React.MouseEvent) => { - if (isDragging && dragStart && selectedComponent) { - const deltaX = e.clientX - dragStart.x - const deltaY = e.clientY - dragStart.y - - const newPosition = { - x: Math.max(0, selectedComponent.position.x + deltaX), - y: Math.max(0, selectedComponent.position.y + deltaY) - } - - moveComponent(selectedComponent.id, newPosition) - setDragStart({ x: e.clientX, y: e.clientY }) - } - } - - const handleMouseUp = () => { - setIsDragging(false) - setDragStart(null) - } + const { setNodeRef, isOver } = useDroppable({ + id: "canvas", + }) return ( -
- {/* Canvas Grid Background */} -
-
+
+ {/* Canvas Header */} +
+

Canvas

+

Drag components here to build your interface

- {/* Components */} - {components.map((component) => ( -
handleComponentClick(component.id, e)} - onMouseDown={(e) => handleComponentMouseDown(component.id, e)} - > - -
- ))} - - {/* Drop Zone Indicator */} - {isDragging && ( -
-
-
- )} - - {/* Empty State */} - {components.length === 0 && ( -
-
-
🎨
-

No components yet

-

Drag components from the palette to get started

+ {/* Canvas Area */} +
{ + setNodeRef(node) + containerRef.current = node + }} + className={`flex-1 relative canvas-grid overflow-auto ${isOver ? "drop-zone-active" : ""}`} + onClick={() => selectComponent(null)} + onMouseMove={(e) => { + if (!dragState || !containerRef.current) return + const rect = containerRef.current.getBoundingClientRect() + const x = e.clientX - rect.left - dragState.offsetX + containerRef.current.scrollLeft + const y = e.clientY - rect.top - dragState.offsetY + containerRef.current.scrollTop + moveComponent(dragState.id, { x: Math.max(0, x), y: Math.max(0, y) }) + }} + onMouseUp={() => setDragState(null)} + onMouseLeave={() => setDragState(null)} + > + {components.length === 0 ? ( +
+
+
+ +
+

Start Building

+

+ Drag components from the sidebar to start building your interface +

+
-
- )} + ) : ( + components.map((component) => ( + { + e.stopPropagation() + selectComponent(component) + }} + onMouseDown={(e) => { + const target = e.currentTarget as HTMLDivElement + const rect = target.getBoundingClientRect() + const offsetX = e.clientX - rect.left + const offsetY = e.clientY - rect.top + setDragState({ id: component.id, offsetX, offsetY }) + }} + /> + )) + )} +
) } diff --git a/src/components/component-palette.tsx b/src/components/component-palette.tsx index 6cde264..4c63fb0 100644 --- a/src/components/component-palette.tsx +++ b/src/components/component-palette.tsx @@ -1,213 +1,132 @@ "use client" +import { useDraggable } from "@dnd-kit/core" +import { Input } from "@/components/ui/input" +import { ScrollArea } from "@/components/ui/scroll-area" import { useState } from "react" -import { Button } from "./ui/button" -import { Input } from "./ui/input" -import { Textarea } from "./ui/textarea" -import { Card, CardContent, CardHeader, CardTitle } from "./ui/card" -import { Checkbox } from "./ui/checkbox" -import { Switch } from "./ui/switch" -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select" -import { RadioGroup, RadioGroupItem } from "./ui/radio-group" -import { Progress } from "./ui/progress" -import { Avatar, AvatarFallback } from "./ui/avatar" -import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./ui/table" -import { Tabs, TabsContent, TabsList, TabsTrigger } from "./ui/tabs" -import { Badge } from "./ui/badge" -import { cn } from "@/lib/utils" -import { - MousePointer, - Square, - Circle, - Type, - Image, - Layout, - BarChart3, - Settings, - User, +import { + Table, Calendar, - Mail, - Phone, - Globe, - Search, - Filter, - Download, - Upload, - Edit, - Trash2, - Plus, - Minus, - Check, - X, - ArrowRight, - ArrowLeft, - ArrowUp, - ArrowDown, + Circle, + Square, + Type, + MousePointer, + CreditCard, ChevronDown, - ChevronUp, - ChevronLeft, - ChevronRight, - Menu, - MoreHorizontal, - Star, - Heart, - Share, - Bookmark, - Flag, - AlertCircle, - Info, - CheckCircle, - XCircle, - AlertTriangle, - HelpCircle + Tags as Tabs, + BarChart3, + ToggleLeft, + User, + Medal as Modal, + Search, } from "lucide-react" -const componentCategories = [ - { - name: "Basic", - icon: Square, - components: [ - { type: "button", name: "Button", icon: MousePointer }, - { type: "input", name: "Input", icon: Type }, - { type: "textarea", name: "Textarea", icon: Type }, - { type: "card", name: "Card", icon: Square }, - ] - }, - { - name: "Form", - icon: Settings, - components: [ - { type: "checkbox", name: "Checkbox", icon: Check }, - { type: "switch", name: "Switch", icon: Settings }, - { type: "select", name: "Select", icon: ChevronDown }, - { type: "radiogroup", name: "Radio Group", icon: Circle }, - ] - }, - { - name: "Data", - icon: BarChart3, - components: [ - { type: "table", name: "Table", icon: Layout }, - { type: "progress", name: "Progress", icon: BarChart3 }, - { type: "tabs", name: "Tabs", icon: Layout }, - ] - }, - { - name: "Media", - icon: Image, - components: [ - { type: "avatar", name: "Avatar", icon: User }, - ] - } +const COMPONENT_TYPES = [ + { id: "data-table", name: "Data Table", icon: Table, category: "Data" }, + { id: "contextmenu", name: "Context Menu", icon: MousePointer, category: "Overlay" }, + { id: "menubar", name: "Menubar", icon: Tabs, category: "Layout" }, + { id: "carousel", name: "Carousel", icon: BarChart3, category: "Display" }, + { id: "drawer", name: "Drawer", icon: Modal, category: "Overlay" }, + { id: "table", name: "Table", icon: Table, category: "Data" }, + { id: "datepicker", name: "Date Picker", icon: Calendar, category: "Input" }, + { id: "radiogroup", name: "Radio Group", icon: Circle, category: "Input" }, + { id: "checkbox", name: "Checkbox", icon: Square, category: "Input" }, + { id: "input", name: "Input", icon: Type, category: "Input" }, + { id: "textarea", name: "Textarea", icon: Type, category: "Input" }, + { id: "button", name: "Button", icon: MousePointer, category: "Action" }, + { id: "card", name: "Card", icon: CreditCard, category: "Layout" }, + { id: "select", name: "Select", icon: ChevronDown, category: "Input" }, + { id: "tabs", name: "Tabs", icon: Tabs, category: "Layout" }, + { id: "progress", name: "Progress", icon: BarChart3, category: "Display" }, + { id: "switch", name: "Switch", icon: ToggleLeft, category: "Input" }, + { id: "avatar", name: "Avatar", icon: User, category: "Display" }, + { id: "dialog", name: "Dialog", icon: Modal, category: "Overlay" }, ] -export function ComponentPalette() { - const [selectedCategory, setSelectedCategory] = useState("Basic") - const [searchQuery, setSearchQuery] = useState("") +function DraggableComponent({ component }: { component: (typeof COMPONENT_TYPES)[0] }) { + const { attributes, listeners, setNodeRef, transform, isDragging } = useDraggable({ + id: component.id, + }) - const filteredComponents = componentCategories - .find(cat => cat.name === selectedCategory) - ?.components.filter(comp => - comp.name.toLowerCase().includes(searchQuery.toLowerCase()) - ) || [] - - return ( -
- {/* Header */} -
-

Components

-

Drag components to canvas

-
- - {/* Search */} -
- setSearchQuery(e.target.value)} - className="w-full" - /> -
- - {/* Categories */} -
-
- {componentCategories.map((category) => ( - - ))} -
-
- - {/* Components List */} -
-
- {filteredComponents.map((component) => ( - - ))} -
- - {filteredComponents.length === 0 && ( -
- -

No components found

-
- )} -
-
- ) -} - -interface ComponentPreviewProps { - type: string - name: string - icon: React.ComponentType<{ className?: string }> -} - -function ComponentPreview({ type, name, icon: Icon }: ComponentPreviewProps) { - const [isDragging, setIsDragging] = useState(false) - - const handleDragStart = (e: React.DragEvent) => { - setIsDragging(true) - e.dataTransfer.setData('application/json', JSON.stringify({ type })) - e.dataTransfer.effectAllowed = 'copy' - } - - const handleDragEnd = () => { - setIsDragging(false) - } + const style = transform + ? { + transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`, + } + : undefined return (
-
-
- -
- - {name} - +
+ + {component.name}
) } + +export function ComponentPalette() { + const [searchTerm, setSearchTerm] = useState("") + const [selectedCategory, setSelectedCategory] = useState("All") + + const categories = ["All", ...Array.from(new Set(COMPONENT_TYPES.map((c) => c.category)))] + + const filteredComponents = COMPONENT_TYPES.filter((component) => { + const matchesSearch = component.name.toLowerCase().includes(searchTerm.toLowerCase()) + const matchesCategory = selectedCategory === "All" || component.category === selectedCategory + return matchesSearch && matchesCategory + }) + + return ( +
+ {/* Header */} +
+

Components

+ + {/* Search */} +
+ + setSearchTerm(e.target.value)} + className="pl-9 bg-input border-border" + /> +
+ + {/* Category Filter */} +
+ {categories.map((category) => ( + + ))} +
+
+ + {/* Component List */} + +
+ {filteredComponents.map((component) => ( + + ))} +
+
+
+ ) +} diff --git a/src/components/component-renderer.tsx b/src/components/component-renderer.tsx index b0206b3..7ea615e 100644 --- a/src/components/component-renderer.tsx +++ b/src/components/component-renderer.tsx @@ -1,135 +1,254 @@ "use client" -import { ComponentInstance } from "@/lib/store" +import type React from "react" +import { useState } from "react" + +import { type ComponentInstance, useEditorStore } from "@/lib/store" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Textarea } from "@/components/ui/textarea" +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Checkbox } from "@/components/ui/checkbox" +import { Switch } from "@/components/ui/switch" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" +import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group" +import { Progress } from "@/components/ui/progress" +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" +import { Calendar } from "@/components/ui/calendar" +import { + ContextMenu, + ContextMenuContent, + ContextMenuItem, + ContextMenuLabel, + ContextMenuSeparator, + ContextMenuTrigger, +} from "@/components/ui/context-menu" +import { + Menubar, + MenubarContent, + MenubarItem, + MenubarMenu, + MenubarTrigger, +} from "@/components/ui/menubar" +import { + Carousel as UiCarousel, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPrevious, +} from "@/components/ui/carousel" +import { + Drawer, + DrawerClose, + DrawerContent, + DrawerDescription, + DrawerHeader, + DrawerTitle, + DrawerTrigger, +} from "@/components/ui/drawer" +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog" +import { Label } from "@/components/ui/label" +import { Plus, Minus, User } from "lucide-react" import { WireframeRenderer } from "./wireframe-renderer" -import { Button } from "./ui/button" -import { Input } from "./ui/input" -import { Textarea } from "./ui/textarea" -import { Card, CardContent, CardHeader, CardTitle } from "./ui/card" -import { Checkbox } from "./ui/checkbox" -import { Switch } from "./ui/switch" -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select" -import { RadioGroup, RadioGroupItem } from "./ui/radio-group" -import { Progress } from "./ui/progress" -import { Avatar, AvatarFallback } from "./ui/avatar" -import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./ui/table" -import { Tabs, TabsContent, TabsList, TabsTrigger } from "./ui/tabs" -import { cn } from "@/lib/utils" interface ComponentRendererProps { component: ComponentInstance - isSelected?: boolean - onClick?: () => void + onClick?: (e: React.MouseEvent) => void onMouseDown?: (e: React.MouseEvent) => void } -export function ComponentRenderer({ - component, - isSelected = false, - onClick, - onMouseDown -}: ComponentRendererProps) { - const { type, props } = component +export function ComponentRenderer({ component, onClick, onMouseDown }: ComponentRendererProps) { + const { selectedComponent, updateComponent } = useEditorStore() + const isSelected = selectedComponent?.id === component.id - // Handle wireframe components - if (type.startsWith('wireframe-')) { - return ( - - ) - } + const [checkboxState, setCheckboxState] = useState(component.props.checked || false) + const [switchState, setSwitchState] = useState(component.props.checked || false) + const [radioValue, setRadioValue] = useState(component.props.value || "") + const [progressValue, setProgressValue] = useState(component.props.value || 50) + const [selectedDate, setSelectedDate] = useState( + component.props.selectedDate ? new Date(component.props.selectedDate) : undefined, + ) + const [tableData, setTableData] = useState( + component.props.rows || [ + ["John", "john@example.com"], + ["Jane", "jane@example.com"], + ["kenil", "kenil@example.com"], + ["kavya", "kavya@example.com"], + ["raj", "raj@example.com"] + ], + ) + const [editingCell, setEditingCell] = useState<{ row: number; col: number } | null>(null) + const [editValue, setEditValue] = useState("") + const [cardTitle, setCardTitle] = useState(component.props.title || "Card Title") + const [cardDescription, setCardDescription] = useState(component.props.description || "Card description") + const [cardContent, setCardContent] = useState(component.props.content || "Card content goes here") + const [editingCardField, setEditingCardField] = useState(null) + const [dataTableSortAsc, setDataTableSortAsc] = useState(true) + const [dataTablePage, setDataTablePage] = useState(1) + const [dataTableSelected, setDataTableSelected] = useState([]) + const [tableSortAsc, setTableSortAsc] = useState(true) + const [tablePage, setTablePage] = useState(1) + const [tableSelected, setTableSelected] = useState([]) - // Handle regular UI components const renderComponent = () => { - switch (type) { - case 'button': + switch (component.type) { + case "button": return ( ) - case 'input': - return ( - - ) + case "input": + return - case 'textarea': - return ( -