UNPKG

aura-glass

Version:

A comprehensive glassmorphism design system for React applications with 142+ production-ready components

363 lines (360 loc) 14.7 kB
'use client'; import { jsx, jsxs } from 'react/jsx-runtime'; import { useState } from 'react'; import { GlassCore } from '../../primitives/GlassCore.js'; import '../../primitives/glass/GlassAdvanced.js'; import '../../primitives/OptimizedGlassCore.js'; import '../../primitives/glass/OptimizedGlassAdvanced.js'; import '../../primitives/MotionNative.js'; import '../../primitives/motion/MotionFramer.js'; import { cn } from '../../lib/utilsComprehensive.js'; import { useDragDrop } from './GlassDragDropProvider.js'; const TreeItem = ({ component, level, isSelected, onSelect, onToggleExpand, expandedItems, componentDefinition }) => { const { deleteComponent, duplicateComponent, onDragStart, dragDropState, componentLibrary } = useDragDrop(); const [showActions, setShowActions] = useState(false); const isExpanded = expandedItems.has(component.id); const hasChildren = component.children.length > 0; const handleDragStart = e => { e.stopPropagation(); onDragStart(component, "element"); }; const handleClick = e => { e.stopPropagation(); onSelect(component.id); }; const handleToggleExpand = e => { e.stopPropagation(); if (hasChildren) { onToggleExpand(component.id); } }; const getComponentIcon = type => { const definition = componentLibrary.find(def => def.type === type); return definition?.icon || "📦"; }; return jsxs("div", { "data-glass-component": true, children: [jsxs("div", { draggable: true, onDragStart: handleDragStart, onClick: handleClick, onMouseEnter: () => setShowActions(true), onMouseLeave: () => setShowActions(false), className: cn("flex items-center gap-2 py-1 px-2 rounded-md cursor-pointer transition-colors relative group", isSelected ? "bg-blue-100 text-blue-900" : "hover:bg-gray-50", dragDropState.draggedItem?.id === component.id && "opacity-50"), style: { paddingLeft: `${level * 16 + 8}px` }, children: [jsx("button", { onClick: handleToggleExpand, className: cn("w-4 h-4 flex items-center justify-center text-xs transition-transform glass-focus glass-touch-target", hasChildren ? "text-gray-500 hover:text-gray-700" : "text-transparent", isExpanded ? "rotate-90" : "rotate-0"), children: hasChildren && "▶" }), jsx("span", { className: "glass-text-sm", children: getComponentIcon(component.type) }), jsxs("div", { className: 'glass-flex-1 min-glass-w-0', children: [jsx("span", { className: 'glass-text-sm font-medium truncate', children: componentDefinition?.name || component.type }), component.props.content && jsxs("span", { className: 'glass-text-xs glass-text-secondary ml-1 truncate', children: ["\"", component.props.content.substring(0, 20), component.props.content.length > 20 ? "..." : "", "\""] })] }), hasChildren && jsx("span", { className: "glass-text-xs glass-text-secondary glass-surface-subtle glass-px-1.5 glass-py-0.5 glass-radius", children: component.children.length }), jsxs("div", { className: "glass-flex glass-items-center glass-gap-1", children: [component.locked && jsx("span", { className: 'glass-text-xs text-primary', title: "Locked", children: "\uD83D\uDD12" }), !component.props.visible && jsx("span", { className: "glass-text-xs glass-text-secondary", title: "Hidden", children: "\uD83D\uDC41\uFE0F\u200D\uD83D\uDDE8\uFE0F" })] }), showActions && jsxs("div", { className: 'glass-flex glass-items-center glass-gap-1 ml-2', children: [jsx("button", { onClick: e => { e.stopPropagation(); duplicateComponent(component.id); }, className: 'w-6 h-6 glass-flex glass-items-center glass-justify-center glass-text-xs glass-text-secondary hover:text-primary hover:glass-surface-subtle glass-radius glass-focus glass-touch-target', title: "Duplicate", children: "\uD83D\uDCCB" }), jsx("button", { onClick: e => { e.stopPropagation(); deleteComponent(component.id); }, className: 'w-6 h-6 glass-flex glass-items-center glass-justify-center glass-text-xs glass-text-secondary hover:text-primary hover:glass-surface-subtle glass-radius glass-focus glass-touch-target', title: "Delete", children: "\uD83D\uDDD1\uFE0F" })] })] }), hasChildren && isExpanded && jsx("div", { children: component.children.map(child => jsx(TreeItem, { component: child, level: level + 1, isSelected: isSelected, onSelect: onSelect, onToggleExpand: onToggleExpand, expandedItems: expandedItems, componentDefinition: componentLibrary.find(def => def.type === child.type) }, child.id)) })] }); }; const GlassPageStructure = ({ className, collapsed = false, onToggleCollapse }) => { const { pageState, componentLibrary, selectComponent, getSelectedComponent, clearPage, exportPage, importPage } = useDragDrop(); const [expandedItems, setExpandedItems] = useState(new Set()); const [searchQuery, setSearchQuery] = useState(""); const selectedComponent = getSelectedComponent(); const toggleExpanded = id => { setExpandedItems(prev => { const newSet = new Set(prev); if (newSet.has(id)) { newSet.delete(id); } else { newSet.add(id); } return newSet; }); }; const expandAll = () => { const allIds = new Set(); const addIds = components => { components.forEach(component => { allIds.add(component.id); addIds(component.children); }); }; addIds(pageState.components); setExpandedItems(allIds); }; const collapseAll = () => { setExpandedItems(new Set()); }; const handleExport = () => { const data = exportPage(); const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = "page-structure.json"; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }; const handleImport = () => { const input = document.createElement("input"); input.type = "file"; input.accept = ".json"; input.onchange = e => { const file = e.target.files?.[0]; if (file) { const reader = new FileReader(); reader.onload = e => { try { const data = JSON.parse(e.target?.result); importPage(data); } catch (error) { alert("Error importing page structure: Invalid JSON file"); } }; reader.readAsText(file); } }; input.click(); }; // Filter components based on search const filteredComponents = searchQuery ? pageState.components.filter(component => { const searchInComponent = comp => { const definition = componentLibrary.find(def => def.type === comp.type); const name = definition?.name || comp.type; const content = comp.props.content || ""; const matches = name.toLowerCase().includes(searchQuery.toLowerCase()) || content.toLowerCase().includes(searchQuery.toLowerCase()) || comp.type.toLowerCase().includes(searchQuery.toLowerCase()); return matches || comp.children.some(searchInComponent); }; return searchInComponent(component); }) : pageState.components; if (collapsed) { return jsx("div", { className: cn("w-12", className), children: jsx(GlassCore, { className: "glass-h-full glass-contrast-guard", children: jsx("button", { onClick: onToggleCollapse, className: 'glass-flex glass-items-center glass-justify-center glass-w-full h-12 glass-text-secondary hover:glass-text-secondary transition-colors glass-focus glass-touch-target glass-focus glass-touch-target glass-contrast-guard', title: "Expand Page Structure", children: jsx("div", { className: "glass-text-lg", children: "\uD83C\uDF33" }) }) }) }); } return jsx("div", { className: cn("w-80 h-full flex flex-col", className), children: jsxs(GlassCore, { className: "glass-h-full glass-flex glass-flex-col glass-contrast-guard", children: [jsxs("div", { className: "glass-flex glass-items-center glass-justify-between glass-p-4 glass-border-b glass-border-subtle", children: [jsx("h2", { className: 'glass-text-lg font-semibold glass-text-secondary', children: "Structure" }), jsx("button", { onClick: onToggleCollapse, className: 'glass-p-2 glass-text-secondary hover:glass-text-secondary transition-colors glass-focus glass-touch-target glass-focus glass-touch-target glass-contrast-guard', title: "Collapse Structure", children: "\u25C0" })] }), jsxs("div", { className: 'glass-p-4 glass-border-b glass-border-subtle space-y-3', children: [jsxs("div", { className: 'relative', children: [jsx("input", { type: "text", placeholder: "Search components...", value: searchQuery, onChange: e => setSearchQuery(e.target.value), className: 'glass-w-full glass-px-3 glass-py-2 pl-10 glass-text-sm glass-border glass-border-subtle glass-radius-lg focus:outline-none focus:ring-2 focus:ring-blue-500' }), jsx("div", { className: 'absolute left-3 glass-top-1/2 transform -translate-y-1/2 glass-text-secondary', children: "\uD83D\uDD0D" }), searchQuery && jsx("button", { onClick: () => setSearchQuery(""), className: 'absolute right-3 glass-top-1/2 transform -translate-y-1/2 glass-text-secondary hover:glass-text-secondary glass-focus glass-touch-target', children: "\u2715" })] }), jsxs("div", { className: "glass-flex glass-items-center glass-justify-between glass-text-xs", children: [jsxs("div", { className: "glass-flex glass-items-center glass-gap-2", children: [jsx("button", { onClick: expandAll, className: 'text-primary hover:text-primary glass-focus glass-touch-target glass-focus glass-touch-target glass-contrast-guard', children: "Expand All" }), jsx("span", { className: 'text-gray-300', children: "|" }), jsx("button", { onClick: collapseAll, className: 'text-primary hover:text-primary glass-focus glass-touch-target glass-focus glass-touch-target glass-contrast-guard', children: "Collapse All" })] }), jsxs("div", { className: "glass-flex glass-items-center glass-gap-2", children: [jsx("button", { onClick: handleExport, className: 'text-primary hover:text-primary glass-focus glass-touch-target glass-focus glass-touch-target glass-contrast-guard', title: "Export Structure", children: "\uD83D\uDCBE" }), jsx("button", { onClick: handleImport, className: 'text-primary hover:text-primary glass-focus glass-touch-target glass-focus glass-touch-target glass-contrast-guard', title: "Import Structure", children: "\uD83D\uDCC1" }), jsx("button", { onClick: clearPage, className: 'text-primary hover:text-primary glass-focus glass-touch-target glass-focus glass-touch-target glass-contrast-guard', title: "Clear All", children: "\uD83D\uDDD1\uFE0F" })] })] })] }), jsx("div", { className: 'glass-flex-1 overflow-y-auto glass-p-2', children: filteredComponents.length === 0 ? jsx("div", { className: "glass-flex glass-items-center glass-justify-center glass-h-full", children: jsxs("div", { className: 'text-center', children: [jsx("div", { className: 'glass-text-4xl mb-4', children: "\uD83C\uDF33" }), jsx("h3", { className: 'glass-text-lg font-medium glass-text-secondary mb-2', children: searchQuery ? "No matches found" : "Empty Page" }), jsx("p", { className: "glass-text-secondary glass-text-sm", children: searchQuery ? "Try a different search term" : "Start by adding components to your page" })] }) }) : jsx("div", { className: 'space-y-1', children: filteredComponents.filter(component => !component.parent) // Only show root components .map(component => jsx(TreeItem, { component: component, level: 0, isSelected: selectedComponent?.id === component.id, onSelect: selectComponent, onToggleExpand: toggleExpanded, expandedItems: expandedItems, componentDefinition: componentLibrary.find(def => def.type === component.type) }, component.id)) }) }), jsx("div", { className: "glass-p-4 glass-surface-subtle glass-border-t glass-border-subtle", children: jsxs("div", { className: 'glass-text-xs glass-text-secondary space-y-1', children: [jsxs("div", { className: "glass-flex glass-justify-between", children: [jsx("span", { children: "Total Components:" }), jsx("span", { children: pageState.components.length })] }), jsxs("div", { className: "glass-flex glass-justify-between", children: [jsx("span", { children: "Root Components:" }), jsx("span", { children: pageState.components.filter(c => !c.parent).length })] }), jsxs("div", { className: "glass-flex glass-justify-between", children: [jsx("span", { children: "Selected:" }), jsx("span", { children: selectedComponent ? selectedComponent.type : "None" })] })] }) })] }) }); }; export { GlassPageStructure }; //# sourceMappingURL=GlassPageStructure.js.map