mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-01-02 22:32:27 +08:00
feat: add warning dialog for theme and UI style changes (#248)
## Summary - Auto-saves diagram to localStorage before theme or UI style changes to prevent data loss - Extracts inline handler to `handleDrawioUiChange` for cleaner code - Renames `toggleDarkMode` to `handleDarkModeChange` for consistency ## Problem Changing themes (dark/light) or draw.io UI styles (min/sketch) causes the DrawIoEmbed component to remount, losing all unsaved edits without warning. ## Solution Added `saveDiagramToStorage()` function that exports the current diagram and saves it to localStorage before any theme/UI change. The existing restore mechanism then loads it back after remount. ## Related Issues Fixes #243
This commit is contained in:
33
app/page.tsx
33
app/page.tsx
@@ -15,8 +15,13 @@ const drawioBaseUrl =
|
|||||||
process.env.NEXT_PUBLIC_DRAWIO_BASE_URL || "https://embed.diagrams.net"
|
process.env.NEXT_PUBLIC_DRAWIO_BASE_URL || "https://embed.diagrams.net"
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const { drawioRef, handleDiagramExport, onDrawioLoad, resetDrawioReady } =
|
const {
|
||||||
useDiagram()
|
drawioRef,
|
||||||
|
handleDiagramExport,
|
||||||
|
onDrawioLoad,
|
||||||
|
resetDrawioReady,
|
||||||
|
saveDiagramToStorage,
|
||||||
|
} = useDiagram()
|
||||||
const [isMobile, setIsMobile] = useState(false)
|
const [isMobile, setIsMobile] = useState(false)
|
||||||
const [isChatVisible, setIsChatVisible] = useState(true)
|
const [isChatVisible, setIsChatVisible] = useState(true)
|
||||||
const [drawioUi, setDrawioUi] = useState<"min" | "sketch">("min")
|
const [drawioUi, setDrawioUi] = useState<"min" | "sketch">("min")
|
||||||
@@ -35,12 +40,10 @@ export default function Home() {
|
|||||||
|
|
||||||
const savedDarkMode = localStorage.getItem("next-ai-draw-io-dark-mode")
|
const savedDarkMode = localStorage.getItem("next-ai-draw-io-dark-mode")
|
||||||
if (savedDarkMode !== null) {
|
if (savedDarkMode !== null) {
|
||||||
// Use saved preference
|
|
||||||
const isDark = savedDarkMode === "true"
|
const isDark = savedDarkMode === "true"
|
||||||
setDarkMode(isDark)
|
setDarkMode(isDark)
|
||||||
document.documentElement.classList.toggle("dark", isDark)
|
document.documentElement.classList.toggle("dark", isDark)
|
||||||
} else {
|
} else {
|
||||||
// First visit: match browser preference
|
|
||||||
const prefersDark = window.matchMedia(
|
const prefersDark = window.matchMedia(
|
||||||
"(prefers-color-scheme: dark)",
|
"(prefers-color-scheme: dark)",
|
||||||
).matches
|
).matches
|
||||||
@@ -58,12 +61,20 @@ export default function Home() {
|
|||||||
setIsLoaded(true)
|
setIsLoaded(true)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const toggleDarkMode = () => {
|
const handleDarkModeChange = async () => {
|
||||||
|
await saveDiagramToStorage()
|
||||||
const newValue = !darkMode
|
const newValue = !darkMode
|
||||||
setDarkMode(newValue)
|
setDarkMode(newValue)
|
||||||
localStorage.setItem("next-ai-draw-io-dark-mode", String(newValue))
|
localStorage.setItem("next-ai-draw-io-dark-mode", String(newValue))
|
||||||
document.documentElement.classList.toggle("dark", newValue)
|
document.documentElement.classList.toggle("dark", newValue)
|
||||||
// Reset so onDrawioLoad fires again after remount
|
resetDrawioReady()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDrawioUiChange = async () => {
|
||||||
|
await saveDiagramToStorage()
|
||||||
|
const newUi = drawioUi === "min" ? "sketch" : "min"
|
||||||
|
localStorage.setItem("drawio-theme", newUi)
|
||||||
|
setDrawioUi(newUi)
|
||||||
resetDrawioReady()
|
resetDrawioReady()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,15 +193,9 @@ export default function Home() {
|
|||||||
isVisible={isChatVisible}
|
isVisible={isChatVisible}
|
||||||
onToggleVisibility={toggleChatPanel}
|
onToggleVisibility={toggleChatPanel}
|
||||||
drawioUi={drawioUi}
|
drawioUi={drawioUi}
|
||||||
onToggleDrawioUi={() => {
|
onToggleDrawioUi={handleDrawioUiChange}
|
||||||
const newUi =
|
|
||||||
drawioUi === "min" ? "sketch" : "min"
|
|
||||||
localStorage.setItem("drawio-theme", newUi)
|
|
||||||
setDrawioUi(newUi)
|
|
||||||
resetDrawioReady()
|
|
||||||
}}
|
|
||||||
darkMode={darkMode}
|
darkMode={darkMode}
|
||||||
onToggleDarkMode={toggleDarkMode}
|
onToggleDarkMode={handleDarkModeChange}
|
||||||
isMobile={isMobile}
|
isMobile={isMobile}
|
||||||
onCloseProtectionChange={setCloseProtection}
|
onCloseProtectionChange={setCloseProtection}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ interface DiagramContextType {
|
|||||||
format: ExportFormat,
|
format: ExportFormat,
|
||||||
sessionId?: string,
|
sessionId?: string,
|
||||||
) => void
|
) => void
|
||||||
|
saveDiagramToStorage: () => Promise<void>
|
||||||
isDrawioReady: boolean
|
isDrawioReady: boolean
|
||||||
onDrawioLoad: () => void
|
onDrawioLoad: () => void
|
||||||
resetDrawioReady: () => void
|
resetDrawioReady: () => void
|
||||||
@@ -82,6 +83,30 @@ export function DiagramProvider({ children }: { children: React.ReactNode }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save current diagram to localStorage (used before theme/UI changes)
|
||||||
|
const saveDiagramToStorage = async (): Promise<void> => {
|
||||||
|
if (!drawioRef.current) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
const currentXml = await Promise.race([
|
||||||
|
new Promise<string>((resolve) => {
|
||||||
|
resolverRef.current = resolve
|
||||||
|
drawioRef.current?.exportDiagram({ format: "xmlsvg" })
|
||||||
|
}),
|
||||||
|
new Promise<string>((_, reject) =>
|
||||||
|
setTimeout(() => reject(new Error("Export timeout")), 2000),
|
||||||
|
),
|
||||||
|
])
|
||||||
|
|
||||||
|
// Only save if diagram has meaningful content (not empty template)
|
||||||
|
if (currentXml && currentXml.length > 300) {
|
||||||
|
localStorage.setItem(STORAGE_DIAGRAM_XML_KEY, currentXml)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to save diagram to storage:", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const loadDiagram = (
|
const loadDiagram = (
|
||||||
chart: string,
|
chart: string,
|
||||||
skipValidation?: boolean,
|
skipValidation?: boolean,
|
||||||
@@ -280,6 +305,7 @@ export function DiagramProvider({ children }: { children: React.ReactNode }) {
|
|||||||
handleDiagramExport,
|
handleDiagramExport,
|
||||||
clearDiagram,
|
clearDiagram,
|
||||||
saveDiagramToFile,
|
saveDiagramToFile,
|
||||||
|
saveDiagramToStorage,
|
||||||
isDrawioReady,
|
isDrawioReady,
|
||||||
onDrawioLoad,
|
onDrawioLoad,
|
||||||
resetDrawioReady,
|
resetDrawioReady,
|
||||||
|
|||||||
Reference in New Issue
Block a user