chore: only show links container toggle if container can be truncated (#2326)

This commit is contained in:
Aman Harwara
2023-05-05 22:09:33 +05:30
committed by GitHub
parent 8c1f4bd064
commit 60e51d334c
2 changed files with 129 additions and 92 deletions

View File

@@ -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<HTMLInputElement>,
) => {
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<HTMLInputElement | null>(null)
const inputRef = useRef<HTMLInputElement | null>(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 (
<div>
<form onSubmit={onFormSubmit}>
<label>
<VisuallyHidden>Link tags, notes or files</VisuallyHidden>
<Combobox
return (
<div>
<form onSubmit={onFormSubmit}>
<label>
<VisuallyHidden>Link tags, notes or files</VisuallyHidden>
<Combobox
store={combobox}
placeholder="Link tags, notes, files..."
className={classNames(
`${tagsLinkedToItem.length > 0 ? 'w-80' : 'mr-10 w-70'}`,
'h-7 w-70 bg-transparent text-sm text-text focus:border-b-2 focus:border-info focus:shadow-none focus:outline-none lg:text-xs',
)}
title={hoverLabel}
id={ElementIds.ItemLinkAutocompleteInput}
ref={mergeRefs([inputRef, forwardedRef])}
onFocus={handleFocus}
onKeyDown={onKeyDown}
/>
</label>
<ComboboxPopover
store={combobox}
placeholder="Link tags, notes, files..."
className={classNames(
`${tagsLinkedToItem.length > 0 ? 'w-80' : 'mr-10 w-70'}`,
'h-7 w-70 bg-transparent text-sm text-text focus:border-b-2 focus:border-info focus:shadow-none focus:outline-none lg:text-xs',
'z-dropdown-menu max-h-[var(--popover-available-height)] w-[var(--popover-anchor-width)] overflow-y-auto rounded bg-default py-2 shadow-main',
unlinkedItems.length === 0 && !shouldShowCreateTag && 'hidden',
)}
title={hoverLabel}
id={ElementIds.ItemLinkAutocompleteInput}
ref={inputRef}
onFocus={handleFocus}
onKeyDown={onKeyDown}
/>
</label>
<ComboboxPopover
store={combobox}
className={classNames(
'z-dropdown-menu max-h-[var(--popover-available-height)] w-[var(--popover-anchor-width)] overflow-y-auto rounded bg-default py-2 shadow-main',
unlinkedItems.length === 0 && !shouldShowCreateTag && 'hidden',
)}
>
{unlinkedItems.map((result) => {
const cannotLinkItem = !isEntitledToNoteLinking && result instanceof SNNote
>
{unlinkedItems.map((result) => {
const cannotLinkItem = !isEntitledToNoteLinking && result instanceof SNNote
return (
return (
<ComboboxItem
key={result.uuid}
className="flex w-full cursor-pointer items-center justify-between gap-4 overflow-hidden py-2 px-3 hover:bg-contrast hover:text-foreground [&[data-active-item]]:bg-info-backdrop"
hideOnClick
onClick={() => {
linkItems(item, result).catch(console.error)
combobox.setValue('')
}}
>
<LinkedItemMeta item={result} searchQuery={searchQuery} />
{cannotLinkItem && <Icon type={PremiumFeatureIconName} className="ml-auto flex-shrink-0 text-info" />}
</ComboboxItem>
)
})}
{shouldShowCreateTag && (
<ComboboxItem
key={result.uuid}
className="flex w-full cursor-pointer items-center justify-between gap-4 overflow-hidden py-2 px-3 hover:bg-contrast hover:text-foreground [&[data-active-item]]:bg-info-backdrop"
hideOnClick
as={Slot}
onClick={() => {
linkItems(item, result).catch(console.error)
void createAndAddNewTag(searchQuery)
combobox.setValue('')
}}
>
<LinkedItemMeta item={result} searchQuery={searchQuery} />
{cannotLinkItem && <Icon type={PremiumFeatureIconName} className="ml-auto flex-shrink-0 text-info" />}
<LinkedItemSearchResultsAddTagOption searchQuery={searchQuery} />
</ComboboxItem>
)
})}
{shouldShowCreateTag && (
<ComboboxItem
hideOnClick
as={Slot}
onClick={() => {
void createAndAddNewTag(searchQuery)
combobox.setValue('')
}}
>
<LinkedItemSearchResultsAddTagOption searchQuery={searchQuery} />
</ComboboxItem>
)}
</ComboboxPopover>
</form>
</div>
)
}
)}
</ComboboxPopover>
</form>
</div>
)
},
)
export default observer(ItemLinkAutocompleteInput)

View File

@@ -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<HTMLInputElement>(null)
const linkContainerRef = useRef<HTMLDivElement>(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 (
<div
className={classNames(
@@ -126,6 +154,7 @@ const LinkedItemBubblesContainer = ({ item, linkingController, hideToggle = fals
allItemsLinkedToItem.length || notesLinkingToItem.length ? 'mt-1' : 'mt-0.5',
isCollapsed ? 'overflow-hidden' : 'flex-wrap',
)}
ref={linkContainerRef}
>
{visibleItems.map((link) => (
<LinkedItemBubble
@@ -148,9 +177,10 @@ const LinkedItemBubblesContainer = ({ item, linkingController, hideToggle = fals
setFocusedId={setFocusedId}
hoverLabel={`Focus input to add a link (${shortcut})`}
item={item}
ref={linkInputRef}
/>
</div>
{itemsToDisplay.length > 0 && !hideToggle && (
{itemsToDisplay.length > 0 && !shouldHideToggle && (
<RoundIconButton
id="toggle-linking-container"
label="Toggle linked items container"