import { useSelectingMouse } from "components/SelectionManager/selectionState"; import { MouseEvent, useCallback } from "react"; import { useUIState } from "src/useUIState"; import { Block } from "./Block"; import { isTextBlock } from "src/utils/isTextBlock"; import { useEntitySetContext } from "components/EntitySetProvider"; import { useReplicache } from "src/replicache"; import { getBlocksWithType } from "src/hooks/queries/useBlocks"; import { focusBlock } from "src/utils/focusBlock"; import { useIsMobile } from "src/hooks/isMobile"; import { scrollIntoViewIfNeeded } from "src/utils/scrollIntoViewIfNeeded"; import { elementId } from "src/utils/elementId"; let debounce: number | null = null; // Track scrolling state for mobile let isScrolling = false; let scrollTimeout: number | null = null; if (typeof window !== "undefined") { window.addEventListener( "scroll", () => { isScrolling = true; if (scrollTimeout) window.clearTimeout(scrollTimeout); scrollTimeout = window.setTimeout(() => { isScrolling = false; }, 150); }, true, ); } export function useBlockMouseHandlers(props: Block) { let entity_set = useEntitySetContext(); let isMobile = useIsMobile(); let { rep } = useReplicache(); let onMouseDown = useCallback( (e: MouseEvent) => { if ((e.target as Element).getAttribute("data-draggable")) return; if ((e.target as Element).tagName === "BUTTON") return; if ((e.target as Element).tagName === "SELECT") return; if ((e.target as Element).tagName === "OPTION") return; if (isMobile && isScrolling) return; if (!entity_set.permissions.write) return; useSelectingMouse.setState({ start: props.value }); if (e.shiftKey) { if ( useUIState.getState().selectedBlocks[0]?.value === props.value && useUIState.getState().selectedBlocks.length === 1 ) return; e.preventDefault(); useUIState.getState().addBlockToSelection(props); } else { if (e.isDefaultPrevented()) return; useUIState.getState().setFocusedBlock({ entityType: "block", entityID: props.value, parent: props.parent, }); useUIState.getState().setSelectedBlock(props); // scroll to the page containing the block, if offscreen let parentPage = elementId.page(props.parent).container; setTimeout(() => { scrollIntoViewIfNeeded( document.getElementById(parentPage), false, "smooth", ); }, 50); } }, [props, entity_set.permissions.write, isMobile], ); let onMouseEnter = useCallback( async (e: MouseEvent) => { if (isMobile && isScrolling) return; if (!entity_set.permissions.write) return; if (debounce) window.clearTimeout(debounce); debounce = window.setTimeout(async () => { debounce = null; if (e.buttons !== 1) return; let selection = useSelectingMouse.getState(); if (!selection.start) return; let siblings = (await rep?.query((tx) => getBlocksWithType(tx, props.parent))) || []; let startIndex = siblings.findIndex((b) => b.value === selection.start); if (startIndex === -1) return; let endIndex = siblings.findIndex((b) => b.value === props.value); let start = Math.min(startIndex, endIndex); let end = Math.max(startIndex, endIndex); let selected = siblings.slice(start, end + 1).map((b) => ({ value: b.value, position: b.position, parent: props.parent, })); useUIState.getState().setSelectedBlocks(selected); }, 15); }, [rep, props, entity_set.permissions.write, isMobile], ); return { onMouseDown, onMouseEnter }; }