Something went wrong. Try again.
a tool for shared writing and social publishing
Something went wrong. Try again.
3.8 kB · 103 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104import { useSelectingMouse } from "components/SelectionManager/selectionState";import { MouseEvent, useCallback, useRef } 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 { getPageBlocks } from "src/replicache/getBlocks";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 mobilelet 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(); // Handlers read everything through a ref so their identity is stable across // renders (a `props` dep would recreate them every render). let latest = useRef({ props, write: entity_set.permissions.write, isMobile, rep, }); latest.current = { props, write: entity_set.permissions.write, isMobile, rep };
let onMouseDown = useCallback((e: MouseEvent) => { let { props, write, isMobile } = latest.current; 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 (!write) return; useSelectingMouse.setState({ start: props.entityID }); if (e.shiftKey) { if ( useUIState.getState().selectedBlocks[0]?.entityID === props.entityID && useUIState.getState().selectedBlocks.length === 1 ) return; e.preventDefault(); useUIState.getState().addBlockToSelection(props); } else { if (e.isDefaultPrevented()) return; useUIState.getState().focusAndSelectBlock(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); } }, []); let onMouseEnter = useCallback(async (e: MouseEvent) => { let { props, write, isMobile, rep } = latest.current; if (isMobile && isScrolling) return; if (!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 = rep ? getPageBlocks(rep, props.parent) : []; let startIndex = siblings.findIndex((b) => b.entityID === selection.start); if (startIndex === -1) return; let endIndex = siblings.findIndex((b) => b.entityID === props.entityID); let start = Math.min(startIndex, endIndex); let end = Math.max(startIndex, endIndex); let selected = siblings.slice(start, end + 1).map((b) => ({ entityID: b.entityID, parent: props.parent, })); useUIState.getState().setSelectedBlocks(selected); }, 15); }, []); return { onMouseDown, onMouseEnter };}