From 9a288057914caccf474189bea9a7a9088281923a Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Sun, 18 Jan 2026 01:26:34 +0000 Subject: [PATCH] Extract dialog state hooks --- .../molecules/ComponentBindingDialog.tsx | 19 ++--- .../molecules/DataSourceEditorDialog.tsx | 54 ++++--------- src/hooks/use-component-binding-dialog.ts | 39 ++++++++++ src/hooks/use-data-source-editor.ts | 77 +++++++++++++++++++ 4 files changed, 138 insertions(+), 51 deletions(-) create mode 100644 src/hooks/use-component-binding-dialog.ts create mode 100644 src/hooks/use-data-source-editor.ts diff --git a/src/components/molecules/ComponentBindingDialog.tsx b/src/components/molecules/ComponentBindingDialog.tsx index 7b8fb9e..95ac768 100644 --- a/src/components/molecules/ComponentBindingDialog.tsx +++ b/src/components/molecules/ComponentBindingDialog.tsx @@ -1,10 +1,10 @@ -import { useState } from 'react' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { BindingEditor } from '@/components/molecules/BindingEditor' import { DataSource, UIComponent } from '@/types/json-ui' import { Link } from '@phosphor-icons/react' +import { useComponentBindingDialog } from '@/hooks/use-component-binding-dialog' interface ComponentBindingDialogProps { open: boolean @@ -21,18 +21,11 @@ export function ComponentBindingDialog({ onOpenChange, onSave, }: ComponentBindingDialogProps) { - const [editingComponent, setEditingComponent] = useState(component) - - const handleSave = () => { - if (!editingComponent) return - onSave(editingComponent) - onOpenChange(false) - } - - const updateBindings = (bindings: Record) => { - if (!editingComponent) return - setEditingComponent({ ...editingComponent, bindings }) - } + const { editingComponent, handleSave, updateBindings } = useComponentBindingDialog({ + component, + onSave, + onOpenChange, + }) if (!editingComponent) return null diff --git a/src/components/molecules/DataSourceEditorDialog.tsx b/src/components/molecules/DataSourceEditorDialog.tsx index 5b13f15..cce4a0c 100644 --- a/src/components/molecules/DataSourceEditorDialog.tsx +++ b/src/components/molecules/DataSourceEditorDialog.tsx @@ -1,4 +1,3 @@ -import { useEffect, useState } from 'react' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' import { DataSource } from '@/types/json-ui' @@ -8,6 +7,7 @@ import { KvSourceFields } from '@/components/molecules/data-source-editor/KvSour import { StaticSourceFields } from '@/components/molecules/data-source-editor/StaticSourceFields' import { ComputedSourceFields } from '@/components/molecules/data-source-editor/ComputedSourceFields' import dataSourceEditorCopy from '@/data/data-source-editor-dialog.json' +import { useDataSourceEditor } from '@/hooks/use-data-source-editor' interface DataSourceEditorDialogProps { open: boolean @@ -24,46 +24,24 @@ export function DataSourceEditorDialog({ onOpenChange, onSave, }: DataSourceEditorDialogProps) { - const [editingSource, setEditingSource] = useState(dataSource) - - useEffect(() => { - setEditingSource(dataSource) - }, [dataSource]) - - const handleSave = () => { - if (!editingSource) return - onSave(editingSource) - onOpenChange(false) - } - - const updateField = (field: K, value: DataSource[K]) => { - if (!editingSource) return - setEditingSource({ ...editingSource, [field]: value }) - } - - const addDependency = (depId: string) => { - if (!editingSource || editingSource.type !== 'computed') return - const deps = editingSource.dependencies || [] - if (!deps.includes(depId)) { - updateField('dependencies', [...deps, depId]) - } - } - - const removeDependency = (depId: string) => { - if (!editingSource || editingSource.type !== 'computed') return - const deps = editingSource.dependencies || [] - updateField('dependencies', deps.filter(d => d !== depId)) - } + const { + editingSource, + updateField, + addDependency, + removeDependency, + handleSave, + availableDeps, + selectedDeps, + unselectedDeps, + } = useDataSourceEditor({ + dataSource, + allDataSources, + onSave, + onOpenChange, + }) if (!editingSource) return null - const availableDeps = allDataSources.filter( - ds => ds.id !== editingSource.id && ds.type !== 'computed', - ) - - const selectedDeps = editingSource.dependencies || [] - const unselectedDeps = availableDeps.filter(ds => !selectedDeps.includes(ds.id)) - return ( diff --git a/src/hooks/use-component-binding-dialog.ts b/src/hooks/use-component-binding-dialog.ts new file mode 100644 index 0000000..f76752a --- /dev/null +++ b/src/hooks/use-component-binding-dialog.ts @@ -0,0 +1,39 @@ +import { useCallback, useEffect, useState } from 'react' +import { UIComponent } from '@/types/json-ui' + +interface UseComponentBindingDialogParams { + component: UIComponent | null + onSave: (component: UIComponent) => void + onOpenChange: (open: boolean) => void +} + +export function useComponentBindingDialog({ + component, + onSave, + onOpenChange, +}: UseComponentBindingDialogParams) { + const [editingComponent, setEditingComponent] = useState(component) + + useEffect(() => { + setEditingComponent(component) + }, [component]) + + const updateBindings = useCallback((bindings: Record) => { + setEditingComponent((prev) => { + if (!prev) return prev + return { ...prev, bindings } + }) + }, []) + + const handleSave = useCallback(() => { + if (!editingComponent) return + onSave(editingComponent) + onOpenChange(false) + }, [editingComponent, onOpenChange, onSave]) + + return { + editingComponent, + updateBindings, + handleSave, + } +} diff --git a/src/hooks/use-data-source-editor.ts b/src/hooks/use-data-source-editor.ts new file mode 100644 index 0000000..4659ded --- /dev/null +++ b/src/hooks/use-data-source-editor.ts @@ -0,0 +1,77 @@ +import { useCallback, useEffect, useMemo, useState } from 'react' +import { DataSource } from '@/types/json-ui' + +interface UseDataSourceEditorParams { + dataSource: DataSource | null + allDataSources: DataSource[] + onSave: (dataSource: DataSource) => void + onOpenChange: (open: boolean) => void +} + +export function useDataSourceEditor({ + dataSource, + allDataSources, + onSave, + onOpenChange, +}: UseDataSourceEditorParams) { + const [editingSource, setEditingSource] = useState(dataSource) + + useEffect(() => { + setEditingSource(dataSource) + }, [dataSource]) + + const updateField = useCallback((field: K, value: DataSource[K]) => { + setEditingSource((prev) => { + if (!prev) return prev + return { ...prev, [field]: value } + }) + }, []) + + const addDependency = useCallback((depId: string) => { + setEditingSource((prev) => { + if (!prev || prev.type !== 'computed') return prev + const deps = prev.dependencies || [] + if (deps.includes(depId)) return prev + return { ...prev, dependencies: [...deps, depId] } + }) + }, []) + + const removeDependency = useCallback((depId: string) => { + setEditingSource((prev) => { + if (!prev || prev.type !== 'computed') return prev + const deps = prev.dependencies || [] + return { ...prev, dependencies: deps.filter((id) => id !== depId) } + }) + }, []) + + const handleSave = useCallback(() => { + if (!editingSource) return + onSave(editingSource) + onOpenChange(false) + }, [editingSource, onOpenChange, onSave]) + + const availableDeps = useMemo(() => { + if (!editingSource) return [] + return allDataSources.filter( + (ds) => ds.id !== editingSource.id && ds.type !== 'computed', + ) + }, [allDataSources, editingSource]) + + const selectedDeps = useMemo(() => editingSource?.dependencies || [], [editingSource]) + + const unselectedDeps = useMemo(() => { + if (!editingSource) return [] + return availableDeps.filter((ds) => !selectedDeps.includes(ds.id)) + }, [availableDeps, editingSource, selectedDeps]) + + return { + editingSource, + updateField, + addDependency, + removeDependency, + handleSave, + availableDeps, + selectedDeps, + unselectedDeps, + } +}