Editor TypeScript
This commit is contained in:
@@ -43,6 +43,10 @@ export class PureCtrl {
|
|||||||
this.deinit();
|
this.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get appState() {
|
||||||
|
return this.application!.getAppState();
|
||||||
|
}
|
||||||
|
|
||||||
/** @private */
|
/** @private */
|
||||||
async resetState() {
|
async resetState() {
|
||||||
this.state = this.getInitialState();
|
this.state = this.getInitialState();
|
||||||
@@ -66,6 +70,10 @@ export class PureCtrl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateUI(func: () => void) {
|
||||||
|
this.$timeout(func);
|
||||||
|
}
|
||||||
|
|
||||||
initProps(props: CtrlProps) {
|
initProps(props: CtrlProps) {
|
||||||
if (Object.keys(this.props).length > 0) {
|
if (Object.keys(this.props).length > 0) {
|
||||||
throw 'Already init-ed props.';
|
throw 'Already init-ed props.';
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -157,10 +157,10 @@ class FooterCtrl extends PureCtrl {
|
|||||||
|
|
||||||
streamItems() {
|
streamItems() {
|
||||||
this.application.streamItems({
|
this.application.streamItems({
|
||||||
contentType: ContentTypes.Component,
|
contentType: ContentType.Component,
|
||||||
stream: async () => {
|
stream: async () => {
|
||||||
this.rooms = this.application.getItems({
|
this.rooms = this.application.getItems({
|
||||||
contentType: ContentTypes.Component
|
contentType: ContentType.Component
|
||||||
}).filter((candidate) => {
|
}).filter((candidate) => {
|
||||||
return candidate.area === 'rooms' && !candidate.deleted;
|
return candidate.area === 'rooms' && !candidate.deleted;
|
||||||
});
|
});
|
||||||
@@ -175,7 +175,7 @@ class FooterCtrl extends PureCtrl {
|
|||||||
contentType: 'SN|Theme',
|
contentType: 'SN|Theme',
|
||||||
stream: async () => {
|
stream: async () => {
|
||||||
const themes = this.application.getDisplayableItems({
|
const themes = this.application.getDisplayableItems({
|
||||||
contentType: ContentTypes.Theme
|
contentType: ContentType.Theme
|
||||||
}).filter((candidate) => {
|
}).filter((candidate) => {
|
||||||
return (
|
return (
|
||||||
!candidate.deleted &&
|
!candidate.deleted &&
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import template from '%/notes.pug';
|
|||||||
import { ApplicationEvent, ContentTypes, removeFromArray } from 'snjs';
|
import { ApplicationEvent, ContentTypes, removeFromArray } from 'snjs';
|
||||||
import { PureCtrl } from '@Controllers';
|
import { PureCtrl } from '@Controllers';
|
||||||
import { AppStateEvent } from '@/services/state';
|
import { AppStateEvent } from '@/services/state';
|
||||||
import { KeyboardModifiers, KeyboardKeys } from '@/services/keyboardManager';
|
import { KeyboardModifier, KeyboardKey } from '@/services/keyboardManager';
|
||||||
import {
|
import {
|
||||||
PrefKeys
|
PrefKeys
|
||||||
} from '@/services/preferencesManager';
|
} from '@/services/preferencesManager';
|
||||||
@@ -155,7 +155,7 @@ class NotesCtrl extends PureCtrl {
|
|||||||
|
|
||||||
streamNotesAndTags() {
|
streamNotesAndTags() {
|
||||||
this.application.streamItems({
|
this.application.streamItems({
|
||||||
contentType: [ContentTypes.Note, ContentTypes.Tag],
|
contentType: [ContentType.Note, ContentType.Tag],
|
||||||
stream: async ({ items }) => {
|
stream: async ({ items }) => {
|
||||||
await this.reloadNotes();
|
await this.reloadNotes();
|
||||||
const selectedNote = this.state.selectedNote;
|
const selectedNote = this.state.selectedNote;
|
||||||
@@ -169,7 +169,7 @@ class NotesCtrl extends PureCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Note has changed values, reset its flags */
|
/** Note has changed values, reset its flags */
|
||||||
const notes = items.filter((item) => item.content_type === ContentTypes.Note);
|
const notes = items.filter((item) => item.content_type === ContentType.Note);
|
||||||
for (const note of notes) {
|
for (const note of notes) {
|
||||||
if(note.deleted) {
|
if(note.deleted) {
|
||||||
continue;
|
continue;
|
||||||
@@ -202,7 +202,7 @@ class NotesCtrl extends PureCtrl {
|
|||||||
title = `Note ${this.state.notes.length + 1}`;
|
title = `Note ${this.state.notes.length + 1}`;
|
||||||
}
|
}
|
||||||
const newNote = await this.application.createManagedItem({
|
const newNote = await this.application.createManagedItem({
|
||||||
contentType: ContentTypes.Note,
|
contentType: ContentType.Note,
|
||||||
content: {
|
content: {
|
||||||
text: '',
|
text: '',
|
||||||
title: title
|
title: title
|
||||||
@@ -680,8 +680,8 @@ class NotesCtrl extends PureCtrl {
|
|||||||
this.newNoteKeyObserver = this.application.getKeyboardService().addKeyObserver({
|
this.newNoteKeyObserver = this.application.getKeyboardService().addKeyObserver({
|
||||||
key: 'n',
|
key: 'n',
|
||||||
modifiers: [
|
modifiers: [
|
||||||
KeyboardModifiers.Meta,
|
KeyboardModifier.Meta,
|
||||||
KeyboardModifiers.Ctrl
|
KeyboardModifier.Ctrl
|
||||||
],
|
],
|
||||||
onKeyDown: (event) => {
|
onKeyDown: (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -690,7 +690,7 @@ class NotesCtrl extends PureCtrl {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.nextNoteKeyObserver = this.application.getKeyboardService().addKeyObserver({
|
this.nextNoteKeyObserver = this.application.getKeyboardService().addKeyObserver({
|
||||||
key: KeyboardKeys.Down,
|
key: KeyboardKey.Down,
|
||||||
elements: [
|
elements: [
|
||||||
document.body,
|
document.body,
|
||||||
this.getSearchBar()
|
this.getSearchBar()
|
||||||
@@ -705,7 +705,7 @@ class NotesCtrl extends PureCtrl {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.nextNoteKeyObserver = this.application.getKeyboardService().addKeyObserver({
|
this.nextNoteKeyObserver = this.application.getKeyboardService().addKeyObserver({
|
||||||
key: KeyboardKeys.Up,
|
key: KeyboardKey.Up,
|
||||||
element: document.body,
|
element: document.body,
|
||||||
onKeyDown: (event) => {
|
onKeyDown: (event) => {
|
||||||
this.selectPreviousNote();
|
this.selectPreviousNote();
|
||||||
@@ -715,8 +715,8 @@ class NotesCtrl extends PureCtrl {
|
|||||||
this.searchKeyObserver = this.application.getKeyboardService().addKeyObserver({
|
this.searchKeyObserver = this.application.getKeyboardService().addKeyObserver({
|
||||||
key: "f",
|
key: "f",
|
||||||
modifiers: [
|
modifiers: [
|
||||||
KeyboardModifiers.Meta,
|
KeyboardModifier.Meta,
|
||||||
KeyboardModifiers.Shift
|
KeyboardModifier.Shift
|
||||||
],
|
],
|
||||||
onKeyDown: (event) => {
|
onKeyDown: (event) => {
|
||||||
const searchBar = this.getSearchBar();
|
const searchBar = this.getSearchBar();
|
||||||
|
|||||||
@@ -1,21 +1,40 @@
|
|||||||
|
import { WebDirective, PanelPuppet } from './../types';
|
||||||
|
import { WebApplication } from './../application';
|
||||||
import {
|
import {
|
||||||
SNNote,
|
SNNote,
|
||||||
SNSmartTag,
|
SNTag,
|
||||||
ContentTypes,
|
ContentType,
|
||||||
ApplicationEvent,
|
ApplicationEvent,
|
||||||
ComponentActions
|
ComponentAction,
|
||||||
|
SNSmartTag,
|
||||||
|
ComponentArea,
|
||||||
|
SNComponent
|
||||||
} from 'snjs';
|
} from 'snjs';
|
||||||
import template from '%/tags.pug';
|
import template from '%/tags.pug';
|
||||||
import { AppStateEvent } from '@/services/state';
|
import { AppStateEvent } from '@/services/state';
|
||||||
import { PANEL_NAME_TAGS } from '@/controllers/constants';
|
import { PANEL_NAME_TAGS } from '@/controllers/constants';
|
||||||
import { PrefKeys } from '@/services/preferencesManager';
|
import { PrefKeys } from '@/services/preferencesManager';
|
||||||
import { STRING_DELETE_TAG } from '@/strings';
|
import { STRING_DELETE_TAG } from '@/strings';
|
||||||
import { PureCtrl } from '@Controllers';
|
import { PureCtrl } from '@Controllers/abstract/pure_ctrl';
|
||||||
|
import { UuidString } from '@/../../../../snjs/dist/@types/types';
|
||||||
|
import { TagMutator } from '@/../../../../snjs/dist/@types/models/app/tag';
|
||||||
|
|
||||||
|
type NoteCounts = Partial<Record<string, number>>
|
||||||
|
|
||||||
class TagsPanelCtrl extends PureCtrl {
|
class TagsPanelCtrl extends PureCtrl {
|
||||||
|
|
||||||
|
/** Passed through template */
|
||||||
|
readonly application!: WebApplication
|
||||||
|
private readonly panelPuppet: PanelPuppet
|
||||||
|
private unregisterComponent?: any
|
||||||
|
component?: SNComponent
|
||||||
|
private editingOriginalName?: string
|
||||||
|
formData: { tagTitle?: string } = {}
|
||||||
|
titles: Partial<Record<UuidString, string>> = {}
|
||||||
|
|
||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
constructor(
|
constructor(
|
||||||
$timeout,
|
$timeout: ng.ITimeoutService,
|
||||||
) {
|
) {
|
||||||
super($timeout);
|
super($timeout);
|
||||||
this.panelPuppet = {
|
this.panelPuppet = {
|
||||||
@@ -25,7 +44,7 @@ class TagsPanelCtrl extends PureCtrl {
|
|||||||
|
|
||||||
deinit() {
|
deinit() {
|
||||||
this.unregisterComponent();
|
this.unregisterComponent();
|
||||||
this.unregisterComponent = null;
|
this.unregisterComponent = undefined;
|
||||||
super.deinit();
|
super.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,12 +56,12 @@ class TagsPanelCtrl extends PureCtrl {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
onAppStart() {
|
async onAppStart() {
|
||||||
super.onAppStart();
|
super.onAppStart();
|
||||||
this.registerComponentHandler();
|
this.registerComponentHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
onAppLaunch() {
|
async onAppLaunch() {
|
||||||
super.onAppLaunch();
|
super.onAppLaunch();
|
||||||
this.loadPreferences();
|
this.loadPreferences();
|
||||||
this.beginStreamingItems();
|
this.beginStreamingItems();
|
||||||
@@ -64,20 +83,21 @@ class TagsPanelCtrl extends PureCtrl {
|
|||||||
* @access private
|
* @access private
|
||||||
*/
|
*/
|
||||||
getMappedTags() {
|
getMappedTags() {
|
||||||
const tags = this.application.getItems({ contentType: ContentTypes.Tag });
|
const tags = this.application.getItems(ContentType.Tag) as SNTag[];
|
||||||
return tags.sort((a, b) => {
|
return tags.sort((a, b) => {
|
||||||
return a.content.title < b.content.title ? -1 : 1;
|
return a.title < b.title ? -1 : 1;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
beginStreamingItems() {
|
beginStreamingItems() {
|
||||||
this.application.streamItems({
|
this.application.streamItems(
|
||||||
contentType: ContentTypes.Tag,
|
ContentType.Tag,
|
||||||
stream: async ({ items }) => {
|
async (items) => {
|
||||||
await this.setState({
|
await this.setState({
|
||||||
tags: this.getMappedTags(),
|
tags: this.getMappedTags(),
|
||||||
smartTags: this.application.getSmartTags(),
|
smartTags: this.application.getSmartTags(),
|
||||||
});
|
});
|
||||||
|
this.reloadTitles(items as SNTag[]);
|
||||||
this.reloadNoteCounts();
|
this.reloadNoteCounts();
|
||||||
if (this.state.selectedTag) {
|
if (this.state.selectedTag) {
|
||||||
/** If the selected tag has been deleted, revert to All view. */
|
/** If the selected tag has been deleted, revert to All view. */
|
||||||
@@ -89,11 +109,17 @@ class TagsPanelCtrl extends PureCtrl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadTitles(tags: Array<SNTag | SNSmartTag>) {
|
||||||
|
for(const tag of tags) {
|
||||||
|
this.titles[tag.uuid] = tag.title;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
onAppStateEvent(eventName, data) {
|
onAppStateEvent(eventName: AppStateEvent, data?: any) {
|
||||||
if (eventName === AppStateEvent.PreferencesChanged) {
|
if (eventName === AppStateEvent.PreferencesChanged) {
|
||||||
this.loadPreferences();
|
this.loadPreferences();
|
||||||
} else if (eventName === AppStateEvent.TagChanged) {
|
} else if (eventName === AppStateEvent.TagChanged) {
|
||||||
@@ -105,7 +131,7 @@ class TagsPanelCtrl extends PureCtrl {
|
|||||||
|
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async onAppEvent(eventName) {
|
async onAppEvent(eventName: ApplicationEvent) {
|
||||||
super.onAppEvent(eventName);
|
super.onAppEvent(eventName);
|
||||||
if (eventName === ApplicationEvent.LocalDataIncrementalLoad) {
|
if (eventName === ApplicationEvent.LocalDataIncrementalLoad) {
|
||||||
this.reloadNoteCounts();
|
this.reloadNoteCounts();
|
||||||
@@ -119,19 +145,25 @@ class TagsPanelCtrl extends PureCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reloadNoteCounts() {
|
reloadNoteCounts() {
|
||||||
let allTags = [];
|
let allTags: Array<SNTag | SNSmartTag> = [];
|
||||||
if (this.state.tags) {
|
if (this.state.tags) {
|
||||||
allTags = allTags.concat(this.state.tags);
|
allTags = allTags.concat(this.state.tags);
|
||||||
}
|
}
|
||||||
if (this.state.smartTags) {
|
if (this.state.smartTags) {
|
||||||
allTags = allTags.concat(this.state.smartTags);
|
allTags = allTags.concat(this.state.smartTags);
|
||||||
}
|
}
|
||||||
const noteCounts = {};
|
const noteCounts: NoteCounts = {};
|
||||||
for (const tag of allTags) {
|
for (const tag of allTags) {
|
||||||
const validNotes = SNNote.filterDummyNotes(tag.notes).filter((note) => {
|
if (tag.isSmartTag()) {
|
||||||
return !note.archived && !note.content.trashed;
|
const notes = this.application.notesMatchingSmartTag(tag as SNSmartTag);
|
||||||
});
|
noteCounts[tag.uuid] = notes.length;
|
||||||
noteCounts[tag.uuid] = validNotes.length;
|
} else {
|
||||||
|
const notes = this.application.referencesForItem(tag, ContentType.Note)
|
||||||
|
.filter((note) => {
|
||||||
|
return !note.archived && !note.trashed;
|
||||||
|
})
|
||||||
|
noteCounts[tag.uuid] = notes.length;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
noteCounts: noteCounts
|
noteCounts: noteCounts
|
||||||
@@ -144,17 +176,22 @@ class TagsPanelCtrl extends PureCtrl {
|
|||||||
}
|
}
|
||||||
const width = this.application.getPrefsService().getValue(PrefKeys.TagsPanelWidth);
|
const width = this.application.getPrefsService().getValue(PrefKeys.TagsPanelWidth);
|
||||||
if (width) {
|
if (width) {
|
||||||
this.panelPuppet.setWidth(width);
|
this.panelPuppet.setWidth!(width);
|
||||||
if (this.panelPuppet.isCollapsed()) {
|
if (this.panelPuppet.isCollapsed!()) {
|
||||||
this.application.getAppState().panelDidResize(
|
this.application.getAppState().panelDidResize(
|
||||||
PANEL_NAME_TAGS,
|
PANEL_NAME_TAGS,
|
||||||
this.panelPuppet.isCollapsed()
|
this.panelPuppet.isCollapsed!()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onPanelResize = (newWidth, lastLeft, isAtMaxWidth, isCollapsed) => {
|
onPanelResize = (
|
||||||
|
newWidth: number,
|
||||||
|
lastLeft: number,
|
||||||
|
isAtMaxWidth: boolean,
|
||||||
|
isCollapsed: boolean
|
||||||
|
) => {
|
||||||
this.application.getPrefsService().setUserPrefValue(
|
this.application.getPrefsService().setUserPrefValue(
|
||||||
PrefKeys.TagsPanelWidth,
|
PrefKeys.TagsPanelWidth,
|
||||||
newWidth,
|
newWidth,
|
||||||
@@ -167,50 +204,49 @@ class TagsPanelCtrl extends PureCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
registerComponentHandler() {
|
registerComponentHandler() {
|
||||||
this.unregisterComponent = this.application.componentManager.registerHandler({
|
this.unregisterComponent = this.application.componentManager!.registerHandler({
|
||||||
identifier: 'tags',
|
identifier: 'tags',
|
||||||
areas: ['tags-list'],
|
areas: [ComponentArea.TagsList],
|
||||||
activationHandler: (component) => {
|
activationHandler: (component) => {
|
||||||
this.component = component;
|
this.component = component;
|
||||||
},
|
},
|
||||||
contextRequestHandler: (component) => {
|
contextRequestHandler: () => {
|
||||||
return null;
|
return undefined;
|
||||||
},
|
},
|
||||||
actionHandler: (_, action, data) => {
|
actionHandler: (_, action, data) => {
|
||||||
if (action === ComponentActions.SelectItem) {
|
if (action === ComponentAction.SelectItem) {
|
||||||
if (data.item.content_type === ContentTypes.Tag) {
|
if (data.item.content_type === ContentType.Tag) {
|
||||||
const tag = this.application.findItem({ uuid: data.item.uuid });
|
const tag = this.application.findItem(data.item.uuid);
|
||||||
if (tag) {
|
if (tag) {
|
||||||
this.selectTag(tag);
|
this.selectTag(tag as SNTag);
|
||||||
}
|
}
|
||||||
} else if (data.item.content_type === ContentTypes.SmartTag) {
|
} else if (data.item.content_type === ContentType.SmartTag) {
|
||||||
this.application.createTemplateItem({
|
this.application.createTemplateItem(
|
||||||
contentType: ContentTypes.SmartTag,
|
ContentType.SmartTag,
|
||||||
content: data.item.content
|
data.item.content
|
||||||
}).then(smartTag => {
|
).then(smartTag => {
|
||||||
this.selectTag(smartTag);
|
this.selectTag(smartTag as SNSmartTag);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (action === ComponentActions.ClearSelection) {
|
} else if (action === ComponentAction.ClearSelection) {
|
||||||
this.selectTag(this.state.smartTags[0]);
|
this.selectTag(this.state.smartTags[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async selectTag(tag) {
|
async selectTag(tag: SNTag) {
|
||||||
if (tag.isSmartTag()) {
|
if (tag.isSmartTag()) {
|
||||||
Object.defineProperty(tag, 'notes', {
|
Object.defineProperty(tag, 'notes', {
|
||||||
get: () => {
|
get: () => {
|
||||||
return this.application.getNotesMatchingSmartTag({
|
return this.application.notesMatchingSmartTag(tag as SNSmartTag);
|
||||||
smartTag: tag
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (tag.content.conflict_of) {
|
if (tag.conflictOf) {
|
||||||
tag.content.conflict_of = null;
|
this.application.changeAndSaveItem(tag.uuid, (mutator) => {
|
||||||
this.application.saveItem({ item: tag });
|
mutator.conflictOf = undefined;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
this.application.getAppState().setSelectedTag(tag);
|
this.application.getAppState().setSelectedTag(tag);
|
||||||
}
|
}
|
||||||
@@ -219,9 +255,9 @@ class TagsPanelCtrl extends PureCtrl {
|
|||||||
if (this.state.editingTag) {
|
if (this.state.editingTag) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const newTag = await this.application.createTemplateItem({
|
const newTag = await this.application.createTemplateItem(
|
||||||
contentType: ContentTypes.Tag
|
ContentType.Tag
|
||||||
});
|
);
|
||||||
this.setState({
|
this.setState({
|
||||||
tags: [newTag].concat(this.state.tags),
|
tags: [newTag].concat(this.state.tags),
|
||||||
previousTag: this.state.selectedTag,
|
previousTag: this.state.selectedTag,
|
||||||
@@ -231,14 +267,14 @@ class TagsPanelCtrl extends PureCtrl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
tagTitleDidChange(tag) {
|
onTagTitleChange(tag: SNTag | SNSmartTag) {
|
||||||
this.setState({
|
this.setState({
|
||||||
editingTag: tag
|
editingTag: tag
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveTag($event, tag) {
|
async saveTag($event: Event, tag: SNTag) {
|
||||||
$event.target.blur();
|
($event.target! as HTMLInputElement).blur();
|
||||||
await this.setState({
|
await this.setState({
|
||||||
editingTag: null,
|
editingTag: null,
|
||||||
});
|
});
|
||||||
@@ -246,8 +282,8 @@ class TagsPanelCtrl extends PureCtrl {
|
|||||||
if (!tag.title || tag.title.length === 0) {
|
if (!tag.title || tag.title.length === 0) {
|
||||||
let newSelectedTag = this.state.selectedTag;
|
let newSelectedTag = this.state.selectedTag;
|
||||||
if (this.state.editingTag) {
|
if (this.state.editingTag) {
|
||||||
tag.title = this.editingOriginalName;
|
this.titles[tag.uuid] = this.editingOriginalName;
|
||||||
this.editingOriginalName = null;
|
this.editingOriginalName = undefined;
|
||||||
} else if (this.state.newTag) {
|
} else if (this.state.newTag) {
|
||||||
newSelectedTag = this.state.previousTag;
|
newSelectedTag = this.state.previousTag;
|
||||||
}
|
}
|
||||||
@@ -259,14 +295,14 @@ class TagsPanelCtrl extends PureCtrl {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.editingOriginalName = null;
|
this.editingOriginalName = undefined;
|
||||||
|
|
||||||
const matchingTag = this.application.findTag({ title: tag.title });
|
const matchingTag = this.application.findTag(tag.title);
|
||||||
const alreadyExists = matchingTag && matchingTag !== tag;
|
const alreadyExists = matchingTag && matchingTag !== tag;
|
||||||
if (this.state.newTag === tag && alreadyExists) {
|
if (this.state.newTag === tag && alreadyExists) {
|
||||||
this.application.alertService.alert({
|
this.application.alertService!.alert(
|
||||||
text: "A tag with this name already exists."
|
"A tag with this name already exists."
|
||||||
});
|
);
|
||||||
this.setState({
|
this.setState({
|
||||||
newTag: null,
|
newTag: null,
|
||||||
tags: this.getMappedTags(),
|
tags: this.getMappedTags(),
|
||||||
@@ -274,40 +310,48 @@ class TagsPanelCtrl extends PureCtrl {
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.application.changeAndSaveItem(tag.uuid, (mutator) => {
|
||||||
this.application.saveItem({ item: tag });
|
const tagMutator = mutator as TagMutator;
|
||||||
|
tagMutator.title = this.titles[tag.uuid]!;
|
||||||
|
});
|
||||||
this.selectTag(tag);
|
this.selectTag(tag);
|
||||||
this.setState({
|
this.setState({
|
||||||
newTag: null
|
newTag: null
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async selectedRenameTag($event, tag) {
|
async selectedRenameTag(tag: SNTag) {
|
||||||
this.editingOriginalName = tag.title;
|
this.editingOriginalName = tag.title;
|
||||||
await this.setState({
|
await this.setState({
|
||||||
editingTag: tag
|
editingTag: tag
|
||||||
});
|
});
|
||||||
document.getElementById('tag-' + tag.uuid).focus();
|
document.getElementById('tag-' + tag.uuid)!.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedDeleteTag(tag) {
|
selectedDeleteTag(tag: SNTag) {
|
||||||
this.removeTag(tag);
|
this.removeTag(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeTag(tag) {
|
removeTag(tag: SNTag) {
|
||||||
this.application.alertService.confirm({
|
this.application.alertService!.confirm(
|
||||||
text: STRING_DELETE_TAG,
|
STRING_DELETE_TAG,
|
||||||
destructive: true,
|
undefined,
|
||||||
onConfirm: () => {
|
undefined,
|
||||||
this.application.deleteItem({ item: tag });
|
undefined,
|
||||||
|
() => {
|
||||||
|
/* On confirm */
|
||||||
|
this.application.deleteItem(tag);
|
||||||
this.selectTag(this.state.smartTags[0]);
|
this.selectTag(this.state.smartTags[0]);
|
||||||
}
|
},
|
||||||
});
|
undefined,
|
||||||
|
true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TagsPanel {
|
export class TagsPanel extends WebDirective {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
super();
|
||||||
this.restrict = 'E';
|
this.restrict = 'E';
|
||||||
this.scope = {
|
this.scope = {
|
||||||
application: '='
|
application: '='
|
||||||
@@ -27,7 +27,7 @@ class RevisionPreviewModalCtrl {
|
|||||||
|
|
||||||
async configure() {
|
async configure() {
|
||||||
this.note = await this.application.createTemplateItem({
|
this.note = await this.application.createTemplateItem({
|
||||||
contentType: ContentTypes.Note,
|
contentType: ContentType.Note,
|
||||||
content: this.content
|
content: this.content
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ class RevisionPreviewModalCtrl {
|
|||||||
* editor object has non-copyable properties like .window, which cannot be transfered
|
* editor object has non-copyable properties like .window, which cannot be transfered
|
||||||
*/
|
*/
|
||||||
const editorCopy = await this.application.createTemplateItem({
|
const editorCopy = await this.application.createTemplateItem({
|
||||||
contentType: ContentTypes.Component,
|
contentType: ContentType.Component,
|
||||||
content: editorForNote.content
|
content: editorForNote.content
|
||||||
});
|
});
|
||||||
this.application.component.setReadonlyStateForComponent(editorCopy, true, true);
|
this.application.component.setReadonlyStateForComponent(editorCopy, true, true);
|
||||||
|
|||||||
@@ -1,125 +0,0 @@
|
|||||||
/** @public */
|
|
||||||
export const KeyboardKeys = {
|
|
||||||
Tab: "Tab",
|
|
||||||
Backspace: "Backspace",
|
|
||||||
Up: "ArrowUp",
|
|
||||||
Down: "ArrowDown",
|
|
||||||
};
|
|
||||||
/** @public */
|
|
||||||
export const KeyboardModifiers = {
|
|
||||||
Shift: "Shift",
|
|
||||||
Ctrl: "Control",
|
|
||||||
/** ⌘ key on Mac, ⊞ key on Windows */
|
|
||||||
Meta: "Meta",
|
|
||||||
Alt: "Alt",
|
|
||||||
};
|
|
||||||
/** @private */
|
|
||||||
const KeyboardKeyEvents = {
|
|
||||||
Down: "KeyEventDown",
|
|
||||||
Up: "KeyEventUp"
|
|
||||||
};
|
|
||||||
|
|
||||||
export class KeyboardManager {
|
|
||||||
constructor() {
|
|
||||||
this.observers = [];
|
|
||||||
this.handleKeyDown = this.handleKeyDown.bind(this);
|
|
||||||
this.handleKeyUp = this.handleKeyUp.bind(this);
|
|
||||||
window.addEventListener('keydown', this.handleKeyDown);
|
|
||||||
window.addEventListener('keyup', this.handleKeyUp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @access public */
|
|
||||||
deinit() {
|
|
||||||
this.observers.length = 0;
|
|
||||||
window.removeEventListener('keydown', this.handleKeyDown);
|
|
||||||
window.removeEventListener('keyup', this.handleKeyUp);
|
|
||||||
this.handleKeyDown = null;
|
|
||||||
this.handleKeyUp = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
modifiersForEvent(event) {
|
|
||||||
const allModifiers = Object.keys(KeyboardModifiers).map((key) => KeyboardModifiers[key]);
|
|
||||||
const eventModifiers = allModifiers.filter((modifier) => {
|
|
||||||
// For a modifier like ctrlKey, must check both event.ctrlKey and event.key.
|
|
||||||
// That's because on keyup, event.ctrlKey would be false, but event.key == Control would be true.
|
|
||||||
const matches = (
|
|
||||||
((event.ctrlKey || event.key === KeyboardModifiers.Ctrl) && modifier === KeyboardModifiers.Ctrl) ||
|
|
||||||
((event.metaKey || event.key === KeyboardModifiers.Meta) && modifier === KeyboardModifiers.Meta) ||
|
|
||||||
((event.altKey || event.key === KeyboardModifiers.Alt) && modifier === KeyboardModifiers.Alt) ||
|
|
||||||
((event.shiftKey || event.key === KeyboardModifiers.Shift) && modifier === KeyboardModifiers.Shift)
|
|
||||||
);
|
|
||||||
|
|
||||||
return matches;
|
|
||||||
});
|
|
||||||
|
|
||||||
return eventModifiers;
|
|
||||||
}
|
|
||||||
|
|
||||||
eventMatchesKeyAndModifiers(event, key, modifiers = []) {
|
|
||||||
const eventModifiers = this.modifiersForEvent(event);
|
|
||||||
|
|
||||||
if (eventModifiers.length !== modifiers.length) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const modifier of modifiers) {
|
|
||||||
if (!eventModifiers.includes(modifier)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modifers match, check key
|
|
||||||
if (!key) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// In the browser, shift + f results in key 'f', but in Electron, shift + f results in 'F'
|
|
||||||
// In our case we don't differentiate between the two.
|
|
||||||
return key.toLowerCase() === event.key.toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
notifyObserver(event, keyEventType) {
|
|
||||||
for (const observer of this.observers) {
|
|
||||||
if (observer.element && event.target !== observer.element) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (observer.elements && !observer.elements.includes(event.target)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (observer.notElement && observer.notElement === event.target) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (observer.notElementIds && observer.notElementIds.includes(event.target.id)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.eventMatchesKeyAndModifiers(event, observer.key, observer.modifiers)) {
|
|
||||||
const callback = keyEventType === KeyboardKeyEvents.Down ? observer.onKeyDown : observer.onKeyUp;
|
|
||||||
if (callback) {
|
|
||||||
callback(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleKeyDown(event) {
|
|
||||||
this.notifyObserver(event, KeyboardKeyEvents.Down);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleKeyUp(event) {
|
|
||||||
this.notifyObserver(event, KeyboardKeyEvents.Up);
|
|
||||||
}
|
|
||||||
|
|
||||||
addKeyObserver({ key, modifiers, onKeyDown, onKeyUp, element, elements, notElement, notElementIds }) {
|
|
||||||
const observer = { key, modifiers, onKeyDown, onKeyUp, element, elements, notElement, notElementIds };
|
|
||||||
this.observers.push(observer);
|
|
||||||
return observer;
|
|
||||||
}
|
|
||||||
|
|
||||||
removeKeyObserver(observer) {
|
|
||||||
this.observers.splice(this.observers.indexOf(observer), 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
147
app/assets/javascripts/services/keyboardManager.ts
Normal file
147
app/assets/javascripts/services/keyboardManager.ts
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
import { removeFromArray } from 'snjs';
|
||||||
|
export enum KeyboardKey {
|
||||||
|
Tab = "Tab",
|
||||||
|
Backspace = "Backspace",
|
||||||
|
Up = "ArrowUp",
|
||||||
|
Down = "ArrowDown",
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum KeyboardModifier {
|
||||||
|
Shift = "Shift",
|
||||||
|
Ctrl = "Control",
|
||||||
|
/** ⌘ key on Mac, ⊞ key on Windows */
|
||||||
|
Meta = "Meta",
|
||||||
|
Alt = "Alt",
|
||||||
|
};
|
||||||
|
|
||||||
|
enum KeyboardKeyEvent {
|
||||||
|
Down = "KeyEventDown",
|
||||||
|
Up = "KeyEventUp"
|
||||||
|
};
|
||||||
|
|
||||||
|
type KeyboardObserver = {
|
||||||
|
key?: KeyboardKey
|
||||||
|
modifiers?: KeyboardModifier[]
|
||||||
|
onKeyDown?: (event: KeyboardEvent) => void
|
||||||
|
onKeyUp?: (event: KeyboardEvent) => void
|
||||||
|
element?: HTMLElement
|
||||||
|
elements?: HTMLElement[]
|
||||||
|
notElement?: HTMLElement
|
||||||
|
notElementIds?: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export class KeyboardManager {
|
||||||
|
|
||||||
|
private observers: KeyboardObserver[] = []
|
||||||
|
private handleKeyDown: any
|
||||||
|
private handleKeyUp: any
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.handleKeyDown = (event: KeyboardEvent) => {
|
||||||
|
this.notifyObserver(event, KeyboardKeyEvent.Down);
|
||||||
|
}
|
||||||
|
this.handleKeyUp = (event: KeyboardEvent) => {
|
||||||
|
this.notifyObserver(event, KeyboardKeyEvent.Up);
|
||||||
|
}
|
||||||
|
window.addEventListener('keydown', this.handleKeyDown);
|
||||||
|
window.addEventListener('keyup', this.handleKeyUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public deinit() {
|
||||||
|
this.observers.length = 0;
|
||||||
|
window.removeEventListener('keydown', this.handleKeyDown);
|
||||||
|
window.removeEventListener('keyup', this.handleKeyUp);
|
||||||
|
this.handleKeyDown = undefined;
|
||||||
|
this.handleKeyUp = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
modifiersForEvent(event: KeyboardEvent) {
|
||||||
|
const allModifiers = Object.values(KeyboardModifier);
|
||||||
|
const eventModifiers = allModifiers.filter((modifier) => {
|
||||||
|
// For a modifier like ctrlKey, must check both event.ctrlKey and event.key.
|
||||||
|
// That's because on keyup, event.ctrlKey would be false, but event.key == Control would be true.
|
||||||
|
const matches = (
|
||||||
|
(
|
||||||
|
(event.ctrlKey || event.key === KeyboardModifier.Ctrl)
|
||||||
|
&& modifier === KeyboardModifier.Ctrl
|
||||||
|
) ||
|
||||||
|
(
|
||||||
|
(event.metaKey || event.key === KeyboardModifier.Meta)
|
||||||
|
&& modifier === KeyboardModifier.Meta
|
||||||
|
) ||
|
||||||
|
(
|
||||||
|
(event.altKey || event.key === KeyboardModifier.Alt)
|
||||||
|
&& modifier === KeyboardModifier.Alt
|
||||||
|
) ||
|
||||||
|
(
|
||||||
|
(event.shiftKey || event.key === KeyboardModifier.Shift)
|
||||||
|
&& modifier === KeyboardModifier.Shift
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return matches;
|
||||||
|
});
|
||||||
|
|
||||||
|
return eventModifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
eventMatchesKeyAndModifiers(
|
||||||
|
event: KeyboardEvent,
|
||||||
|
key: KeyboardKey,
|
||||||
|
modifiers: KeyboardModifier[] = []
|
||||||
|
) {
|
||||||
|
const eventModifiers = this.modifiersForEvent(event);
|
||||||
|
if (eventModifiers.length !== modifiers.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (const modifier of modifiers) {
|
||||||
|
if (!eventModifiers.includes(modifier)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Modifers match, check key
|
||||||
|
if (!key) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// In the browser, shift + f results in key 'f', but in Electron, shift + f results in 'F'
|
||||||
|
// In our case we don't differentiate between the two.
|
||||||
|
return key.toLowerCase() === event.key.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyObserver(event: KeyboardEvent, keyEvent: KeyboardKeyEvent) {
|
||||||
|
const target = event.target as HTMLElement;
|
||||||
|
for (const observer of this.observers) {
|
||||||
|
if (observer.element && event.target !== observer.element) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (observer.elements && !observer.elements.includes(target)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (observer.notElement && observer.notElement === event.target) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (observer.notElementIds && observer.notElementIds.includes(target.id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.eventMatchesKeyAndModifiers(event, observer.key!, observer.modifiers)) {
|
||||||
|
const callback = keyEvent === KeyboardKeyEvent.Down
|
||||||
|
? observer.onKeyDown
|
||||||
|
: observer.onKeyUp;
|
||||||
|
if (callback) {
|
||||||
|
callback(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addKeyObserver(observer: KeyboardObserver) {
|
||||||
|
this.observers.push(observer);
|
||||||
|
return () => {
|
||||||
|
removeFromArray(this.observers, observer);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,7 +26,7 @@ export class NativeExtManager extends ApplicationService {
|
|||||||
get extManagerPred() {
|
get extManagerPred() {
|
||||||
const extManagerId = 'org.standardnotes.extensions-manager';
|
const extManagerId = 'org.standardnotes.extensions-manager';
|
||||||
return SNPredicate.CompoundPredicate([
|
return SNPredicate.CompoundPredicate([
|
||||||
new SNPredicate('content_type', '=', ContentTypes.Component),
|
new SNPredicate('content_type', '=', ContentType.Component),
|
||||||
new SNPredicate('package_info.identifier', '=', extManagerId)
|
new SNPredicate('package_info.identifier', '=', extManagerId)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -34,7 +34,7 @@ export class NativeExtManager extends ApplicationService {
|
|||||||
get batchManagerPred() {
|
get batchManagerPred() {
|
||||||
const batchMgrId = 'org.standardnotes.batch-manager';
|
const batchMgrId = 'org.standardnotes.batch-manager';
|
||||||
return SNPredicate.CompoundPredicate([
|
return SNPredicate.CompoundPredicate([
|
||||||
new SNPredicate('content_type', '=', ContentTypes.Component),
|
new SNPredicate('content_type', '=', ContentType.Component),
|
||||||
new SNPredicate('package_info.identifier', '=', batchMgrId)
|
new SNPredicate('package_info.identifier', '=', batchMgrId)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -65,8 +65,8 @@ export class NativeExtManager extends ApplicationService {
|
|||||||
}
|
}
|
||||||
// Handle addition of SN|ExtensionRepo permission
|
// Handle addition of SN|ExtensionRepo permission
|
||||||
const permission = extensionsManager.content.permissions.find((p) => p.name === STREAM_ITEMS_PERMISSION);
|
const permission = extensionsManager.content.permissions.find((p) => p.name === STREAM_ITEMS_PERMISSION);
|
||||||
if (!permission.content_types.includes(ContentTypes.ExtensionRepo)) {
|
if (!permission.content_types.includes(ContentType.ExtensionRepo)) {
|
||||||
permission.content_types.push(ContentTypes.ExtensionRepo);
|
permission.content_types.push(ContentType.ExtensionRepo);
|
||||||
needsSync = true;
|
needsSync = true;
|
||||||
}
|
}
|
||||||
if (needsSync) {
|
if (needsSync) {
|
||||||
@@ -92,13 +92,13 @@ export class NativeExtManager extends ApplicationService {
|
|||||||
{
|
{
|
||||||
name: STREAM_ITEMS_PERMISSION,
|
name: STREAM_ITEMS_PERMISSION,
|
||||||
content_types: [
|
content_types: [
|
||||||
ContentTypes.Component,
|
ContentType.Component,
|
||||||
ContentTypes.Theme,
|
ContentType.Theme,
|
||||||
ContentTypes.ServerExtension,
|
ContentType.ServerExtension,
|
||||||
ContentTypes.ActionsExtension,
|
ContentType.ActionsExtension,
|
||||||
ContentTypes.Mfa,
|
ContentType.Mfa,
|
||||||
ContentTypes.Editor,
|
ContentType.Editor,
|
||||||
ContentTypes.ExtensionRepo
|
ContentType.ExtensionRepo
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -110,7 +110,7 @@ export class NativeExtManager extends ApplicationService {
|
|||||||
}
|
}
|
||||||
const payload = CreateMaxPayloadFromAnyObject({
|
const payload = CreateMaxPayloadFromAnyObject({
|
||||||
object: {
|
object: {
|
||||||
content_type: ContentTypes.Component,
|
content_type: ContentType.Component,
|
||||||
content: content
|
content: content
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -146,7 +146,7 @@ export class NativeExtManager extends ApplicationService {
|
|||||||
}
|
}
|
||||||
const payload = CreateMaxPayloadFromAnyObject({
|
const payload = CreateMaxPayloadFromAnyObject({
|
||||||
object: {
|
object: {
|
||||||
content_type: ContentTypes.Component,
|
content_type: ContentType.Component,
|
||||||
content: content
|
content: content
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -172,8 +172,8 @@ export class NativeExtManager extends ApplicationService {
|
|||||||
}
|
}
|
||||||
// Handle addition of SN|ExtensionRepo permission
|
// Handle addition of SN|ExtensionRepo permission
|
||||||
const permission = batchManager.content.permissions.find((p) => p.name === STREAM_ITEMS_PERMISSION);
|
const permission = batchManager.content.permissions.find((p) => p.name === STREAM_ITEMS_PERMISSION);
|
||||||
if (!permission.content_types.includes(ContentTypes.ExtensionRepo)) {
|
if (!permission.content_types.includes(ContentType.ExtensionRepo)) {
|
||||||
permission.content_types.push(ContentTypes.ExtensionRepo);
|
permission.content_types.push(ContentType.ExtensionRepo);
|
||||||
needsSync = true;
|
needsSync = true;
|
||||||
}
|
}
|
||||||
if (needsSync) {
|
if (needsSync) {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export class PreferencesManager extends ApplicationService {
|
|||||||
|
|
||||||
streamPreferences() {
|
streamPreferences() {
|
||||||
this.application.streamItems({
|
this.application.streamItems({
|
||||||
contentType: ContentTypes.UserPrefs,
|
contentType: ContentType.UserPrefs,
|
||||||
stream: () => {
|
stream: () => {
|
||||||
this.loadSingleton();
|
this.loadSingleton();
|
||||||
}
|
}
|
||||||
@@ -41,7 +41,7 @@ export class PreferencesManager extends ApplicationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async loadSingleton() {
|
async loadSingleton() {
|
||||||
const contentType = ContentTypes.UserPrefs;
|
const contentType = ContentType.UserPrefs;
|
||||||
const predicate = new SNPredicate('content_type', '=', contentType);
|
const predicate = new SNPredicate('content_type', '=', contentType);
|
||||||
this.userPreferences = await this.application.singletonManager.findOrCreateSingleton({
|
this.userPreferences = await this.application.singletonManager.findOrCreateSingleton({
|
||||||
predicate: predicate,
|
predicate: predicate,
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ export class AppState {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async setSelectedNote(note: SNNote) {
|
async setSelectedNote(note?: SNNote) {
|
||||||
const run = async () => {
|
const run = async () => {
|
||||||
const previousNote = this.selectedNote;
|
const previousNote = this.selectedNote;
|
||||||
this.selectedNote = note;
|
this.selectedNote = note;
|
||||||
@@ -166,6 +166,12 @@ export class AppState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNoteTags(note: SNNote) {
|
||||||
|
return this.application.referencesForItem(note).filter((ref) => {
|
||||||
|
return ref.content_type === note.content_type;
|
||||||
|
}) as SNTag[]
|
||||||
|
}
|
||||||
|
|
||||||
getSelectedTag() {
|
getSelectedTag() {
|
||||||
return this.selectedTag;
|
return this.selectedTag;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ export class WebDirective implements ng.IDirective {
|
|||||||
controllerAs?: string;
|
controllerAs?: string;
|
||||||
bindToController?: boolean | { [boundProperty: string]: string };
|
bindToController?: boolean | { [boundProperty: string]: string };
|
||||||
restrict?: string;
|
restrict?: string;
|
||||||
|
replace?: boolean
|
||||||
scope?: boolean | { [boundProperty: string]: string };
|
scope?: boolean | { [boundProperty: string]: string };
|
||||||
template?: string | ((tElement: any, tAttrs: any) => string);
|
template?: string | ((tElement: any, tAttrs: any) => string);
|
||||||
}
|
}
|
||||||
@@ -15,4 +16,13 @@ export enum PasswordWizardType {
|
|||||||
export interface PasswordWizardScope extends Partial<ng.IScope> {
|
export interface PasswordWizardScope extends Partial<ng.IScope> {
|
||||||
type: PasswordWizardType,
|
type: PasswordWizardType,
|
||||||
application: any
|
application: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PanelPuppet = {
|
||||||
|
onReady: () => void
|
||||||
|
ready?: boolean
|
||||||
|
setWidth?: (width: number) => void
|
||||||
|
setLeft?: (left: number) => void
|
||||||
|
isCollapsed?: () => boolean
|
||||||
|
flash?: () => void
|
||||||
}
|
}
|
||||||
@@ -17,12 +17,12 @@
|
|||||||
)
|
)
|
||||||
.title
|
.title
|
||||||
input#note-title-editor.input(
|
input#note-title-editor.input(
|
||||||
ng-blur='self.onNameBlur()',
|
ng-blur='self.onTitleBlur()',
|
||||||
ng-change='self.onTitleChange()',
|
ng-change='self.onTitleChange()',
|
||||||
ng-disabled='self.noteLocked',
|
ng-disabled='self.noteLocked',
|
||||||
ng-focus='self.onNameFocus()',
|
ng-focus='self.onTitleFocus()',
|
||||||
ng-keyup='$event.keyCode == 13 && self.onTitleEnter($event)',
|
ng-keyup='$event.keyCode == 13 && self.onTitleEnter($event)',
|
||||||
ng-model='self.state.note.title',
|
ng-model='self.editorValues.title',
|
||||||
select-on-click='true',
|
select-on-click='true',
|
||||||
spellcheck='false')
|
spellcheck='false')
|
||||||
#save-status
|
#save-status
|
||||||
@@ -39,11 +39,11 @@
|
|||||||
application='self.application'
|
application='self.application'
|
||||||
)
|
)
|
||||||
input.tags-input(
|
input.tags-input(
|
||||||
ng-blur='self.saveTags()',
|
ng-blur='self.saveTagsFromStrings()',
|
||||||
ng-disabled='self.noteLocked',
|
ng-disabled='self.noteLocked',
|
||||||
ng-if='!(self.state.tagsComponent && self.state.tagsComponent.active)',
|
ng-if='!(self.state.tagsComponent && self.state.tagsComponent.active)',
|
||||||
ng-keyup='$event.keyCode == 13 && $event.target.blur();',
|
ng-keyup='$event.keyCode == 13 && $event.target.blur();',
|
||||||
ng-model='self.state.mutable.tagsString',
|
ng-model='self.editorValues.tagsInputValue',
|
||||||
placeholder='#tags',
|
placeholder='#tags',
|
||||||
spellcheck='false',
|
spellcheck='false',
|
||||||
type='text'
|
type='text'
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
)
|
)
|
||||||
menu-row(
|
menu-row(
|
||||||
action='self.selectedMenuItem(true); self.toggleNotePreview()',
|
action='self.selectedMenuItem(true); self.toggleNotePreview()',
|
||||||
circle="self.state.note.content.hidePreview ? 'danger' : 'success'",
|
circle="self.state.note.hidePreview ? 'danger' : 'success'",
|
||||||
circle-align="'right'",
|
circle-align="'right'",
|
||||||
desc="'Hide or unhide the note preview from the list of notes'",
|
desc="'Hide or unhide the note preview from the list of notes'",
|
||||||
label="'Preview'"
|
label="'Preview'"
|
||||||
@@ -130,7 +130,7 @@
|
|||||||
.sk-menu-panel-header
|
.sk-menu-panel-header
|
||||||
.sk-menu-panel-header-title Global Display
|
.sk-menu-panel-header-title Global Display
|
||||||
menu-row(
|
menu-row(
|
||||||
action="self.selectedMenuItem(true); self.toggleKey(self.prefKeyMonospace)",
|
action="self.selectedMenuItem(true); self.togglePrefKey(self.prefKeyMonospace)",
|
||||||
circle="self.state.monospaceEnabled ? 'success' : 'neutral'",
|
circle="self.state.monospaceEnabled ? 'success' : 'neutral'",
|
||||||
desc="'Toggles the font style for the default editor'",
|
desc="'Toggles the font style for the default editor'",
|
||||||
disabled='self.state.selectedEditor',
|
disabled='self.state.selectedEditor',
|
||||||
@@ -138,7 +138,7 @@
|
|||||||
subtitle="self.state.selectedEditor ? 'Not available with editor extensions' : null"
|
subtitle="self.state.selectedEditor ? 'Not available with editor extensions' : null"
|
||||||
)
|
)
|
||||||
menu-row(
|
menu-row(
|
||||||
action="self.selectedMenuItem(true); self.toggleKey(self.prefKeySpellcheck)",
|
action="self.selectedMenuItem(true); self.togglePrefKey(self.prefKeySpellcheck)",
|
||||||
circle="self.state.spellcheck ? 'success' : 'neutral'",
|
circle="self.state.spellcheck ? 'success' : 'neutral'",
|
||||||
desc="'Toggles spellcheck for the default editor'",
|
desc="'Toggles spellcheck for the default editor'",
|
||||||
disabled='self.state.selectedEditor',
|
disabled='self.state.selectedEditor',
|
||||||
@@ -149,7 +149,7 @@
|
|||||||
: (self.state.isDesktop ? 'May degrade editor performance' : null)
|
: (self.state.isDesktop ? 'May degrade editor performance' : null)
|
||||||
`)
|
`)
|
||||||
menu-row(
|
menu-row(
|
||||||
action="self.selectedMenuItem(true); self.toggleKey(self.prefKeyMarginResizers)",
|
action="self.selectedMenuItem(true); self.togglePrefKey(self.prefKeyMarginResizers)",
|
||||||
circle="self.state.marginResizersEnabled ? 'success' : 'neutral'",
|
circle="self.state.marginResizersEnabled ? 'success' : 'neutral'",
|
||||||
desc="'Allows for editor left and right margins to be resized'",
|
desc="'Allows for editor left and right margins to be resized'",
|
||||||
faded='!self.state.marginResizersEnabled',
|
faded='!self.state.marginResizersEnabled',
|
||||||
@@ -217,7 +217,7 @@
|
|||||||
ng-click='self.clickedTextArea()',
|
ng-click='self.clickedTextArea()',
|
||||||
ng-focus='self.onContentFocus()',
|
ng-focus='self.onContentFocus()',
|
||||||
ng-if='!self.state.selectedEditor',
|
ng-if='!self.state.selectedEditor',
|
||||||
ng-model='self.state.note.text',
|
ng-model='self.editorValues.text',
|
||||||
ng-model-options='{ debounce: self.state.editorDebounce}',
|
ng-model-options='{ debounce: self.state.editorDebounce}',
|
||||||
ng-readonly='self.noteLocked',
|
ng-readonly='self.noteLocked',
|
||||||
ng-trim='false'
|
ng-trim='false'
|
||||||
|
|||||||
@@ -120,8 +120,8 @@
|
|||||||
.note-preview(
|
.note-preview(
|
||||||
ng-if=`
|
ng-if=`
|
||||||
!self.state.hideNotePreview &&
|
!self.state.hideNotePreview &&
|
||||||
!note.content.hidePreview &&
|
!note.hidePreview &&
|
||||||
!note.content.protected`
|
!note.protected`
|
||||||
)
|
)
|
||||||
.html-preview(
|
.html-preview(
|
||||||
ng-bind-html='note.content.preview_html',
|
ng-bind-html='note.content.preview_html',
|
||||||
|
|||||||
@@ -23,7 +23,11 @@
|
|||||||
ng-repeat='tag in self.state.smartTags'
|
ng-repeat='tag in self.state.smartTags'
|
||||||
)
|
)
|
||||||
.tag-info
|
.tag-info
|
||||||
input.title(ng-disabled='true', ng-model='tag.title')
|
input.title(
|
||||||
|
ng-disabled='true',
|
||||||
|
ng-change='self.onTagTitleChange(tag)'
|
||||||
|
ng-model='self.titles[tag.uuid]'
|
||||||
|
)
|
||||||
.count(ng-show='tag.content.isAllTag') {{self.state.noteCounts[tag.uuid]}}
|
.count(ng-show='tag.content.isAllTag') {{self.state.noteCounts[tag.uuid]}}
|
||||||
.tags-title-section.section-title-bar
|
.tags-title-section.section-title-bar
|
||||||
.section-title-bar-header
|
.section-title-bar-header
|
||||||
@@ -38,12 +42,12 @@
|
|||||||
.tag-icon #
|
.tag-icon #
|
||||||
input.title(
|
input.title(
|
||||||
ng-attr-id='tag-{{tag.uuid}}',
|
ng-attr-id='tag-{{tag.uuid}}',
|
||||||
ng-blur='self.saveTag($event, tag)',
|
ng-blur='self.saveTag($event, tag)'
|
||||||
ng-change='self.tagTitleDidChange(tag)',
|
ng-change='self.onTagTitleChange(tag)',
|
||||||
|
ng-model='self.titles[tag.uuid]',
|
||||||
ng-class="{'editing' : self.state.editingTag == tag}",
|
ng-class="{'editing' : self.state.editingTag == tag}",
|
||||||
ng-click='self.selectTag(tag)',
|
ng-click='self.selectTag(tag)',
|
||||||
ng-keyup='$event.keyCode == 13 && $event.target.blur()',
|
ng-keyup='$event.keyCode == 13 && $event.target.blur()',
|
||||||
ng-model='tag.title',
|
|
||||||
should-focus='self.state.newTag || self.state.editingTag == tag',
|
should-focus='self.state.newTag || self.state.editingTag == tag',
|
||||||
sn-autofocus='true',
|
sn-autofocus='true',
|
||||||
spellcheck='false'
|
spellcheck='false'
|
||||||
@@ -53,8 +57,8 @@
|
|||||||
.danger.small-text.bold(ng-show='tag.errorDecrypting && !tag.waitingForKey') Missing Keys
|
.danger.small-text.bold(ng-show='tag.errorDecrypting && !tag.waitingForKey') Missing Keys
|
||||||
.info.small-text.bold(ng-show='tag.errorDecrypting && tag.waitingForKey') Waiting For Keys
|
.info.small-text.bold(ng-show='tag.errorDecrypting && tag.waitingForKey') Waiting For Keys
|
||||||
.menu(ng-show='self.state.selectedTag == tag')
|
.menu(ng-show='self.state.selectedTag == tag')
|
||||||
a.item(ng-click='self.selectedRenameTag($event, tag)', ng-show='!self.state.editingTag') Rename
|
a.item(ng-click='self.selectedRenameTag(tag)' ng-show='!self.state.editingTag') Rename
|
||||||
a.item(ng-click='self.saveTag($event, tag)', ng-show='self.state.editingTag') Save
|
a.item(ng-click='self.saveTag($event, tag)' ng-show='self.state.editingTag') Save
|
||||||
a.item(ng-click='self.selectedDeleteTag(tag)') Delete
|
a.item(ng-click='self.selectedDeleteTag(tag)') Delete
|
||||||
.no-tags-placeholder(ng-show='self.state.tags.length == 0')
|
.no-tags-placeholder(ng-show='self.state.tags.length == 0')
|
||||||
| No tags. Create one using the add button above.
|
| No tags. Create one using the add button above.
|
||||||
|
|||||||
Reference in New Issue
Block a user