a tool for shared writing and social publishing
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

add different selection manager!

+55 -8
-1
app/[doc_id]/page.tsx
··· 22 22 let initialFacts = (data as unknown as Fact<keyof typeof Attributes>[]) || []; 23 23 return ( 24 24 <ReplicacheProvider name={props.params.doc_id} initialFacts={initialFacts}> 25 - <SelectionManager /> 26 25 <Cards rootCard={props.params.doc_id} /> 27 26 </ReplicacheProvider> 28 27 );
+49 -3
components/Blocks.tsx
··· 7 7 useEditorStates, 8 8 } from "components/TextBlock"; 9 9 import { generateKeyBetween } from "fractional-indexing"; 10 - import { useEffect, useMemo } from "react"; 10 + import { useEffect, useMemo, useRef, useState } from "react"; 11 11 import { useSubscribe } from "replicache-react"; 12 12 import { elementId } from "src/utils/elementId"; 13 13 import { TextSelection } from "prosemirror-state"; 14 - import { useSelectingMouse } from "components/SelectionManager"; 14 + import { 15 + restoreSelection, 16 + saveSelection, 17 + useSelectingMouse, 18 + } from "components/SelectionManager"; 15 19 import { ImageBlock } from "./ImageBlock"; 16 20 import { useUIState } from "src/useUIState"; 17 21 import { focusCard } from "./Cards"; ··· 23 27 }; 24 28 export function Blocks(props: { entityID: string }) { 25 29 let rep = useReplicache(); 30 + let ref = useRef<HTMLDivElement | null>(null); 31 + let previous = useRef("none"); 32 + useEffect(() => { 33 + let cb = () => { 34 + let selection = window.getSelection(); 35 + if (previous.current !== selection?.type) { 36 + let ranges = saveSelection(); 37 + requestAnimationFrame(() => { 38 + restoreSelection(ranges); 39 + }); 40 + } 41 + if (selection?.type === "Range") { 42 + if (ref.current) ref.current.contentEditable = "true"; 43 + let range = selection.getRangeAt(0); 44 + if (range.startContainer !== range.endContainer) { 45 + let contents = range.cloneContents(); 46 + let entityIDs: string[] = []; 47 + for (let child of contents.children) { 48 + let entityID = child.getAttribute("entityID"); 49 + if (entityID) entityIDs.push(entityID); 50 + } 51 + useUIState.getState().setSelectedBlocks(entityIDs); 52 + } else { 53 + useUIState.getState().setSelectedBlocks([]); 54 + } 55 + } else { 56 + if (ref.current) ref.current.contentEditable = "false"; 57 + } 58 + previous.current = selection?.type || "None"; 59 + }; 60 + document.addEventListener("selectionchange", cb); 61 + let pointerUp = () => { 62 + if (useUIState.getState().selectedBlock.length > 1) 63 + window.getSelection()?.removeAllRanges(); 64 + }; 65 + window.addEventListener("pointerup", pointerUp); 66 + return () => { 67 + window.removeEventListener("pointerup", pointerUp); 68 + document.removeEventListener("selectionchange", cb); 69 + }; 70 + }, []); 26 71 let initialValue = useMemo( 27 72 () => 28 73 rep.initialFacts ··· 72 117 73 118 let lastBlock = blocks[blocks.length - 1]; 74 119 return ( 75 - <div className="w-full flex flex-col gap-1 p-2"> 120 + <div ref={ref} className="w-full flex flex-col gap-1 p-2 outline-none"> 76 121 {blocks.map((f, index, arr) => { 77 122 return ( 78 123 <Block ··· 206 251 ]); 207 252 return ( 208 253 <div 254 + entityID={props.entityID} 209 255 onMouseDown={(e) => { 210 256 if (e.shiftKey) { 211 257 e.preventDefault();
+2 -4
components/SelectionManager.tsx
··· 11 11 let moreThanOneSelected = useUIState((s) => s.selectedBlock.length > 1); 12 12 useEffect(() => { 13 13 if (moreThanOneSelected) { 14 - (document.activeElement as HTMLElement | null)?.blur(); 15 - window.getSelection()?.removeAllRanges(); 16 14 } 17 15 }, [moreThanOneSelected]); 18 16 let dragStart = useSelectingMouse((s) => s.start); ··· 74 72 }, [dragStart]); 75 73 return null; 76 74 } 77 - function saveSelection() { 75 + export function saveSelection() { 78 76 let selection = window.getSelection(); 79 77 if (selection && selection.rangeCount > 0) { 80 78 let ranges: Range[] = []; ··· 85 83 } 86 84 return []; 87 85 } 88 - function restoreSelection(savedRanges: Range[]) { 86 + export function restoreSelection(savedRanges: Range[]) { 89 87 if (savedRanges) { 90 88 let selection = window.getSelection() || new Selection(); 91 89 selection.removeAllRanges();
+4
src/useUIState.ts
··· 22 22 set((state) => { 23 23 return { ...state, selectedBlock: [block] }; 24 24 }), 25 + setSelectedBlocks: (blocks: string[]) => 26 + set((state) => { 27 + return { ...state, selectedBlock: blocks }; 28 + }), 25 29 addBlockToSelection: (block: string) => 26 30 set((state) => { 27 31 if (state.selectedBlock.includes(block)) return state;