feat: use panel width event instead of ResizeObserver
This commit is contained in:
@@ -10,7 +10,7 @@ import { AppState } from '@/ui_models/app_state';
|
||||
type Props = {
|
||||
application: WebApplication;
|
||||
appState: AppState;
|
||||
tagsRef: RefObject<HTMLButtonElement[]>
|
||||
tagsRef: RefObject<HTMLButtonElement[]>;
|
||||
};
|
||||
|
||||
export const AutocompleteTagInput: FunctionalComponent<Props> = ({
|
||||
@@ -25,7 +25,7 @@ export const AutocompleteTagInput: FunctionalComponent<Props> = ({
|
||||
const [hintVisible, setHintVisible] = useState(true);
|
||||
|
||||
const getActiveNoteTagResults = (query: string) => {
|
||||
const { activeNote } = appState.notes;
|
||||
const { activeNote } = appState.activeNote;
|
||||
return application.searchTags(query, activeNote);
|
||||
};
|
||||
|
||||
@@ -41,10 +41,13 @@ export const AutocompleteTagInput: FunctionalComponent<Props> = ({
|
||||
setTagResults(getActiveNoteTagResults(''));
|
||||
};
|
||||
|
||||
const [closeOnBlur, setLockCloseOnBlur] = useCloseOnBlur(dropdownRef, (visible: boolean) => {
|
||||
setDropdownVisible(visible);
|
||||
clearResults();
|
||||
});
|
||||
const [closeOnBlur, setLockCloseOnBlur] = useCloseOnBlur(
|
||||
dropdownRef,
|
||||
(visible: boolean) => {
|
||||
setDropdownVisible(visible);
|
||||
clearResults();
|
||||
}
|
||||
);
|
||||
|
||||
const showDropdown = () => {
|
||||
const { clientHeight } = document.documentElement;
|
||||
@@ -61,7 +64,7 @@ export const AutocompleteTagInput: FunctionalComponent<Props> = ({
|
||||
|
||||
const onTagOptionClick = async (tag: SNTag) => {
|
||||
setLockCloseOnBlur(true);
|
||||
await appState.notes.addTagToActiveNote(tag);
|
||||
await appState.activeNote.addTagToActiveNote(tag);
|
||||
inputRef.current.focus();
|
||||
setTagResults(getActiveNoteTagResults(searchQuery));
|
||||
setLockCloseOnBlur(false);
|
||||
@@ -69,7 +72,7 @@ export const AutocompleteTagInput: FunctionalComponent<Props> = ({
|
||||
|
||||
const createAndAddNewTag = async () => {
|
||||
const newTag = await application.findOrCreateTag(searchQuery);
|
||||
await appState.notes.addTagToActiveNote(newTag);
|
||||
await appState.activeNote.addTagToActiveNote(newTag);
|
||||
clearResults();
|
||||
inputRef.current.focus();
|
||||
};
|
||||
@@ -84,7 +87,9 @@ export const AutocompleteTagInput: FunctionalComponent<Props> = ({
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setHintVisible(searchQuery !== '' && !tagResults.some((tag) => tag.title === searchQuery));
|
||||
setHintVisible(
|
||||
searchQuery !== '' && !tagResults.some((tag) => tag.title === searchQuery)
|
||||
);
|
||||
}, [tagResults, searchQuery]);
|
||||
|
||||
return (
|
||||
@@ -100,7 +105,12 @@ export const AutocompleteTagInput: FunctionalComponent<Props> = ({
|
||||
onBlur={closeOnBlur}
|
||||
onFocus={showDropdown}
|
||||
onKeyUp={(event) => {
|
||||
if (event.key === 'Backspace' && searchQuery === '' && tagsRef.current && tagsRef.current.length > 1) {
|
||||
if (
|
||||
event.key === 'Backspace' &&
|
||||
searchQuery === '' &&
|
||||
tagsRef.current &&
|
||||
tagsRef.current.length > 1
|
||||
) {
|
||||
tagsRef.current[tagsRef.current.length - 1].focus();
|
||||
}
|
||||
}}
|
||||
@@ -128,7 +138,8 @@ export const AutocompleteTagInput: FunctionalComponent<Props> = ({
|
||||
<span
|
||||
key={index}
|
||||
className={
|
||||
substring.toLowerCase() === searchQuery.toLowerCase()
|
||||
substring.toLowerCase() ===
|
||||
searchQuery.toLowerCase()
|
||||
? 'font-bold whitespace-pre-wrap'
|
||||
: 'whitespace-pre-wrap'
|
||||
}
|
||||
@@ -151,13 +162,12 @@ export const AutocompleteTagInput: FunctionalComponent<Props> = ({
|
||||
onClick={onTagHintClick}
|
||||
onBlur={closeOnBlur}
|
||||
>
|
||||
<span>
|
||||
Create new tag:
|
||||
</span>
|
||||
<span
|
||||
className="bg-contrast rounded text-xs color-text p-1 flex ml-2"
|
||||
>
|
||||
<Icon type="hashtag" className="sn-icon--small color-neutral mr-1" />
|
||||
<span>Create new tag:</span>
|
||||
<span className="bg-contrast rounded text-xs color-text p-1 flex ml-2">
|
||||
<Icon
|
||||
type="hashtag"
|
||||
className="sn-icon--small color-neutral mr-1"
|
||||
/>
|
||||
{searchQuery}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { toDirective } from './utils';
|
||||
import { Icon } from './Icon';
|
||||
import { AutocompleteTagInput } from './AutocompleteTagInput';
|
||||
import { WebApplication } from '@/ui_models/application';
|
||||
import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
|
||||
import { useEffect, useRef, useState } from 'preact/hooks';
|
||||
import { SNTag } from '@standardnotes/snjs';
|
||||
|
||||
type Props = {
|
||||
@@ -13,14 +13,14 @@ type Props = {
|
||||
};
|
||||
|
||||
const TAGS_ROW_RIGHT_MARGIN = 92;
|
||||
const TAGS_ROW_HEIGHT = 32;
|
||||
const TAGS_ROW_HEIGHT = 36;
|
||||
const MIN_OVERFLOW_TOP = 76;
|
||||
const TAG_RIGHT_MARGIN = 8;
|
||||
const TAGS_RIGHT_MARGIN = 8;
|
||||
|
||||
const NoteTags = observer(({ application, appState }: Props) => {
|
||||
const { activeNoteTags } = appState.notes;
|
||||
const [tagsContainerMaxWidth, setTagsContainerMaxWidth] =
|
||||
useState<number | 'auto'>('auto');
|
||||
const { tags, tagsContainerPosition, tagsContainerMaxWidth } =
|
||||
appState.activeNote;
|
||||
|
||||
const [overflowedTagsCount, setOverflowedTagsCount] = useState(0);
|
||||
const [overflowCountPosition, setOverflowCountPosition] = useState(0);
|
||||
const [tagsContainerCollapsed, setTagsContainerCollapsed] = useState(true);
|
||||
@@ -32,23 +32,28 @@ const NoteTags = observer(({ application, appState }: Props) => {
|
||||
tagsRef.current = [];
|
||||
|
||||
const onTagBackspacePress = async (tag: SNTag) => {
|
||||
await appState.notes.removeTagFromActiveNote(tag);
|
||||
await appState.activeNote.removeTagFromActiveNote(tag);
|
||||
|
||||
if (tagsRef.current.length > 1) {
|
||||
tagsRef.current[tagsRef.current.length - 1].focus();
|
||||
}
|
||||
};
|
||||
|
||||
const reloadOverflowCount = useCallback(() => {
|
||||
const editorElement = document.getElementById('editor-column');
|
||||
const expandTags = () => {
|
||||
setContainerHeight(tagsContainerRef.current.scrollHeight);
|
||||
setTagsContainerCollapsed(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
appState.activeNote.reloadTagsContainerLayout();
|
||||
let overflowCount = 0;
|
||||
for (const [index, tagElement] of tagsRef.current.entries()) {
|
||||
if (tagElement.getBoundingClientRect().top >= MIN_OVERFLOW_TOP) {
|
||||
if (overflowCount === 0) {
|
||||
setOverflowCountPosition(
|
||||
tagsRef.current[index - 1].getBoundingClientRect().right -
|
||||
(editorElement ? editorElement.getBoundingClientRect().left : 0) +
|
||||
TAG_RIGHT_MARGIN
|
||||
(tagsContainerPosition ?? 0) +
|
||||
TAGS_RIGHT_MARGIN
|
||||
);
|
||||
}
|
||||
overflowCount += 1;
|
||||
@@ -59,34 +64,12 @@ const NoteTags = observer(({ application, appState }: Props) => {
|
||||
if (!tagsContainerCollapsed) {
|
||||
setContainerHeight(tagsContainerRef.current.scrollHeight);
|
||||
}
|
||||
}, [tagsContainerCollapsed]);
|
||||
|
||||
const expandTags = () => {
|
||||
setContainerHeight(tagsContainerRef.current.scrollHeight);
|
||||
setTagsContainerCollapsed(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const editorElement = document.getElementById('editor-column');
|
||||
const resizeObserver = new ResizeObserver((entries) => {
|
||||
const entry = entries[0];
|
||||
const { width } = entry.contentRect;
|
||||
setTagsContainerMaxWidth(width);
|
||||
reloadOverflowCount();
|
||||
});
|
||||
|
||||
if (editorElement) {
|
||||
resizeObserver.observe(editorElement);
|
||||
}
|
||||
|
||||
return () => {
|
||||
resizeObserver.disconnect();
|
||||
};
|
||||
}, [reloadOverflowCount]);
|
||||
|
||||
useEffect(() => {
|
||||
reloadOverflowCount();
|
||||
}, [activeNoteTags, reloadOverflowCount]);
|
||||
}, [
|
||||
appState.activeNote,
|
||||
tags,
|
||||
tagsContainerCollapsed,
|
||||
tagsContainerPosition,
|
||||
]);
|
||||
|
||||
const tagClass = `bg-contrast border-0 rounded text-xs color-text py-1 pr-2 flex items-center
|
||||
mt-2 mr-2 cursor-pointer hover:bg-secondary-contrast focus:bg-secondary-contrast`;
|
||||
@@ -99,7 +82,7 @@ const NoteTags = observer(({ application, appState }: Props) => {
|
||||
>
|
||||
<div
|
||||
ref={tagsContainerRef}
|
||||
className={`absolute flex flex-wrap ${
|
||||
className={`absolute flex flex-wrap pl-1 -ml-1 ${
|
||||
tagsContainerCollapsed ? 'overflow-hidden' : ''
|
||||
}`}
|
||||
style={{
|
||||
@@ -108,7 +91,7 @@ const NoteTags = observer(({ application, appState }: Props) => {
|
||||
marginRight: TAGS_ROW_RIGHT_MARGIN,
|
||||
}}
|
||||
>
|
||||
{activeNoteTags.map((tag, index) => (
|
||||
{tags.map((tag: SNTag, index: number) => (
|
||||
<button
|
||||
className={`${tagClass} pl-1`}
|
||||
style={{ maxWidth: tagsContainerMaxWidth }}
|
||||
|
||||
Reference in New Issue
Block a user