new tab infrastructure

This commit is contained in:
zadam 2019-05-12 12:58:55 +02:00
parent 637547a3fa
commit 14c420b782
13 changed files with 90 additions and 63 deletions

View File

@ -91,16 +91,18 @@ function getTabContexts() {
/** @returns {TabContext} */
function getActiveTabContext() {
for (const ctx of tabContexts) {
if (ctx.$tabContent.is(":visible")) {
return ctx;
}
const activeTabEl = tabRow.activeTabEl;
if (!activeTabEl) {
return null;
}
const tabId = activeTabEl.getAttribute('data-tab-id');
return tabContexts.find(tc => tc.tabId === tabId);
}
async function showTab(tabId) {
tabId = parseInt(tabId);
for (const ctx of tabContexts) {
ctx.$tabContent.toggle(ctx.tabId === tabId);
}
@ -114,14 +116,32 @@ async function showTab(tabId) {
treeService.clearSelectedNodes();
const newActiveTabContext = getActiveTabContext();
const newActiveNode = await treeService.getNodeFromPath(newActiveTabContext.notePath);
if (newActiveNode && newActiveNode.isVisible()) {
newActiveNode.setActive(true, { noEvents: true });
newActiveNode.setSelected(true);
if (newActiveTabContext && newActiveTabContext.notePath) {
const newActiveNode = await treeService.getNodeFromPath(newActiveTabContext.notePath);
if (newActiveNode && newActiveNode.isVisible()) {
newActiveNode.setActive(true, {noEvents: true});
newActiveNode.setSelected(true);
}
}
}
async function renderComponent(ctx) {
for (const componentType in ctx.components) {
if (componentType !== ctx.note.type) {
ctx.components[componentType].cleanup();
}
}
ctx.$noteDetailComponents.hide();
ctx.$noteTitle.show(); // this can be hidden by empty detail
ctx.$noteTitle.removeAttr("readonly"); // this can be set by protected session service
await ctx.getComponent().render();
}
/**
* @param {TabContext} ctx
* @param {NoteFull} note
@ -129,6 +149,8 @@ async function showTab(tabId) {
async function loadNoteDetailToContext(ctx, note, notePath) {
ctx.setNote(note, notePath);
openTabsChanged();
if (utils.isDesktop()) {
// needs to happen after loading the note itself because it references active noteId
ctx.attributes.refreshAttributes();
@ -147,17 +169,7 @@ async function loadNoteDetailToContext(ctx, note, notePath) {
ctx.noteType.mime(ctx.note.mime);
}
for (const componentType in ctx.components) {
if (componentType !== ctx.note.type) {
ctx.components[componentType].cleanup();
}
}
ctx.$noteDetailComponents.hide();
ctx.$noteTitle.removeAttr("readonly"); // this can be set by protected session service
await ctx.getComponent().show(ctx);
await renderComponent(ctx);
} finally {
ctx.noteChangeDisabled = false;
}
@ -180,11 +192,16 @@ async function loadNoteDetailToContext(ctx, note, notePath) {
}
}
async function loadNoteDetail(notePath, options = {}) {
async function loadNoteDetail(origNotePath, options = {}) {
const newTab = !!options.newTab;
const activate = !!options.activate;
notePath = await treeService.resolveNotePath(notePath);
const notePath = await treeService.resolveNotePath(origNotePath);
if (!notePath) {
console.error(`Cannot resolve note path ${origNotePath}`);
return;
}
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
const loadedNote = await loadNote(noteId);
@ -293,6 +310,15 @@ $tabContentsContainer.on("drop", e => {
});
});
tabRow.addListener('newTab', async () => {
const ctx = new TabContext(tabRow);
tabContexts.push(ctx);
renderComponent(ctx);
await tabRow.setCurrentTab(ctx.tab);
});
tabRow.addListener('activeTabChange', async ({ detail }) => {
const tabId = detail.tabEl.getAttribute('data-tab-id');
@ -302,7 +328,7 @@ tabRow.addListener('activeTabChange', async ({ detail }) => {
});
tabRow.addListener('tabRemove', async ({ detail }) => {
const tabId = parseInt(detail.tabEl.getAttribute('data-tab-id'));
const tabId = detail.tabEl.getAttribute('data-tab-id');
const tabContentToDelete = tabContexts.find(nc => nc.tabId === tabId);
@ -386,7 +412,7 @@ async function saveOpenTabs() {
const openTabs = [];
for (const tabEl of tabRow.tabEls) {
const tabId = parseInt(tabEl.getAttribute('data-tab-id'));
const tabId = tabEl.getAttribute('data-tab-id');
const tabContext = tabContexts.find(tc => tc.tabId === tabId);
if (tabContext) {

View File

@ -21,7 +21,7 @@ class NoteDetailCode {
this.$executeScriptButton.click(() => this.executeCurrentNote());
}
async show() {
async render() {
await libraryLoader.requireLibrary(libraryLoader.CODE_MIRROR);
if (!this.codeEditor) {

View File

@ -1,7 +1,5 @@
import utils from "./utils.js";
import server from "./server.js";
import protectedSessionHolder from "./protected_session_holder.js";
import noteDetailService from "./note_detail.js";
class NoteDetailFile {
/**
@ -33,7 +31,7 @@ class NoteDetailFile {
});
}
async show() {
async render() {
const attributes = await server.get('notes/' + this.ctx.note.noteId + '/attributes');
const attributeMap = utils.toObject(attributes, l => [l.name, l.value]);

View File

@ -42,7 +42,7 @@ class NoteDetailImage {
});
}
async show() {
async render() {
const attributes = await server.get('notes/' + this.ctx.note.noteId + '/attributes');
const attributeMap = utils.toObject(attributes, l => [l.name, l.value]);

View File

@ -20,7 +20,7 @@ class NoteDetailProtectedSession {
});
}
show() {
render() {
this.$component.show();
}

View File

@ -207,7 +207,7 @@ class NoteDetailRelationMap {
return id.substr(13);
}
async show() {
async render() {
this.$component.show();
await libraryLoader.requireLibrary(libraryLoader.RELATION_MAP);

View File

@ -14,10 +14,10 @@ class NoteDetailRender {
this.$noteDetailRenderContent = ctx.$tabContent.find('.note-detail-render-content');
this.$renderButton = ctx.$tabContent.find('.render-button');
this.$renderButton.click(this.show);
this.$renderButton.click(this.render);
}
async show() {
async render() {
const attributes = await attributeService.getAttributes();
const renderNotes = attributes.filter(attr =>
attr.type === 'relation'

View File

@ -19,7 +19,7 @@ class NoteDetailSearch {
});
}
show() {
render() {
this.$help.html(searchNotesService.getHelpText());
this.$component.show();

View File

@ -27,7 +27,7 @@ class NoteDetailText {
})
}
async show() {
async render() {
if (!this.textEditor) {
await libraryLoader.requireLibrary(libraryLoader.CKEDITOR);

View File

@ -7,8 +7,9 @@ import treeUtils from "./tree_utils.js";
import utils from "./utils.js";
import {NoteTypeContext} from "./note_type.js";
import noteDetailService from "./note_detail.js";
import noteDetailCode from "./note_detail_code.js";
import noteDetailEmpty from "./note_detail_empty.js";
import noteDetailText from "./note_detail_text.js";
import noteDetailCode from "./note_detail_code.js";
import noteDetailFile from "./note_detail_file.js";
import noteDetailImage from "./note_detail_image.js";
import noteDetailSearch from "./note_detail_search.js";
@ -20,8 +21,9 @@ import protectedSessionService from "./protected_session.js";
const $tabContentsContainer = $("#note-tab-container");
const componentClasses = {
'code': noteDetailCode,
'empty': noteDetailEmpty,
'text': noteDetailText,
'code': noteDetailCode,
'file': noteDetailFile,
'image': noteDetailImage,
'search': noteDetailSearch,
@ -30,19 +32,14 @@ const componentClasses = {
'protected-session': noteDetailProtectedSession
};
let tabIdCounter = 1;
class TabContext {
/**
* @param {TabRow} tabRow
*/
constructor(tabRow) {
this.tabId = tabIdCounter++;
this.tabRow = tabRow;
this.tab = this.tabRow.addTab({
title: '', // will be set later
id: this.tabId
});
this.tab = this.tabRow.addTab();
this.tabId = this.tab.getAttribute('data-tab-id');
this.$tabContent = $(".note-tab-content-template").clone();
this.$tabContent.removeClass('note-tab-content-template');
@ -51,6 +48,7 @@ class TabContext {
$tabContentsContainer.append(this.$tabContent);
this.$noteTitle = this.$tabContent.find(".note-title");
this.$noteTitleRow = this.$tabContent.find(".note-title-row");
this.$noteDetailComponents = this.$tabContent.find(".note-detail-component");
this.$childrenOverview = this.$tabContent.find(".children-overview");
this.$scriptArea = this.$tabContent.find(".note-detail-script-area");
@ -82,9 +80,6 @@ class TabContext {
this.noteId = note.noteId;
this.notePath = notePath;
this.note = note;
this.tab.setAttribute('data-note-id', this.noteId);
this.$tabContent.attr('data-note-id', note.noteId);
this.tabRow.updateTab(this.tab, {title: note.title});
this.attributes.invalidateAttributes();
@ -108,19 +103,25 @@ class TabContext {
}
getComponent() {
let type = this.note.type;
let type;
if (this.note.isProtected) {
if (protectedSessionHolder.isProtectedSessionAvailable()) {
protectedSessionHolder.touchProtectedSession();
}
else {
type = 'protected-session';
if (this.note) {
type = this.note.type;
// user shouldn't be able to edit note title
this.$noteTitle.prop("readonly", true);
if (this.note.isProtected) {
if (protectedSessionHolder.isProtectedSessionAvailable()) {
protectedSessionHolder.touchProtectedSession();
} else {
type = 'protected-session';
// user shouldn't be able to edit note title
this.$noteTitle.prop("readonly", true);
}
}
}
else {
type = 'empty';
}
if (!(type in this.components)) {
this.components[type] = new componentClasses[type](this);

View File

@ -38,6 +38,7 @@ class TabRow {
constructor() {
this.draggabillies = [];
this.eventListeners = {};
this.tabIdCounter = 1;
}
init(el) {
@ -164,12 +165,13 @@ class TabRow {
const div = document.createElement('div');
div.innerHTML = tabTemplate;
const tabEl = div.firstElementChild;
tabEl.setAttribute('data-tab-id', "t" + this.tabIdCounter++);
tabEl.classList.add('note-tab-was-just-added');
setTimeout(() => tabEl.classList.remove('note-tab-was-just-added'), 500);
tabProperties = Object.assign({}, defaultTapProperties, tabProperties);
this.tabContentEl.appendChild(tabEl);
this.newTabEl.before(tabEl);
this.setVisibility();
this.setTabCloseEventListener(tabEl);
this.updateTab(tabEl, tabProperties);
@ -266,10 +268,6 @@ class TabRow {
updateTab(tabEl, tabProperties) {
tabEl.querySelector('.note-tab-title').textContent = tabProperties.title;
if (tabProperties.id) {
tabEl.setAttribute('data-tab-id', tabProperties.id);
}
}
cleanUpPreviouslyDraggedTabs() {
@ -374,6 +372,8 @@ class TabRow {
this.tabContentEl.appendChild(this.newTabEl);
this.layoutTabs();
this.newTabEl.addEventListener('click', _ => this.emit('newTab'));
}
closest(value, array) {

View File

@ -78,7 +78,7 @@ class TreeContextMenu {
async selectContextMenuItem(event, cmd) {
if (cmd === 'openInTab') {
const notePath = treeUtils.getNotePath(this.node);
const notePath = await treeUtils.getNotePath(this.node);
noteDetailService.openInTab(notePath);
}

View File

@ -15,6 +15,8 @@
<div class="note-detail-code note-detail-component"></div>
<% include details/empty.ejs %>
<% include details/search.ejs %>
<% include details/render.ejs %>