feat: integrate Radix UI toggle components and replace custom toast implementation with Sonner
- Added @radix-ui/react-toggle and @radix-ui/react-toggle-group to package dependencies. - Implemented ToggleGroup and Toggle components for improved UI interaction. - Replaced custom toast notifications with Sonner for better user feedback. - Updated CanvasPage and ProjectsPage to utilize new toast notifications. - Removed obsolete toast context and provider. - Enhanced ProjectsPage with a toggle view for project display (cards/table).
This commit is contained in:
@@ -57,6 +57,7 @@ import {
|
||||
isConnectionAllowed,
|
||||
} from '@/lib/nodeRegistry'
|
||||
import type { AppNode, AppEdge } from '@/lib/nodeTypes'
|
||||
import { toast } from 'sonner'
|
||||
import {
|
||||
loadGraphFromStorage,
|
||||
saveGraphToStorage,
|
||||
@@ -116,8 +117,6 @@ function getExampleGraph(): { nodes: AppNode[]; edges: AppEdge[] } {
|
||||
}
|
||||
}
|
||||
|
||||
export type CanvasMessage = { type: 'success' | 'error'; text: string }
|
||||
|
||||
function FlowFitViewOnLoad() {
|
||||
const nodesInitialized = useNodesInitialized()
|
||||
const { fitView } = useReactFlow()
|
||||
@@ -178,7 +177,6 @@ export function CanvasPage({ projectId }: CanvasPageProps) {
|
||||
const [rfInstance, setRfInstance] = React.useState<unknown>(null)
|
||||
const [renamingNodeId, setRenamingNodeId] = React.useState<string | null>(null)
|
||||
const [connectionFrom, setConnectionFrom] = React.useState<{ nodeId: string; sourceHandle?: string } | null>(null)
|
||||
const [canvasMessage, setCanvasMessage] = React.useState<CanvasMessage | null>(null)
|
||||
const wrapperRef = useRef<HTMLDivElement | null>(null)
|
||||
const lastClickRef = useRef<{ clientX: number; clientY: number } | null>(null)
|
||||
const flowActionsRef = useRef<{ pasteAtViewportCenter: () => void; fitView: () => void } | null>(null)
|
||||
@@ -279,12 +277,6 @@ export function CanvasPage({ projectId }: CanvasPageProps) {
|
||||
|
||||
const onInit = useCallback((instance: unknown) => setRfInstance(instance), [])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!canvasMessage) return
|
||||
const t = setTimeout(() => setCanvasMessage(null), 3000)
|
||||
return () => clearTimeout(t)
|
||||
}, [canvasMessage])
|
||||
|
||||
const onNodeDragStart = useCallback(() => saveForDragEnd(), [saveForDragEnd])
|
||||
const onNodeDragStop = useCallback(() => commitDragEnd(), [commitDragEnd])
|
||||
|
||||
@@ -297,7 +289,7 @@ export function CanvasPage({ projectId }: CanvasPageProps) {
|
||||
a.download = `project${PROJECT_FILE_EXT}`
|
||||
a.click()
|
||||
URL.revokeObjectURL(url)
|
||||
setCanvasMessage({ type: 'success', text: 'Project exported' })
|
||||
toast.success('Project exported')
|
||||
}, [nodes, edges])
|
||||
|
||||
const handleImportProject = useCallback(() => importInputRef.current?.click(), [])
|
||||
@@ -305,7 +297,7 @@ export function CanvasPage({ projectId }: CanvasPageProps) {
|
||||
const handleLoadExample = useCallback(() => {
|
||||
const { nodes: exampleNodes, edges: exampleEdges } = getExampleGraph()
|
||||
setStateImmediate({ nodes: exampleNodes, edges: exampleEdges })
|
||||
setCanvasMessage({ type: 'success', text: 'Example loaded' })
|
||||
toast.success('Example loaded')
|
||||
}, [setStateImmediate])
|
||||
|
||||
const onImportFileChange = useCallback(
|
||||
@@ -319,17 +311,17 @@ export function CanvasPage({ projectId }: CanvasPageProps) {
|
||||
const text = reader.result as string
|
||||
const state = JSON.parse(text) as { version?: number; nodes?: unknown[]; edges?: unknown[] }
|
||||
if (!state || !Array.isArray(state.nodes) || !Array.isArray(state.edges)) {
|
||||
setCanvasMessage({ type: 'error', text: 'Invalid file: expected nodes and edges arrays' })
|
||||
toast.error('Invalid file: expected nodes and edges arrays')
|
||||
return
|
||||
}
|
||||
setStateImmediate({ nodes: state.nodes as AppNode[], edges: state.edges as AppEdge[] })
|
||||
setCanvasMessage(
|
||||
state.version != null && state.version > PROJECT_VERSION
|
||||
? { type: 'error', text: 'Project was created with a newer app version' }
|
||||
: { type: 'success', text: 'Project loaded' }
|
||||
)
|
||||
if (state.version != null && state.version > PROJECT_VERSION) {
|
||||
toast.error('Project was created with a newer app version')
|
||||
} else {
|
||||
toast.success('Project loaded')
|
||||
}
|
||||
} catch {
|
||||
setCanvasMessage({ type: 'error', text: 'Invalid file: not valid JSON' })
|
||||
toast.error('Invalid file: not valid JSON')
|
||||
}
|
||||
}
|
||||
reader.readAsText(file)
|
||||
@@ -517,17 +509,6 @@ export function CanvasPage({ projectId }: CanvasPageProps) {
|
||||
Backend API: {apiTodosCount >= 0 ? `${apiTodosCount} todos` : 'unavailable'}
|
||||
</div>
|
||||
)}
|
||||
{canvasMessage && (
|
||||
<div
|
||||
role="status"
|
||||
className={`shrink-0 px-3 py-2 text-sm ${canvasMessage.type === 'success'
|
||||
? 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-200'
|
||||
: 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-200'
|
||||
}`}
|
||||
>
|
||||
{canvasMessage.text}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex-1 min-h-0 relative flex flex-col">
|
||||
<div className="flex-1 min-h-0 flex flex-col">
|
||||
<FlowContext.Provider value={flowContextValue}>
|
||||
|
||||
Reference in New Issue
Block a user