refactor: store refs in components

This commit is contained in:
Antonella Sgarlatta
2021-06-03 17:45:43 -03:00
parent 3d0c8d5cce
commit 2b40ccfe13
4 changed files with 139 additions and 121 deletions

View File

@@ -12,12 +12,10 @@ type Props = {
export const AutocompleteTagInput = observer(({ appState }: Props) => {
const {
autocompleteInputFocused,
autocompleteSearchQuery,
autocompleteTagHintVisible,
autocompleteTagResults,
autocompleteTagResultElements,
autocompleteInputElement,
tagElements,
tags,
} = appState.noteTags;
@@ -26,6 +24,7 @@ export const AutocompleteTagInput = observer(({ appState }: Props) => {
useState<number | 'auto'>('auto');
const dropdownRef = useRef<HTMLDivElement>();
const inputRef = useRef<HTMLInputElement>();
const [closeOnBlur] = useCloseOnBlur(dropdownRef, (visible: boolean) => {
setDropdownVisible(visible);
@@ -33,11 +32,9 @@ export const AutocompleteTagInput = observer(({ appState }: Props) => {
});
const showDropdown = () => {
if (autocompleteInputElement) {
const { clientHeight } = document.documentElement;
const inputRect = autocompleteInputElement.getBoundingClientRect();
setDropdownMaxHeight(clientHeight - inputRect.bottom - 32 * 2);
}
const { clientHeight } = document.documentElement;
const inputRect = inputRef.current.getBoundingClientRect();
setDropdownMaxHeight(clientHeight - inputRect.bottom - 32 * 2);
setDropdownVisible(true);
};
@@ -55,14 +52,15 @@ export const AutocompleteTagInput = observer(({ appState }: Props) => {
const onKeyDown = (event: KeyboardEvent) => {
switch (event.key) {
case 'Backspace':
if (autocompleteSearchQuery === '' && tagElements.length > 0) {
tagElements[tagElements.length - 1]?.focus();
case 'ArrowLeft':
if (autocompleteSearchQuery === '' && tags.length > 0) {
appState.noteTags.setFocusedTagUuid(tags[tags.length - 1].uuid);
}
break;
case 'ArrowDown':
event.preventDefault();
if (autocompleteTagResultElements.length > 0) {
autocompleteTagResultElements[0]?.focus();
if (autocompleteTagResults.length > 0) {
appState.noteTags.setFocusedTagResultUuid(autocompleteTagResults[0].uuid);
}
break;
default:
@@ -70,10 +68,27 @@ export const AutocompleteTagInput = observer(({ appState }: Props) => {
}
};
const onFocus = () => {
showDropdown();
appState.noteTags.setAutocompleteInputFocused(true);
};
const onBlur = (event: FocusEvent) => {
closeOnBlur(event);
appState.noteTags.setAutocompleteInputFocused(false);
};
useEffect(() => {
appState.noteTags.searchActiveNoteAutocompleteTags();
}, [appState.noteTags]);
useEffect(() => {
if (autocompleteInputFocused) {
inputRef.current.focus();
appState.noteTags.setAutocompleteInputFocused(false);
}
}, [appState.noteTags, autocompleteInputFocused]);
return (
<form
onSubmit={onFormSubmit}
@@ -81,18 +96,14 @@ export const AutocompleteTagInput = observer(({ appState }: Props) => {
>
<Disclosure open={dropdownVisible} onChange={showDropdown}>
<input
ref={(element) => {
if (element) {
appState.noteTags.setAutocompleteInputElement(element);
}
}}
ref={inputRef}
className="w-80 bg-default text-xs color-text no-border h-7 focus:outline-none focus:shadow-none focus:border-bottom"
value={autocompleteSearchQuery}
onChange={onSearchQueryChange}
type="text"
placeholder="Add tag"
onBlur={closeOnBlur}
onFocus={showDropdown}
onBlur={onBlur}
onFocus={onFocus}
onKeyDown={onKeyDown}
/>
{dropdownVisible && (

View File

@@ -1,6 +1,7 @@
import { AppState } from '@/ui_models/app_state';
import { SNTag } from '@standardnotes/snjs';
import { observer } from 'mobx-react-lite';
import { useEffect, useRef } from 'preact/hooks';
import { Icon } from './Icon';
type Props = {
@@ -11,7 +12,9 @@ type Props = {
export const AutocompleteTagResult = observer(
({ appState, tagResult, closeOnBlur }: Props) => {
const { autocompleteInputElement, autocompleteSearchQuery, autocompleteTagResults } = appState.noteTags;
const { autocompleteSearchQuery, autocompleteTagResults, focusedTagResultUuid } = appState.noteTags;
const tagResultRef = useRef<HTMLButtonElement>();
const onTagOptionClick = async (tag: SNTag) => {
await appState.noteTags.addTagToActiveNote(tag);
@@ -24,34 +27,44 @@ export const AutocompleteTagResult = observer(
case 'ArrowUp':
event.preventDefault();
if (tagResultIndex === 0) {
autocompleteInputElement?.focus();
appState.noteTags.setAutocompleteInputFocused(true);
} else {
appState.noteTags.getPreviousAutocompleteTagResultElement(tagResult)?.focus();
appState.noteTags.focusPreviousTagResult(tagResult);
}
break;
case 'ArrowDown':
event.preventDefault();
appState.noteTags.getNextAutocompleteTagResultElement(tagResult)?.focus();
appState.noteTags.focusNextTagResult(tagResult);
break;
default:
return;
}
};
const onFocus = () => {
appState.noteTags.setFocusedTagResultUuid(tagResult.uuid);
};
const onBlur = (event: FocusEvent) => {
closeOnBlur(event);
appState.noteTags.setFocusedTagResultUuid(undefined);
};
useEffect(() => {
if (focusedTagResultUuid === tagResult.uuid) {
tagResultRef.current.focus();
appState.noteTags.setFocusedTagResultUuid(undefined);
}
}, [appState.noteTags, focusedTagResultUuid, tagResult]);
return (
<button
ref={(element) => {
if (element) {
appState.noteTags.setAutocompleteTagResultElement(
tagResult,
element
);
}
}}
ref={tagResultRef}
type="button"
className="sn-dropdown-item"
onClick={() => onTagOptionClick(tagResult)}
onBlur={closeOnBlur}
onFocus={onFocus}
onBlur={onBlur}
onKeyDown={onKeyDown}
>
<Icon type="hashtag" className="color-neutral mr-2 min-h-5 min-w-5" />

View File

@@ -1,5 +1,5 @@
import { Icon } from './Icon';
import { useRef, useState } from 'preact/hooks';
import { useEffect, useRef, useState } from 'preact/hooks';
import { AppState } from '@/ui_models/app_state';
import { SNTag } from '@standardnotes/snjs/dist/@types';
import { observer } from 'mobx-react-lite';
@@ -10,11 +10,16 @@ type Props = {
};
export const NoteTag = observer(({ appState, tag }: Props) => {
const { focusedTagUuid, tags } = appState.noteTags;
const [showDeleteButton, setShowDeleteButton] = useState(false);
const [tagClicked, setTagClicked] = useState(false);
const deleteTagRef = useRef<HTMLButtonElement>();
const tagRef = useRef<HTMLButtonElement>();
const deleteTag = () => {
appState.noteTags.focusPreviousTag(tag);
appState.noteTags.removeTagFromActiveNote(tag);
};
@@ -34,39 +39,49 @@ export const NoteTag = observer(({ appState, tag }: Props) => {
};
const onFocus = () => {
appState.noteTags.setFocusedTagUuid(tag.uuid);
setShowDeleteButton(true);
};
const onBlur = (event: FocusEvent) => {
const relatedTarget = event.relatedTarget as Node;
if (relatedTarget !== deleteTagRef.current) {
appState.noteTags.setFocusedTagUuid(undefined);
setShowDeleteButton(false);
}
};
const onKeyDown = (event: KeyboardEvent) => {
const tagIndex = appState.noteTags.getTagIndex(tag, tags);
switch (event.key) {
case 'Backspace':
deleteTag();
break;
case 'ArrowLeft':
appState.noteTags.getPreviousTagElement(tag)?.focus();
appState.noteTags.focusPreviousTag(tag);
break;
case 'ArrowRight':
appState.noteTags.getNextTagElement(tag)?.focus();
if (tagIndex === tags.length - 1) {
appState.noteTags.setAutocompleteInputFocused(true);
} else {
appState.noteTags.focusNextTag(tag);
}
break;
default:
return;
}
};
useEffect(() => {
if (focusedTagUuid === tag.uuid) {
tagRef.current.focus();
appState.noteTags.setFocusedTagUuid(undefined);
}
}, [appState.noteTags, focusedTagUuid, tag]);
return (
<button
ref={(element) => {
if (element) {
appState.noteTags.setTagElement(tag, element);
}
}}
ref={tagRef}
className="sn-tag pl-1 pr-2 mr-2"
onClick={onTagClick}
onKeyDown={onKeyDown}