mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
component => type widget refactoring
This commit is contained in:
parent
a99c016818
commit
746181689f
@ -141,7 +141,7 @@ async function printActiveNote() {
|
||||
|
||||
await libraryLoader.requireLibrary(libraryLoader.PRINT_THIS);
|
||||
|
||||
$tabContext.$tabContent.find('.note-detail-component:visible').printThis({
|
||||
$tabContext.$tabContent.find('.note-detail-printable:visible').printThis({
|
||||
header: $("<h2>").text($tabContext.note && $tabContext.note.title).prop('outerHTML') ,
|
||||
importCSS: false,
|
||||
loadCSS: [
|
||||
|
@ -60,6 +60,11 @@ class TabContext extends Component {
|
||||
|
||||
bundleService.executeRelationBundles(this.note, 'runOnNoteView', this);
|
||||
|
||||
if (this.note.isProtected && protectedSessionHolder.isProtectedSessionAvailable()) {
|
||||
// FIXME: there are probably more places where this should be done
|
||||
protectedSessionHolder.touchProtectedSession();
|
||||
}
|
||||
|
||||
this.trigger('tabNoteSwitched', {tabId: this.tabId});
|
||||
}
|
||||
|
||||
|
@ -16,25 +16,25 @@ const TPL = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
const componentClasses = {
|
||||
'empty': "./detail/note_detail_empty.js",
|
||||
'text': "./detail/note_detail_text.js",
|
||||
'code': "./detail/note_detail_code.js",
|
||||
'file': "./detail/note_detail_file.js",
|
||||
'image': "./detail/note_detail_image.js",
|
||||
'search': "./detail/note_detail_search.js",
|
||||
'render': "./detail/note_detail_render.js",
|
||||
'relation-map': "./detail/note_detail_relation_map.js",
|
||||
'protected-session': "./detail/note_detail_protected_session.js",
|
||||
'book': "./detail/note_detail_book.js"
|
||||
const typeWidgetClasses = {
|
||||
'empty': "./type_widgets/note_detail_empty.js",
|
||||
'text': "./type_widgets/text.js",
|
||||
'code': "./type_widgets/code.js",
|
||||
'file': "./type_widgets/file.js",
|
||||
'image': "./type_widgets/note_detail_image.js",
|
||||
'search': "./type_widgets/note_detail_search.js",
|
||||
'render': "./type_widgets/note_detail_render.js",
|
||||
'relation-map': "./type_widgets/note_detail_relation_map.js",
|
||||
'protected-session': "./type_widgets/note_detail_protected_session.js",
|
||||
'book': "./type_widgets/note_detail_book.js"
|
||||
};
|
||||
|
||||
export default class NoteDetailWidget extends TabAwareWidget {
|
||||
constructor(appContext) {
|
||||
super(appContext);
|
||||
|
||||
this.components = {};
|
||||
this.componentPromises = {};
|
||||
this.typeWidgets = {};
|
||||
this.typeWidgetPromises = {};
|
||||
}
|
||||
|
||||
doRender() {
|
||||
@ -43,18 +43,23 @@ export default class NoteDetailWidget extends TabAwareWidget {
|
||||
return this.$widget;
|
||||
}
|
||||
|
||||
async noteSwitched() {
|
||||
await this.initComponent(/**disableAutoBook*/);
|
||||
async refresh() {
|
||||
this.type = this.getWidgetType(/*disableAutoBook*/);
|
||||
|
||||
for (const componentType in this.components) {
|
||||
if (componentType !== this.type) {
|
||||
this.components[componentType].cleanup();
|
||||
if (!(this.type in this.typeWidgetPromises)) {
|
||||
this.typeWidgetPromises[this.type] = this.initWidgetType(this.type);
|
||||
}
|
||||
|
||||
await this.typeWidgetPromises[this.type];
|
||||
|
||||
for (const typeWidget of Object.values(this.typeWidgets)) {
|
||||
if (typeWidget.constructor.getType() !== this.type) {
|
||||
typeWidget.cleanup();
|
||||
typeWidget.toggle(false);
|
||||
}
|
||||
}
|
||||
|
||||
this.$widget.find('.note-detail-component').hide();
|
||||
|
||||
this.getComponent().show();
|
||||
this.getTypeWidget().toggle(true);
|
||||
|
||||
this.setupClasses();
|
||||
}
|
||||
@ -75,36 +80,26 @@ export default class NoteDetailWidget extends TabAwareWidget {
|
||||
this.$widget.toggleClass("protected", note.isProtected);
|
||||
}
|
||||
|
||||
getComponent() {
|
||||
if (!this.components[this.type]) {
|
||||
throw new Error("Could not find component for type: " + this.type);
|
||||
getTypeWidget() {
|
||||
if (!this.typeWidgets[this.type]) {
|
||||
throw new Error("Could not find typeWidget for type: " + this.type);
|
||||
}
|
||||
|
||||
return this.components[this.type];
|
||||
}
|
||||
|
||||
async initComponent(disableAutoBook = false) {
|
||||
this.type = this.getComponentType(disableAutoBook);
|
||||
|
||||
if (!(this.type in this.componentPromises)) {
|
||||
this.componentPromises[this.type] = this.reallyInitComponent(this.type);
|
||||
}
|
||||
|
||||
await this.componentPromises[this.type];
|
||||
return this.typeWidgets[this.type];
|
||||
}
|
||||
|
||||
async reallyInitComponent(type) {
|
||||
const clazz = await import(componentClasses[type]);
|
||||
async initWidgetType(type) {
|
||||
const clazz = await import(typeWidgetClasses[type]);
|
||||
|
||||
this.components[this.type] = new clazz.default(this.appContext);
|
||||
this.children.push(this.components[this.type]);
|
||||
this.typeWidgets[this.type] = new clazz.default(this.appContext);
|
||||
this.children.push(this.typeWidgets[this.type]);
|
||||
|
||||
this.components[this.type].renderTo(this.$widget);
|
||||
this.typeWidgets[this.type].renderTo(this.$widget);
|
||||
|
||||
this.components[this.type].eventReceived('setTabContext', {tabContext: this.tabContext});
|
||||
this.typeWidgets[this.type].eventReceived('setTabContext', {tabContext: this.tabContext});
|
||||
}
|
||||
|
||||
getComponentType(disableAutoBook) {
|
||||
getWidgetType(disableAutoBook) {
|
||||
const note = this.tabContext.note;
|
||||
|
||||
if (!note) {
|
||||
@ -113,20 +108,15 @@ export default class NoteDetailWidget extends TabAwareWidget {
|
||||
|
||||
let type = note.type;
|
||||
|
||||
if (type === 'text' && !disableAutoBook && utils.isHtmlEmpty(note.content) && note.hasChildren()) {
|
||||
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);
|
||||
}
|
||||
if (note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable()) {
|
||||
type = 'protected-session';
|
||||
}
|
||||
|
||||
return type;
|
||||
|
@ -135,6 +135,10 @@ export default class NoteTitleWidget extends TabAwareWidget {
|
||||
|
||||
this.$noteTitle.val(note.title);
|
||||
|
||||
if (note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable()) {
|
||||
this.$noteTitle.prop("readonly", true);
|
||||
}
|
||||
|
||||
this.$protectButton.toggleClass("active", note.isProtected);
|
||||
this.$protectButton.prop("disabled", note.isProtected);
|
||||
this.$unprotectButton.toggleClass("active", !note.isProtected);
|
||||
|
@ -4,14 +4,16 @@ import toastService from "../../services/toast.js";
|
||||
import server from "../../services/server.js";
|
||||
import noteDetailService from "../../services/note_detail.js";
|
||||
import keyboardActionService from "../../services/keyboard_actions.js";
|
||||
import TabAwareWidget from "../tab_aware_widget.js";
|
||||
import TypeWidget from "./type_widget.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="note-detail-code note-detail-component">
|
||||
<div class="note-detail-code note-detail-printable">
|
||||
<div class="note-detail-code-editor"></div>
|
||||
</div>`;
|
||||
|
||||
class NoteDetailCode extends TabAwareWidget {
|
||||
class CodeTypeWidget extends TypeWidget {
|
||||
static getType() { return "code"; }
|
||||
|
||||
doRender() {
|
||||
this.$widget = $(TPL);
|
||||
this.$editor = this.$widget.find('.note-detail-code-editor');
|
||||
@ -58,22 +60,19 @@ class NoteDetailCode extends TabAwareWidget {
|
||||
//this.onNoteChange(() => this.tabContext.noteChanged());
|
||||
}
|
||||
|
||||
refresh() {
|
||||
// lazy loading above can take time and tab might have been already switched to another note
|
||||
if (this.tabContext.note && this.tabContext.note.type === 'code') {
|
||||
// CodeMirror breaks pretty badly on null so even though it shouldn't happen (guarded by consistency check)
|
||||
// we provide fallback
|
||||
this.codeEditor.setValue(this.tabContext.note.content || "");
|
||||
doRefresh() {
|
||||
// CodeMirror breaks pretty badly on null so even though it shouldn't happen (guarded by consistency check)
|
||||
// we provide fallback
|
||||
this.codeEditor.setValue(this.tabContext.note.content || "");
|
||||
|
||||
const info = CodeMirror.findModeByMIME(this.tabContext.note.mime);
|
||||
const info = CodeMirror.findModeByMIME(this.tabContext.note.mime);
|
||||
|
||||
if (info) {
|
||||
this.codeEditor.setOption("mode", info.mime);
|
||||
CodeMirror.autoLoadMode(this.codeEditor, info.mode);
|
||||
}
|
||||
|
||||
this.show();
|
||||
if (info) {
|
||||
this.codeEditor.setOption("mode", info.mime);
|
||||
CodeMirror.autoLoadMode(this.codeEditor, info.mode);
|
||||
}
|
||||
|
||||
this.show();
|
||||
}
|
||||
|
||||
show() {
|
||||
@ -127,4 +126,4 @@ class NoteDetailCode extends TabAwareWidget {
|
||||
}
|
||||
}
|
||||
|
||||
export default NoteDetailCode;
|
||||
export default CodeTypeWidget;
|
@ -2,10 +2,10 @@ import utils from "../../services/utils.js";
|
||||
import server from "../../services/server.js";
|
||||
import toastService from "../../services/toast.js";
|
||||
import noteDetailService from "../../services/note_detail.js";
|
||||
import TabAwareWidget from "../tab_aware_widget.js";
|
||||
import TypeWidget from "./type_widget.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="note-detail-file note-detail-component">
|
||||
<div class="note-detail-file note-detail-printable">
|
||||
<style>
|
||||
.note-detail-file {
|
||||
padding: 10px;
|
||||
@ -55,7 +55,9 @@ const TPL = `
|
||||
<input type="file" class="file-upload-new-revision-input" style="display: none">
|
||||
</div>`;
|
||||
|
||||
class NoteDetailFile extends TabAwareWidget{
|
||||
class FileTypeWidget extends TypeWidget {
|
||||
static getType() { return "file"; }
|
||||
|
||||
doRender() {
|
||||
this.$widget = $(TPL);
|
||||
this.$fileNoteId = this.$widget.find(".file-note-id");
|
||||
@ -115,7 +117,7 @@ class NoteDetailFile extends TabAwareWidget{
|
||||
return this.$widget;
|
||||
}
|
||||
|
||||
async refresh() {
|
||||
async doRefresh() {
|
||||
const attributes = await server.get('notes/' + this.tabContext.note.noteId + '/attributes');
|
||||
const attributeMap = utils.toObject(attributes, l => [l.name, l.value]);
|
||||
|
||||
@ -155,4 +157,4 @@ class NoteDetailFile extends TabAwareWidget{
|
||||
scrollToTop() {}
|
||||
}
|
||||
|
||||
export default NoteDetailFile;
|
||||
export default FileTypeWidget;
|
@ -3,6 +3,7 @@ import treeService from '../../services/tree.js';
|
||||
import noteAutocompleteService from '../../services/note_autocomplete.js';
|
||||
import mimeTypesService from '../../services/mime_types.js';
|
||||
import TabAwareWidget from "../tab_aware_widget.js";
|
||||
import TypeWidget from "./type_widget.js";
|
||||
|
||||
const ENABLE_INSPECTOR = false;
|
||||
|
||||
@ -41,7 +42,7 @@ const mentionSetup = {
|
||||
};
|
||||
|
||||
const TPL = `
|
||||
<div class="note-detail-text note-detail-component">
|
||||
<div class="note-detail-text note-detail-printable">
|
||||
<style>
|
||||
.note-detail-text h1 { font-size: 2.0em; }
|
||||
.note-detail-text h2 { font-size: 1.8em; }
|
||||
@ -72,7 +73,9 @@ const TPL = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
class NoteDetailText extends TabAwareWidget {
|
||||
class TextTypeWidget extends TypeWidget {
|
||||
static getType() { return "text"; }
|
||||
|
||||
doRender() {
|
||||
this.$widget = $(TPL);
|
||||
this.$editor = this.$widget.find('.note-detail-text-editor');
|
||||
@ -130,15 +133,12 @@ class NoteDetailText extends TabAwareWidget {
|
||||
}
|
||||
}
|
||||
|
||||
async refresh() {
|
||||
// lazy loading above can take time and tab might have been already switched to another note
|
||||
if (this.tabContext.note && this.tabContext.note.type === 'text') {
|
||||
this.textEditor.isReadOnly = await this.isReadOnly();
|
||||
async doRefresh() {
|
||||
this.textEditor.isReadOnly = await this.isReadOnly();
|
||||
|
||||
this.$widget.show();
|
||||
this.$widget.show();
|
||||
|
||||
this.textEditor.setData(this.tabContext.note.content);
|
||||
}
|
||||
this.textEditor.setData(this.tabContext.note.content);
|
||||
}
|
||||
|
||||
getContent() {
|
||||
@ -188,4 +188,4 @@ class NoteDetailText extends TabAwareWidget {
|
||||
}
|
||||
}
|
||||
|
||||
export default NoteDetailText;
|
||||
export default TextTypeWidget;
|
18
src/public/javascripts/widgets/type_widgets/type_widget.js
Normal file
18
src/public/javascripts/widgets/type_widgets/type_widget.js
Normal file
@ -0,0 +1,18 @@
|
||||
import TabAwareWidget from "../tab_aware_widget.js";
|
||||
|
||||
export default class TypeWidget extends TabAwareWidget {
|
||||
// for overriding
|
||||
static getType() {}
|
||||
|
||||
doRefresh() {}
|
||||
|
||||
refresh() {
|
||||
if (!this.tabContext.note || this.tabContext.note.type !== this.constructor.getType()) {
|
||||
this.toggle(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.doRefresh();
|
||||
}
|
||||
}
|
@ -104,15 +104,4 @@ span.fancytree-expander {
|
||||
margin-top: 4px;
|
||||
border-width: 2px;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
.note-detail-component-wrapper {
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
flex-direction: column;
|
||||
/* for some reason detail overflows a little bit so we subtract few pixels */
|
||||
height: calc(100% - 25px);
|
||||
/* large left padding is necessary for ckeditor gutter in detail-only (smartphone) layout */
|
||||
padding-left: 35px;
|
||||
padding-top: 10px;
|
||||
}
|
@ -100,25 +100,6 @@ span.fancytree-node.muted { opacity: 0.6; }
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.note-detail-component-wrapper {
|
||||
flex-grow: 100;
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
flex-basis: content;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 500px;
|
||||
overflow-wrap: break-word; /* otherwise CKEditor fails miserably on super long lines */
|
||||
font-family: var(--detail-font-family);
|
||||
font-size: var(--detail-font-size);
|
||||
}
|
||||
|
||||
.note-detail-component {
|
||||
flex-grow: 100;
|
||||
display: none;
|
||||
}
|
||||
|
||||
/** 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,4 +1,4 @@
|
||||
<div class="note-detail-book note-detail-component">
|
||||
<div class="note-detail-book note-detail-printable">
|
||||
<div class="btn-group floating-button" style="right: 20px; top: 20px;">
|
||||
<button type="button"
|
||||
class="expand-children-button btn icon-button bx bx-move-vertical"
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class="note-detail-empty note-detail-component">
|
||||
<div class="note-detail-empty note-detail-printable">
|
||||
<div class="form-group">
|
||||
<label>Open note by typing note's title into input below or choose a note in the tree.</label>
|
||||
<div class="input-group">
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class="note-detail-image note-detail-component">
|
||||
<div class="note-detail-image note-detail-printable">
|
||||
<div style="display: flex; justify-content: space-evenly; margin: 10px;">
|
||||
<button class="image-download btn btn-sm btn-primary" type="button">Download</button>
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class="protected-session-password-component note-detail-component">
|
||||
<div class="protected-session-password-component note-detail-printable">
|
||||
<form class="protected-session-password-form">
|
||||
<div class="form-group">
|
||||
<label for="protected-session-password-in-detail">Showing protected note requires entering your password:</label>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class="note-detail-relation-map note-detail-component">
|
||||
<div class="note-detail-relation-map note-detail-printable">
|
||||
<button class="relation-map-create-child-note btn btn-sm floating-button" type="button"
|
||||
title="Create new child note and add it into this relation map">
|
||||
<span class="bx bx-folder-plus"></span>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class="note-detail-render note-detail-component">
|
||||
<div class="note-detail-render note-detail-printable">
|
||||
<div class="note-detail-render-help alert alert-warning">
|
||||
<p><strong>This help note is shown because this note of type Render HTML doesn't have required relation to function properly.</strong></p>
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class="note-detail-search note-detail-component">
|
||||
<div class="note-detail-search note-detail-printable">
|
||||
<div style="display: flex; align-items: center; margin-right: 20px; margin-top: 15px;">
|
||||
<strong>Search string: </strong>
|
||||
<textarea rows="4" style="width: auto !important; flex-grow: 4" class="search-string form-control"></textarea>
|
||||
|
@ -53,12 +53,12 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="note-detail-component-wrapper">
|
||||
<div class="note-detail-text note-detail-component" tabindex="10000">
|
||||
<div class="note-detail-printable-wrapper">
|
||||
<div class="note-detail-text note-detail-printable" tabindex="10000">
|
||||
<div class="note-detail-text-editor"></div>
|
||||
</div>
|
||||
|
||||
<div class="note-detail-code note-detail-component">
|
||||
<div class="note-detail-code note-detail-printable">
|
||||
<div class="note-detail-code-editor"></div>
|
||||
</div>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user