From 60e51d334cf21039dee6eabeab5425d5b9a4a04e Mon Sep 17 00:00:00 2001 From: Aman Harwara Date: Fri, 5 May 2023 22:09:33 +0530 Subject: [PATCH] chore: only show links container toggle if container can be truncated (#2326) --- .../LinkedItems/ItemLinkAutocompleteInput.tsx | 187 +++++++++--------- .../LinkedItemBubblesContainer.tsx | 34 +++- 2 files changed, 129 insertions(+), 92 deletions(-) diff --git a/packages/web/src/javascripts/Components/LinkedItems/ItemLinkAutocompleteInput.tsx b/packages/web/src/javascripts/Components/LinkedItems/ItemLinkAutocompleteInput.tsx index 5c6662ac1..4fb108f26 100644 --- a/packages/web/src/javascripts/Components/LinkedItems/ItemLinkAutocompleteInput.tsx +++ b/packages/web/src/javascripts/Components/LinkedItems/ItemLinkAutocompleteInput.tsx @@ -1,4 +1,12 @@ -import { FormEventHandler, KeyboardEventHandler, useDeferredValue, useEffect, useRef } from 'react' +import { + FormEventHandler, + ForwardedRef, + KeyboardEventHandler, + forwardRef, + useDeferredValue, + useEffect, + useRef, +} from 'react' import { observer } from 'mobx-react-lite' import { classNames } from '@standardnotes/utils' import { LinkingController } from '@/Controllers/LinkingController' @@ -13,6 +21,7 @@ import { Slot } from '@radix-ui/react-slot' import Icon from '../Icon/Icon' import { PremiumFeatureIconName } from '../Icon/PremiumFeatureIcon' import { KeyboardKey } from '@standardnotes/ui-services' +import { mergeRefs } from '@/Hooks/mergeRefs' type Props = { linkingController: LinkingController @@ -23,118 +32,116 @@ type Props = { item: DecryptedItem } -const ItemLinkAutocompleteInput = ({ - linkingController, - focusPreviousItem, - focusedId, - setFocusedId, - hoverLabel, - item, -}: Props) => { - const application = useApplication() +const ItemLinkAutocompleteInput = forwardRef( + ( + { linkingController, focusPreviousItem, focusedId, setFocusedId, hoverLabel, item }: Props, + forwardedRef: ForwardedRef, + ) => { + const application = useApplication() - const { getLinkedTagsForItem, linkItems, createAndAddNewTag, isEntitledToNoteLinking } = linkingController + const { getLinkedTagsForItem, linkItems, createAndAddNewTag, isEntitledToNoteLinking } = linkingController - const tagsLinkedToItem = getLinkedTagsForItem(item) || [] + const tagsLinkedToItem = getLinkedTagsForItem(item) || [] - const combobox = useComboboxStore() - const value = combobox.useState('value') - const searchQuery = useDeferredValue(value) + const combobox = useComboboxStore() + const value = combobox.useState('value') + const searchQuery = useDeferredValue(value) - const { unlinkedItems, shouldShowCreateTag } = getLinkingSearchResults(searchQuery, application, item) + const { unlinkedItems, shouldShowCreateTag } = getLinkingSearchResults(searchQuery, application, item) - const inputRef = useRef(null) + const inputRef = useRef(null) - const onFormSubmit: FormEventHandler = async (event) => { - event.preventDefault() - if (searchQuery !== '') { - await createAndAddNewTag(searchQuery) - combobox.setValue('') + const onFormSubmit: FormEventHandler = async (event) => { + event.preventDefault() + if (searchQuery !== '') { + await createAndAddNewTag(searchQuery) + combobox.setValue('') + } } - } - const handleFocus = () => { - if (focusedId !== ElementIds.ItemLinkAutocompleteInput) { - setFocusedId(ElementIds.ItemLinkAutocompleteInput) + const handleFocus = () => { + if (focusedId !== ElementIds.ItemLinkAutocompleteInput) { + setFocusedId(ElementIds.ItemLinkAutocompleteInput) + } } - } - const onKeyDown: KeyboardEventHandler = (event) => { - switch (event.key) { - case KeyboardKey.Left: - if (searchQuery.length === 0) { - focusPreviousItem() - } - break + const onKeyDown: KeyboardEventHandler = (event) => { + switch (event.key) { + case KeyboardKey.Left: + if (searchQuery.length === 0) { + focusPreviousItem() + } + break + } } - } - useEffect(() => { - if (focusedId === ElementIds.ItemLinkAutocompleteInput) { - inputRef.current?.focus() - } - }, [focusedId]) + useEffect(() => { + if (focusedId === ElementIds.ItemLinkAutocompleteInput) { + inputRef.current?.focus() + } + }, [focusedId]) - return ( -
-
- - - {unlinkedItems.map((result) => { - const cannotLinkItem = !isEntitledToNoteLinking && result instanceof SNNote + > + {unlinkedItems.map((result) => { + const cannotLinkItem = !isEntitledToNoteLinking && result instanceof SNNote - return ( + return ( + { + linkItems(item, result).catch(console.error) + combobox.setValue('') + }} + > + + {cannotLinkItem && } + + ) + })} + {shouldShowCreateTag && ( { - linkItems(item, result).catch(console.error) + void createAndAddNewTag(searchQuery) combobox.setValue('') }} > - - {cannotLinkItem && } + - ) - })} - {shouldShowCreateTag && ( - { - void createAndAddNewTag(searchQuery) - combobox.setValue('') - }} - > - - - )} - -
-
- ) -} + )} + + + + ) + }, +) export default observer(ItemLinkAutocompleteInput) diff --git a/packages/web/src/javascripts/Components/LinkedItems/LinkedItemBubblesContainer.tsx b/packages/web/src/javascripts/Components/LinkedItems/LinkedItemBubblesContainer.tsx index 99eea43b2..3465119f7 100644 --- a/packages/web/src/javascripts/Components/LinkedItems/LinkedItemBubblesContainer.tsx +++ b/packages/web/src/javascripts/Components/LinkedItems/LinkedItemBubblesContainer.tsx @@ -2,7 +2,7 @@ import { observer } from 'mobx-react-lite' import ItemLinkAutocompleteInput from './ItemLinkAutocompleteInput' import { LinkingController } from '@/Controllers/LinkingController' import LinkedItemBubble from './LinkedItemBubble' -import { useCallback, useEffect, useMemo, useState } from 'react' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useResponsiveAppPane } from '../Panes/ResponsivePaneProvider' import { ElementIds } from '@/Constants/ElementIDs' import { classNames } from '@standardnotes/utils' @@ -112,6 +112,34 @@ const LinkedItemBubblesContainer = ({ item, linkingController, hideToggle = fals const visibleItems = isCollapsed ? itemsToDisplay.slice(0, 5) : itemsToDisplay const nonVisibleItems = itemsToDisplay.length - visibleItems.length + const [canShowContainerToggle, setCanShowContainerToggle] = useState(false) + const linkInputRef = useRef(null) + const linkContainerRef = useRef(null) + useEffect(() => { + const container = linkContainerRef.current + const linkInput = linkInputRef.current + + if (!container || !linkInput) { + return + } + + const resizeObserver = new ResizeObserver(() => { + if (container.clientHeight > linkInput.clientHeight) { + setCanShowContainerToggle(true) + } else { + setCanShowContainerToggle(false) + } + }) + + resizeObserver.observe(linkContainerRef.current) + + return () => { + resizeObserver.disconnect() + } + }, []) + + const shouldHideToggle = hideToggle || (!canShowContainerToggle && !isCollapsed) + return (
{visibleItems.map((link) => (
- {itemsToDisplay.length > 0 && !hideToggle && ( + {itemsToDisplay.length > 0 && !shouldHideToggle && (