aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
302 lines (299 loc) • 12.2 kB
JavaScript
'use client';
import { jsx, jsxs } from 'react/jsx-runtime';
import { useState, useEffect, useCallback } from 'react';
import { cn } from '../../lib/utilsComprehensive.js';
import { DragDropProvider, useDragDrop } from './GlassDragDropProvider.js';
import { GlassComponentPalette } from './GlassComponentPalette.js';
import { GlassCanvas } from './GlassCanvas.js';
import { GlassPropertyPanel } from './GlassPropertyPanel.js';
import { GlassPageStructure } from './GlassPageStructure.js';
const Toolbar = () => {
const {
pageState,
undo,
redo,
canUndo,
canRedo,
togglePreviewMode,
setActiveBreakpoint,
toggleGrid,
toggleSnapToGrid,
clearPage,
exportPage,
importPage
} = useDragDrop();
const handleSave = useCallback(() => {
const data = exportPage();
localStorage.setItem("glass-page-builder-autosave", JSON.stringify(data));
// You could also call an onSave prop here
}, [exportPage]);
const handleLoad = useCallback(() => {
const saved = localStorage.getItem("glass-page-builder-autosave");
if (saved) {
try {
const data = JSON.parse(saved);
importPage(data);
} catch (error) {
console.error("Error loading saved data:", error);
}
}
}, [importPage]);
// Auto-save every 30 seconds
useEffect(() => {
const interval = setInterval(() => {
if (pageState.components.length > 0) {
handleSave();
}
}, 30000);
return () => clearInterval(interval);
}, [pageState.components.length, handleSave]);
return jsxs("div", {
"data-glass-component": true,
className: "glass-flex glass-items-center glass-justify-between glass-p-4 glass-surface-subtle glass-border-b glass-border-subtle",
children: [jsxs("div", {
className: "glass-flex glass-items-center glass-gap-4",
children: [jsxs("div", {
className: "glass-flex glass-items-center glass-gap-2",
children: [jsx("h1", {
className: 'glass-text-xl font-bold glass-text-secondary',
children: "Page Builder"
}), jsx("span", {
className: "glass-text-xs glass-text-secondary glass-surface-subtle glass-px-2 glass-py-1 glass-radius",
children: "v1.0"
})]
}), jsx("div", {
className: 'w-px h-6 glass-surface-subtle'
}), jsxs("div", {
className: "glass-flex glass-items-center glass-gap-2",
children: [jsxs("button", {
onClick: clearPage,
className: 'glass-flex glass-items-center glass-gap-2 glass-px-3 glass-py-2 glass-text-sm glass-text-secondary hover:glass-surface-subtle glass-radius-md transition-colors glass-focus glass-touch-target glass-contrast-guard glass-focus glass-touch-target glass-contrast-guard',
children: [jsx("span", {
children: "\uD83D\uDDD1\uFE0F"
}), "New"]
}), jsxs("button", {
onClick: handleSave,
className: 'glass-flex glass-items-center glass-gap-2 glass-px-3 glass-py-2 glass-text-sm glass-text-secondary hover:glass-surface-subtle glass-radius-md transition-colors glass-focus glass-touch-target glass-contrast-guard glass-focus glass-touch-target glass-contrast-guard',
children: [jsx("span", {
children: "\uD83D\uDCBE"
}), "Save"]
}), jsxs("button", {
onClick: handleLoad,
className: 'glass-flex glass-items-center glass-gap-2 glass-px-3 glass-py-2 glass-text-sm glass-text-secondary hover:glass-surface-subtle glass-radius-md transition-colors glass-focus glass-touch-target glass-contrast-guard glass-focus glass-touch-target glass-contrast-guard',
children: [jsx("span", {
children: "\uD83D\uDCC1"
}), "Load"]
})]
}), jsx("div", {
className: 'w-px h-6 glass-surface-subtle'
}), jsxs("div", {
className: "glass-flex glass-items-center glass-gap-1",
children: [jsx("button", {
onClick: undo,
disabled: !canUndo(),
className: cn("p-2 rounded-md transition-colors", canUndo() ? "text-gray-700 hover:bg-gray-100" : "text-gray-400 cursor-not-allowed"),
title: "Undo (Ctrl+Z)",
children: "\u21B6"
}), jsx("button", {
onClick: redo,
disabled: !canRedo(),
className: cn("p-2 rounded-md transition-colors", canRedo() ? "text-gray-700 hover:bg-gray-100" : "text-gray-400 cursor-not-allowed"),
title: "Redo (Ctrl+Y)",
children: "\u21B7"
})]
})]
}), jsx("div", {
className: "glass-flex glass-items-center glass-gap-2 glass-surface-subtle glass-radius-lg glass-p-1",
children: [{
key: "desktop",
icon: "🖥️",
label: "Desktop"
}, {
key: "tablet",
icon: "📱",
label: "Tablet"
}, {
key: "mobile",
icon: "📱",
label: "Mobile"
}].map(breakpoint => jsxs("button", {
onClick: () => setActiveBreakpoint(breakpoint.key),
className: cn("flex items-center gap-2 px-3 py-2 text-sm rounded-md transition-colors", pageState.activeBreakpoint === breakpoint.key ? "bg-white text-gray-900 shadow-sm" : "text-gray-600 hover:text-gray-900"),
title: breakpoint.label,
children: [jsx("span", {
children: breakpoint.icon
}), breakpoint.label]
}, breakpoint.key))
}), jsxs("div", {
className: "glass-flex glass-items-center glass-gap-4",
children: [jsxs("div", {
className: "glass-flex glass-items-center glass-gap-2",
children: [jsxs("button", {
onClick: toggleGrid,
className: cn("flex items-center gap-2 px-3 py-2 text-sm rounded-md transition-colors", pageState.showGrid ? "bg-blue-100 text-blue-700" : "text-gray-700 hover:bg-gray-100"),
children: [jsx("span", {
children: "\u229E"
}), "Grid"]
}), jsxs("button", {
onClick: toggleSnapToGrid,
className: cn("flex items-center gap-2 px-3 py-2 text-sm rounded-md transition-colors", pageState.snapToGrid ? "bg-blue-100 text-blue-700" : "text-gray-700 hover:bg-gray-100"),
children: [jsx("span", {
children: "\uD83E\uDDF2"
}), "Snap"]
})]
}), jsx("div", {
className: 'w-px h-6 glass-surface-subtle'
}), jsxs("div", {
className: "glass-flex glass-items-center glass-gap-2",
children: [jsxs("button", {
onClick: togglePreviewMode,
className: cn("flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-md transition-colors", pageState.previewMode ? "bg-green-600 text-white hover:bg-green-700" : "bg-gray-100 text-gray-700 hover:bg-gray-200"),
children: [jsx("span", {
children: pageState.previewMode ? "👁️" : "✏️"
}), pageState.previewMode ? "Exit Preview" : "Preview"]
}), jsxs("button", {
onClick: () => {
const data = exportPage();
console.log("Publishing:", data);
alert("Page published! (Demo - check console for data)");
},
className: 'glass-flex glass-items-center glass-gap-2 glass-px-4 glass-py-2 glass-text-sm font-medium glass-surface-blue text-primary hover:glass-surface-blue glass-radius-md transition-colors',
children: [jsx("span", {
children: "\uD83D\uDE80"
}), "Publish"]
})]
})]
})]
});
};
const PageBuilderCore = ({
className,
initialData,
onSave,
onPreview,
onPublish
}) => {
const {
importPage
} = useDragDrop();
const [leftPanelCollapsed, setLeftPanelCollapsed] = useState(false);
const [rightPanelCollapsed, setRightPanelCollapsed] = useState(false);
const [activeLeftPanel, setActiveLeftPanel] = useState("components");
// Load initial data
useEffect(() => {
if (initialData) {
importPage(initialData);
} else {
// Try to load from localStorage
const saved = localStorage.getItem("glass-page-builder-autosave");
if (saved) {
try {
const data = JSON.parse(saved);
importPage(data);
} catch (error) {
console.error("Error loading saved data:", error);
}
}
}
}, [initialData, importPage]);
// Keyboard shortcuts
useEffect(() => {
const handleKeyDown = e => {
if ((e.metaKey || e.ctrlKey) && !e.shiftKey && !e.altKey) {
switch (e.key) {
case "z":
e.preventDefault();
// undo() would be called here if we had access to it
break;
case "y":
e.preventDefault();
// redo() would be called here if we had access to it
break;
case "s":
e.preventDefault();
break;
case "p":
e.preventDefault();
break;
}
}
};
document.addEventListener("keydown", handleKeyDown);
return () => document.removeEventListener("keydown", handleKeyDown);
}, [onSave, onPreview]);
return jsxs("div", {
className: cn("h-screen flex flex-col overflow-hidden bg-gray-100", className),
children: [jsx(Toolbar, {}), jsxs("div", {
className: 'glass-flex-1 glass-flex overflow-hidden',
children: [jsxs("div", {
className: "glass-flex",
children: [!leftPanelCollapsed && jsxs("div", {
className: 'w-12 glass-surface-subtle glass-border-r glass-border-subtle glass-flex glass-flex-col',
children: [jsxs("button", {
onClick: () => setActiveLeftPanel("components"),
className: cn("flex flex-col items-center gap-1 p-3 text-xs transition-colors", activeLeftPanel === "components" ? "bg-blue-50 text-blue-600" : "text-gray-600 hover:bg-gray-50"),
title: "Components",
children: [jsx("span", {
className: "glass-text-lg",
children: "\uD83D\uDCE6"
}), "Comp"]
}), jsxs("button", {
onClick: () => setActiveLeftPanel("structure"),
className: cn("flex flex-col items-center gap-1 p-3 text-xs transition-colors", activeLeftPanel === "structure" ? "bg-blue-50 text-blue-600" : "text-gray-600 hover:bg-gray-50"),
title: "Structure",
children: [jsx("span", {
className: "glass-text-lg",
children: "\uD83C\uDF33"
}), "Tree"]
})]
}), jsx("div", {
className: "glass-border-r glass-border-subtle",
children: activeLeftPanel === "components" ? jsx(GlassComponentPalette, {
collapsed: leftPanelCollapsed,
onToggleCollapse: () => setLeftPanelCollapsed(!leftPanelCollapsed)
}) : jsx(GlassPageStructure, {
collapsed: leftPanelCollapsed,
onToggleCollapse: () => setLeftPanelCollapsed(!leftPanelCollapsed)
})
})]
}), jsx(GlassCanvas, {}), jsx("div", {
className: "glass-border-l glass-border-subtle",
children: jsx(GlassPropertyPanel, {
collapsed: rightPanelCollapsed,
onToggleCollapse: () => setRightPanelCollapsed(!rightPanelCollapsed)
})
})]
}), jsxs("div", {
className: 'h-6 glass-surface-primary text-gray-300 glass-text-xs glass-flex glass-items-center glass-justify-between glass-px-4',
children: [jsxs("div", {
className: "glass-flex glass-items-center glass-gap-4",
children: [jsx("span", {
children: "\uD83D\uDFE2 Ready"
}), jsx("span", {
children: "Auto-save: ON"
})]
}), jsxs("div", {
className: "glass-flex glass-items-center glass-gap-4",
children: [jsx("span", {
children: "Zoom: 100%"
}), jsxs("span", {
children: ["Grid: ", `${"OFF"}`]
})]
})]
})]
});
};
const GlassPageBuilder = ({
className,
...restProps
}) => {
return jsx(DragDropProvider, {
className: className,
children: jsx(PageBuilderCore, {
...restProps,
className: className
})
});
};
export { GlassPageBuilder };
//# sourceMappingURL=GlassPageBuilder.js.map