import { Block, StartBlock, Step, StepType, Target } from 'bot-engine' import { createContext, Dispatch, ReactNode, SetStateAction, useContext, useState, } from 'react' import { parseNewBlock, parseNewStep } from 'services/graph' import { insertItemInList } from 'services/utils' export const stubLength = 20 export const blockWidth = 300 export const blockAnchorsOffset = { left: { x: 0, y: 20, }, top: { x: blockWidth / 2, y: 0, }, right: { x: blockWidth, y: 20, }, } export const firstStepOffsetY = 88 export const spaceBetweenSteps = 66 export type Coordinates = { x: number; y: number } type Position = Coordinates & { scale: number } export type Anchor = { coordinates: Coordinates } export type Node = Omit & { steps: (Step & { sourceAnchorsPosition: { left: Coordinates; right: Coordinates } })[] } export type NewBlockPayload = { x: number y: number type?: StepType step?: Step } const graphPositionDefaultValue = { x: 400, y: 100, scale: 1 } const graphContext = createContext<{ graphPosition: Position setGraphPosition: Dispatch> connectingIds: { blockId: string; stepId: string; target?: Target } | null setConnectingIds: Dispatch< SetStateAction<{ blockId: string; stepId: string; target?: Target } | null> > startBlock?: StartBlock setStartBlock: Dispatch> blocks: Block[] setBlocks: Dispatch> addNewBlock: (props: NewBlockPayload) => void updateBlockPosition: (blockId: string, newPositon: Coordinates) => void addNewStepToBlock: ( blockId: string, step: StepType | Step, index: number ) => void removeStepFromBlock: (blockId: string, stepId: string) => void addTarget: (connectingIds: { blockId: string stepId: string target?: Target }) => void removeTarget: (connectingIds: { blockId: string; stepId: string }) => void }>({ graphPosition: graphPositionDefaultValue, setGraphPosition: () => console.log("I'm not instantiated"), connectingIds: null, setConnectingIds: () => console.log("I'm not instantiated"), blocks: [], setBlocks: () => console.log("I'm not instantiated"), updateBlockPosition: () => console.log("I'm not instantiated"), addNewStepToBlock: () => console.log("I'm not instantiated"), addNewBlock: () => console.log("I'm not instantiated"), removeStepFromBlock: () => console.log("I'm not instantiated"), addTarget: () => console.log("I'm not instantiated"), removeTarget: () => console.log("I'm not instantiated"), setStartBlock: () => console.log("I'm not instantiated"), }) export const GraphProvider = ({ children }: { children: ReactNode }) => { const [graphPosition, setGraphPosition] = useState(graphPositionDefaultValue) const [connectingIds, setConnectingIds] = useState<{ blockId: string stepId: string target?: Target } | null>(null) const [blocks, setBlocks] = useState([]) const [startBlock, setStartBlock] = useState() const addNewBlock = ({ x, y, type, step }: NewBlockPayload) => { const boardCoordinates = { x, y, } setBlocks((blocks) => [ ...blocks.filter((block) => block.steps.length > 0), parseNewBlock({ step, type, totalBlocks: blocks.length, initialCoordinates: boardCoordinates, }), ]) } const updateBlockPosition = (blockId: string, newPosition: Coordinates) => { setBlocks((blocks) => blocks.map((block) => block.id === blockId ? { ...block, graphCoordinates: newPosition } : block ) ) } const addNewStepToBlock = ( blockId: string, step: StepType | Step, index: number ) => { setBlocks((blocks) => blocks .map((block) => block.id === blockId ? { ...block, steps: insertItemInList( block.steps, index, typeof step === 'string' ? parseNewStep(step as StepType, block.id) : { ...step, blockId: block.id } ), } : block ) .filter((block) => block.steps.length > 0) ) } const removeStepFromBlock = (blockId: string, stepId: string) => { setBlocks((blocks) => blocks.map((block) => block.id === blockId ? { ...block, steps: [...block.steps.filter((step) => step.id !== stepId)], } : block ) ) } const addTarget = ({ blockId, stepId, target, }: { blockId: string stepId: string target?: Target }) => { startBlock && blockId === 'start-block' ? setStartBlock({ ...startBlock, steps: [{ ...startBlock.steps[0], target }], }) : setBlocks((blocks) => blocks.map((block) => block.id === blockId ? { ...block, steps: [ ...block.steps.map((step) => step.id === stepId ? { ...step, target } : step ), ], } : block ) ) } const removeTarget = ({ blockId, stepId, }: { blockId: string stepId: string }) => { setBlocks((blocks) => blocks.map((block) => block.id === blockId ? { ...block, steps: [ ...block.steps.map((step) => step.id === stepId ? { ...step, target: undefined } : step ), ], } : block ) ) } return ( {children} ) } export const useGraph = () => useContext(graphContext)