import { isSelectingContext, selectionContext, } from "../context/SelectionContext"; import styled from "styled-components"; import { editingCellContext } from "../context/EditorContext"; import React, { useRef } from "react"; import { hoveredCellContext } from "../context/hoveredCellContext"; type IProps = { x: number; y: number; style: React.CSSProperties; children: React.ReactNode; hasError: boolean; isSelectable: boolean; borderRightColor?: string; isReadonly: boolean; }; /** * This component is in charge of the mouse handlers on the cell. * It also attaches the invalid indicator */ export const CellWrapper = React.forwardRef( ( { x, y, style, children, hasError, isSelectable, borderRightColor, isReadonly, }: IProps, ref: React.ForwardedRef ) => { const setSelection = selectionContext.useSetter(); const setIsSelecting = isSelectingContext.useSetter(); const isSelectingRef = isSelectingContext.useRef(); const editingCellRef = editingCellContext.useRef(); const setEditingCell = editingCellContext.useSetter(); const cellRef = useRef(null); const setHoveredCell = hoveredCellContext.useSetter(); // Select the cell, and initiate a drag selection const handleMouseDown = isSelectable ? (e: React.MouseEvent) => { if (e.button !== 0) { return; } if ( editingCellRef.current?.x === x && editingCellRef.current?.y === y ) { return; } setEditingCell(null); setSelection({ primary: { x, y }, start: { x, y }, height: 1, width: 1, }); setIsSelecting(true); } : undefined; // Edit the cell on double click const handleDoubleClick = isSelectable && !isReadonly ? () => { setEditingCell({ x, y }); } : undefined; // Hover the cell when cursor goes over. // Change the selection size if mouse is down while it is moving const handleMouseEnter = () => { if (cellRef.current) { setHoveredCell({ el: cellRef.current, x, y }); } if (isSelectable && isSelectingRef.current) { setSelection( (selection) => selection && { primary: selection.primary, start: { x: Math.min(x, selection.primary.x), y: Math.min(y, selection.primary.y), }, height: Math.abs(y - selection.primary.y) + 1, width: Math.abs(x - selection.primary.x) + 1, } ); } }; return ( { cellRef.current = el; if (ref) { if (typeof ref === "function") { ref(el); } else { ref.current = el; } } }} onMouseDown={handleMouseDown} onDoubleClick={handleDoubleClick} onMouseEnter={handleMouseEnter} style={style} borderRightColor={borderRightColor} isReadonly={isReadonly} > {hasError ? ( <> ) : null} {children} ); } ); const InvalidIndicator = styled.div` height: 0; width: 0; border-top: 6px solid ${({ theme }) => theme.errorColor}; border-left: 6px solid transparent; position: absolute; right: 0; top: 0; `; const CellContainer = styled.div<{ borderRightColor?: string; isReadonly: boolean; }>` border-right: 1px solid ${({ borderRightColor, theme }) => borderRightColor ?? theme.cells.borderColor}; border-bottom: 1px solid ${({ theme }) => theme.cells.borderColor}; background: ${({ isReadonly, theme }) => isReadonly ? theme.cells.readonlyBackgroundColor : theme.cells.backgroundColor}; display: flex; align-items: center; `;