diff --git a/app/assets/javascripts/components/NoteTag.tsx b/app/assets/javascripts/components/NoteTag.tsx index dae7b727f..6d15c04d8 100644 --- a/app/assets/javascripts/components/NoteTag.tsx +++ b/app/assets/javascripts/components/NoteTag.tsx @@ -1,26 +1,31 @@ import { Icon } from './Icon'; -import { FunctionalComponent, RefObject } from 'preact'; +import { FunctionalComponent } from 'preact'; import { useCallback, useRef, useState } from 'preact/hooks'; import { AppState } from '@/ui_models/app_state'; import { SNTag } from '@standardnotes/snjs/dist/@types'; import { useEffect } from 'react'; +import { useCloseOnBlur, useCloseOnClickOutside } from './utils'; type Props = { appState: AppState; tag: SNTag; - overflowButtonRef: RefObject; }; -export const NoteTag: FunctionalComponent = ({ appState, tag, overflowButtonRef }) => { +export const NoteTag: FunctionalComponent = ({ appState, tag }) => { const { tags, tagsContainerMaxWidth, } = appState.activeNote; const [overflowed, setOverflowed] = useState(false); - const [showDeleteButton, setShowDeleteButton] = useState(false); + const [contextMenuOpen, setContextMenuOpen] = useState(false); + const [contextMenuPosition, setContextMenuPosition] = useState({ top: 0, left: 0 }); - const deleteTagRef = useRef(); + const contextMenuRef = useRef(); + const tagRef = useRef(); + + const [closeOnBlur] = useCloseOnBlur(contextMenuRef, setContextMenuOpen); + useCloseOnClickOutside(contextMenuRef, setContextMenuOpen); const deleteTag = async () => { await appState.activeNote.removeTagFromActiveNote(tag); @@ -36,21 +41,6 @@ export const NoteTag: FunctionalComponent = ({ appState, tag, overflowBut appState.setSelectedTag(tag); }; - const onFocus = () => { - appState.activeNote.setTagFocused(true); - setShowDeleteButton(true); - }; - - const onBlur = (event: FocusEvent) => { - const relatedTarget = event.relatedTarget as Node; - if (relatedTarget === overflowButtonRef.current) { - (event.target as HTMLButtonElement).focus(); - } else if (relatedTarget !== deleteTagRef.current) { - appState.activeNote.setTagFocused(false); - setShowDeleteButton(false); - } - }; - const reloadOverflowed = useCallback(() => { const overflowed = appState.activeNote.isTagOverflowed(tag); setOverflowed(overflowed); @@ -60,44 +50,67 @@ export const NoteTag: FunctionalComponent = ({ appState, tag, overflowBut reloadOverflowed(); }, [reloadOverflowed, tags, tagsContainerMaxWidth]); + const contextMenuListener = (event: MouseEvent) => { + event.preventDefault(); + setContextMenuPosition({ + top: event.clientY, + left: event.clientX, + }); + setContextMenuOpen(true); + }; + + useEffect(() => { + tagRef.current.addEventListener('contextmenu', contextMenuListener); + return () => { + tagRef.current.removeEventListener('contextmenu', contextMenuListener); + }; + }, []); + return ( - + {contextMenuOpen && ( +
- - + +
)} - + ); }; diff --git a/app/assets/javascripts/components/NoteTagsContainer.tsx b/app/assets/javascripts/components/NoteTagsContainer.tsx index aa4cee774..8627df267 100644 --- a/app/assets/javascripts/components/NoteTagsContainer.tsx +++ b/app/assets/javascripts/components/NoteTagsContainer.tsx @@ -88,7 +88,6 @@ const NoteTagsContainer = observer(({ application, appState }: Props) => { key={tag.uuid} appState={appState} tag={tag} - overflowButtonRef={overflowButtonRef} /> ))} diff --git a/app/assets/stylesheets/_sn.scss b/app/assets/stylesheets/_sn.scss index 049c47392..30cbe2b88 100644 --- a/app/assets/stylesheets/_sn.scss +++ b/app/assets/stylesheets/_sn.scss @@ -190,6 +190,10 @@ min-width: 1.25rem; } +.min-w-40 { + min-width: 10rem; +} + .h-1px { height: 1px; } @@ -366,6 +370,10 @@ @extend .duration-150; @extend .slide-down-animation; } + + &.sn-dropdown--small { + @extend .min-w-40; + } } /** Lesser specificity will give priority to reach's styles */