UNPKG

aura-glass

Version:

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

715 lines (712 loc) 17.5 kB
'use client'; import { jsx } from 'react/jsx-runtime'; import { forwardRef, useState, useRef, useCallback, createContext, useContext } from 'react'; import { cn } from '../../lib/utilsComprehensive.js'; const DragDropContext = /*#__PURE__*/createContext(null); // Default component library const defaultComponentLibrary = [ // Layout Components { id: 'container', type: 'container', name: 'Container', icon: '📦', category: 'layout', defaultProps: { padding: '16px', margin: '0px', backgroundColor: 'transparent', borderRadius: '0px', maxWidth: '100%', display: 'block' }, editableProps: { padding: { type: 'text', label: 'Padding' }, margin: { type: 'text', label: 'Margin' }, backgroundColor: { type: 'color', label: 'Background Color' }, borderRadius: { type: 'text', label: 'Border Radius' }, maxWidth: { type: 'text', label: 'Max Width' }, display: { type: 'select', label: 'Display', options: ['block', 'flex', 'grid', 'inline-block'] } }, allowChildren: true }, { id: 'row', type: 'row', name: 'Row', icon: '↔️', category: 'layout', defaultProps: { gap: '16px', justifyContent: 'flex-start', alignItems: 'stretch', wrap: 'nowrap', padding: '0px' }, editableProps: { gap: { type: 'text', label: 'Gap' }, justifyContent: { type: 'select', label: 'Justify Content', options: ['flex-start', 'center', 'flex-end', 'space-between', 'space-around'] }, alignItems: { type: 'select', label: 'Align Items', options: ['stretch', 'flex-start', 'center', 'flex-end'] }, wrap: { type: 'select', label: 'Flex Wrap', options: ['nowrap', 'wrap', 'wrap-reverse'] }, padding: { type: 'text', label: 'Padding' } }, allowChildren: true, acceptsTypes: ['column', 'container', 'text', 'image', 'button'] }, { id: 'column', type: 'column', name: 'Column', icon: '↕️', category: 'layout', defaultProps: { flex: '1', padding: '16px', gap: '8px', alignItems: 'stretch' }, editableProps: { flex: { type: 'text', label: 'Flex' }, padding: { type: 'text', label: 'Padding' }, gap: { type: 'text', label: 'Gap' }, alignItems: { type: 'select', label: 'Align Items', options: ['stretch', 'flex-start', 'center', 'flex-end'] } }, allowChildren: true }, // Content Components { id: 'text', type: 'text', name: 'Text', icon: '📝', category: 'content', defaultProps: { content: 'Edit this text', fontSize: '16px', fontWeight: '400', color: '#333333', textAlign: 'left', lineHeight: '1.5', fontFamily: 'system-ui, -apple-system, sans-serif' }, editableProps: { content: { type: 'text', label: 'Content' }, fontSize: { type: 'text', label: 'Font Size' }, fontWeight: { type: 'select', label: 'Font Weight', options: ['100', '200', '300', '400', '500', '600', '700', '800', '900'] }, color: { type: 'color', label: 'Text Color' }, textAlign: { type: 'select', label: 'Text Align', options: ['left', 'center', 'right', 'justify'] }, lineHeight: { type: 'text', label: 'Line Height' }, fontFamily: { type: 'text', label: 'Font Family' } }, allowChildren: false }, { id: 'heading', type: 'heading', name: 'Heading', icon: '🏷️', category: 'content', defaultProps: { content: 'Heading Text', level: 'h2', fontSize: '32px', fontWeight: '600', color: 'var(--glass-gray-900)', textAlign: 'left', margin: '16px 0' }, editableProps: { content: { type: 'text', label: 'Content' }, level: { type: 'select', label: 'Heading Level', options: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] }, fontSize: { type: 'text', label: 'Font Size' }, fontWeight: { type: 'select', label: 'Font Weight', options: ['300', '400', '500', '600', '700', '800', '900'] }, color: { type: 'color', label: 'Text Color' }, textAlign: { type: 'select', label: 'Text Align', options: ['left', 'center', 'right'] }, margin: { type: 'text', label: 'Margin' } }, allowChildren: false }, // Media Components { id: 'image', type: 'image', name: 'Image', icon: '🖼️', category: 'media', defaultProps: { src: 'https://via.placeholder.com/400x300', alt: 'Placeholder image', width: '100%', height: 'auto', borderRadius: '0px', objectFit: 'cover' }, editableProps: { src: { type: 'image', label: 'Image Source' }, alt: { type: 'text', label: 'Alt Text' }, width: { type: 'text', label: 'Width' }, height: { type: 'text', label: 'Height' }, borderRadius: { type: 'text', label: 'Border Radius' }, objectFit: { type: 'select', label: 'Object Fit', options: ['cover', 'contain', 'fill', 'scale-down', 'none'] } }, allowChildren: false }, // Interactive Components { id: 'button', type: 'button', name: 'Button', icon: '🔘', category: 'interactive', defaultProps: { text: 'Click me', variant: 'primary', size: 'medium', disabled: false, href: '', onClick: 'alert("Button clicked!")' }, editableProps: { text: { type: 'text', label: 'Button Text' }, variant: { type: 'select', label: 'Variant', options: ['primary', 'secondary', 'outline', 'ghost'] }, size: { type: 'select', label: 'Size', options: ['small', 'medium', 'large'] }, disabled: { type: 'boolean', label: 'Disabled' }, href: { type: 'text', label: 'Link URL' }, onClick: { type: 'text', label: 'On Click (JavaScript)' } }, allowChildren: false }, // Advanced Components { id: 'card', type: 'card', name: 'Card', icon: '🃏', category: 'advanced', defaultProps: { padding: '24px', borderRadius: '12px', backgroundColor: 'var(--glass-white)', boxShadow: 'var(--glass-elev-2)', border: '1px solid var(--glass-gray-200)' }, editableProps: { padding: { type: 'text', label: 'Padding' }, borderRadius: { type: 'text', label: 'Border Radius' }, backgroundColor: { type: 'color', label: 'Background Color' }, boxShadow: { type: 'text', label: 'Box Shadow' }, border: { type: 'text', label: 'Border' } }, allowChildren: true }]; const DragDropProvider = /*#__PURE__*/forwardRef(({ children, className, ...props }, ref) => { const [dragDropState, setDragDropState] = useState({ isDragging: false, draggedType: null }); const [pageState, setPageState] = useState({ components: [], history: [[]], historyIndex: 0, previewMode: false, activeBreakpoint: 'desktop', showGrid: false, snapToGrid: true }); const componentCounter = useRef(0); const generateId = useCallback(() => { return `comp_${Date.now()}_${++componentCounter.current}`; }, []); const saveToHistory = useCallback(() => { setPageState(prev => { const newHistory = prev.history.slice(0, prev.historyIndex + 1); newHistory.push([...prev.components]); return { ...prev, history: newHistory.slice(-50), // Keep last 50 states historyIndex: newHistory.length - 1 }; }); }, []); const onDragStart = useCallback((item, type) => { setDragDropState({ isDragging: true, draggedItem: item, draggedType: type }); }, []); const onDragEnd = useCallback(() => { setDragDropState({ isDragging: false, draggedType: null }); }, []); const onDragOver = useCallback((targetId, position) => { setDragDropState(prev => ({ ...prev, dropTarget: targetId, dropPosition: position })); }, []); const onDrop = useCallback((targetId, position) => { if (!dragDropState.draggedItem || !dragDropState.draggedType) return; if (dragDropState.draggedType === 'component') { addComponent(dragDropState.draggedItem, targetId, position); } else if (dragDropState.draggedType === 'element') { const element = dragDropState.draggedItem; if (targetId && element.id !== targetId) { moveComponent(element.id, targetId, position || 'inside'); } } onDragEnd(); }, [dragDropState]); const addComponent = useCallback((definition, targetId, position) => { const newComponent = { id: generateId(), type: definition.type, props: { ...definition.defaultProps }, children: [], parent: targetId && position === 'inside' ? targetId : undefined, order: 0 }; setPageState(prev => { const components = [...prev.components]; if (targetId && position) { const targetIndex = components.findIndex(c => c.id === targetId); if (targetIndex !== -1) { switch (position) { case 'before': components.splice(targetIndex, 0, newComponent); break; case 'after': components.splice(targetIndex + 1, 0, newComponent); break; case 'inside': newComponent.parent = targetId; const target = components[targetIndex]; if (target) { target.children.push(newComponent); } break; } } } else { components.push(newComponent); } return { ...prev, components, selectedComponent: newComponent.id }; }); saveToHistory(); }, [generateId, saveToHistory]); const updateComponent = useCallback((id, props) => { setPageState(prev => { const updateInArray = components => { return components.map(component => { if (component.id === id) { return { ...component, props: { ...component.props, ...props } }; } if (component.children.length > 0) { return { ...component, children: updateInArray(component.children) }; } return component; }); }; return { ...prev, components: updateInArray(prev.components) }; }); }, []); const deleteComponent = useCallback(id => { setPageState(prev => { const removeFromArray = components => { return components.filter(component => { if (component.id === id) return false; component.children = removeFromArray(component.children); return true; }); }; return { ...prev, components: removeFromArray(prev.components), selectedComponent: prev.selectedComponent === id ? undefined : prev.selectedComponent }; }); saveToHistory(); }, [saveToHistory]); const duplicateComponent = useCallback(id => { setPageState(prev => { const findComponent = components => { for (const component of components) { if (component.id === id) return component; const found = findComponent(component.children); if (found) return found; } return null; }; const original = findComponent(prev.components); if (!original) return prev; const duplicateRecursive = comp => ({ ...comp, id: generateId(), children: comp.children.map(duplicateRecursive) }); const duplicate = duplicateRecursive(original); return { ...prev, components: [...prev.components, duplicate], selectedComponent: duplicate.id }; }); saveToHistory(); }, [generateId, saveToHistory]); const moveComponent = useCallback((id, targetId, position) => { // Implementation for moving components within the tree saveToHistory(); }, [saveToHistory]); const selectComponent = useCallback(id => { setPageState(prev => ({ ...prev, selectedComponent: id })); }, []); const getSelectedComponent = useCallback(() => { if (!pageState.selectedComponent) return undefined; const findInArray = components => { for (const component of components) { if (component.id === pageState.selectedComponent) return component; const found = findInArray(component.children); if (found) return found; } return undefined; }; return findInArray(pageState.components); }, [pageState.selectedComponent, pageState.components]); const getComponentById = useCallback(id => { const findInArray = components => { for (const component of components) { if (component.id === id) return component; const found = findInArray(component.children); if (found) return found; } return undefined; }; return findInArray(pageState.components); }, [pageState.components]); const undo = useCallback(() => { setPageState(prev => { if (prev.historyIndex > 0) { return { ...prev, components: prev.history[prev.historyIndex - 1], historyIndex: prev.historyIndex - 1 }; } return prev; }); }, []); const redo = useCallback(() => { setPageState(prev => { if (prev.historyIndex < prev.history.length - 1) { return { ...prev, components: prev.history[prev.historyIndex + 1], historyIndex: prev.historyIndex + 1 }; } return prev; }); }, []); const canUndo = useCallback(() => pageState.historyIndex > 0, [pageState.historyIndex]); const canRedo = useCallback(() => pageState.historyIndex < pageState.history.length - 1, [pageState.historyIndex, pageState.history.length]); const copyComponent = useCallback(id => { const component = getComponentById(id); if (component) { setPageState(prev => ({ ...prev, clipboardComponent: component })); } }, [getComponentById]); const pasteComponent = useCallback(targetId => { if (pageState.clipboardComponent) { const definition = { id: pageState.clipboardComponent.type, type: pageState.clipboardComponent.type, name: pageState.clipboardComponent.type, icon: '📋', category: 'content', defaultProps: pageState.clipboardComponent.props, editableProps: {} }; addComponent(definition, targetId); } }, [pageState.clipboardComponent, addComponent]); const clearPage = useCallback(() => { setPageState(prev => ({ ...prev, components: [], selectedComponent: undefined })); saveToHistory(); }, [saveToHistory]); const exportPage = useCallback(() => { return { components: pageState.components, timestamp: new Date().toISOString(), version: '1.0.0' }; }, [pageState.components]); const importPage = useCallback(data => { if (data.components) { setPageState(prev => ({ ...prev, components: data.components, selectedComponent: undefined })); saveToHistory(); } }, [saveToHistory]); const togglePreviewMode = useCallback(() => { setPageState(prev => ({ ...prev, previewMode: !prev.previewMode })); }, []); const setActiveBreakpoint = useCallback(breakpoint => { setPageState(prev => ({ ...prev, activeBreakpoint: breakpoint })); }, []); const toggleGrid = useCallback(() => { setPageState(prev => ({ ...prev, showGrid: !prev.showGrid })); }, []); const toggleSnapToGrid = useCallback(() => { setPageState(prev => ({ ...prev, snapToGrid: !prev.snapToGrid })); }, []); const value = { pageState, componentLibrary: defaultComponentLibrary, dragDropState, onDragStart, onDragEnd, onDragOver, onDrop, addComponent, updateComponent, deleteComponent, duplicateComponent, moveComponent, selectComponent, getSelectedComponent, getComponentById, undo, redo, canUndo, canRedo, saveToHistory, copyComponent, pasteComponent, clearPage, exportPage, importPage, togglePreviewMode, setActiveBreakpoint, toggleGrid, toggleSnapToGrid }; return jsx("div", { ref: ref, className: cn('glass-dragdrop-provider', className), "data-glass-component": true, ...props, children: jsx(DragDropContext.Provider, { value: value, children: children }) }); }); DragDropProvider.displayName = 'GlassDragDropProvider'; const useDragDrop = () => { const context = useContext(DragDropContext); if (!context) { throw new Error('useDragDrop must be used within a DragDropProvider'); } return context; }; export { DragDropProvider, DragDropProvider as GlassDragDropProvider, useDragDrop }; //# sourceMappingURL=GlassDragDropProvider.js.map