UNPKG

react-weekly-table

Version:

React weekly scheduler <br/> By default build time ranges for a week, supports up to 31 days <br/> Can work with different timezones, data always return to UTC+0

180 lines (179 loc) 6.32 kB
import { jsx as _jsx } from "react/jsx-runtime"; import { createContext, useCallback, useContext, useEffect, useRef, useState, } from 'react'; import { useCells } from './CellsProvider'; import { useScheduler } from './Scheduler'; const CanvasContext = createContext({}); /** * Drawer provider context hook * @returns [CanvasProps]{@link CanvasProps} */ export const useCanvas = () => useContext(CanvasContext); /** * Drawing provider, used for timeblocks creation */ const CanvasProvider = ({ children }) => { const { headerHeightProp, helperWidthProp, bottomHeightProp } = useScheduler(); const { offsetWidth, offsetHeight, cells, setCells } = useCells(); /** * Drawing state */ const [isDrawing, setIsDrawing] = useState(false); /** * Canvas reference */ const canvasRef = useRef(null); /** * Context reference */ const contextRef = useRef(null); /** * Start point relative coordinates */ const [start, setStart] = useState([0, 0]); /** * End point relative coordinates */ const [end, setEnd] = useState(undefined); /** * Current position relative coordinates */ const [current, setCurrent] = useState(undefined); /** * Drawing area init */ useEffect(() => { const canvas = canvasRef.current; if (!canvas) return; canvas.width = offsetWidth * 2; canvas.height = offsetHeight * 2; canvas.style.width = `${offsetWidth}px`; canvas.style.height = `${offsetHeight}px`; canvas.draggable = false; const context = canvas.getContext('2d'); if (!context) return; context.scale(2, 2); context.lineCap = 'round'; context.strokeStyle = 'blue'; context.lineWidth = 1; context.fillStyle = '#0000FF19'; contextRef.current = context; }, [offsetHeight, offsetWidth]); /** * Start drawing callback */ const startDrawing = useCallback(({ nativeEvent }) => { const { offsetX, offsetY } = nativeEvent; if (!contextRef.current) return; contextRef.current.beginPath(); contextRef.current.moveTo(offsetX, offsetY); setIsDrawing(true); setStart([offsetX, offsetY]); setEnd(undefined); }, []); /** * Clearing canvas callback */ const clearCanvas = useCallback(() => { if (!contextRef.current) { return; } contextRef.current.clearRect(0, 0, offsetWidth, offsetHeight); }, [offsetHeight, offsetWidth]); /** * End drawing callback */ const finishDrawing = useCallback(({ nativeEvent }) => { if (!contextRef.current) return; if (!isDrawing) return; contextRef.current.closePath(); setIsDrawing(false); setEnd([nativeEvent.offsetX, nativeEvent.offsetY]); setCurrent(undefined); clearCanvas(); }, //eslint-disable-next-line [isDrawing]); /** * Drawing process callback */ const draw = useCallback(({ nativeEvent }) => { if (!isDrawing || !contextRef.current) { return; } setCurrent([nativeEvent.offsetX, nativeEvent.offsetY]); contextRef.current.clearRect(0, 0, offsetWidth, offsetHeight); contextRef.current.strokeRect(start[0], start[1], nativeEvent.offsetX - start[0], nativeEvent.offsetY - start[1]); contextRef.current.fillRect(start[0] + 1, start[1] + 1, nativeEvent.offsetX - start[0] - 1, nativeEvent.offsetY - start[1] - 1); }, [isDrawing, offsetHeight, offsetWidth, start]); /** * Finding cell index by coordinates */ const findCell = useCallback((position) => { if (!position || !cells) return -1; if (position[0] >= offsetWidth - 5) position[0] = offsetWidth - 5; if (position[0] <= helperWidthProp) position[0] = helperWidthProp; if (position[1] <= headerHeightProp) position[1] = headerHeightProp; if (position[1] >= offsetHeight - bottomHeightProp) position[1] = offsetHeight - bottomHeightProp - 1; return cells.findIndex((cell) => cell.position.x <= position[0] && position[0] <= cell.position.w && cell.position.y <= position[1] && position[1] <= cell.position.h); }, [ offsetWidth, cells, helperWidthProp, headerHeightProp, offsetHeight, bottomHeightProp, ]); /** * Start, end, current coordinates changes watcher * Calculates selected cells area and set isSelected property to it */ useEffect(() => { if (end === undefined && current === undefined) return; const startCell = findCell(start); if (startCell === -1) return; const endCell = findCell(current === undefined ? end : current); if (endCell === -1) return; const sCell = cells[startCell]; const eCell = cells[endCell]; const sRow = sCell.row < eCell.row ? sCell.row : eCell.row; const sCol = sCell.column < eCell.column ? sCell.column : eCell.column; const eRow = eCell.row > sCell.row ? eCell.row : sCell.row; const eCol = eCell.column > sCell.column ? eCell.column : sCell.column; const insertCells = cells.map((cell) => { if (cell.row >= sRow && cell.row <= eRow && cell.column >= sCol && cell.column <= eCol) { return Object.assign(Object.assign({}, cell), { isSelected: true }); } else return cell; }); setCells(insertCells); //eslint-disable-next-line }, [start, end, current]); return (_jsx(CanvasContext.Provider, Object.assign({ value: { canvasRef, startDrawing, finishDrawing, draw, isDrawing, } }, { children: children }))); }; export default CanvasProvider;