Newer
Older
NocodeToolOnRDL / src / hooks / useComponent.ts
@Sakoda2269 Sakoda2269 11 days ago 9 KB 命名修正
import type { ButtonGeneralData, Component, GeneralData, TableGeneralData, TextFieldGeneralData, TextGeneralData, UIData } from "../types/component";
import { ComponentType } from "../types/component";
import type { Screen } from "../types/screen";
import { useCallback, useMemo, useRef, useState } from "react";
import type { Dispatch, SetStateAction } from "react";

export interface ComponentState {
    selectingComponent: Component | null,
    selectingComponentIndex: number,
    curScreenComponents: Component[]
}

export type SelectComponentListenerType = (selected: Component | null) => void

export interface ComponentActions {
    addComponent: (type: ComponentType) => void,
    addComponents: (components: Component[], screenId: string) => void
    moveComponent: (index: number, uiData: UIData) => void,
    updateComponent: (index: number, newComponent: Component) => void,
    deleteComponent: (index: number, id: string) => void,
    setSelectComponentListener: (listner: SelectComponentListenerType) => void
    selectComponent: (index: number) => void,
    resetSelectingComponent: () => void,
    activateSelectMode: () => void
}

export interface ComponentDispatch {
    setSelectingComponentIndex: Dispatch<SetStateAction<number>>,
}

export interface ComponentStateAndActions {
    componentState: ComponentState,
    componentActions: ComponentActions,
    componentDispatch: ComponentDispatch
}

