Skip to content

Commit

Permalink
feat: remove overflowed tags feature
Browse files Browse the repository at this point in the history
  • Loading branch information
Antonella Sgarlatta committed Jun 2, 2021
1 parent ba1f151 commit 02f3c7c
Show file tree
Hide file tree
Showing 4 changed files with 8 additions and 190 deletions.
20 changes: 2 additions & 18 deletions app/assets/javascripts/components/AutocompleteTagInput.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { WebApplication } from '@/ui_models/application';
import { SNTag } from '@standardnotes/snjs';
import { FunctionalComponent } from 'preact';
import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
import { useEffect, useRef, useState } from 'preact/hooks';
import { Icon } from './Icon';
import { Disclosure, DisclosurePanel } from '@reach/disclosure';
import { useCloseOnBlur } from './utils';
Expand All @@ -16,7 +16,7 @@ export const AutocompleteTagInput: FunctionalComponent<Props> = ({
application,
appState,
}) => {
const { tagElements, tags, tagsContainerMaxWidth, tagsOverflowed } = appState.activeNote;
const { tagElements } = appState.activeNote;

const [searchQuery, setSearchQuery] = useState('');
const [dropdownVisible, setDropdownVisible] = useState(false);
Expand Down Expand Up @@ -82,19 +82,6 @@ export const AutocompleteTagInput: FunctionalComponent<Props> = ({
await createAndAddNewTag();
};

const reloadInputOverflowed = useCallback(() => {
const overflowed = !tagsOverflowed && appState.activeNote.isElementOverflowed(inputRef.current);
appState.activeNote.setInputOverflowed(overflowed);
}, [appState.activeNote, tagsOverflowed]);

useEffect(() => {
reloadInputOverflowed();
}, [
reloadInputOverflowed,
tagsContainerMaxWidth,
tags,
]);

useEffect(() => {
setHintVisible(
searchQuery !== '' && !tagResults.some((tag) => tag.title === searchQuery)
Expand All @@ -111,7 +98,6 @@ export const AutocompleteTagInput: FunctionalComponent<Props> = ({
onChange={onSearchQueryChange}
type="text"
placeholder="Add tag"
tabIndex={tagsOverflowed ? -1 : 0}
onBlur={closeOnBlur}
onFocus={showDropdown}
onKeyUp={(event) => {
Expand Down Expand Up @@ -139,7 +125,6 @@ export const AutocompleteTagInput: FunctionalComponent<Props> = ({
className="sn-dropdown-item"
onClick={() => onTagOptionClick(tag)}
onBlur={closeOnBlur}
tabIndex={tagsOverflowed ? -1 : 0}
>
<Icon type="hashtag" className="color-neutral mr-2 min-h-5 min-w-5" />
<span className="whitespace-nowrap overflow-hidden overflow-ellipsis">
Expand Down Expand Up @@ -177,7 +162,6 @@ export const AutocompleteTagInput: FunctionalComponent<Props> = ({
className="sn-dropdown-item"
onClick={onTagHintClick}
onBlur={closeOnBlur}
tabIndex={tagsOverflowed ? -1 : 0}
>
<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">
Expand Down
15 changes: 1 addition & 14 deletions app/assets/javascripts/components/NoteTag.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Icon } from './Icon';
import { FunctionalComponent } from 'preact';
import { useCallback, useRef, useState } from 'preact/hooks';
import { useRef, useState } from 'preact/hooks';
import { AppState } from '@/ui_models/app_state';
import { SNTag } from '@standardnotes/snjs/dist/@types';
import { useEffect } from 'react';
Expand All @@ -13,12 +13,9 @@ type Props = {

export const NoteTag: FunctionalComponent<Props> = ({ appState, tag }) => {
const {
tags,
tagsContainerExpanded,
tagsContainerMaxWidth,
} = appState.activeNote;

const [overflowed, setOverflowed] = useState(false);
const [contextMenuOpen, setContextMenuOpen] = useState(false);
const [contextMenuPosition, setContextMenuPosition] = useState({ top: 0, left: 0 });

Expand All @@ -36,15 +33,6 @@ export const NoteTag: FunctionalComponent<Props> = ({ appState, tag }) => {
appState.setSelectedTag(tag);
};

const reloadOverflowed = useCallback(() => {
const overflowed = appState.activeNote.isTagOverflowed(tag);
setOverflowed(overflowed);
}, [appState.activeNote, tag]);

useEffect(() => {
reloadOverflowed();
}, [reloadOverflowed, tags, tagsContainerExpanded, tagsContainerMaxWidth]);

const contextMenuListener = (event: MouseEvent) => {
event.preventDefault();
setContextMenuPosition({
Expand Down Expand Up @@ -78,7 +66,6 @@ export const NoteTag: FunctionalComponent<Props> = ({ appState, tag }) => {
deleteTag();
}
}}
tabIndex={overflowed ? -1 : 0}
onBlur={closeOnBlur}
>
<Icon type="hashtag" className="sn-icon--small color-neutral mr-1" />
Expand Down
77 changes: 5 additions & 72 deletions app/assets/javascripts/components/NoteTagsContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { AppState } from '@/ui_models/app_state';
import { observer } from 'mobx-react-lite';
import { toDirective, useCloseOnClickOutside } from './utils';
import { toDirective } from './utils';
import { AutocompleteTagInput } from './AutocompleteTagInput';
import { WebApplication } from '@/ui_models/application';
import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
import { NoteTag } from './NoteTag';
import { useEffect } from 'preact/hooks';

type Props = {
application: WebApplication;
Expand All @@ -13,72 +13,17 @@ type Props = {

const NoteTagsContainer = observer(({ application, appState }: Props) => {
const {
inputOverflowed,
overflowCountPosition,
overflowedTagsCount,
tagElements,
tags,
tagsContainerMaxWidth,
tagsContainerExpanded,
tagsOverflowed,
} = appState.activeNote;

const [expandedContainerHeight, setExpandedContainerHeight] = useState(0);

const tagsContainerRef = useRef<HTMLDivElement>();
const overflowButtonRef = useRef<HTMLButtonElement>();

useCloseOnClickOutside(tagsContainerRef, (expanded: boolean) => {
if (tagsContainerExpanded) {
appState.activeNote.setTagsContainerExpanded(expanded);
}
});

const reloadExpandedContainerHeight = useCallback(() => {
setExpandedContainerHeight(tagsContainerRef.current.scrollHeight);
}, []);

useEffect(() => {
appState.activeNote.reloadTagsContainerLayout();
reloadExpandedContainerHeight();
}, [
appState.activeNote,
reloadExpandedContainerHeight,
tags,
tagsContainerMaxWidth,
]);

useEffect(() => {
let tagResizeObserver: ResizeObserver;
if (ResizeObserver) {
tagResizeObserver = new ResizeObserver(() => {
appState.activeNote.reloadTagsContainerLayout();
reloadExpandedContainerHeight();
});
tagElements.forEach(
(tagElement) => tagElement && tagResizeObserver.observe(tagElement)
);
}

return () => {
if (tagResizeObserver) {
tagResizeObserver.disconnect();
}
};
}, [appState.activeNote, reloadExpandedContainerHeight, tagElements]);
appState.activeNote.reloadTagsContainerMaxWidth();
}, [appState.activeNote]);

return (
<div
className={`flex transition-height duration-150 relative ${
inputOverflowed ? 'h-18' : 'h-9'
}`}
style={tagsContainerExpanded ? { height: expandedContainerHeight } : {}}
>
<div
ref={tagsContainerRef}
className={`absolute bg-default flex flex-wrap pl-1 -ml-1 ${
inputOverflowed ? 'h-18' : 'h-9'
} ${tagsContainerExpanded || !tagsOverflowed ? '' : 'overflow-hidden'}`}
className="bg-default flex flex-wrap pl-1 -ml-1"
style={{
maxWidth: tagsContainerMaxWidth,
}}
Expand All @@ -92,18 +37,6 @@ const NoteTagsContainer = observer(({ application, appState }: Props) => {
))}
<AutocompleteTagInput application={application} appState={appState} />
</div>
{tagsOverflowed && (
<button
ref={overflowButtonRef}
type="button"
className="sn-tag ml-1 absolute"
onClick={() => appState.activeNote.setTagsContainerExpanded(true)}
style={{ left: overflowCountPosition }}
>
+{overflowedTagsCount}
</button>
)}
</div>
);
});

Expand Down
86 changes: 0 additions & 86 deletions app/assets/javascripts/ui_models/app_state/active_note_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,47 +5,29 @@ import {
} from '@standardnotes/snjs';
import {
action,
computed,
makeObservable,
observable,
} from 'mobx';
import { WebApplication } from '../application';
import { AppState } from './app_state';

export class ActiveNoteState {
inputOverflowed = false;
overflowCountPosition = 0;
overflowedTagsCount = 0;
tagElements: (HTMLButtonElement | undefined)[] = [];
tagFocused = false;
tags: SNTag[] = [];
tagsContainerMaxWidth: number | 'auto' = 0;
tagsContainerExpanded = false;

constructor(
private application: WebApplication,
private appState: AppState,
appEventListeners: (() => void)[]
) {
makeObservable(this, {
inputOverflowed: observable,
overflowCountPosition: observable,
overflowedTagsCount: observable,
tagElements: observable,
tagFocused: observable,
tags: observable,
tagsContainerExpanded: observable,
tagsContainerMaxWidth: observable,

tagsOverflowed: computed,

setInputOverflowed: action,
setOverflowCountPosition: action,
setOverflowedTagsCount: action,
setTagElement: action,
setTagFocused: action,
setTags: action,
setTagsContainerExpanded: action,
setTagsContainerMaxWidth: action,
reloadTags: action,
});
Expand All @@ -60,34 +42,13 @@ export class ActiveNoteState {
get activeNote(): SNNote | undefined {
return this.appState.notes.activeEditor?.note;
}

get tagsOverflowed(): boolean {
return this.overflowedTagsCount > 0 && !this.tagsContainerExpanded;
}

setInputOverflowed(overflowed: boolean): void {
this.inputOverflowed = overflowed;
}

setOverflowCountPosition(position: number): void {
this.overflowCountPosition = position;
}

setOverflowedTagsCount(count: number): void {
this.overflowedTagsCount = count;
}

setTagElement(tag: SNTag, element: HTMLButtonElement): void {
const tagIndex = this.getTagIndex(tag);
if (tagIndex > -1) {
this.tagElements.splice(tagIndex, 1, element);
}
}

setTagFocused(focused: boolean): void {
this.tagFocused = focused;
}

setTagElements(elements: (HTMLButtonElement | undefined)[]): void {
this.tagElements = elements;
}
Expand All @@ -96,10 +57,6 @@ export class ActiveNoteState {
this.tags = tags;
}

setTagsContainerExpanded(expanded: boolean): void {
this.tagsContainerExpanded = expanded;
}

setTagsContainerMaxWidth(width: number): void {
this.tagsContainerMaxWidth = width;
}
Expand All @@ -122,25 +79,6 @@ export class ActiveNoteState {
}
}

isElementOverflowed(element: HTMLElement): boolean {
if (
this.tagElements.length === 0 ||
!this.tagElements[0]
) {
return false;
}
const firstTagTop = this.tagElements[0].offsetTop;
return element.offsetTop > firstTagTop;
}

isTagOverflowed(tag: SNTag): boolean {
if (this.tagsContainerExpanded) {
return false;
}
const tagElement = this.getTagElement(tag);
return tagElement ? this.isElementOverflowed(tagElement) : false;
}

reloadTags(): void {
const { activeNote } = this;
if (activeNote) {
Expand All @@ -151,24 +89,6 @@ export class ActiveNoteState {
}
}

reloadOverflowCountPosition(): void {
const lastVisibleTagIndex = this.tagElements.findIndex(tagElement => tagElement && this.isElementOverflowed(tagElement)) - 1;
if (lastVisibleTagIndex > -1 && this.tagElements.length > lastVisibleTagIndex) {
const lastVisibleTagElement = this.tagElements[lastVisibleTagIndex];
if (lastVisibleTagElement) {
const { offsetLeft, offsetWidth } = lastVisibleTagElement;
this.setOverflowCountPosition(offsetLeft + offsetWidth);
}
}
}

reloadOverflowedTagsCount(): void {
const count = this.tagElements.filter((tagElement) =>
tagElement && this.isElementOverflowed(tagElement)
).length;
this.setOverflowedTagsCount(count);
}

reloadTagsContainerMaxWidth(): void {
const EDITOR_ELEMENT_ID = 'editor-column';
const defaultFontSize = parseFloat(window.getComputedStyle(
Expand All @@ -183,12 +103,6 @@ export class ActiveNoteState {
}
}

reloadTagsContainerLayout(): void {
this.reloadTagsContainerMaxWidth();
this.reloadOverflowedTagsCount();
this.reloadOverflowCountPosition();
}

async addTagToActiveNote(tag: SNTag): Promise<void> {
const { activeNote } = this;
if (activeNote) {
Expand Down

0 comments on commit 02f3c7c

Please sign in to comment.