import { Block, Edge, IdMap, Source, Step, Target } from 'models' import { createContext, Dispatch, MutableRefObject, ReactNode, SetStateAction, useContext, useEffect, useState, } from 'react' 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 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 const graphPositionDefaultValue = { x: 400, y: 100, scale: 1 } export type ConnectingIds = { source: Source target?: Target } type StepId = string type ButtonId = string export type Endpoint = { id: StepId | ButtonId ref: MutableRefObject } export type BlocksCoordinates = IdMap const graphContext = createContext<{ blocksCoordinates: BlocksCoordinates updateBlockCoordinates: (blockId: string, newCoord: Coordinates) => void graphPosition: Position setGraphPosition: Dispatch> connectingIds: ConnectingIds | null setConnectingIds: Dispatch> previewingEdge?: Edge setPreviewingEdge: Dispatch> sourceEndpoints: IdMap addSourceEndpoint: (endpoint: Endpoint) => void targetEndpoints: IdMap addTargetEndpoint: (endpoint: Endpoint) => void openedStepId?: string setOpenedStepId: Dispatch> isReadOnly: boolean focusedBlockId?: string setFocusedBlockId: Dispatch> // eslint-disable-next-line @typescript-eslint/ban-ts-comment //@ts-ignore }>({ graphPosition: graphPositionDefaultValue, connectingIds: null, }) export const GraphProvider = ({ children, blocks, isReadOnly = false, }: { children: ReactNode blocks: Block[] isReadOnly?: boolean }) => { const [graphPosition, setGraphPosition] = useState(graphPositionDefaultValue) const [connectingIds, setConnectingIds] = useState(null) const [previewingEdge, setPreviewingEdge] = useState() const [sourceEndpoints, setSourceEndpoints] = useState>({}) const [targetEndpoints, setTargetEndpoints] = useState>({}) const [openedStepId, setOpenedStepId] = useState() const [blocksCoordinates, setBlocksCoordinates] = useState( {} ) const [focusedBlockId, setFocusedBlockId] = useState() useEffect(() => { setBlocksCoordinates( blocks.reduce( (coords, block) => ({ ...coords, [block.id]: block.graphCoordinates, }), {} ) ) // eslint-disable-next-line react-hooks/exhaustive-deps }, [blocks]) const addSourceEndpoint = (endpoint: Endpoint) => { setSourceEndpoints((endpoints) => ({ ...endpoints, [endpoint.id]: endpoint, })) } const addTargetEndpoint = (endpoint: Endpoint) => { setTargetEndpoints((endpoints) => ({ ...endpoints, [endpoint.id]: endpoint, })) } const updateBlockCoordinates = (blockId: string, newCoord: Coordinates) => setBlocksCoordinates((blocksCoordinates) => ({ ...blocksCoordinates, [blockId]: newCoord, })) return ( {children} ) } export const useGraph = () => useContext(graphContext)