export function useComponent(
    curScreenIndex: number,
    curScreen: Screen,
    setScreens: Dispatch<SetStateAction<Screen[]>>
): ComponentStateAndActions {

    const [selectingComponentIndex, setSelectingComponentIndex] = useState<number>(-1);
    const [selectMode, setSelectMode] = useState<boolean>(false);
    // const [selectComponentCallback, setSelectComponentCallback] = useState<SelectComponentListenerType | null>(null);
    const selectComponentCallbackRef = useRef<SelectComponentListenerType | null>(null);
    const curScreenComponents: Component[] = useMemo(() => curScreen.components, [curScreen])
    const selectingComponent: Component | null = useMemo(() => {
        if (selectingComponentIndex === -1) {
            return null;
        }
        return curScreenComponents[selectingComponentIndex]
    }, [curScreenComponents, selectingComponentIndex]);

    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: [] }
        }
    }


    const addComponent = async (type: ComponentType) => {
        let nextIndex = 0;
        if (curScreenComponents.length > 0) {
            nextIndex = curScreenComponents[curScreenComponents.length - 1].index + 1;
        }

        const uiData: UIData = {
            posX: 100,
            posY: 100,
            width: 75,
            height: 25
        }
        const newId = "w" + self.crypto.randomUUID().replace(/-/g, "")

        let newComponent: Component;

        switch (type) {
            case ComponentType.Button:
                newComponent = {
                    id: newId,
                    name: newId,
                    type: type,
                    index: nextIndex,
                    screenId: curScreen.id,
                    generalData: getInitialGeneralData(type) as ButtonGeneralData,
                    style: {},
                    uiData: uiData,
                }
                break;
            case ComponentType.Text:
                newComponent = {
                    id: newId,
                    name: newId,
                    type: type,
                    index: nextIndex,
                    screenId: curScreen.id,
                    generalData: getInitialGeneralData(type) as TextGeneralData,
                    style: {},
                    uiData: uiData,
                }
                break;
            case ComponentType.TextField:
                newComponent = {
                    id: newId,
                    name: newId,
                    type: type,
                    index: nextIndex,
                    screenId: curScreen.id,
                    generalData: getInitialGeneralData(type) as TextFieldGeneralData,
                    style: {},
                    uiData: uiData,
                }
                break;
            case ComponentType.Table:
                newComponent = {
                    id: newId,
                    name: newId,
                    type: type,
                    index: nextIndex,
                    screenId: curScreen.id,
                    generalData: getInitialGeneralData(type) as TableGeneralData,
                    style: {},
                    uiData: uiData,
                }
                break;
        }

        setScreens(screens => screens.map((screen, i) =>
            i === curScreenIndex ? {
                ...screen,
                components: [...screen.components, newComponent]
            } : screen
        ));

        // await fetch(`/api/projects/${curScreen.projectId}/screens/${curScreen.id}/components`, {
        //     method: "POST",
        //     headers: {
        //         'Content-Type': 'application/json',
        //     },
        //     body: JSON.stringify({
        //         newComponents: [newComponent],
        //     }),
        // });
    }

    const addComponents = async (components: Component[], screenId: string) => {
        setScreens(screens => screens.map((screen) =>
            screen.id === screenId ? {
                ...screen,
                components: [...screen.components, ...components]
            } : screen
        ));

        // await fetch(`/api/projects/${curScreen.projectId}/screens/${screenId}/components`, {
        //     method: "POST",
        //     headers: {
        //         'Content-Type': 'application/json',
        //     },
        //     body: JSON.stringify({
        //         newComponents: components,
        //     }),
        // });
    }

    const moveComponent = async (index: number, uiData: UIData) => {
        if (!selectingComponent) return;
        setScreens(screens => screens.map((screen, i) =>
            i === curScreenIndex ? {
                ...screen,
                components: screens[i].components.map((comp, j) => j === index ? { ...comp, uiData: uiData } : comp)
            } : screen
        ));
        // await fetch(`/api/projects/${curScreen.projectId}/screens/${curScreen.id}/components/${selectingComponent.id}/uiData`, {
        //     method: "PUT",
        //     headers: {
        //         'Content-Type': 'application/json',
        //     },
        //     body: JSON.stringify({ uiData: uiData }),
        // });
    }

    const deleteComponent = async (index: number) => {
        setScreens((prev) => (prev.map((screen, scrIdx) => scrIdx === curScreenIndex ? {
            ...screen,
            components: screen.components.filter((_, compIdx) => compIdx !== index)
        } : screen)));
        
        // await fetch(`/api/projects/${curScreen.projectId}/screens/${curScreen.id}/components/${id}`, {
        //     method: "DELETE",
        // });
    }

    const updateComponent = (index: number, newComponent: Component) => {
        if (index == -1) { //-1の時は現在選択中のコンポーネント
            index = selectingComponentIndex
        }
        console.log(newComponent);
        setScreens(screens => screens.map((screen, i) =>
            i === curScreenIndex ? {
                ...screen,
                components: screens[i].components.map((comp, j) => j === index ? newComponent : comp)
            } : screen
        ));
    }

    const setSelectComponentListener = useCallback((listener: SelectComponentListenerType) => {
        selectComponentCallbackRef.current = listener;
    }, [])

    const selectComponent = (index: number) => {
        if (selectMode) {
            selectComponentCallbackRef.current?.(curScreenComponents[index]);
            selectComponentCallbackRef.current = null;
            setSelectMode(false);
        } else {
            setSelectingComponentIndex(index);
        }
    }


    const resetSelectingComponent = () => {
        if (selectMode) {
            selectComponentCallbackRef.current?.(null);
            selectComponentCallbackRef.current = null;
            setSelectMode(false);
        } else {
            setSelectingComponentIndex(-1);
        }
    }

    const activateSelectMode = () => {
        setSelectMode(true);
    }

    return {
        componentState: {
            selectingComponent,
            selectingComponentIndex,
            curScreenComponents
        },
        componentActions: {
            addComponent,
            addComponents,
            deleteComponent,
            moveComponent,
            updateComponent,
            setSelectComponentListener,
            selectComponent,
            resetSelectingComponent,
            activateSelectMode
        },
        componentDispatch: {
            setSelectingComponentIndex
        }
    }

}