From 3db87099e00491d0635771dfb9442bc9f9e02026 Mon Sep 17 00:00:00 2001 From: Antonella Sgarlatta Date: Wed, 2 Jun 2021 12:20:39 -0300 Subject: [PATCH] fix: make sure input is visible when no tags are overflowed --- .../components/AutocompleteTagInput.tsx | 29 +++++++++++---- .../components/NoteTagsContainer.tsx | 36 ++++++++++--------- .../ui_models/app_state/active_note_state.ts | 33 ++++++++++------- app/assets/stylesheets/_sn.scss | 4 +++ 4 files changed, 67 insertions(+), 35 deletions(-) diff --git a/app/assets/javascripts/components/AutocompleteTagInput.tsx b/app/assets/javascripts/components/AutocompleteTagInput.tsx index af4b4c096..09cdfaa82 100644 --- a/app/assets/javascripts/components/AutocompleteTagInput.tsx +++ b/app/assets/javascripts/components/AutocompleteTagInput.tsx @@ -1,7 +1,7 @@ import { WebApplication } from '@/ui_models/application'; import { SNTag } from '@standardnotes/snjs'; import { FunctionalComponent, RefObject } from 'preact'; -import { useEffect, useRef, useState } from 'preact/hooks'; +import { useCallback, useEffect, useRef, useState } from 'preact/hooks'; import { Icon } from './Icon'; import { Disclosure, DisclosurePanel } from '@reach/disclosure'; import { useCloseOnBlur } from './utils'; @@ -11,15 +11,15 @@ type Props = { application: WebApplication; appState: AppState; tagsRef: RefObject; - tabIndex: number; }; export const AutocompleteTagInput: FunctionalComponent = ({ application, appState, tagsRef, - tabIndex, }) => { + const { tags, tagsContainerMaxWidth, tagsOverflowed } = appState.activeNote; + const [searchQuery, setSearchQuery] = useState(''); const [dropdownVisible, setDropdownVisible] = useState(false); const [dropdownMaxHeight, setDropdownMaxHeight] = @@ -84,6 +84,23 @@ export const AutocompleteTagInput: FunctionalComponent = ({ await createAndAddNewTag(); }; + const reloadInputOverflowed = useCallback(() => { + let overflowed = false; + if (!tagsOverflowed && tagsRef.current && tagsRef.current.length > 0) { + const firstTagTop = tagsRef.current[0].offsetTop; + overflowed = inputRef.current.offsetTop > firstTagTop; + } + appState.activeNote.setInputOverflowed(overflowed); + }, [appState.activeNote, tagsOverflowed, tagsRef]); + + useEffect(() => { + reloadInputOverflowed(); + }, [ + reloadInputOverflowed, + tagsContainerMaxWidth, + tags, + ]); + useEffect(() => { setHintVisible( searchQuery !== '' && !tagResults.some((tag) => tag.title === searchQuery) @@ -100,7 +117,7 @@ export const AutocompleteTagInput: FunctionalComponent = ({ onChange={onSearchQueryChange} type="text" placeholder="Add tag" - tabIndex={tabIndex} + tabIndex={tagsOverflowed ? -1 : 0} onBlur={closeOnBlur} onFocus={showDropdown} onKeyUp={(event) => { @@ -129,7 +146,7 @@ export const AutocompleteTagInput: FunctionalComponent = ({ className="sn-dropdown-item" onClick={() => onTagOptionClick(tag)} onBlur={closeOnBlur} - tabIndex={tabIndex} + tabIndex={tagsOverflowed ? -1 : 0} > @@ -167,7 +184,7 @@ export const AutocompleteTagInput: FunctionalComponent = ({ className="sn-dropdown-item" onClick={onTagHintClick} onBlur={closeOnBlur} - tabIndex={tabIndex} + tabIndex={tagsOverflowed ? -1 : 0} > Create new tag: diff --git a/app/assets/javascripts/components/NoteTagsContainer.tsx b/app/assets/javascripts/components/NoteTagsContainer.tsx index 0b5724417..0c82e6948 100644 --- a/app/assets/javascripts/components/NoteTagsContainer.tsx +++ b/app/assets/javascripts/components/NoteTagsContainer.tsx @@ -14,13 +14,14 @@ type Props = { const NoteTagsContainer = observer(({ application, appState }: Props) => { const { + inputOverflowed, overflowedTagsCount, tags, tagsContainerMaxWidth, tagsContainerExpanded, tagsOverflowed, } = appState.activeNote; - + const [expandedContainerHeight, setExpandedContainerHeight] = useState(0); const [lastVisibleTagIndex, setLastVisibleTagIndex] = useState(null); @@ -82,8 +83,10 @@ const NoteTagsContainer = observer(({ application, appState }: Props) => { return; } if (tagsRef.current[lastVisibleTagIndex]) { - const { offsetLeft: lastVisibleTagLeft, clientWidth: lastVisibleTagWidth } = - tagsRef.current[lastVisibleTagIndex]; + const { + offsetLeft: lastVisibleTagLeft, + clientWidth: lastVisibleTagWidth, + } = tagsRef.current[lastVisibleTagIndex]; setOverflowCountPosition(lastVisibleTagLeft + lastVisibleTagWidth); } }, [lastVisibleTagIndex, tagsContainerExpanded]); @@ -108,11 +111,7 @@ const NoteTagsContainer = observer(({ application, appState }: Props) => { useEffect(() => { reloadTagsContainerLayout(); - }, [ - reloadTagsContainerLayout, - tags, - tagsContainerMaxWidth, - ]); + }, [reloadTagsContainerLayout, tags, tagsContainerMaxWidth]); useEffect(() => { let tagResizeObserver: ResizeObserver; @@ -120,7 +119,9 @@ const NoteTagsContainer = observer(({ application, appState }: Props) => { tagResizeObserver = new ResizeObserver(() => { reloadTagsContainerLayout(); }); - tagsRef.current.forEach((tagElement) => tagResizeObserver.observe(tagElement)); + tagsRef.current.forEach((tagElement) => + tagResizeObserver.observe(tagElement) + ); } return () => { @@ -132,15 +133,17 @@ const NoteTagsContainer = observer(({ application, appState }: Props) => { return (
{ index={index} tag={tag} maxWidth={tagsContainerMaxWidth} - overflowed={!tagsContainerExpanded && + overflowed={ + !tagsContainerExpanded && !!lastVisibleTagIndex && - index > lastVisibleTagIndex} + index > lastVisibleTagIndex + } /> ))}
{tagsOverflowed && ( diff --git a/app/assets/javascripts/ui_models/app_state/active_note_state.ts b/app/assets/javascripts/ui_models/app_state/active_note_state.ts index e987d3a9b..75dd594c9 100644 --- a/app/assets/javascripts/ui_models/app_state/active_note_state.ts +++ b/app/assets/javascripts/ui_models/app_state/active_note_state.ts @@ -13,11 +13,12 @@ import { WebApplication } from '../application'; import { AppState } from './app_state'; export class ActiveNoteState { + inputOverflowed = false; + overflowedTagsCount = 0; tags: SNTag[] = []; tagsContainerMaxWidth: number | 'auto' = 0; - tagsContainerExpanded = false; - overflowedTagsCount = 0; tagFocused = false; + tagsContainerExpanded = false; constructor( private application: WebApplication, @@ -25,18 +26,20 @@ export class ActiveNoteState { appEventListeners: (() => void)[] ) { makeObservable(this, { - tags: observable, - tagsContainerMaxWidth: observable, - tagsContainerExpanded: observable, + inputOverflowed: observable, overflowedTagsCount: observable, + tags: observable, tagFocused: observable, + tagsContainerExpanded: observable, + tagsContainerMaxWidth: observable, tagsOverflowed: computed, - setTagsContainerMaxWidth: action, - setTagsContainerExpanded: action, + setInputOverflowed: action, setOverflowedTagsCount: action, setTagFocused: action, + setTagsContainerExpanded: action, + setTagsContainerMaxWidth: action, reloadTags: action, }); @@ -58,12 +61,8 @@ export class ActiveNoteState { return this.overflowedTagsCount > 0 && !this.tagsContainerExpanded; } - setTagsContainerMaxWidth(width: number): void { - this.tagsContainerMaxWidth = width; - } - - setTagsContainerExpanded(expanded: boolean): void { - this.tagsContainerExpanded = expanded; + setInputOverflowed(overflowed: boolean): void { + this.inputOverflowed = overflowed; } setOverflowedTagsCount(count: number): void { @@ -74,6 +73,14 @@ export class ActiveNoteState { this.tagFocused = focused; } + setTagsContainerExpanded(expanded: boolean): void { + this.tagsContainerExpanded = expanded; + } + + setTagsContainerMaxWidth(width: number): void { + this.tagsContainerMaxWidth = width; + } + reloadTags(): void { const { activeNote } = this; if (activeNote) { diff --git a/app/assets/stylesheets/_sn.scss b/app/assets/stylesheets/_sn.scss index 8f691256a..3d74c1b44 100644 --- a/app/assets/stylesheets/_sn.scss +++ b/app/assets/stylesheets/_sn.scss @@ -222,6 +222,10 @@ height: 2.5rem; } +.h-18 { + height: 4.5rem; +} + .max-h-120 { max-height: 30rem; }