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 = {
|
type Props = {
|
||||||
application: WebApplication;
|
application: WebApplication;
|
||||||
appState: AppState;
|
appState: AppState;
|
||||||
tagsRef: RefObject<HTMLButtonElement[]>
|
tagsRef: RefObject<HTMLButtonElement[]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AutocompleteTagInput: FunctionalComponent<Props> = ({
|
export const AutocompleteTagInput: FunctionalComponent<Props> = ({
|
||||||
@@ -25,7 +25,7 @@ export const AutocompleteTagInput: FunctionalComponent<Props> = ({
|
|||||||
const [hintVisible, setHintVisible] = useState(true);
|
const [hintVisible, setHintVisible] = useState(true);
|
||||||
|
|
||||||
const getActiveNoteTagResults = (query: string) => {
|
const getActiveNoteTagResults = (query: string) => {
|
||||||
const { activeNote } = appState.notes;
|
const { activeNote } = appState.activeNote;
|
||||||
return application.searchTags(query, activeNote);
|
return application.searchTags(query, activeNote);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -41,10 +41,13 @@ export const AutocompleteTagInput: FunctionalComponent<Props> = ({
|
|||||||
setTagResults(getActiveNoteTagResults(''));
|
setTagResults(getActiveNoteTagResults(''));
|
||||||
};
|
};
|
||||||
|
|
||||||
const [closeOnBlur, setLockCloseOnBlur] = useCloseOnBlur(dropdownRef, (visible: boolean) => {
|
const [closeOnBlur, setLockCloseOnBlur] = useCloseOnBlur(
|
||||||
setDropdownVisible(visible);
|
dropdownRef,
|
||||||
clearResults();
|
(visible: boolean) => {
|
||||||
});
|
setDropdownVisible(visible);
|
||||||
|
clearResults();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const showDropdown = () => {
|
const showDropdown = () => {
|
||||||
const { clientHeight } = document.documentElement;
|
const { clientHeight } = document.documentElement;
|
||||||
@@ -61,7 +64,7 @@ export const AutocompleteTagInput: FunctionalComponent<Props> = ({
|
|||||||
|
|
||||||
const onTagOptionClick = async (tag: SNTag) => {
|
const onTagOptionClick = async (tag: SNTag) => {
|
||||||
setLockCloseOnBlur(true);
|
setLockCloseOnBlur(true);
|
||||||
await appState.notes.addTagToActiveNote(tag);
|
await appState.activeNote.addTagToActiveNote(tag);
|
||||||
inputRef.current.focus();
|
inputRef.current.focus();
|
||||||
setTagResults(getActiveNoteTagResults(searchQuery));
|
setTagResults(getActiveNoteTagResults(searchQuery));
|
||||||
setLockCloseOnBlur(false);
|
setLockCloseOnBlur(false);
|
||||||
@@ -69,7 +72,7 @@ export const AutocompleteTagInput: FunctionalComponent<Props> = ({
|
|||||||
|
|
||||||
const createAndAddNewTag = async () => {
|
const createAndAddNewTag = async () => {
|
||||||
const newTag = await application.findOrCreateTag(searchQuery);
|
const newTag = await application.findOrCreateTag(searchQuery);
|
||||||
await appState.notes.addTagToActiveNote(newTag);
|
await appState.activeNote.addTagToActiveNote(newTag);
|
||||||
clearResults();
|
clearResults();
|
||||||
inputRef.current.focus();
|
inputRef.current.focus();
|
||||||
};
|
};
|
||||||
@@ -84,7 +87,9 @@ export const AutocompleteTagInput: FunctionalComponent<Props> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setHintVisible(searchQuery !== '' && !tagResults.some((tag) => tag.title === searchQuery));
|
setHintVisible(
|
||||||
|
searchQuery !== '' && !tagResults.some((tag) => tag.title === searchQuery)
|
||||||
|
);
|
||||||
}, [tagResults, searchQuery]);
|
}, [tagResults, searchQuery]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -100,7 +105,12 @@ export const AutocompleteTagInput: FunctionalComponent<Props> = ({
|
|||||||
onBlur={closeOnBlur}
|
onBlur={closeOnBlur}
|
||||||
onFocus={showDropdown}
|
onFocus={showDropdown}
|
||||||
onKeyUp={(event) => {
|
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();
|
tagsRef.current[tagsRef.current.length - 1].focus();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@@ -128,7 +138,8 @@ export const AutocompleteTagInput: FunctionalComponent<Props> = ({
|
|||||||
<span
|
<span
|
||||||
key={index}
|
key={index}
|
||||||
className={
|
className={
|
||||||
substring.toLowerCase() === searchQuery.toLowerCase()
|
substring.toLowerCase() ===
|
||||||
|
searchQuery.toLowerCase()
|
||||||
? 'font-bold whitespace-pre-wrap'
|
? 'font-bold whitespace-pre-wrap'
|
||||||
: 'whitespace-pre-wrap'
|
: 'whitespace-pre-wrap'
|
||||||
}
|
}
|
||||||
@@ -151,13 +162,12 @@ export const AutocompleteTagInput: FunctionalComponent<Props> = ({
|
|||||||
onClick={onTagHintClick}
|
onClick={onTagHintClick}
|
||||||
onBlur={closeOnBlur}
|
onBlur={closeOnBlur}
|
||||||
>
|
>
|
||||||
<span>
|
<span>Create new tag:</span>
|
||||||
Create new tag:
|
<span className="bg-contrast rounded text-xs color-text p-1 flex ml-2">
|
||||||
</span>
|
<Icon
|
||||||
<span
|
type="hashtag"
|
||||||
className="bg-contrast rounded text-xs color-text p-1 flex ml-2"
|
className="sn-icon--small color-neutral mr-1"
|
||||||
>
|
/>
|
||||||
<Icon type="hashtag" className="sn-icon--small color-neutral mr-1" />
|
|
||||||
{searchQuery}
|
{searchQuery}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { toDirective } from './utils';
|
|||||||
import { Icon } from './Icon';
|
import { Icon } from './Icon';
|
||||||
import { AutocompleteTagInput } from './AutocompleteTagInput';
|
import { AutocompleteTagInput } from './AutocompleteTagInput';
|
||||||
import { WebApplication } from '@/ui_models/application';
|
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';
|
import { SNTag } from '@standardnotes/snjs';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -13,14 +13,14 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const TAGS_ROW_RIGHT_MARGIN = 92;
|
const TAGS_ROW_RIGHT_MARGIN = 92;
|
||||||
const TAGS_ROW_HEIGHT = 32;
|
const TAGS_ROW_HEIGHT = 36;
|
||||||
const MIN_OVERFLOW_TOP = 76;
|
const MIN_OVERFLOW_TOP = 76;
|
||||||
const TAG_RIGHT_MARGIN = 8;
|
const TAGS_RIGHT_MARGIN = 8;
|
||||||
|
|
||||||
const NoteTags = observer(({ application, appState }: Props) => {
|
const NoteTags = observer(({ application, appState }: Props) => {
|
||||||
const { activeNoteTags } = appState.notes;
|
const { tags, tagsContainerPosition, tagsContainerMaxWidth } =
|
||||||
const [tagsContainerMaxWidth, setTagsContainerMaxWidth] =
|
appState.activeNote;
|
||||||
useState<number | 'auto'>('auto');
|
|
||||||
const [overflowedTagsCount, setOverflowedTagsCount] = useState(0);
|
const [overflowedTagsCount, setOverflowedTagsCount] = useState(0);
|
||||||
const [overflowCountPosition, setOverflowCountPosition] = useState(0);
|
const [overflowCountPosition, setOverflowCountPosition] = useState(0);
|
||||||
const [tagsContainerCollapsed, setTagsContainerCollapsed] = useState(true);
|
const [tagsContainerCollapsed, setTagsContainerCollapsed] = useState(true);
|
||||||
@@ -32,23 +32,28 @@ const NoteTags = observer(({ application, appState }: Props) => {
|
|||||||
tagsRef.current = [];
|
tagsRef.current = [];
|
||||||
|
|
||||||
const onTagBackspacePress = async (tag: SNTag) => {
|
const onTagBackspacePress = async (tag: SNTag) => {
|
||||||
await appState.notes.removeTagFromActiveNote(tag);
|
await appState.activeNote.removeTagFromActiveNote(tag);
|
||||||
|
|
||||||
if (tagsRef.current.length > 1) {
|
if (tagsRef.current.length > 1) {
|
||||||
tagsRef.current[tagsRef.current.length - 1].focus();
|
tagsRef.current[tagsRef.current.length - 1].focus();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const reloadOverflowCount = useCallback(() => {
|
const expandTags = () => {
|
||||||
const editorElement = document.getElementById('editor-column');
|
setContainerHeight(tagsContainerRef.current.scrollHeight);
|
||||||
|
setTagsContainerCollapsed(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
appState.activeNote.reloadTagsContainerLayout();
|
||||||
let overflowCount = 0;
|
let overflowCount = 0;
|
||||||
for (const [index, tagElement] of tagsRef.current.entries()) {
|
for (const [index, tagElement] of tagsRef.current.entries()) {
|
||||||
if (tagElement.getBoundingClientRect().top >= MIN_OVERFLOW_TOP) {
|
if (tagElement.getBoundingClientRect().top >= MIN_OVERFLOW_TOP) {
|
||||||
if (overflowCount === 0) {
|
if (overflowCount === 0) {
|
||||||
setOverflowCountPosition(
|
setOverflowCountPosition(
|
||||||
tagsRef.current[index - 1].getBoundingClientRect().right -
|
tagsRef.current[index - 1].getBoundingClientRect().right -
|
||||||
(editorElement ? editorElement.getBoundingClientRect().left : 0) +
|
(tagsContainerPosition ?? 0) +
|
||||||
TAG_RIGHT_MARGIN
|
TAGS_RIGHT_MARGIN
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
overflowCount += 1;
|
overflowCount += 1;
|
||||||
@@ -59,34 +64,12 @@ const NoteTags = observer(({ application, appState }: Props) => {
|
|||||||
if (!tagsContainerCollapsed) {
|
if (!tagsContainerCollapsed) {
|
||||||
setContainerHeight(tagsContainerRef.current.scrollHeight);
|
setContainerHeight(tagsContainerRef.current.scrollHeight);
|
||||||
}
|
}
|
||||||
}, [tagsContainerCollapsed]);
|
}, [
|
||||||
|
appState.activeNote,
|
||||||
const expandTags = () => {
|
tags,
|
||||||
setContainerHeight(tagsContainerRef.current.scrollHeight);
|
tagsContainerCollapsed,
|
||||||
setTagsContainerCollapsed(false);
|
tagsContainerPosition,
|
||||||
};
|
]);
|
||||||
|
|
||||||
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]);
|
|
||||||
|
|
||||||
const tagClass = `bg-contrast border-0 rounded text-xs color-text py-1 pr-2 flex items-center
|
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`;
|
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
|
<div
|
||||||
ref={tagsContainerRef}
|
ref={tagsContainerRef}
|
||||||
className={`absolute flex flex-wrap ${
|
className={`absolute flex flex-wrap pl-1 -ml-1 ${
|
||||||
tagsContainerCollapsed ? 'overflow-hidden' : ''
|
tagsContainerCollapsed ? 'overflow-hidden' : ''
|
||||||
}`}
|
}`}
|
||||||
style={{
|
style={{
|
||||||
@@ -108,7 +91,7 @@ const NoteTags = observer(({ application, appState }: Props) => {
|
|||||||
marginRight: TAGS_ROW_RIGHT_MARGIN,
|
marginRight: TAGS_ROW_RIGHT_MARGIN,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{activeNoteTags.map((tag, index) => (
|
{tags.map((tag: SNTag, index: number) => (
|
||||||
<button
|
<button
|
||||||
className={`${tagClass} pl-1`}
|
className={`${tagClass} pl-1`}
|
||||||
style={{ maxWidth: tagsContainerMaxWidth }}
|
style={{ maxWidth: tagsContainerMaxWidth }}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ interface PanelResizerScope {
|
|||||||
index: number
|
index: number
|
||||||
minWidth: number
|
minWidth: number
|
||||||
onResizeFinish: () => ResizeFinishCallback
|
onResizeFinish: () => ResizeFinishCallback
|
||||||
|
onWidthEvent?: () => void
|
||||||
panelId: string
|
panelId: string
|
||||||
property: PanelSide
|
property: PanelSide
|
||||||
}
|
}
|
||||||
@@ -53,6 +54,7 @@ class PanelResizerCtrl implements PanelResizerScope {
|
|||||||
index!: number
|
index!: number
|
||||||
minWidth!: number
|
minWidth!: number
|
||||||
onResizeFinish!: () => ResizeFinishCallback
|
onResizeFinish!: () => ResizeFinishCallback
|
||||||
|
onWidthEvent?: () => () => void
|
||||||
panelId!: string
|
panelId!: string
|
||||||
property!: PanelSide
|
property!: PanelSide
|
||||||
|
|
||||||
@@ -102,6 +104,7 @@ class PanelResizerCtrl implements PanelResizerScope {
|
|||||||
|
|
||||||
$onDestroy() {
|
$onDestroy() {
|
||||||
(this.onResizeFinish as any) = undefined;
|
(this.onResizeFinish as any) = undefined;
|
||||||
|
(this.onWidthEvent as any) = undefined;
|
||||||
(this.control as any) = undefined;
|
(this.control as any) = undefined;
|
||||||
window.removeEventListener(WINDOW_EVENT_RESIZE, this.handleResize);
|
window.removeEventListener(WINDOW_EVENT_RESIZE, this.handleResize);
|
||||||
document.removeEventListener(MouseEventType.Move, this.onMouseMove);
|
document.removeEventListener(MouseEventType.Move, this.onMouseMove);
|
||||||
@@ -246,6 +249,9 @@ class PanelResizerCtrl implements PanelResizerScope {
|
|||||||
this.handleLeftEvent(event);
|
this.handleLeftEvent(event);
|
||||||
} else {
|
} else {
|
||||||
this.handleWidthEvent(event);
|
this.handleWidthEvent(event);
|
||||||
|
if (this.onWidthEvent) {
|
||||||
|
this.onWidthEvent()();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,6 +393,7 @@ export class PanelResizer extends WebDirective {
|
|||||||
index: '=',
|
index: '=',
|
||||||
minWidth: '=',
|
minWidth: '=',
|
||||||
onResizeFinish: '&',
|
onResizeFinish: '&',
|
||||||
|
onWidthEvent: '&',
|
||||||
panelId: '=',
|
panelId: '=',
|
||||||
property: '='
|
property: '='
|
||||||
};
|
};
|
||||||
|
|||||||
100
app/assets/javascripts/ui_models/app_state/active_note_state.ts
Normal file
100
app/assets/javascripts/ui_models/app_state/active_note_state.ts
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
import {
|
||||||
|
SNNote,
|
||||||
|
ContentType,
|
||||||
|
SNTag,
|
||||||
|
} from '@standardnotes/snjs';
|
||||||
|
import {
|
||||||
|
action,
|
||||||
|
makeObservable,
|
||||||
|
observable,
|
||||||
|
} from 'mobx';
|
||||||
|
import { WebApplication } from '../application';
|
||||||
|
import { AppState } from './app_state';
|
||||||
|
|
||||||
|
export class ActiveNoteState {
|
||||||
|
tags: SNTag[] = [];
|
||||||
|
tagsContainerPosition? = 0;
|
||||||
|
tagsContainerMaxWidth: number | 'auto' = 'auto';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private application: WebApplication,
|
||||||
|
private appState: AppState,
|
||||||
|
appEventListeners: (() => void)[]
|
||||||
|
) {
|
||||||
|
makeObservable(this, {
|
||||||
|
tags: observable,
|
||||||
|
tagsContainerPosition: observable,
|
||||||
|
tagsContainerMaxWidth: observable,
|
||||||
|
|
||||||
|
setTagsContainerPosition: action,
|
||||||
|
setTagsContainerMaxWidth: action,
|
||||||
|
reloadTags: action,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.tagsContainerPosition = document
|
||||||
|
.getElementById('editor-column')
|
||||||
|
?.getBoundingClientRect().left;
|
||||||
|
|
||||||
|
appEventListeners.push(
|
||||||
|
application.streamItems(
|
||||||
|
ContentType.Tag,
|
||||||
|
() => {
|
||||||
|
this.reloadTags();
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get activeNote(): SNNote | undefined {
|
||||||
|
return this.appState.notes.activeEditor?.note;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTagsContainerPosition(position: number): void {
|
||||||
|
this.tagsContainerPosition = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTagsContainerMaxWidth(width: number): void {
|
||||||
|
this.tagsContainerMaxWidth = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadTags(): void {
|
||||||
|
const { activeNote } = this;
|
||||||
|
if (activeNote) {
|
||||||
|
this.tags = this.application.getSortedTagsForNote(activeNote);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadTagsContainerLayout(): void {
|
||||||
|
const editorElementId = 'editor-column';
|
||||||
|
const { clientWidth } = document.documentElement;
|
||||||
|
const editorPosition =
|
||||||
|
document.getElementById(editorElementId)?.getBoundingClientRect()
|
||||||
|
.left ?? 0;
|
||||||
|
this.appState.activeNote.setTagsContainerPosition(editorPosition);
|
||||||
|
this.appState.activeNote.setTagsContainerMaxWidth(
|
||||||
|
clientWidth - editorPosition
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async addTagToActiveNote(tag: SNTag): Promise<void> {
|
||||||
|
const { activeNote } = this;
|
||||||
|
if (activeNote) {
|
||||||
|
await this.application.changeItem(tag.uuid, (mutator) => {
|
||||||
|
mutator.addItemAsRelationship(activeNote);
|
||||||
|
});
|
||||||
|
this.application.sync();
|
||||||
|
this.reloadTags();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeTagFromActiveNote(tag: SNTag): Promise<void> {
|
||||||
|
const { activeNote } = this;
|
||||||
|
if (activeNote) {
|
||||||
|
await this.application.changeItem(tag.uuid, (mutator) => {
|
||||||
|
mutator.removeItemAsRelationship(activeNote);
|
||||||
|
});
|
||||||
|
this.application.sync();
|
||||||
|
this.reloadTags();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ import { Bridge } from '@/services/bridge';
|
|||||||
import { storage, StorageKey } from '@/services/localStorage';
|
import { storage, StorageKey } from '@/services/localStorage';
|
||||||
import { AccountMenuState } from './account_menu_state';
|
import { AccountMenuState } from './account_menu_state';
|
||||||
import { ActionsMenuState } from './actions_menu_state';
|
import { ActionsMenuState } from './actions_menu_state';
|
||||||
|
import { ActiveNoteState } from './active_note_state';
|
||||||
import { NoAccountWarningState } from './no_account_warning_state';
|
import { NoAccountWarningState } from './no_account_warning_state';
|
||||||
import { SyncState } from './sync_state';
|
import { SyncState } from './sync_state';
|
||||||
import { SearchOptionsState } from './search_options_state';
|
import { SearchOptionsState } from './search_options_state';
|
||||||
@@ -62,6 +63,7 @@ export class AppState {
|
|||||||
showBetaWarning: boolean;
|
showBetaWarning: boolean;
|
||||||
readonly accountMenu = new AccountMenuState();
|
readonly accountMenu = new AccountMenuState();
|
||||||
readonly actionsMenu = new ActionsMenuState();
|
readonly actionsMenu = new ActionsMenuState();
|
||||||
|
readonly activeNote: ActiveNoteState;
|
||||||
readonly noAccountWarning: NoAccountWarningState;
|
readonly noAccountWarning: NoAccountWarningState;
|
||||||
readonly sync = new SyncState();
|
readonly sync = new SyncState();
|
||||||
readonly searchOptions: SearchOptionsState;
|
readonly searchOptions: SearchOptionsState;
|
||||||
@@ -81,8 +83,14 @@ export class AppState {
|
|||||||
this.$timeout = $timeout;
|
this.$timeout = $timeout;
|
||||||
this.$rootScope = $rootScope;
|
this.$rootScope = $rootScope;
|
||||||
this.application = application;
|
this.application = application;
|
||||||
|
this.activeNote = new ActiveNoteState(
|
||||||
|
application,
|
||||||
|
this,
|
||||||
|
this.appEventObserverRemovers
|
||||||
|
);
|
||||||
this.notes = new NotesState(
|
this.notes = new NotesState(
|
||||||
this.application,
|
application,
|
||||||
|
this,
|
||||||
async () => {
|
async () => {
|
||||||
await this.notifyEvent(AppStateEvent.ActiveEditorChanged);
|
await this.notifyEvent(AppStateEvent.ActiveEditorChanged);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
} from 'mobx';
|
} from 'mobx';
|
||||||
import { WebApplication } from '../application';
|
import { WebApplication } from '../application';
|
||||||
import { Editor } from '../editor';
|
import { Editor } from '../editor';
|
||||||
|
import { AppState } from './app_state';
|
||||||
|
|
||||||
export class NotesState {
|
export class NotesState {
|
||||||
lastSelectedNote: SNNote | undefined;
|
lastSelectedNote: SNNote | undefined;
|
||||||
@@ -29,10 +30,10 @@ export class NotesState {
|
|||||||
};
|
};
|
||||||
contextMenuMaxHeight: number | 'auto' = 'auto';
|
contextMenuMaxHeight: number | 'auto' = 'auto';
|
||||||
showProtectedWarning = false;
|
showProtectedWarning = false;
|
||||||
activeNoteTags: SNTag[] = [];
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private application: WebApplication,
|
private application: WebApplication,
|
||||||
|
private appState: AppState,
|
||||||
private onActiveEditorChanged: () => Promise<void>,
|
private onActiveEditorChanged: () => Promise<void>,
|
||||||
appEventListeners: (() => void)[]
|
appEventListeners: (() => void)[]
|
||||||
) {
|
) {
|
||||||
@@ -41,12 +42,10 @@ export class NotesState {
|
|||||||
contextMenuOpen: observable,
|
contextMenuOpen: observable,
|
||||||
contextMenuPosition: observable,
|
contextMenuPosition: observable,
|
||||||
showProtectedWarning: observable,
|
showProtectedWarning: observable,
|
||||||
activeNoteTags: observable,
|
|
||||||
|
|
||||||
selectedNotesCount: computed,
|
selectedNotesCount: computed,
|
||||||
trashedNotesCount: computed,
|
trashedNotesCount: computed,
|
||||||
|
|
||||||
reloadActiveNoteTags: action,
|
|
||||||
setContextMenuOpen: action,
|
setContextMenuOpen: action,
|
||||||
setContextMenuPosition: action,
|
setContextMenuPosition: action,
|
||||||
setContextMenuMaxHeight: action,
|
setContextMenuMaxHeight: action,
|
||||||
@@ -65,24 +64,12 @@ export class NotesState {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
appEventListeners.push(
|
|
||||||
application.streamItems(
|
|
||||||
ContentType.Tag,
|
|
||||||
() => {
|
|
||||||
this.reloadActiveNoteTags();
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get activeEditor(): Editor | undefined {
|
get activeEditor(): Editor | undefined {
|
||||||
return this.application.editorGroup.editors[0];
|
return this.application.editorGroup.editors[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
get activeNote(): SNNote | undefined {
|
|
||||||
return this.activeEditor?.note;
|
|
||||||
}
|
|
||||||
|
|
||||||
get selectedNotesCount(): number {
|
get selectedNotesCount(): number {
|
||||||
return Object.keys(this.selectedNotes).length;
|
return Object.keys(this.selectedNotes).length;
|
||||||
}
|
}
|
||||||
@@ -163,13 +150,6 @@ export class NotesState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reloadActiveNoteTags(): void {
|
|
||||||
const { activeNote } = this;
|
|
||||||
if (activeNote) {
|
|
||||||
this.activeNoteTags = this.application.getSortedTagsForNote(activeNote);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async openEditor(noteUuid: string): Promise<void> {
|
private async openEditor(noteUuid: string): Promise<void> {
|
||||||
if (this.activeEditor?.note?.uuid === noteUuid) {
|
if (this.activeEditor?.note?.uuid === noteUuid) {
|
||||||
return;
|
return;
|
||||||
@@ -187,7 +167,7 @@ export class NotesState {
|
|||||||
this.activeEditor.setNote(note);
|
this.activeEditor.setNote(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.reloadActiveNoteTags();
|
this.appState.activeNote.reloadTags();
|
||||||
await this.onActiveEditorChanged();
|
await this.onActiveEditorChanged();
|
||||||
|
|
||||||
if (note.waitingForKey) {
|
if (note.waitingForKey) {
|
||||||
@@ -368,36 +348,12 @@ export class NotesState {
|
|||||||
isTagInSelectedNotes(tag: SNTag): boolean {
|
isTagInSelectedNotes(tag: SNTag): boolean {
|
||||||
const selectedNotes = Object.values(this.selectedNotes);
|
const selectedNotes = Object.values(this.selectedNotes);
|
||||||
return selectedNotes.every((note) =>
|
return selectedNotes.every((note) =>
|
||||||
this.application
|
this.appState
|
||||||
.getAppState()
|
|
||||||
.getNoteTags(note)
|
.getNoteTags(note)
|
||||||
.find((noteTag) => noteTag.uuid === tag.uuid)
|
.find((noteTag) => noteTag.uuid === tag.uuid)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async addTagToActiveNote(tag: SNTag): Promise<void> {
|
|
||||||
const { activeNote } = this;
|
|
||||||
if (activeNote) {
|
|
||||||
await this.application.changeItem(tag.uuid, (mutator) => {
|
|
||||||
mutator.addItemAsRelationship(activeNote);
|
|
||||||
});
|
|
||||||
this.application.sync();
|
|
||||||
this.reloadActiveNoteTags();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async removeTagFromActiveNote(tag: SNTag): Promise<void> {
|
|
||||||
const { activeNote } = this;
|
|
||||||
if (activeNote) {
|
|
||||||
await this.application.changeItem(tag.uuid, (mutator) => {
|
|
||||||
mutator.removeItemAsRelationship(activeNote);
|
|
||||||
});
|
|
||||||
this.application.sync();
|
|
||||||
this.reloadActiveNoteTags();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
setShowProtectedWarning(show: boolean): void {
|
setShowProtectedWarning(show: boolean): void {
|
||||||
this.showProtectedWarning = show;
|
this.showProtectedWarning = show;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -169,5 +169,6 @@
|
|||||||
default-width="300"
|
default-width="300"
|
||||||
hoverable="true"
|
hoverable="true"
|
||||||
on-resize-finish="self.onPanelResize"
|
on-resize-finish="self.onPanelResize"
|
||||||
|
on-width-event="self.onPanelWidthEvent"
|
||||||
panel-id="'notes-column'"
|
panel-id="'notes-column'"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesCtrlState> {
|
|||||||
};
|
};
|
||||||
this.onWindowResize = this.onWindowResize.bind(this);
|
this.onWindowResize = this.onWindowResize.bind(this);
|
||||||
this.onPanelResize = this.onPanelResize.bind(this);
|
this.onPanelResize = this.onPanelResize.bind(this);
|
||||||
|
this.onPanelWidthEvent = this.onPanelWidthEvent.bind(this);
|
||||||
window.addEventListener('resize', this.onWindowResize, true);
|
window.addEventListener('resize', this.onWindowResize, true);
|
||||||
this.registerKeyboardShortcuts();
|
this.registerKeyboardShortcuts();
|
||||||
this.autorun(async () => {
|
this.autorun(async () => {
|
||||||
@@ -133,6 +134,7 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesCtrlState> {
|
|||||||
window.removeEventListener('resize', this.onWindowResize, true);
|
window.removeEventListener('resize', this.onWindowResize, true);
|
||||||
(this.onWindowResize as any) = undefined;
|
(this.onWindowResize as any) = undefined;
|
||||||
(this.onPanelResize as any) = undefined;
|
(this.onPanelResize as any) = undefined;
|
||||||
|
(this.onPanelWidthEvent as any) = undefined;
|
||||||
this.newNoteKeyObserver();
|
this.newNoteKeyObserver();
|
||||||
this.nextNoteKeyObserver();
|
this.nextNoteKeyObserver();
|
||||||
this.previousNoteKeyObserver();
|
this.previousNoteKeyObserver();
|
||||||
@@ -408,7 +410,7 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesCtrlState> {
|
|||||||
await this.appState.createEditor(title);
|
await this.appState.createEditor(title);
|
||||||
await this.flushUI();
|
await this.flushUI();
|
||||||
await this.reloadNotes();
|
await this.reloadNotes();
|
||||||
await this.appState.notes.reloadActiveNoteTags();
|
await this.appState.activeNote.reloadTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleTagChange(tag: SNTag) {
|
async handleTagChange(tag: SNTag) {
|
||||||
@@ -643,7 +645,7 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesCtrlState> {
|
|||||||
|
|
||||||
onPanelResize(
|
onPanelResize(
|
||||||
newWidth: number,
|
newWidth: number,
|
||||||
_: number,
|
newLeft: number,
|
||||||
__: boolean,
|
__: boolean,
|
||||||
isCollapsed: boolean
|
isCollapsed: boolean
|
||||||
) {
|
) {
|
||||||
@@ -657,6 +659,10 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesCtrlState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onPanelWidthEvent(): void {
|
||||||
|
this.appState.activeNote.reloadTagsContainerLayout();
|
||||||
|
}
|
||||||
|
|
||||||
paginate() {
|
paginate() {
|
||||||
this.notesToDisplay += this.pageSize;
|
this.notesToDisplay += this.pageSize;
|
||||||
this.reloadNotes();
|
this.reloadNotes();
|
||||||
|
|||||||
@@ -61,6 +61,10 @@
|
|||||||
margin-right: 0.25rem;
|
margin-right: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.-ml-1 {
|
||||||
|
margin-left: -0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
.py-1 {
|
.py-1 {
|
||||||
padding-top: 0.25rem;
|
padding-top: 0.25rem;
|
||||||
padding-bottom: 0.25rem;
|
padding-bottom: 0.25rem;
|
||||||
|
|||||||
Reference in New Issue
Block a user