widgetizing note detail WIP

This commit is contained in:
zadam 2020-01-13 21:48:44 +01:00
parent 8b9c235465
commit c9bc4ad108
8 changed files with 183 additions and 164 deletions

View File

@ -10,6 +10,7 @@ import keyboardActionService from "./keyboard_actions.js";
import TabRowWidget from "./tab_row.js"; import TabRowWidget from "./tab_row.js";
import NoteTitleWidget from "../widgets/note_title.js"; import NoteTitleWidget from "../widgets/note_title.js";
import PromotedAttributesWidget from "../widgets/promoted_attributes.js"; import PromotedAttributesWidget from "../widgets/promoted_attributes.js";
import NoteDetailWidget from "../widgets/note_detail.js";
class AppContext { class AppContext {
constructor() { constructor() {
@ -29,29 +30,40 @@ class AppContext {
$("#global-menu-wrapper").after(contents); $("#global-menu-wrapper").after(contents);
this.promotedAttributes = new PromotedAttributesWidget(this);
$("#center-pane").prepend(this.promotedAttributes.render());
this.noteTitleWidget = new NoteTitleWidget(this);
$("#center-pane").prepend(this.noteTitleWidget.render());
this.noteTreeWidget = new NoteTreeWidget(this); this.noteTreeWidget = new NoteTreeWidget(this);
this.widgets = [ const leftPaneWidgets = [
new GlobalButtonsWidget(this), new GlobalButtonsWidget(this),
new SearchBoxWidget(this), new SearchBoxWidget(this),
new SearchResultsWidget(this), new SearchResultsWidget(this),
this.noteTreeWidget this.noteTreeWidget
]; ];
for (const widget of this.widgets) { for (const widget of leftPaneWidgets) {
const $widget = widget.render(); const $widget = widget.render();
$leftPane.append($widget); $leftPane.append($widget);
} }
this.widgets.push(this.noteTitleWidget); const $centerPane = $("#center-pane");
this.widgets.push(this.promotedAttributes);
const centerPaneWidgets = [
new NoteTitleWidget(this),
new PromotedAttributesWidget(this),
new NoteDetailWidget(this)
];
for (const widget of centerPaneWidgets) {
const $widget = widget.render();
$centerPane.append($widget);
}
this.widgets = [
this.tabRow,
...leftPaneWidgets,
...centerPaneWidgets
];
} }
trigger(name, data) { trigger(name, data) {

View File

@ -5,6 +5,11 @@ import server from "./server.js";
import noteDetailService from "./note_detail.js"; import noteDetailService from "./note_detail.js";
import keyboardActionService from "./keyboard_actions.js"; import keyboardActionService from "./keyboard_actions.js";
const TPL = `
<div class="note-detail-code note-detail-component">
<div class="note-detail-code-editor"></div>
</div>`;
class NoteDetailCode { class NoteDetailCode {
/** /**

View File

@ -39,13 +39,46 @@ const mentionSetup = {
] ]
}; };
const TPL = `
<div class="note-detail-text note-detail-component">
<style>
.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 {
overflow: auto;
font-family: var(--detail-text-font-family);
}
.note-detail-text-editor {
padding-top: 10px;
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 */
min-height: 500px;
}
.note-detail-text p:first-child, .note-detail-text::before {
margin-top: 0;
}
</style>
<div class="note-detail-text-editor" tabindex="10000"></div>
</div>
`;
class NoteDetailText { class NoteDetailText {
/** /**
* @param {TabContext} ctx * @param {TabContext} ctx
*/ */
constructor(ctx) { constructor(ctx, $parent) {
this.$component = $(TPL);
$parent.append(this.$component);
this.ctx = ctx; this.ctx = ctx;
this.$component = ctx.$tabContent.find('.note-detail-text');
this.$editorEl = this.$component.find('.note-detail-text-editor'); this.$editorEl = this.$component.find('.note-detail-text-editor');
this.textEditorPromise = null; this.textEditorPromise = null;
this.textEditor = null; this.textEditor = null;

View File

@ -8,21 +8,6 @@ import optionsService from "./options.js";
import Sidebar from "./sidebar.js"; import Sidebar from "./sidebar.js";
import appContext from "./app_context.js"; import appContext from "./app_context.js";
const $tabContentsContainer = $("#note-tab-container");
const componentClasses = {
'empty': "./note_detail_empty.js",
'text': "./note_detail_text.js",
'code': "./note_detail_code.js",
'file': "./note_detail_file.js",
'image': "./note_detail_image.js",
'search': "./note_detail_search.js",
'render': "./note_detail_render.js",
'relation-map': "./note_detail_relation_map.js",
'protected-session': "./note_detail_protected_session.js",
'book': "./note_detail_book.js"
};
let showSidebarInNewTab = true; let showSidebarInNewTab = true;
optionsService.addLoadListener(options => { optionsService.addLoadListener(options => {
@ -49,15 +34,8 @@ class TabContext {
this.initialized = true; this.initialized = true;
this.$tabContent = $(".note-tab-content-template").clone(); this.$tabContent = $("<div>"); // FIXME
this.$tabContent.removeClass('note-tab-content-template');
this.$tabContent.attr('data-tab-id', this.tabId);
this.$tabContent.hide();
$tabContentsContainer.append(this.$tabContent);
this.$noteDetailComponents = this.$tabContent.find(".note-detail-component");
this.$scriptArea = this.$tabContent.find(".note-detail-script-area");
this.noteChangeDisabled = false; this.noteChangeDisabled = false;
this.isNoteChanged = false; this.isNoteChanged = false;
this.attributes = new Attributes(this); this.attributes = new Attributes(this);
@ -69,20 +47,6 @@ class TabContext {
this.sidebar = new Sidebar(this, sidebarState); this.sidebar = new Sidebar(this, sidebarState);
} }
this.components = {};
await this.initComponent();
}
async initComponent(disableAutoBook = false) {
this.type = this.getComponentType(disableAutoBook);
if (!(this.type in this.components)) {
const clazz = await import(componentClasses[this.type]);
this.components[this.type] = new clazz.default(this);
}
} }
async setNote(note, notePath) { async setNote(note, notePath) {
@ -95,8 +59,6 @@ class TabContext {
return; return;
} }
this.$scriptArea.empty();
if (utils.isDesktop()) { if (utils.isDesktop()) {
this.attributes.refreshAttributes(); this.attributes.refreshAttributes();
} else { } else {
@ -115,7 +77,7 @@ class TabContext {
this.noteChangeDisabled = true; this.noteChangeDisabled = true;
try { try {
await this.renderComponent();
} finally { } finally {
this.noteChangeDisabled = false; this.noteChangeDisabled = false;
} }
@ -137,7 +99,8 @@ class TabContext {
bundleService.executeRelationBundles(this.note, 'runOnNoteView', this); bundleService.executeRelationBundles(this.note, 'runOnNoteView', this);
// after loading new note make sure editor is scrolled to the top // after loading new note make sure editor is scrolled to the top
this.getComponent().scrollToTop(); // FIXME
//this.getComponent().scrollToTop();
appContext.trigger('activeNoteChanged'); appContext.trigger('activeNoteChanged');
} }
@ -146,18 +109,15 @@ class TabContext {
if (!this.initialized) { if (!this.initialized) {
await this.initTabContent(); await this.initTabContent();
this.$tabContent.show(); // show immediately so that user can see something
if (this.note) { if (this.note) {
await this.setNote(this.note, this.notePath); await this.setNote(this.note, this.notePath);
} }
else { else {
// FIXME
await this.renderComponent(); // render empty page await this.renderComponent(); // render empty page
} }
} }
this.$tabContent.show();
if (this.sidebar) { if (this.sidebar) {
this.sidebar.show(); this.sidebar.show();
} }
@ -167,18 +127,7 @@ class TabContext {
} }
async renderComponent(disableAutoBook = false) { async renderComponent(disableAutoBook = false) {
await this.initComponent(disableAutoBook); // FIXME
for (const componentType in this.components) {
if (componentType !== this.type) {
this.components[componentType].cleanup();
}
}
this.$noteDetailComponents.hide();
this.getComponent().show();
await this.getComponent().render();
} }
setTitleBar() { setTitleBar() {
@ -235,36 +184,11 @@ class TabContext {
} }
getComponent() { getComponent() {
if (!this.components[this.type]) { // FIXME
throw new Error("Could not find component for type: " + this.type);
}
return this.components[this.type];
} }
getComponentType(disableAutoBook) { getComponentType(disableAutoBook) {
if (!this.note) { // FIXME
return "empty";
}
let type = this.note.type;
if (type === 'text' && !disableAutoBook && utils.isHtmlEmpty(this.note.content) && this.note.hasChildren()) {
type = 'book';
}
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);
}
}
return type;
} }
async activate() { async activate() {
@ -272,6 +196,8 @@ class TabContext {
} }
async saveNote() { async saveNote() {
return; // FIXME
if (this.note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable()) { if (this.note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable()) {
return; return;
} }
@ -316,8 +242,8 @@ class TabContext {
this.isNoteChanged = true; this.isNoteChanged = true;
// FIMXE: trigger noteChanged event // FIXME: trigger noteChanged event
this.$savedIndicator.fadeOut(); //this.$savedIndicator.fadeOut();
} }
async remove() { async remove() {

View File

@ -0,0 +1,108 @@
import TabAwareWidget from "./tab_aware_widget.js";
import utils from "../services/utils.js";
import protectedSessionHolder from "../services/protected_session_holder.js";
const TPL = `
<div class="note-detail">
<style>
.note-detail-content {
display: flex;
flex-direction: column;
flex-grow: 100;
height: 100%;
width: 100%;
}
</style>
</div>
`;
const componentClasses = {
'empty': "../services/note_detail_empty.js",
'text': "../services/note_detail_text.js",
'code': "../services/note_detail_code.js",
'file': "../services/note_detail_file.js",
'image': "../services/note_detail_image.js",
'search': "../services/note_detail_search.js",
'render': "../services/note_detail_render.js",
'relation-map': "../services/note_detail_relation_map.js",
'protected-session': "../services/note_detail_protected_session.js",
'book': "../services/note_detail_book.js"
};
export default class NoteDetailWidget extends TabAwareWidget {
constructor(appContext) {
super(appContext);
this.components = {};
}
doRender() {
this.$widget = $(TPL);
return this.$widget;
}
async activeTabChanged() {
await this.initComponent(/**disableAutoBook*/);
for (const componentType in this.components) {
// FIXME
this.components[componentType].ctx = this.tabContext;
if (componentType !== this.type) {
this.components[componentType].cleanup();
}
}
this.$widget.find('.note-detail-component').hide();
this.getComponent().show();
await this.getComponent().render();
}
getComponent() {
if (!this.components[this.type]) {
throw new Error("Could not find component for type: " + this.type);
}
return this.components[this.type];
}
async initComponent(disableAutoBook = false) {
this.type = this.getComponentType(disableAutoBook);
if (!(this.type in this.components)) {
const clazz = await import(componentClasses[this.type]);
this.components[this.type] = new clazz.default(this, this.$widget);
}
}
getComponentType(disableAutoBook) {
const note = this.tabContext.note;
if (!note) {
return "empty";
}
let type = note.type;
if (type === 'text' && !disableAutoBook && utils.isHtmlEmpty(note.content) && note.hasChildren()) {
type = 'book';
}
if (note.isProtected) {
if (protectedSessionHolder.isProtectedSessionAvailable()) {
protectedSessionHolder.touchProtectedSession();
} else {
type = 'protected-session';
// FIXME
// user shouldn't be able to edit note title
//this.$noteTitle.prop("readonly", true);
}
}
return type;
}
}

View File

@ -100,14 +100,6 @@ span.fancytree-node.muted { opacity: 0.6; }
width: 100%; width: 100%;
} }
.note-detail-content {
display: flex;
flex-direction: column;
flex-grow: 100;
height: 100%;
width: 100%;
}
.note-detail-component-wrapper { .note-detail-component-wrapper {
flex-grow: 100; flex-grow: 100;
position: relative; position: relative;
@ -127,30 +119,6 @@ span.fancytree-node.muted { opacity: 0.6; }
display: none; 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 {
overflow: auto;
font-family: var(--detail-text-font-family);
}
.note-detail-text-editor {
padding-top: 10px;
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 */
min-height: 500px;
}
.note-detail-text p:first-child, .note-detail-text::before {
margin-top: 0;
}
/** we disable shield background when in zen mode because I couldn't get it to stay static /** we disable shield background when in zen mode because I couldn't get it to stay static
(it kept growing with content) */ (it kept growing with content) */
#container:not(.zen-mode) .note-tab-content.protected { #container:not(.zen-mode) .note-tab-content.protected {

View File

@ -1,33 +0,0 @@
<div id="center-pane">
<div id="note-tab-container">
<div class="note-tab-content note-tab-content-template">
<div class="note-detail-content">
<div class="note-detail-component-wrapper">
<div class="note-detail-text note-detail-component">
<div class="note-detail-text-editor" tabindex="10000"></div>
</div>
<div class="note-detail-code note-detail-component">
<div class="note-detail-code-editor"></div>
</div>
<% include details/empty.ejs %>
<% include details/search.ejs %>
<% include details/render.ejs %>
<% include details/file.ejs %>
<% include details/image.ejs %>
<% include details/relation_map.ejs %>
<% include details/protected_session_password.ejs %>
<% include details/book.ejs %>
</div>
</div>
</div>
</div>
</div>

View File

@ -136,7 +136,7 @@
<div style="display: flex; flex-grow: 1; flex-shrink: 1; min-height: 0;"> <div style="display: flex; flex-grow: 1; flex-shrink: 1; min-height: 0;">
<div id="left-pane" class="hide-in-zen-mode"></div> <div id="left-pane" class="hide-in-zen-mode"></div>
<% include center.ejs %> <div id="center-pane"></div>
<% include sidebar.ejs %> <% include sidebar.ejs %>
</div> </div>