From 73eb8040ddf9a2b6689da8ba50d69fecc9ad0efc Mon Sep 17 00:00:00 2001 From: Aman Harwara Date: Fri, 5 May 2023 21:59:11 +0530 Subject: [PATCH 1/2] chore: only show links container toggle if container can be truncated --- .../LinkedItemBubblesContainer.tsx | 32 +++++++++++++++++-- packages/web/src/javascripts/Utils/Utils.ts | 4 +++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/packages/web/src/javascripts/Components/LinkedItems/LinkedItemBubblesContainer.tsx b/packages/web/src/javascripts/Components/LinkedItems/LinkedItemBubblesContainer.tsx index 99eea43b2c9..0ebf2c535ce 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' @@ -13,6 +13,7 @@ import { FOCUS_TAGS_INPUT_COMMAND, keyboardStringForShortcut } from '@standardno import { useCommandService } from '../CommandProvider' import { useItemLinks } from '@/Hooks/useItemLinks' import RoundIconButton from '../Button/RoundIconButton' +import { remToPx } from '@/Utils' type Props = { linkingController: LinkingController @@ -112,6 +113,32 @@ 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 linkContainerRef = useRef(null) + useEffect(() => { + const container = linkContainerRef.current + + if (!container) { + return + } + + const resizeObserver = new ResizeObserver(() => { + const LinkInputHeightInRem = 1.75 + + if (container.clientHeight > remToPx(LinkInputHeightInRem)) { + setCanShowContainerToggle(true) + } else { + setCanShowContainerToggle(false) + } + }) + + resizeObserver.observe(linkContainerRef.current) + + return () => { + resizeObserver.disconnect() + } + }, []) + return (
{visibleItems.map((link) => (
- {itemsToDisplay.length > 0 && !hideToggle && ( + {itemsToDisplay.length > 0 && !hideToggle && canShowContainerToggle && ( { reader.readAsDataURL(blob) }) } + +export const remToPx = (rem: number) => { + return rem * parseFloat(getComputedStyle(document.documentElement).fontSize) +} From 6c7aa41cb5ad47fcfd51a16e7b915e52374ba9e8 Mon Sep 17 00:00:00 2001 From: Aman Harwara Date: Fri, 5 May 2023 22:03:04 +0530 Subject: [PATCH 2/2] chore: refactor --- .../LinkedItems/ItemLinkAutocompleteInput.tsx | 187 +++++++++--------- .../LinkedItemBubblesContainer.tsx | 14 +- packages/web/src/javascripts/Utils/Utils.ts | 4 - 3 files changed, 105 insertions(+), 100 deletions(-) diff --git a/packages/web/src/javascripts/Components/LinkedItems/ItemLinkAutocompleteInput.tsx b/packages/web/src/javascripts/Components/LinkedItems/ItemLinkAutocompleteInput.tsx index 5c6662ac126..4fb108f26f3 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 0ebf2c535ce..3465119f737 100644 --- a/packages/web/src/javascripts/Components/LinkedItems/LinkedItemBubblesContainer.tsx +++ b/packages/web/src/javascripts/Components/LinkedItems/LinkedItemBubblesContainer.tsx @@ -13,7 +13,6 @@ import { FOCUS_TAGS_INPUT_COMMAND, keyboardStringForShortcut } from '@standardno import { useCommandService } from '../CommandProvider' import { useItemLinks } from '@/Hooks/useItemLinks' import RoundIconButton from '../Button/RoundIconButton' -import { remToPx } from '@/Utils' type Props = { linkingController: LinkingController @@ -114,18 +113,18 @@ const LinkedItemBubblesContainer = ({ item, linkingController, hideToggle = fals 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) { + if (!container || !linkInput) { return } const resizeObserver = new ResizeObserver(() => { - const LinkInputHeightInRem = 1.75 - - if (container.clientHeight > remToPx(LinkInputHeightInRem)) { + if (container.clientHeight > linkInput.clientHeight) { setCanShowContainerToggle(true) } else { setCanShowContainerToggle(false) @@ -139,6 +138,8 @@ const LinkedItemBubblesContainer = ({ item, linkingController, hideToggle = fals } }, []) + const shouldHideToggle = hideToggle || (!canShowContainerToggle && !isCollapsed) + return (
- {itemsToDisplay.length > 0 && !hideToggle && canShowContainerToggle && ( + {itemsToDisplay.length > 0 && !shouldHideToggle && ( { reader.readAsDataURL(blob) }) } - -export const remToPx = (rem: number) => { - return rem * parseFloat(getComputedStyle(document.documentElement).fontSize) -}