mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
widgetizing note detail WIP
This commit is contained in:
parent
8b9c235465
commit
c9bc4ad108
@ -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) {
|
||||
|
@ -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 {
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
@ -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() {
|
||||
|
108
src/public/javascripts/widgets/note_detail.js
Normal file
108
src/public/javascripts/widgets/note_detail.js
Normal 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;
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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>
|
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user