fix: add arrows navigation for tag hint
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { AppState } from '@/ui_models/app_state';
|
import { AppState } from '@/ui_models/app_state';
|
||||||
import { observer } from 'mobx-react-lite';
|
import { observer } from 'mobx-react-lite';
|
||||||
|
import { useRef, useEffect } from 'preact/hooks';
|
||||||
import { Icon } from './Icon';
|
import { Icon } from './Icon';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -9,6 +10,10 @@ type Props = {
|
|||||||
|
|
||||||
export const AutocompleteTagHint = observer(
|
export const AutocompleteTagHint = observer(
|
||||||
({ appState, closeOnBlur }: Props) => {
|
({ appState, closeOnBlur }: Props) => {
|
||||||
|
const { autocompleteTagHintFocused } = appState.noteTags;
|
||||||
|
|
||||||
|
const hintRef = useRef<HTMLButtonElement>();
|
||||||
|
|
||||||
const { autocompleteSearchQuery, autocompleteTagResults } =
|
const { autocompleteSearchQuery, autocompleteTagResults } =
|
||||||
appState.noteTags;
|
appState.noteTags;
|
||||||
|
|
||||||
@@ -16,16 +21,46 @@ export const AutocompleteTagHint = observer(
|
|||||||
await appState.noteTags.createAndAddNewTag();
|
await appState.noteTags.createAndAddNewTag();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onFocus = () => {
|
||||||
|
appState.noteTags.setAutocompleteTagHintFocused(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onBlur = (event: FocusEvent) => {
|
||||||
|
closeOnBlur(event);
|
||||||
|
appState.noteTags.setAutocompleteTagHintFocused(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onKeyDown = (event: KeyboardEvent) => {
|
||||||
|
if (event.key === 'ArrowUp') {
|
||||||
|
if (autocompleteTagResults.length > 0) {
|
||||||
|
const lastTagResult =
|
||||||
|
autocompleteTagResults[autocompleteTagResults.length - 1];
|
||||||
|
appState.noteTags.setFocusedTagResultUuid(lastTagResult.uuid);
|
||||||
|
} else {
|
||||||
|
appState.noteTags.setAutocompleteInputFocused(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (autocompleteTagHintFocused) {
|
||||||
|
hintRef.current.focus();
|
||||||
|
}
|
||||||
|
}, [appState.noteTags, autocompleteTagHintFocused]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{autocompleteTagResults.length > 0 && (
|
{autocompleteTagResults.length > 0 && (
|
||||||
<div className="h-1px my-2 bg-border"></div>
|
<div className="h-1px my-2 bg-border"></div>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
|
ref={hintRef}
|
||||||
type="button"
|
type="button"
|
||||||
className="sn-dropdown-item"
|
className="sn-dropdown-item"
|
||||||
onClick={onTagHintClick}
|
onClick={onTagHintClick}
|
||||||
onBlur={closeOnBlur}
|
onFocus={onFocus}
|
||||||
|
onBlur={onBlur}
|
||||||
|
onKeyDown={onKeyDown}
|
||||||
>
|
>
|
||||||
<span>Create new tag:</span>
|
<span>Create new tag:</span>
|
||||||
<span className="bg-contrast rounded text-xs color-text py-1 pl-1 pr-2 flex items-center ml-2">
|
<span className="bg-contrast rounded text-xs color-text py-1 pl-1 pr-2 flex items-center ml-2">
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ export const AutocompleteTagInput = observer(({ appState }: Props) => {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (autocompleteTagResults.length > 0) {
|
if (autocompleteTagResults.length > 0) {
|
||||||
appState.noteTags.setFocusedTagResultUuid(autocompleteTagResults[0].uuid);
|
appState.noteTags.setFocusedTagResultUuid(autocompleteTagResults[0].uuid);
|
||||||
|
} else if (autocompleteTagHintVisible) {
|
||||||
|
appState.noteTags.setAutocompleteTagHintFocused(true);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -12,7 +12,12 @@ type Props = {
|
|||||||
|
|
||||||
export const AutocompleteTagResult = observer(
|
export const AutocompleteTagResult = observer(
|
||||||
({ appState, tagResult, closeOnBlur }: Props) => {
|
({ appState, tagResult, closeOnBlur }: Props) => {
|
||||||
const { autocompleteSearchQuery, autocompleteTagResults, focusedTagResultUuid } = appState.noteTags;
|
const {
|
||||||
|
autocompleteSearchQuery,
|
||||||
|
autocompleteTagHintVisible,
|
||||||
|
autocompleteTagResults,
|
||||||
|
focusedTagResultUuid,
|
||||||
|
} = appState.noteTags;
|
||||||
|
|
||||||
const tagResultRef = useRef<HTMLButtonElement>();
|
const tagResultRef = useRef<HTMLButtonElement>();
|
||||||
|
|
||||||
@@ -23,7 +28,10 @@ export const AutocompleteTagResult = observer(
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onKeyDown = (event: KeyboardEvent) => {
|
const onKeyDown = (event: KeyboardEvent) => {
|
||||||
const tagResultIndex = appState.noteTags.getTagIndex(tagResult, autocompleteTagResults);
|
const tagResultIndex = appState.noteTags.getTagIndex(
|
||||||
|
tagResult,
|
||||||
|
autocompleteTagResults
|
||||||
|
);
|
||||||
switch (event.key) {
|
switch (event.key) {
|
||||||
case 'ArrowUp':
|
case 'ArrowUp':
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -35,7 +43,14 @@ export const AutocompleteTagResult = observer(
|
|||||||
break;
|
break;
|
||||||
case 'ArrowDown':
|
case 'ArrowDown':
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
appState.noteTags.focusNextTagResult(tagResult);
|
if (
|
||||||
|
tagResultIndex === autocompleteTagResults.length - 1 &&
|
||||||
|
autocompleteTagHintVisible
|
||||||
|
) {
|
||||||
|
appState.noteTags.setAutocompleteTagHintFocused(true);
|
||||||
|
} else {
|
||||||
|
appState.noteTags.focusNextTagResult(tagResult);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { AppState } from './app_state';
|
|||||||
export class NoteTagsState {
|
export class NoteTagsState {
|
||||||
autocompleteInputFocused = false;
|
autocompleteInputFocused = false;
|
||||||
autocompleteSearchQuery = '';
|
autocompleteSearchQuery = '';
|
||||||
|
autocompleteTagHintFocused = false;
|
||||||
autocompleteTagResults: SNTag[] = [];
|
autocompleteTagResults: SNTag[] = [];
|
||||||
focusedTagResultUuid: UuidString | undefined = undefined;
|
focusedTagResultUuid: UuidString | undefined = undefined;
|
||||||
focusedTagUuid: UuidString | undefined = undefined;
|
focusedTagUuid: UuidString | undefined = undefined;
|
||||||
@@ -20,6 +21,7 @@ export class NoteTagsState {
|
|||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
autocompleteInputFocused: observable,
|
autocompleteInputFocused: observable,
|
||||||
autocompleteSearchQuery: observable,
|
autocompleteSearchQuery: observable,
|
||||||
|
autocompleteTagHintFocused: observable,
|
||||||
autocompleteTagResults: observable,
|
autocompleteTagResults: observable,
|
||||||
focusedTagUuid: observable,
|
focusedTagUuid: observable,
|
||||||
focusedTagResultUuid: observable,
|
focusedTagResultUuid: observable,
|
||||||
@@ -33,6 +35,7 @@ export class NoteTagsState {
|
|||||||
focusPreviousTag: action,
|
focusPreviousTag: action,
|
||||||
setAutocompleteInputFocused: action,
|
setAutocompleteInputFocused: action,
|
||||||
setAutocompleteSearchQuery: action,
|
setAutocompleteSearchQuery: action,
|
||||||
|
setAutocompleteTagHintFocused: action,
|
||||||
setAutocompleteTagResults: action,
|
setAutocompleteTagResults: action,
|
||||||
setFocusedTagResultUuid: action,
|
setFocusedTagResultUuid: action,
|
||||||
setFocusedTagUuid: action,
|
setFocusedTagUuid: action,
|
||||||
@@ -69,6 +72,10 @@ export class NoteTagsState {
|
|||||||
this.autocompleteSearchQuery = query;
|
this.autocompleteSearchQuery = query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setAutocompleteTagHintFocused(focused: boolean): void {
|
||||||
|
this.autocompleteTagHintFocused = focused;
|
||||||
|
}
|
||||||
|
|
||||||
setAutocompleteTagResults(results: SNTag[]): void {
|
setAutocompleteTagResults(results: SNTag[]): void {
|
||||||
this.autocompleteTagResults = results;
|
this.autocompleteTagResults = results;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user