diff --git a/src/hooks/useCurrentComponents.ts b/src/hooks/useCurrentComponents.ts new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/hooks/useCurrentComponents.ts diff --git a/src/hooks/useCurrentScreen.ts b/src/hooks/useCurrentScreen.ts new file mode 100644 index 0000000..51289f3 --- /dev/null +++ b/src/hooks/useCurrentScreen.ts @@ -0,0 +1,71 @@ +import { useCallback, useRef, useState } from "react"; +import type { Screen } from "../types/screen"; +import type { Component } from "../types/component"; + +export interface CurrentScreenStatesAndActions { + states: { + curScreen: Screen, + curScreenIndex: number + curScreenComponents: Component[] + selectingComponent: Component + selectingComponentIndex: number + } + actions: { + changeCurScreen: (index: number) => void + setSelectComponentListener: (listener: SelectComponentListenerType) => void + resetSelectingComponent: () => void + activateSelectMode: () => void + } +} + +export type SelectComponentListenerType = (selected: Component | null) => void + +export function useCurrentScreen(screens: Screen[]): CurrentScreenStatesAndActions { + + const [curScreenIndex, setCurScreenIndex] = useState(0); + const [selectingComponentIndex, setSelectingComponentIndex] = useState(-1); + const [selectMode, setSelectMode] = useState(false); + const selectComponentCallbackRef = useRef(null); + + const curScreen = screens[curScreenIndex]; + const components = screens[curScreenIndex].components; + const selectingComponent = screens[curScreenIndex].components[selectingComponentIndex]; + + const changeCurScreen = (index: number) => { + if (index >= screens.length) return; + setCurScreenIndex(index); + } + + const setSelectComponentListener = useCallback((listener: SelectComponentListenerType) => { + selectComponentCallbackRef.current = listener; + }, []) + + const resetSelectingComponent = () => { + if (selectMode) { + setSelectMode(false); + } else { + setSelectingComponentIndex(-1); + } + } + + const activateSelectMode = () => { + setSelectMode(true); + } + + return { + states: { + curScreen, + curScreenIndex, + curScreenComponents: components, + selectingComponent, + selectingComponentIndex, + }, + actions: { + changeCurScreen, + setSelectComponentListener, + resetSelectingComponent, + activateSelectMode + } + } + +} \ No newline at end of file diff --git a/src/hooks/useProject.ts b/src/hooks/useProject.ts new file mode 100644 index 0000000..a57557d --- /dev/null +++ b/src/hooks/useProject.ts @@ -0,0 +1,256 @@ +import { useState } from "react"; +import type { Project } from "../types/project"; +import type { Screen } from "../types/screen"; +import type { UIData, Component, GeneralData, ButtonGeneralData, TextGeneralData, TextFieldGeneralData, TableGeneralData } from "../types/component"; +import { ComponentType } from "../types/component"; +import type { Table } from "../types/table"; + +function getInitialGeneralData(type: ComponentType): GeneralData { + switch (type) { + case ComponentType.Text: + return { text: "text" } + case ComponentType.Button: + return { text: "button" } + case ComponentType.TextField: + return { text: "text field" } + case ComponentType.Table: + return { referenceTableName: "", columns: [] } + } +} + +export interface ProjectStates { + project: Project, + screens: Screen[], + tables: Table[] +} + +export interface ProjectActions { + addScreen: (name: string) => void + updateScreen: (screen: Screen) => void + deleteScreen: (screenId: string) => void + addComponent: (screenId: string, type: ComponentType) => void + addComponents: (screenId: string, components: Component[]) => void + updateComponent: (screenId: string, component: Component) => void + moveComponent: (screenId: string, componentId: string, uiData: UIData) => void + deleteComponent: (screenIndex: number, componentIndex: number) => void + addTable: (table: Table) => void + updateTable: (table: Table) => void + deleteTable: (tableId: string) => void +} + +export interface ProjectStatesAndActions { + projectStates: ProjectStates + projectActions: ProjectActions +} + +export function useProject(initProject: Project = { + id: "", + title: "", + detail: "", + createdTime: "", + updatedTime: "", + screens: [ + { + id: "screen1", + components: [], + index: 0, + name: "screen1", + projectId: "" + } + ], + tables: [] +}): ProjectStatesAndActions { + + const [project, setProject] = useState(initProject); + const screens = project.screens; + const tables = project.tables; + + + const addScreen = (name: string) => { + const nextIndex = screens[screens.length - 1].index + 1; + const newScreenId = "screen" + nextIndex; + const newScreen: Screen = { + id: newScreenId, + components: [], + index: nextIndex, + name: name, + projectId: project.id + } + setProject(prev => ({ ...prev, screens: [...prev.screens, newScreen] })); + return newScreenId; + } + + const deleteScreen = (screenId: string) => { + setProject(prev => ({ ...prev, screens: prev.screens.filter(s => s.id !== screenId) })); + } + + const updateScreen = (screen: Screen) => { + setProject(prev => ({ ...prev, screens: prev.screens.map(s => s.id === screen.id ? screen : s) })); + } + + const addComponent = (screenId: string, type: ComponentType) => { + const screen = screens.find(s => s.id === screenId); + if (!screen) { + return; + } + const nextIndex = screen.components.length > 0 ? screen.components[screen.components.length - 1].index + 1 : 0; + const uiData: UIData = { + posX: 100, + posY: 100, + width: 75, + height: 25 + }; + const newId = screen.id + "Widget" + nextIndex; + let newComponent: Component; + switch (type) { + case ComponentType.Button: + newComponent = { + id: newId, + name: newId, + type: type, + index: nextIndex, + screenId: screen.id, + generalData: getInitialGeneralData(type) as ButtonGeneralData, + style: {}, + uiData: uiData, + } + break; + case ComponentType.Text: + newComponent = { + id: newId, + name: newId, + type: type, + index: nextIndex, + screenId: screen.id, + generalData: getInitialGeneralData(type) as TextGeneralData, + style: {}, + uiData: uiData, + } + break; + case ComponentType.TextField: + newComponent = { + id: newId, + name: newId, + type: type, + index: nextIndex, + screenId: screen.id, + generalData: getInitialGeneralData(type) as TextFieldGeneralData, + style: {}, + uiData: uiData, + } + break; + case ComponentType.Table: + newComponent = { + id: newId, + name: newId, + type: type, + index: nextIndex, + screenId: screen.id, + generalData: getInitialGeneralData(type) as TableGeneralData, + style: {}, + uiData: uiData, + } + break; + } + setProject(prev => ( + { + ...prev, + screens: prev.screens.map((s, i) => s.id === screenId ? { + ...prev.screens[i], + components: [...prev.screens[i].components, newComponent] + } : s) + } + )) + } + + const addComponents = (screenId: string, components: Component[]) => { + setProject(prev => ( + { + ...prev, + screens: prev.screens.map((s, i) => s.id === screenId ? { + ...prev.screens[i], + components: [...prev.screens[i].components, ...components] + } : s) + } + )) + } + + const moveComponent = (screenId: string, componentId: string, uiData: UIData) => { + setProject(prev => ( + { + ...prev, + screens: prev.screens.map((s, i) => s.id === screenId ? { + ...prev.screens[i], + components: prev.screens[i].components.map((c, i) => c.id === componentId ? { + ...prev.screens[i].components[i], + uiData: uiData + } : c) + } : s) + } + )) + } + + const updateComponent = (screenId: string, component: Component) => { + setProject(prev => ( + { + ...prev, + screens: prev.screens.map((s, i) => s.id === screenId ? { + ...prev.screens[i], + components: prev.screens[i].components.map(c => c.id === component.id ? component : c) + } : s) + } + )) + } + + const deleteComponent = (screenIndex: number, componentIndex: number) => { + setProject(prev => ( + { + ...prev, + screens: prev.screens.map((s, i) => i === screenIndex ? { + ...prev.screens[i], + components: prev.screens[i].components.filter((_, i) => i !== componentIndex) + } : s) + } + )) + } + + const addTable = (table: Table) => { + setProject(prev => ({ + ...prev, + tables: [...prev.tables, table] + })) + } + + const updateTable = (table: Table) => { + setProject(prev => ({ + ...prev, + tables: prev.tables.map(t => t.id === table.id ? table : t) + })); + } + + const deleteTable = (tableId: string) => { + setProject(prev => ({ + ...prev, + tables: prev.tables.filter(table => table.id !== tableId) + })) + } + + return { + projectStates: { project, screens, tables }, + projectActions: { + addScreen, + updateScreen, + deleteScreen, + addComponent, + addComponents, + updateComponent, + moveComponent, + deleteComponent, + addTable, + updateTable, + deleteTable + } + } + + +} \ No newline at end of file