diff --git a/src/components/Editable/useMultiline.ts b/src/components/Editable/useMultiline.ts index 8c7f6720f8..5de8d3050c 100644 --- a/src/components/Editable/useMultiline.ts +++ b/src/components/Editable/useMultiline.ts @@ -25,8 +25,8 @@ const useMultiline = (contentRef: React.RefObject, simplePath: Simp if (!contentRef.current) return const height = contentRef.current.getBoundingClientRect().height - // 1.72 must match line-height as defined in .thought-container - const singleLineHeight = fontSize * 1.72 + // must match line-height as defined in .thought-container + const singleLineHeight = fontSize * 2 // .editable.multiline gets 5px of padding-top to offset the collapsed line-height // we need to account for padding-top, otherwise it can cause a false positive const paddingTop = parseInt(window.getComputedStyle(contentRef.current).paddingTop) diff --git a/src/components/LayoutTree.tsx b/src/components/LayoutTree.tsx index 80974e8669..f9ddbef169 100644 --- a/src/components/LayoutTree.tsx +++ b/src/components/LayoutTree.tsx @@ -109,6 +109,7 @@ const crossContextualKey = (contextChain: Path[] | undefined, id: ThoughtId) => const useSizeTracking = () => { // Track dynamic thought sizes from inner refs via VirtualThought. These are used to set the absolute y position which enables animation between any two states. isVisible is used to crop hidden thoughts. const [sizes, setSizes] = useState>({}) + const fontSize = useSelector(state => state.fontSize) const unmounted = useRef(false) // Track debounced height removals @@ -146,17 +147,24 @@ const useSizeTracking = () => { key: string }) => { if (height !== null) { + // To create the correct selection behavior, thoughts must clipped on the top and bottom. This creates a gap between thoughts. To eliminate this gap, thoughts are rendered with a slight overlap. + // See: clipPath in the editable recipe + const lineHeightOverlap = fontSize / 8 + const heightClipped = height - lineHeightOverlap + // cancel thought removal timeout clearTimeout(sizeRemovalTimeouts.current.get(key)) sizeRemovalTimeouts.current.delete(key) setSizes(sizesOld => - height === sizesOld[key]?.height && width === sizesOld[key]?.width && isVisible === sizesOld[key]?.isVisible + heightClipped === sizesOld[key]?.height && + width === sizesOld[key]?.width && + isVisible === sizesOld[key]?.isVisible ? sizesOld : { ...sizesOld, [key]: { - height, + height: heightClipped, width: width || undefined, isVisible, }, @@ -166,7 +174,7 @@ const useSizeTracking = () => { removeSize(key) } }, - [removeSize], + [fontSize, removeSize], ) useEffect(() => { diff --git a/src/components/NoOtherContexts.tsx b/src/components/NoOtherContexts.tsx index 23384ae64a..61b7182d3b 100644 --- a/src/components/NoOtherContexts.tsx +++ b/src/components/NoOtherContexts.tsx @@ -19,7 +19,7 @@ const NoOtherContexts = ({ allowSingleContext }: { allowSingleContext?: boolean; textNote(), css({ fontSize: 'sm', - lineHeight: 1.72, + lineHeight: '2', // use padding instead of margin to ensure it affects height for LayoutTree node y calculation paddingBottom: '0.75em', }), diff --git a/src/components/Thought.tsx b/src/components/Thought.tsx index 3c832ecead..1a857efcd0 100644 --- a/src/components/Thought.tsx +++ b/src/components/Thought.tsx @@ -420,7 +420,7 @@ const ThoughtContainer = ({ className={css({ /* Use line-height to vertically center the text and bullet. We cannot use padding since it messes up the selection. This needs to be overwritten on multiline elements. See ".child .editable" below. */ /* must match value used in Editable useMultiline */ - lineHeight: '1.72', + lineHeight: '2', // ensure that ThoughtAnnotation is positioned correctly position: 'relative', ...(hideBullet ? { marginLeft: -12 } : null),