From 0d11cadc186977fc7dd6c8deaebe9b10a1cf9931 Mon Sep 17 00:00:00 2001 From: zadam Date: Thu, 2 May 2019 22:24:43 +0200 Subject: [PATCH] tabs wip --- libraries/chrome-tabs/chrome-tabs.css | 25 +--- libraries/chrome-tabs/chrome-tabs.js | 16 +-- src/public/javascripts/desktop.js | 10 -- src/public/javascripts/services/attributes.js | 3 + src/public/javascripts/services/link.js | 6 +- .../javascripts/services/note_context.js | 53 ++++--- .../javascripts/services/note_detail.js | 71 ++++++---- .../javascripts/services/note_detail_file.js | 119 ++++++++-------- .../javascripts/services/note_detail_image.js | 132 ++++++++++-------- .../javascripts/services/note_detail_text.js | 4 +- src/public/javascripts/services/tree.js | 2 +- .../javascripts/services/tree_context_menu.js | 11 +- src/public/stylesheets/desktop.css | 3 +- src/public/stylesheets/mobile.css | 2 +- src/public/stylesheets/style.css | 52 +++---- src/views/details/file.ejs | 2 +- src/views/tabs.ejs | 17 +-- 17 files changed, 264 insertions(+), 264 deletions(-) diff --git a/libraries/chrome-tabs/chrome-tabs.css b/libraries/chrome-tabs/chrome-tabs.css index 7c4a51f75..b7dd59644 100644 --- a/libraries/chrome-tabs/chrome-tabs.css +++ b/libraries/chrome-tabs/chrome-tabs.css @@ -149,27 +149,4 @@ .chrome-tabs.chrome-tabs-is-sorting .chrome-tab:not(.chrome-tab-is-dragging), .chrome-tabs:not(.chrome-tabs-is-sorting) .chrome-tab.chrome-tab-was-just-dragged { transition: transform 120ms ease-in-out; -} -.chrome-tabs .chrome-tabs-bottom-bar { - position: absolute; - bottom: 0; - height: 4px; - left: 0; - width: 100%; - background: #fff; - z-index: 10; -} -.chrome-tabs-optional-shadow-below-bottom-bar { - position: relative; - height: 1px; - width: 100%; - background-image: url("data:image/svg+xml;utf8,"); - background-size: 1px 1px; - background-repeat: repeat-x; - background-position: 0% 0%; -} -@media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { - .chrome-tabs-optional-shadow-below-bottom-bar { - background-image: url("data:image/svg+xml;utf8,"); - } -} +} \ No newline at end of file diff --git a/libraries/chrome-tabs/chrome-tabs.js b/libraries/chrome-tabs/chrome-tabs.js index 3de62e123..a6ea988eb 100644 --- a/libraries/chrome-tabs/chrome-tabs.js +++ b/libraries/chrome-tabs/chrome-tabs.js @@ -41,12 +41,7 @@ const tabTemplate = `
-
-
- -
-
@@ -211,6 +206,8 @@ this.cleanUpPreviouslyDraggedTabs() this.layoutTabs() this.setupDraggabilly() + + return tabEl } setTabCloseEventListener(tabEl) { @@ -251,15 +248,6 @@ updateTab(tabEl, tabProperties) { tabEl.querySelector('.chrome-tab-title').textContent = tabProperties.title - const faviconEl = tabEl.querySelector('.chrome-tab-favicon') - if (tabProperties.favicon) { - faviconEl.style.backgroundImage = `url('${ tabProperties.favicon }')` - faviconEl.removeAttribute('hidden', '') - } else { - faviconEl.setAttribute('hidden', '') - faviconEl.removeAttribute('style') - } - if (tabProperties.id) { tabEl.setAttribute('data-tab-id', tabProperties.id) } diff --git a/src/public/javascripts/desktop.js b/src/public/javascripts/desktop.js index b333e9385..a08e51c7e 100644 --- a/src/public/javascripts/desktop.js +++ b/src/public/javascripts/desktop.js @@ -157,13 +157,3 @@ noteTypeService.init(); linkService.init(); noteAutocompleteService.init(); - -$(document).ready(() => { - const el = $('.chrome-tabs')[0]; - const chromeTabs = new ChromeTabs(); - chromeTabs.init(el); - - el.addEventListener('activeTabChange', ({detail}) => console.log('Active tab changed', detail.tabEl)); - el.addEventListener('tabAdd', ({detail}) => console.log('Tab added', detail.tabEl)); - el.addEventListener('tabRemove', ({detail}) => console.log('Tab removed', detail.tabEl)); -}); \ No newline at end of file diff --git a/src/public/javascripts/services/attributes.js b/src/public/javascripts/services/attributes.js index 200025602..cb7f7a50d 100644 --- a/src/public/javascripts/services/attributes.js +++ b/src/public/javascripts/services/attributes.js @@ -36,6 +36,9 @@ async function getAttributes() { } async function showAttributes() { + // FIXME tabs + return; + $promotedAttributesContainer.empty(); $attributeList.hide(); $attributeListInner.empty(); diff --git a/src/public/javascripts/services/link.js b/src/public/javascripts/services/link.js index 24aef28d3..8b2a5e524 100644 --- a/src/public/javascripts/services/link.js +++ b/src/public/javascripts/services/link.js @@ -106,9 +106,9 @@ function init() { // of opening the link in new window/tab $(document).on('click', "a[data-action='note']", goToLink); $(document).on('click', 'div.popover-content a, div.ui-tooltip-content a', goToLink); -$(document).on('dblclick', '#note-detail-text a', goToLink); -$(document).on('click', '#note-detail-render a', goToLink); -$(document).on('click', '#note-detail-text.ck-read-only a', goToLink); +$(document).on('dblclick', '.note-detail-text a', goToLink); +$(document).on('click', '.note-detail-render a', goToLink); +$(document).on('click', '.note-detail-text.ck-read-only a', goToLink); $(document).on('click', 'span.ck-button__label', e => { // this is a link preview dialog from CKEditor link editing // for some reason clicked element is span diff --git a/src/public/javascripts/services/note_context.js b/src/public/javascripts/services/note_context.js index 76ecb3dfc..eba3294ad 100644 --- a/src/public/javascripts/services/note_context.js +++ b/src/public/javascripts/services/note_context.js @@ -13,7 +13,15 @@ import noteDetailSearch from "./note_detail_search.js"; import noteDetailRender from "./note_detail_render.js"; import noteDetailRelationMap from "./note_detail_relation_map.js"; -const $noteTabsContainer = $("#note-tab-container"); +const $noteTabContentsContainer = $("#note-tab-container"); + +const el = $('.chrome-tabs')[0]; +const chromeTabs = new ChromeTabs(); +chromeTabs.init(el); + +el.addEventListener('activeTabChange', ({detail}) => console.log('Active tab changed', detail.tabEl)); +el.addEventListener('tabAdd', ({detail}) => console.log('Tab added', detail.tabEl)); +el.addEventListener('tabRemove', ({detail}) => console.log('Tab removed', detail.tabEl)); const componentClasses = { 'code': noteDetailCode, @@ -26,17 +34,17 @@ const componentClasses = { }; class NoteContext { - constructor(noteId) { + constructor(note) { /** @type {NoteFull} */ - this.note = null; - this.noteId = noteId; - this.$noteTab = $noteTabsContainer.find(`[data-note-id="${noteId}"]`); - this.$noteTitle = this.$noteTab.find(".note-title"); - this.$noteDetailComponents = this.$noteTab.find(".note-detail-component"); - this.$protectButton = this.$noteTab.find(".protect-button"); - this.$unprotectButton = this.$noteTab.find(".unprotect-button"); - this.$childrenOverview = this.$noteTab.find(".children-overview"); - this.$scriptArea = this.$noteTab.find(".note-detail-script-area"); + this.note = note; + this.noteId = note.noteId; + this.$noteTabContent = $noteTabContentsContainer.find(`[data-note-id="${this.noteId}"]`); + this.$noteTitle = this.$noteTabContent.find(".note-title"); + this.$noteDetailComponents = this.$noteTabContent.find(".note-detail-component"); + this.$protectButton = this.$noteTabContent.find(".protect-button"); + this.$unprotectButton = this.$noteTabContent.find(".unprotect-button"); + this.$childrenOverview = this.$noteTabContent.find(".children-overview"); + this.$scriptArea = this.$noteTabContent.find(".note-detail-script-area"); this.isNoteChanged = false; this.components = {}; @@ -47,6 +55,19 @@ class NoteContext { treeService.setNoteTitle(this.noteId, title); }); + + this.tab = chromeTabs.addTab({ + title: note.title, + favicon: false + }); + } + + setNote(note) { + this.noteId = note.noteId; + this.note = note; + this.$noteTabContent.attr('data-note-id', note.noteId); + + chromeTabs.updateTab(this.tab, {title: note.title}); } getComponent(type) { @@ -135,20 +156,20 @@ class NoteContext { } updateNoteView() { - this.$noteTab.toggleClass("protected", this.note.isProtected); + this.$noteTabContent.toggleClass("protected", this.note.isProtected); this.$protectButton.toggleClass("active", this.note.isProtected); this.$protectButton.prop("disabled", this.note.isProtected); this.$unprotectButton.toggleClass("active", !this.note.isProtected); this.$unprotectButton.prop("disabled", !this.note.isProtected || !protectedSessionHolder.isProtectedSessionAvailable()); - for (const clazz of Array.from(this.$noteTab[0].classList)) { // create copy to safely iterate over while removing classes + for (const clazz of Array.from(this.$noteTabContent[0].classList)) { // create copy to safely iterate over while removing classes if (clazz.startsWith("type-") || clazz.startsWith("mime-")) { - this.$noteTab.removeClass(clazz); + this.$noteTabContent.removeClass(clazz); } } - this.$noteTab.addClass(utils.getNoteTypeClass(this.note.type)); - this.$noteTab.addClass(utils.getMimeTypeClass(this.note.mime)); + this.$noteTabContent.addClass(utils.getNoteTypeClass(this.note.type)); + this.$noteTabContent.addClass(utils.getMimeTypeClass(this.note.mime)); } } diff --git a/src/public/javascripts/services/note_detail.js b/src/public/javascripts/services/note_detail.js index 1d737690f..aba0b62bc 100644 --- a/src/public/javascripts/services/note_detail.js +++ b/src/public/javascripts/services/note_detail.js @@ -13,7 +13,7 @@ import attributeService from "./attributes.js"; import utils from "./utils.js"; import importDialog from "../dialogs/import.js"; -const $noteTabsContainer = $("#note-tab-container"); +const $noteTabContentsContainer = $("#note-tab-container"); const $savedIndicator = $("#saved-indicator"); let noteChangeDisabled = false; @@ -43,18 +43,11 @@ async function reload() { await loadNoteDetail(getActiveNoteId()); } +async function openInTab(noteId) { + await loadNoteDetail(noteId, true); +} + async function switchToNote(noteId) { - if (Object.keys(noteContexts).length === 0) { - const tabContent = $("#note-tab-content-template").clone(); - - tabContent.removeAttr('id'); - tabContent.attr('data-note-id', noteId); - - $noteTabsContainer.append(tabContent); - - noteContexts[noteId] = new NoteContext(noteId); - } - //if (getActiveNoteId() !== noteId) { await saveNotesIfChanged(); @@ -71,7 +64,7 @@ function onNoteChange(func) { } async function saveNotesIfChanged() { - for (const ctx of Object.values(noteContexts)) { + for (const ctx of noteContexts) { await ctx.saveNoteIfChanged(); } @@ -93,13 +86,15 @@ async function handleProtectedSession() { return newSessionCreated; } -/** @type {Object.} */ -const noteContexts = {}; +/** @type {NoteContext[]} */ +const noteContexts = []; /** @returns {NoteContext} */ function getContext(noteId) { - if (noteId in noteContexts) { - return noteContexts[noteId]; + const noteContext = noteContexts.find(nc => nc.noteId === noteId); + + if (noteContext) { + return noteContext; } else { throw new Error(`Can't find note context for ${noteId}`); @@ -108,21 +103,36 @@ function getContext(noteId) { /** @returns {NoteContext} */ function getActiveContext() { - const currentTreeNode = treeService.getActiveNode(); - - return getContext(currentTreeNode.data.noteId); -} - -function showTab(noteId) { - for (const ctx of Object.values(noteContexts)) { - ctx.$noteTab.toggle(ctx.noteId === noteId); + for (const ctx of noteContexts) { + if (ctx.$noteTabContent.is(":visible")) { + return ctx; + } } } -async function loadNoteDetail(noteId) { - const ctx = getContext(noteId); +function showTab(noteId) { + for (const ctx of noteContexts) { + ctx.$noteTabContent.toggle(ctx.noteId === noteId); + } +} + +async function loadNoteDetail(noteId, newTab = false) { const loadedNote = await loadNote(noteId); + if (noteContexts.length === 0 || newTab) { + const tabContent = $("#note-tab-content-template").clone(); + + tabContent.removeAttr('id'); + tabContent.attr('data-note-id', noteId); + + $noteTabContentsContainer.append(tabContent); + + noteContexts.push(new NoteContext(loadedNote)); + } + + const ctx = getActiveContext(); + ctx.setNote(loadedNote); + // we will try to render the new note only if it's still the active one in the tree // this is useful when user quickly switches notes (by e.g. holding down arrow) so that we don't // try to render all those loaded notes one after each other. This only guarantees that correct note @@ -247,11 +257,11 @@ messagingService.subscribeToSyncMessages(syncData => { } }); -$noteTabsContainer.on("dragover", e => e.preventDefault()); +$noteTabContentsContainer.on("dragover", e => e.preventDefault()); -$noteTabsContainer.on("dragleave", e => e.preventDefault()); +$noteTabContentsContainer.on("dragleave", e => e.preventDefault()); -$noteTabsContainer.on("drop", e => { +$noteTabContentsContainer.on("drop", e => { importDialog.uploadFiles(getActiveNoteId(), e.originalEvent.dataTransfer.files, { safeImport: true, shrinkImages: true, @@ -269,6 +279,7 @@ setInterval(saveNotesIfChanged, 3000); export default { reload, + openInTab, switchToNote, loadNote, getActiveNote, diff --git a/src/public/javascripts/services/note_detail_file.js b/src/public/javascripts/services/note_detail_file.js index c32c63e03..98319f522 100644 --- a/src/public/javascripts/services/note_detail_file.js +++ b/src/public/javascripts/services/note_detail_file.js @@ -3,65 +3,74 @@ import server from "./server.js"; import protectedSessionHolder from "./protected_session_holder.js"; import noteDetailService from "./note_detail.js"; -const $component = $('#note-detail-file'); +class NoteDetailFile { + /** + * @param {NoteContext} ctx + */ + constructor(ctx) { + this.$component = ctx.$noteTabContent.find('.note-detail-file'); + this.$fileNoteId = ctx.$noteTabContent.find(".file-note-id"); + this.$fileName = ctx.$noteTabContent.find(".file-filename"); + this.$fileType = ctx.$noteTabContent.find(".file-filetype"); + this.$fileSize = ctx.$noteTabContent.find(".file-filesize"); + this.$previewRow = ctx.$noteTabContent.find(".file-preview-row"); + this.$previewContent = ctx.$noteTabContent.find(".file-preview-content"); + this.$downloadButton = ctx.$noteTabContent.find(".file-download"); + this.$openButton = ctx.$noteTabContent.find(".file-open"); -const $fileNoteId = $("#file-note-id"); -const $fileName = $("#file-filename"); -const $fileType = $("#file-filetype"); -const $fileSize = $("#file-filesize"); -const $previewRow = $("#file-preview-row"); -const $previewContent = $("#file-preview-content"); -const $downloadButton = $("#file-download"); -const $openButton = $("#file-open"); + this.$downloadButton.click(() => utils.download(this.getFileUrl())); -async function show() { - const activeNote = noteDetailService.getActiveNote(); + this.$openButton.click(() => { + if (utils.isElectron()) { + const open = require("open"); - const attributes = await server.get('notes/' + activeNote.noteId + '/attributes'); - const attributeMap = utils.toObject(attributes, l => [l.name, l.value]); - - $component.show(); - - $fileNoteId.text(activeNote.noteId); - $fileName.text(attributeMap.originalFileName || "?"); - $fileSize.text((attributeMap.fileSize || "?") + " bytes"); - $fileType.text(activeNote.mime); - - if (activeNote.content) { - $previewRow.show(); - $previewContent.text(activeNote.content); - } - else { - $previewRow.hide(); + open(this.getFileUrl()); + } + else { + window.location.href = this.getFileUrl(); + } + }); } - // open doesn't work for protected notes since it works through browser which isn't in protected session - $openButton.toggle(!activeNote.isProtected); + async show() { + const activeNote = noteDetailService.getActiveNote(); + + const attributes = await server.get('notes/' + activeNote.noteId + '/attributes'); + const attributeMap = utils.toObject(attributes, l => [l.name, l.value]); + + this.$component.show(); + + this.$fileNoteId.text(activeNote.noteId); + this.$fileName.text(attributeMap.originalFileName || "?"); + this.$fileSize.text((attributeMap.fileSize || "?") + " bytes"); + this.$fileType.text(activeNote.mime); + + if (activeNote.content) { + this.$previewRow.show(); + this.$previewContent.text(activeNote.content); + } + else { + this.$previewRow.hide(); + } + + // open doesn't work for protected notes since it works through browser which isn't in protected session + this.$openButton.toggle(!activeNote.isProtected); + } + + getFileUrl() { + // electron needs absolute URL so we extract current host, port, protocol + return utils.getHost() + "/api/notes/" + noteDetailService.getActiveNoteId() + "/download"; + } + + getContent() {} + + focus() {} + + onNoteChange() {} + + cleanup() {} + + scrollToTop() {} } -$downloadButton.click(() => utils.download(getFileUrl())); - -$openButton.click(() => { - if (utils.isElectron()) { - const open = require("open"); - - open(getFileUrl()); - } - else { - window.location.href = getFileUrl(); - } -}); - -function getFileUrl() { - // electron needs absolute URL so we extract current host, port, protocol - return utils.getHost() + "/api/notes/" + noteDetailService.getActiveNoteId() + "/download"; -} - -export default { - show, - getContent: () => null, - focus: () => null, - onNoteChange: () => null, - cleanup: () => null, - scrollToTop: () => null -} \ No newline at end of file +export default NoteDetailFile; \ No newline at end of file diff --git a/src/public/javascripts/services/note_detail_image.js b/src/public/javascripts/services/note_detail_image.js index 0202080d0..027410afa 100644 --- a/src/public/javascripts/services/note_detail_image.js +++ b/src/public/javascripts/services/note_detail_image.js @@ -1,75 +1,85 @@ import utils from "./utils.js"; -import protectedSessionHolder from "./protected_session_holder.js"; import noteDetailService from "./note_detail.js"; import infoService from "./info.js"; import server from "./server.js"; -const $component = $('#note-detail-image'); -const $imageWrapper = $('#note-detail-image-wrapper'); -const $imageView = $('#note-detail-image-view'); +class NoteDetailImage { + /** + * @param {NoteContext} ctx + */ + constructor(ctx) { + this.$component = ctx.$noteTabContent.find('.note-detail-image'); + this.$imageWrapper = ctx.$noteTabContent.find('.note-detail-image-wrapper'); + this.$imageView = ctx.$noteTabContent.find('.note-detail-image-view'); + this.$copyToClipboardButton = ctx.$noteTabContent.find(".image-copy-to-clipboard"); + this.$fileName = ctx.$noteTabContent.find(".image-filename"); + this.$fileType = ctx.$noteTabContent.find(".image-filetype"); + this.$fileSize = ctx.$noteTabContent.find(".image-filesize"); -const $imageDownloadButton = $("#image-download"); -const $copyToClipboardButton = $("#image-copy-to-clipboard"); -const $fileName = $("#image-filename"); -const $fileType = $("#image-filetype"); -const $fileSize = $("#image-filesize"); + this.$imageDownloadButton = ctx.$noteTabContent.find(".image-download"); + this.$imageDownloadButton.click(() => utils.download(this.getFileUrl())); -async function show() { - const activeNote = noteDetailService.getActiveNote(); + this.$copyToClipboardButton.click(() => { + this.$imageWrapper.attr('contenteditable','true'); - const attributes = await server.get('notes/' + activeNote.noteId + '/attributes'); - const attributeMap = utils.toObject(attributes, l => [l.name, l.value]); + try { + this.selectImage(this.$imageWrapper.get(0)); - $component.show(); + const success = document.execCommand('copy'); - $fileName.text(attributeMap.originalFileName || "?"); - $fileSize.text((attributeMap.fileSize || "?") + " bytes"); - $fileType.text(activeNote.mime); - - $imageView.prop("src", `api/images/${activeNote.noteId}/${activeNote.title}`); -} - -$imageDownloadButton.click(() => utils.download(getFileUrl())); - -function selectImage(element) { - const selection = window.getSelection(); - const range = document.createRange(); - range.selectNodeContents(element); - selection.removeAllRanges(); - selection.addRange(range); -} - -$copyToClipboardButton.click(() => { - $imageWrapper.attr('contenteditable','true'); - - try { - selectImage($imageWrapper.get(0)); - - const success = document.execCommand('copy'); - - if (success) { - infoService.showMessage("Image copied to the clipboard"); - } - else { - infoService.showAndLogError("Could not copy the image to clipboard."); - } + if (success) { + infoService.showMessage("Image copied to the clipboard"); + } + else { + infoService.showAndLogError("Could not copy the image to clipboard."); + } + } + finally { + window.getSelection().removeAllRanges(); + this.$imageWrapper.removeAttr('contenteditable'); + } + }); } - finally { - window.getSelection().removeAllRanges(); - $imageWrapper.removeAttr('contenteditable'); - } -}); -function getFileUrl() { - // electron needs absolute URL so we extract current host, port, protocol - return utils.getHost() + "/api/notes/" + noteDetailService.getActiveNoteId() + "/download"; + async show() { + const activeNote = noteDetailService.getActiveNote(); + + const attributes = await server.get('notes/' + activeNote.noteId + '/attributes'); + const attributeMap = utils.toObject(attributes, l => [l.name, l.value]); + + this.$component.show(); + + this.$fileName.text(attributeMap.originalFileName || "?"); + this.$fileSize.text((attributeMap.fileSize || "?") + " bytes"); + this.$fileType.text(activeNote.mime); + + this.$imageView.prop("src", `api/images/${activeNote.noteId}/${activeNote.title}`); + } + + selectImage(element) { + const selection = window.getSelection(); + const range = document.createRange(); + range.selectNodeContents(element); + selection.removeAllRanges(); + selection.addRange(range); + } + + getFileUrl() { + // electron needs absolute URL so we extract current host, port, protocol + return utils.getHost() + "/api/notes/" + noteDetailService.getActiveNoteId() + "/download"; + } + + getContent() {} + + focus() {} + + onNoteChange() {} + + cleanup() {} + + scrollToTop() { + this.$component.scrollTop(0); + } } -export default { - show, - getContent: () => null, - focus: () => null, - onNoteChange: () => null, - cleanup: () => null, - scrollToTop: () => $component.scrollTop(0) -} \ No newline at end of file +export default NoteDetailImage \ No newline at end of file diff --git a/src/public/javascripts/services/note_detail_text.js b/src/public/javascripts/services/note_detail_text.js index 7a351038a..16c21632d 100644 --- a/src/public/javascripts/services/note_detail_text.js +++ b/src/public/javascripts/services/note_detail_text.js @@ -9,7 +9,7 @@ class NoteDetailText { */ constructor(ctx) { this.ctx = ctx; - this.$component = ctx.$noteTab.find('.note-detail-text'); + this.$component = ctx.$noteTabContent.find('.note-detail-text'); this.textEditor = null; this.$component.on("dblclick", "img", e => { @@ -53,7 +53,7 @@ class NoteDetailText { this.$component.show(); - this.textEditor.setData(this.ctx.note.content); +// this.textEditor.setData(this.ctx.note.content); } getContent() { diff --git a/src/public/javascripts/services/tree.js b/src/public/javascripts/services/tree.js index a314e56e4..e9775470e 100644 --- a/src/public/javascripts/services/tree.js +++ b/src/public/javascripts/services/tree.js @@ -485,7 +485,7 @@ function initFancyTree(tree) { }); $tree.on('contextmenu', '.fancytree-node', function(e) { - treeContextMenuService.getContextMenuItems(e).then(contextMenuItems => { + treeContextMenuService.getContextMenuItems(e).then(([node, contextMenuItems]) => { contextMenuWidget.initContextMenu(e, contextMenuItems, treeContextMenuService.selectContextMenuItem); }); diff --git a/src/public/javascripts/services/tree_context_menu.js b/src/public/javascripts/services/tree_context_menu.js index aca05ac4d..06a0dad4b 100644 --- a/src/public/javascripts/services/tree_context_menu.js +++ b/src/public/javascripts/services/tree_context_menu.js @@ -11,6 +11,7 @@ import infoService from "./info.js"; import treeCache from "./tree_cache.js"; import syncService from "./sync.js"; import hoistedNoteService from './hoisted_note.js'; +import noteDetailService from './note_detail.js'; let clipboardIds = []; let clipboardMode = null; @@ -103,6 +104,7 @@ async function getTopLevelItems(event) { const insertChildNoteEnabled = note.type !== 'search'; return [ + { title: "Open in new tab", cmd: "openInTab", uiIcon: "empty" }, { title: "Insert note after Ctrl+O", cmd: "insertNoteAfter", uiIcon: "plus", items: insertNoteAfterEnabled ? getNoteTypeItems("insertNoteAfter") : null, enabled: insertNoteAfterEnabled }, @@ -143,9 +145,7 @@ async function getTopLevelItems(event) { async function getContextMenuItems(event) { const items = await getTopLevelItems(event); - // Activate node on right-click const node = $.ui.fancytree.getNode(event); - node.setActive(); // right click resets selection to just this node // this is important when e.g. you right click on a note while having different note active @@ -153,14 +153,17 @@ async function getContextMenuItems(event) { node.setSelected(true); treeService.clearSelectedNodes(); - return items; + return [node, items]; } async function selectContextMenuItem(event, cmd) { // context menu is always triggered on current node const node = treeService.getActiveNode(); - if (cmd.startsWith("insertNoteAfter")) { + if (cmd === 'openInTab') { + noteDetailService.openInTab(node.data.noteId); + } + else if (cmd.startsWith("insertNoteAfter")) { const parentNoteId = node.data.parentNoteId; const isProtected = await treeUtils.getParentProtectedStatus(node); const type = cmd.split("_")[1]; diff --git a/src/public/stylesheets/desktop.css b/src/public/stylesheets/desktop.css index 58072cf7b..c09f21d4c 100644 --- a/src/public/stylesheets/desktop.css +++ b/src/public/stylesheets/desktop.css @@ -31,7 +31,6 @@ body { #note-tab-container { grid-area: tab-container; min-height: 0; - min-height: 0; } #search-box { @@ -148,7 +147,7 @@ li.dropdown-submenu:hover > ul.dropdown-menu { border: 1px solid var(--main-border-color); } -#note-info-table td, #note-info-table th { +.note-info-table td, .note-info-table th { padding: 15px; } diff --git a/src/public/stylesheets/mobile.css b/src/public/stylesheets/mobile.css index 523258c6c..8f4dd9ab3 100644 --- a/src/public/stylesheets/mobile.css +++ b/src/public/stylesheets/mobile.css @@ -50,7 +50,7 @@ html, body { padding-left: 35px; } -#note-title-row { +.note-title-row { display: flex; padding-left: 15px; flex-shrink: 0; diff --git a/src/public/stylesheets/style.css b/src/public/stylesheets/style.css index fc06904a5..082128dfd 100644 --- a/src/public/stylesheets/style.css +++ b/src/public/stylesheets/style.css @@ -48,7 +48,7 @@ button.close { color: var(--main-text-color) !important; } -#note-title { +.note-title { margin-left: 15px; margin-right: 10px; font-size: 150%; @@ -95,7 +95,7 @@ ul.fancytree-container { content: "\e9ba"; } -#note-title[readonly] { +.note-title[readonly] { background: inherit; } @@ -103,13 +103,17 @@ ul.fancytree-container { display: none; } +#note-tab-content-template { + display: none; +} + .note-tab-content { display: flex; flex-direction: column; height: 100%; } -#note-detail-component-wrapper { +.note-detail-component-wrapper { flex-grow: 100; position: relative; overflow: auto; @@ -125,14 +129,14 @@ ul.fancytree-container { display: none; } -#note-detail-text h1 { font-size: 2.0em; } -#note-detail-text h2 { font-size: 1.8em; } -#note-detail-text h3 { font-size: 1.6em; } -#note-detail-text h4 { font-size: 1.4em; } -#note-detail-text h5 { font-size: 1.2em; } -#note-detail-text h6 { font-size: 1.1em; } +.note-detail-text h1 { font-size: 2.0em; } +.note-detail-text h2 { font-size: 1.8em; } +.note-detail-text h3 { font-size: 1.6em; } +.note-detail-text h4 { font-size: 1.4em; } +.note-detail-text h5 { font-size: 1.2em; } +.note-detail-text h6 { font-size: 1.1em; } -#note-detail-text { +.note-detail-text { border: 0 !important; box-shadow: none !important; /* This is because with empty content height of editor is 0 and it's impossible to click into it */ @@ -142,7 +146,7 @@ ul.fancytree-container { font-family: var(--detail-text-font-family); } -#note-detail-text p:first-child, #note-detail-text::before { +.note-detail-text p:first-child, .note-detail-text::before { margin-top: 0; } @@ -352,12 +356,12 @@ div.ui-tooltip { color: #aaa !important; } -#note-detail-code { +.note-detail-code { min-height: 200px; overflow: auto; } -#note-detail-render { +.note-detail-render { min-height: 200px; } @@ -376,7 +380,7 @@ div.ui-tooltip { border-right: none; } -#note-type-dropdown { +.note-type-dropdown { max-height: 500px; overflow-y: auto; overflow-x: hidden; @@ -403,7 +407,7 @@ div.ui-tooltip { margin-right: 5px; } -#file-table th, #file-table td { +.file-table th, .file-table td { padding: 10px; font-size: larger; } @@ -464,7 +468,7 @@ div.ui-tooltip { background-color: var(--button-disabled-background-color) !important; } -#note-path-list a.current { +.note-path-list a.current { font-weight: bold; } @@ -473,12 +477,12 @@ button.icon-button { padding: 2px; } -#note-actions { +.note-actions { margin-left: 10px; margin-right: 10px; } -#note-actions .dropdown-menu { +.note-actions .dropdown-menu { width: 15em; } @@ -495,7 +499,7 @@ button.icon-button { padding: 0; } -#note-detail-promoted-attributes { +.note-detail-promoted-attributes { margin: auto; /* setting the display to block since "table" doesn't support scrolling */ display: block; @@ -505,15 +509,15 @@ button.icon-button { overflow: auto; } -#note-detail-promoted-attributes td, #note-detail-promoted-attributes th { +.note-detail-promoted-attributes td, .note-detail-promoted-attributes th { padding: 5px; } -#note-detail-image { +.note-detail-image { text-align: center; } -#note-detail-image-view { +.note-detail-image-view { max-width: 100%; } @@ -521,7 +525,7 @@ pre:not(.CodeMirror-line) { color: var(--main-text-color) !important; } -#file-preview-content { +.file-preview-content { background-color: var(--accented-background-color); padding: 15px; max-width: 600px; @@ -580,7 +584,7 @@ table.promoted-attributes-in-tooltip td, table.promoted-attributes-in-tooltip th padding: 10px; } -#note-detail-render-help { +.note-detail-render-help { margin: 50px; padding: 20px; } diff --git a/src/views/details/file.ejs b/src/views/details/file.ejs index bfb801cd6..1ea689150 100644 --- a/src/views/details/file.ejs +++ b/src/views/details/file.ejs @@ -16,7 +16,7 @@ File size: - + Preview:

diff --git a/src/views/tabs.ejs b/src/views/tabs.ejs
index 38e0f728f..5a80d24b4 100644
--- a/src/views/tabs.ejs
+++ b/src/views/tabs.ejs
@@ -1,20 +1,5 @@
 
-
-
-
-
Google
-
-
-
-
-
-
-
Facebook
-
-
-
-
-
+