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 NoteTitleWidget from "../widgets/note_title.js";
import PromotedAttributesWidget from "../widgets/promoted_attributes.js";
import NoteDetailWidget from "../widgets/note_detail.js";
class AppContext {
constructor() {
@ -29,29 +30,40 @@ class AppContext {
$("#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.widgets = [
const leftPaneWidgets = [
new GlobalButtonsWidget(this),
new SearchBoxWidget(this),
new SearchResultsWidget(this),
this.noteTreeWidget
];
for (const widget of this.widgets) {
for (const widget of leftPaneWidgets) {
const $widget = widget.render();
$leftPane.append($widget);
}
this.widgets.push(this.noteTitleWidget);
this.widgets.push(this.promotedAttributes);
const $centerPane = $("#center-pane");
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) {

View File

@ -5,6 +5,11 @@ import server from "./server.js";
import noteDetailService from "./note_detail.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 {
/**

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 {
/**
* @param {TabContext} ctx
*/
constructor(ctx) {
constructor(ctx, $parent) {
this.$component = $(TPL);
$parent.append(this.$component);
this.ctx = ctx;
this.$component = ctx.$tabContent.find('.note-detail-text');
this.$editorEl = this.$component.find('.note-detail-text-editor');
this.textEditorPromise = null;
this.textEditor = null;

View File

@ -8,21 +8,6 @@ import optionsService from "./options.js";
import Sidebar from "./sidebar.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;
optionsService.addLoadListener(options => {
@ -49,15 +34,8 @@ class TabContext {
this.initialized = true;
this.$tabContent = $(".note-tab-content-template").clone();
this.$tabContent.removeClass('note-tab-content-template');
this.$tabContent.attr('data-tab-id', this.tabId);
this.$tabContent.hide();
this.$tabContent = $("<div>"); // FIXME
$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.isNoteChanged = false;
this.attributes = new Attributes(this);
@ -69,20 +47,6 @@ class TabContext {
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) {
@ -95,8 +59,6 @@ class TabContext {
return;
}
this.$scriptArea.empty();
if (utils.isDesktop()) {
this.attributes.refreshAttributes();
} else {
@ -115,7 +77,7 @@ class TabContext {
this.noteChangeDisabled = true;
try {
await this.renderComponent();
} finally {
this.noteChangeDisabled = false;
}
@ -137,7 +99,8 @@ class TabContext {
bundleService.executeRelationBundles(this.note, 'runOnNoteView', this);
// after loading new note make sure editor is scrolled to the top
this.getComponent().scrollToTop();
// FIXME
//this.getComponent().scrollToTop();
appContext.trigger('activeNoteChanged');
}
@ -146,18 +109,15 @@ class TabContext {
if (!this.initialized) {
await this.initTabContent();
this.$tabContent.show(); // show immediately so that user can see something
if (this.note) {
await this.setNote(this.note, this.notePath);
}
else {
// FIXME
await this.renderComponent(); // render empty page
}
}
this.$tabContent.show();
if (this.sidebar) {
this.sidebar.show();
}
@ -167,18 +127,7 @@ class TabContext {
}
async renderComponent(disableAutoBook = false) {
await this.initComponent(disableAutoBook);
for (const componentType in this.components) {
if (componentType !== this.type) {
this.components[componentType].cleanup();
}
}
this.$noteDetailComponents.hide();
this.getComponent().show();
await this.getComponent().render();
// FIXME
}
setTitleBar() {
@ -235,36 +184,11 @@ class TabContext {
}
getComponent() {
if (!this.components[this.type]) {
throw new Error("Could not find component for type: " + this.type);
}
return this.components[this.type];
// FIXME
}
getComponentType(disableAutoBook) {
if (!this.note) {
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;
// FIXME
}
async activate() {
@ -272,6 +196,8 @@ class TabContext {
}
async saveNote() {
return; // FIXME
if (this.note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable()) {
return;
}
@ -316,8 +242,8 @@ class TabContext {
this.isNoteChanged = true;
// FIMXE: trigger noteChanged event
this.$savedIndicator.fadeOut();
// FIXME: trigger noteChanged event
//this.$savedIndicator.fadeOut();
}
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%;
}
.note-detail-content {
display: flex;
flex-direction: column;
flex-grow: 100;
height: 100%;
width: 100%;
}
.note-detail-component-wrapper {
flex-grow: 100;
position: relative;
@ -127,30 +119,6 @@ span.fancytree-node.muted { opacity: 0.6; }
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
(it kept growing with content) */
#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 id="left-pane" class="hide-in-zen-mode"></div>
<% include center.ejs %>
<div id="center-pane"></div>
<% include sidebar.ejs %>
</div>