fix: adjust layout for all zoom values and font sizes
This commit is contained in:
@@ -12,28 +12,25 @@ type Props = {
|
||||
appState: AppState;
|
||||
};
|
||||
|
||||
const TAGS_ROW_HEIGHT = 36;
|
||||
const MIN_OVERFLOW_TOP = 76;
|
||||
const TAG_RIGHT_MARGIN = 8;
|
||||
|
||||
const NoteTags = observer(({ application, appState }: Props) => {
|
||||
const {
|
||||
overflowedTagsCount,
|
||||
tags,
|
||||
tagsContainerPosition,
|
||||
tagsContainerMaxWidth,
|
||||
tagsContainerExpanded,
|
||||
tagsOverflowed,
|
||||
} = appState.activeNote;
|
||||
|
||||
const [containerHeight, setContainerHeight] =
|
||||
useState(TAGS_ROW_HEIGHT);
|
||||
|
||||
const [expandedContainerHeight, setExpandedContainerHeight] = useState(0);
|
||||
const [lastVisibleTagIndex, setLastVisibleTagIndex] =
|
||||
useState<number | null>(null);
|
||||
const [overflowCountPosition, setOverflowCountPosition] = useState(0);
|
||||
|
||||
const containerRef = useRef<HTMLDivElement>();
|
||||
const tagsContainerRef = useRef<HTMLDivElement>();
|
||||
const tagsRef = useRef<HTMLButtonElement[]>([]);
|
||||
const overflowButtonRef = useRef<HTMLButtonElement>();
|
||||
|
||||
tagsRef.current = [];
|
||||
|
||||
useCloseOnClickOutside(tagsContainerRef, (expanded: boolean) => {
|
||||
@@ -42,16 +39,16 @@ const NoteTags = observer(({ application, appState }: Props) => {
|
||||
}
|
||||
});
|
||||
|
||||
const onTagBackspacePress = async (tag: SNTag) => {
|
||||
const onTagBackspacePress = async (tag: SNTag, index: number) => {
|
||||
await appState.activeNote.removeTagFromActiveNote(tag);
|
||||
|
||||
if (tagsRef.current.length > 1) {
|
||||
tagsRef.current[tagsRef.current.length - 1].focus();
|
||||
if (index > 0) {
|
||||
tagsRef.current[index - 1].focus();
|
||||
}
|
||||
};
|
||||
|
||||
const onTagClick = (clickedTag: SNTag) => {
|
||||
const tagIndex = tags.findIndex(tag => tag.uuid === clickedTag.uuid);
|
||||
const tagIndex = tags.findIndex((tag) => tag.uuid === clickedTag.uuid);
|
||||
if (tagsRef.current[tagIndex] === document.activeElement) {
|
||||
appState.setSelectedTag(clickedTag);
|
||||
}
|
||||
@@ -65,31 +62,29 @@ const NoteTags = observer(({ application, appState }: Props) => {
|
||||
if (tagsContainerExpanded) {
|
||||
return false;
|
||||
}
|
||||
return tagElement.getBoundingClientRect().top >= MIN_OVERFLOW_TOP;
|
||||
const firstTagTop = tagsRef.current[0].offsetTop;
|
||||
return tagElement.offsetTop > firstTagTop;
|
||||
},
|
||||
[tagsContainerExpanded]
|
||||
);
|
||||
|
||||
const reloadOverflowCountPosition = useCallback(() => {
|
||||
const reloadLastVisibleTagIndex = useCallback(() => {
|
||||
if (tagsContainerExpanded) {
|
||||
return tags.length - 1;
|
||||
}
|
||||
const firstOverflowedTagIndex = tagsRef.current.findIndex((tagElement) =>
|
||||
isTagOverflowed(tagElement)
|
||||
);
|
||||
if (tagsContainerExpanded || firstOverflowedTagIndex < 1) {
|
||||
return;
|
||||
if (firstOverflowedTagIndex > -1) {
|
||||
setLastVisibleTagIndex(firstOverflowedTagIndex - 1);
|
||||
} else {
|
||||
setLastVisibleTagIndex(null);
|
||||
}
|
||||
const previousTagRect =
|
||||
tagsRef.current[firstOverflowedTagIndex - 1].getBoundingClientRect();
|
||||
const position =
|
||||
previousTagRect.right - (tagsContainerPosition ?? 0) + TAG_RIGHT_MARGIN;
|
||||
setOverflowCountPosition(position);
|
||||
}, [isTagOverflowed, tagsContainerExpanded, tagsContainerPosition]);
|
||||
}, [isTagOverflowed, tags, tagsContainerExpanded]);
|
||||
|
||||
const reloadContainersHeight = useCallback(() => {
|
||||
const containerHeight = tagsContainerExpanded
|
||||
? tagsContainerRef.current.scrollHeight
|
||||
: TAGS_ROW_HEIGHT;
|
||||
setContainerHeight(containerHeight);
|
||||
}, [tagsContainerExpanded]);
|
||||
const reloadExpandedContainersHeight = useCallback(() => {
|
||||
setExpandedContainerHeight(tagsContainerRef.current.scrollHeight);
|
||||
}, []);
|
||||
|
||||
const reloadOverflowCount = useCallback(() => {
|
||||
const count = tagsRef.current.filter((tagElement) =>
|
||||
@@ -98,72 +93,108 @@ const NoteTags = observer(({ application, appState }: Props) => {
|
||||
appState.activeNote.setOverflowedTagsCount(count);
|
||||
}, [appState.activeNote, isTagOverflowed]);
|
||||
|
||||
const reloadOverflowCountPosition = useCallback(() => {
|
||||
if (tagsContainerExpanded || !lastVisibleTagIndex) {
|
||||
return;
|
||||
}
|
||||
const { offsetLeft: lastVisibleTagLeft, clientWidth: lastVisibleTagWidth } =
|
||||
tagsRef.current[lastVisibleTagIndex];
|
||||
console.log(tagsRef.current[0].offsetLeft);
|
||||
setOverflowCountPosition(lastVisibleTagLeft + lastVisibleTagWidth);
|
||||
}, [lastVisibleTagIndex, tagsContainerExpanded]);
|
||||
|
||||
const expandTags = () => {
|
||||
appState.activeNote.setTagsContainerExpanded(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const reloadTagsContainerLayout = useCallback(() => {
|
||||
appState.activeNote.reloadTagsContainerLayout();
|
||||
reloadOverflowCountPosition();
|
||||
reloadContainersHeight();
|
||||
reloadLastVisibleTagIndex();
|
||||
reloadExpandedContainersHeight();
|
||||
reloadOverflowCount();
|
||||
reloadOverflowCountPosition();
|
||||
}, [
|
||||
appState.activeNote,
|
||||
reloadOverflowCountPosition,
|
||||
reloadContainersHeight,
|
||||
reloadLastVisibleTagIndex,
|
||||
reloadExpandedContainersHeight,
|
||||
reloadOverflowCount,
|
||||
tags,
|
||||
reloadOverflowCountPosition,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
reloadTagsContainerLayout;
|
||||
}, [
|
||||
reloadTagsContainerLayout,
|
||||
tags,
|
||||
tagsContainerMaxWidth,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
let tagResizeObserver: ResizeObserver;
|
||||
if (ResizeObserver) {
|
||||
tagResizeObserver = new ResizeObserver(() => {
|
||||
reloadTagsContainerLayout();
|
||||
});
|
||||
tagsRef.current.forEach((tagElement) => tagResizeObserver.observe(tagElement));
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (tagResizeObserver) {
|
||||
tagResizeObserver.disconnect();
|
||||
}
|
||||
};
|
||||
}, [reloadTagsContainerLayout]);
|
||||
|
||||
const tagClass = `h-6 bg-contrast border-0 rounded text-xs color-text py-1 pr-2 flex items-center
|
||||
mt-2 cursor-pointer hover:bg-secondary-contrast focus:bg-secondary-contrast`;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex transition-height duration-150"
|
||||
className="flex transition-height duration-150 h-9 relative"
|
||||
ref={containerRef}
|
||||
style={{ height: containerHeight }}
|
||||
style={tagsContainerExpanded ? { height: expandedContainerHeight } : {}}
|
||||
>
|
||||
<div
|
||||
ref={tagsContainerRef}
|
||||
className={`absolute flex flex-wrap pl-1 -ml-1 ${
|
||||
tagsContainerExpanded ? '' : 'overflow-hidden'
|
||||
className={`absolute bg-default h-9 flex flex-wrap pl-1 -ml-1 ${
|
||||
tagsContainerExpanded ? '' : 'overflow-y-hidden'
|
||||
}`}
|
||||
style={{
|
||||
maxWidth: tagsContainerMaxWidth,
|
||||
height: TAGS_ROW_HEIGHT,
|
||||
}}
|
||||
>
|
||||
{tags.map((tag: SNTag, index: number) => (
|
||||
<button
|
||||
className={`${tagClass} pl-1 mr-2 transition-opacity duration-150 ${
|
||||
isTagOverflowed(tagsRef.current[index])
|
||||
? 'opacity-0'
|
||||
: 'opacity-1'
|
||||
}`}
|
||||
style={{ maxWidth: tagsContainerMaxWidth }}
|
||||
ref={(element) => {
|
||||
if (element) {
|
||||
tagsRef.current[index] = element;
|
||||
}
|
||||
}}
|
||||
onClick={() => onTagClick(tag)}
|
||||
onKeyUp={(event) => {
|
||||
if (event.key === 'Backspace') {
|
||||
onTagBackspacePress(tag);
|
||||
}
|
||||
}}
|
||||
tabIndex={isTagOverflowed(tagsRef.current[index]) ? -1 : 0}
|
||||
>
|
||||
<Icon
|
||||
type="hashtag"
|
||||
className="sn-icon--small color-neutral mr-1"
|
||||
/>
|
||||
<span className="whitespace-nowrap overflow-hidden overflow-ellipsis">
|
||||
{tag.title}
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
{tags.map((tag: SNTag, index: number) => {
|
||||
const overflowed =
|
||||
!tagsContainerExpanded &&
|
||||
lastVisibleTagIndex &&
|
||||
index > lastVisibleTagIndex;
|
||||
return (
|
||||
<button
|
||||
className={`${tagClass} pl-1 mr-2`}
|
||||
style={{ maxWidth: tagsContainerMaxWidth }}
|
||||
ref={(element) => {
|
||||
if (element) {
|
||||
tagsRef.current[index] = element;
|
||||
}
|
||||
}}
|
||||
onClick={() => onTagClick(tag)}
|
||||
onKeyUp={(event) => {
|
||||
if (event.key === 'Backspace') {
|
||||
onTagBackspacePress(tag, index);
|
||||
}
|
||||
}}
|
||||
tabIndex={overflowed ? -1 : 0}
|
||||
>
|
||||
<Icon
|
||||
type="hashtag"
|
||||
className="sn-icon--small color-neutral mr-1"
|
||||
/>
|
||||
<span className="whitespace-nowrap overflow-y-hidden overflow-ellipsis">
|
||||
{tag.title}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
<AutocompleteTagInput
|
||||
application={application}
|
||||
appState={appState}
|
||||
@@ -175,9 +206,9 @@ const NoteTags = observer(({ application, appState }: Props) => {
|
||||
<button
|
||||
ref={overflowButtonRef}
|
||||
type="button"
|
||||
className={`${tagClass} pl-2 absolute`}
|
||||
style={{ left: overflowCountPosition }}
|
||||
className={`${tagClass} pl-2 ml-1 absolute`}
|
||||
onClick={expandTags}
|
||||
style={{ left: overflowCountPosition }}
|
||||
>
|
||||
+{overflowedTagsCount}
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user