diff --git a/src/public/javascripts/services/app_context.js b/src/public/javascripts/services/app_context.js index 803fbe53d..00628bc98 100644 --- a/src/public/javascripts/services/app_context.js +++ b/src/public/javascripts/services/app_context.js @@ -17,7 +17,6 @@ class AppContext extends Component { super(null); this.layout = layout; - this.tabManager = new TabManager(this); this.executors = []; } @@ -45,16 +44,22 @@ class AppContext extends Component { this.triggerEvent(eventName); }); + this.tabManager = new TabManager(); + this.executors = [ this.tabManager, - new DialogCommandExecutor(this), - new Entrypoints(this) + new DialogCommandExecutor(), + new Entrypoints() ]; - this.children = [ rootWidget, ...this.executors ]; + this.child(rootWidget); + + for (const executor of this.executors) { + this.child(executor); + } if (utils.isElectron()) { - this.children.push(new ZoomService(this)); + this.child(new ZoomService()); import("./spell_check.js").then(spellCheckService => spellCheckService.initSpellCheck()); } diff --git a/src/public/javascripts/services/entrypoints.js b/src/public/javascripts/services/entrypoints.js index fceb738c8..aba20aaca 100644 --- a/src/public/javascripts/services/entrypoints.js +++ b/src/public/javascripts/services/entrypoints.js @@ -8,8 +8,8 @@ import appContext from "./app_context.js"; import Component from "../widgets/component.js"; export default class Entrypoints extends Component { - constructor(parent) { - super(parent); + constructor() { + super(); // hot keys are active also inside inputs and content editables jQuery.hotkeys.options.filterInputAcceptingElements = false; diff --git a/src/public/javascripts/services/tab_context.js b/src/public/javascripts/services/tab_context.js index 12ac88574..2089642ec 100644 --- a/src/public/javascripts/services/tab_context.js +++ b/src/public/javascripts/services/tab_context.js @@ -9,15 +9,12 @@ import hoistedNoteService from "./hoisted_note.js"; class TabContext extends Component { /** - * @param {Component} parent * @param {string|null} tabId */ - constructor(parent, tabId = null) { - super(parent); + constructor(tabId = null) { + super(); this.tabId = tabId || utils.randomString(4); - - this.triggerEvent('newTabOpened', {tabId: this.tabId}); } async setNote(inputNotePath) { diff --git a/src/public/javascripts/services/tab_manager.js b/src/public/javascripts/services/tab_manager.js index 10756a0ca..771d36d57 100644 --- a/src/public/javascripts/services/tab_manager.js +++ b/src/public/javascripts/services/tab_manager.js @@ -6,11 +6,10 @@ import treeCache from "./tree_cache.js"; import treeService from "./tree.js"; import utils from "./utils.js"; import TabContext from "./tab_context.js"; -import appContext from "./app_context.js"; export default class TabManager extends Component { - constructor(parent) { - super(parent); + constructor() { + super(); this.activeTabId = null; @@ -183,8 +182,11 @@ export default class TabManager extends Component { } openEmptyTab(tabId) { - const tabContext = new TabContext(appContext, tabId); - this.children.push(tabContext); + const tabContext = new TabContext(tabId); + this.child(tabContext); + + this.triggerEvent('newTabOpened', {tabId: this.tabId}) + return tabContext; } diff --git a/src/public/javascripts/services/zoom.js b/src/public/javascripts/services/zoom.js index ed968e908..ea5e41a13 100644 --- a/src/public/javascripts/services/zoom.js +++ b/src/public/javascripts/services/zoom.js @@ -5,8 +5,8 @@ const MIN_ZOOM = 0.5; const MAX_ZOOM = 2.0; export default class ZoomService extends Component { - constructor(parent) { - super(parent); + constructor() { + super(); this.setZoomFactor(options.getFloat('zoomFactor')); } diff --git a/src/public/javascripts/widgets/component.js b/src/public/javascripts/widgets/component.js index 5fec19f72..de1f70bee 100644 --- a/src/public/javascripts/widgets/component.js +++ b/src/public/javascripts/widgets/component.js @@ -13,6 +13,15 @@ export default class Component { setParent(parent) { /** @type Component */ this.parent = parent; + return this; + } + + child(component) { + component.setParent(this); + + this.children.push(component); + + return this; } async handleEvent(name, data) { diff --git a/src/public/javascripts/widgets/flex_container.js b/src/public/javascripts/widgets/flex_container.js index 549618298..6134db478 100644 --- a/src/public/javascripts/widgets/flex_container.js +++ b/src/public/javascripts/widgets/flex_container.js @@ -4,12 +4,12 @@ export default class FlexContainer extends BasicWidget { constructor(direction) { super(); - if (!direction) { - throw new Error(`Direction argument missing, use either 'row' or 'column'`); + if (!direction || !['row', 'column'].includes(direction)) { + throw new Error(`Direction argument given as "${direction}", use either 'row' or 'column'`); } this.attrs = { - style: 'display: flex;' + style: `display: flex; flex-direction: ${direction};`, }; this.children = []; @@ -25,13 +25,8 @@ export default class FlexContainer extends BasicWidget { return this; } - rowFlex() { - this.css('flex-direction', 'row'); - return this; - } - - columnFlex() { - this.css('flex-direction', 'column'); + collapsible() { + this.css('min-height', '0'); return this; } @@ -40,10 +35,6 @@ export default class FlexContainer extends BasicWidget { return this; } - child(widgetFactory) { - this.children = widgetFactory(this); - } - doRender() { this.$widget = $(`
`); @@ -55,8 +46,6 @@ export default class FlexContainer extends BasicWidget { this.$widget.attr(key, this.attrs[key]); } - if (!this.children) - for (const widget of this.children) { this.$widget.append(widget.render()); } diff --git a/src/public/javascripts/widgets/layout.js b/src/public/javascripts/widgets/layout.js index 5d76556e6..649355408 100644 --- a/src/public/javascripts/widgets/layout.js +++ b/src/public/javascripts/widgets/layout.js @@ -28,43 +28,42 @@ import SidePaneToggles from "./side_pane_toggles.js"; export default class Layout { getRootWidget(appContext) { - const root = new FlexContainer(appContext) + const root = new FlexContainer('column').id('root-widget') .child(new FlexContainer('row') .child(new GlobalMenuWidget()) .child(new TabRowWidget()) .child(new TitleBarButtonsWidget())) .child(new StandardTopWidget()) - new FlexContainer({ 'flex-direction': 'row', 'min-height': '0' }, [ - new SidePaneContainer('left', [ - new GlobalButtonsWidget(), - new SearchBoxWidget(), - new SearchResultsWidget(), - new NoteTreeWidget() - ]), - new FlexContainer({ id: 'center-pane', 'flex-direction': 'column' }, [ - new FlexContainer({ 'flex-direction': 'row' }, [ - new TabCachingWidget(new NotePathsWidget()), - new NoteTitleWidget(), - new RunScriptButtonsWidget(), - new ProtectedNoteSwitchWidget(), - new NoteTypeWidget(), - new NoteActionsWidget() - ]), - new TabCachingWidget(new PromotedAttributesWidget()), - new TabCachingWidget(new NoteDetailWidget()) - ]), - new SidePaneContainer('right', [ - new NoteInfoWidget(), - new TabCachingWidget(() => new CalendarWidget()), - new TabCachingWidget(() => new AttributesWidget()), - new TabCachingWidget(() => new LinkMapWidget()), - new TabCachingWidget(() => new NoteRevisionsWidget()), - new TabCachingWidget(() => new SimilarNotesWidget()), - new TabCachingWidget(() => new WhatLinksHereWidget()) - ]), - new SidePaneToggles() - ]) - ]); + .child(new FlexContainer('row').collapsible() + .child(new SidePaneContainer('left') + .child(new GlobalButtonsWidget()) + .child(new SearchBoxWidget()) + .child(new SearchResultsWidget()) + .child(new NoteTreeWidget()) + ) + .child(new FlexContainer('column').id('center-pane') + .child(new FlexContainer('row') + .child(new TabCachingWidget(() => new NotePathsWidget())) + .child(new NoteTitleWidget()) + .child(new RunScriptButtonsWidget()) + .child(new ProtectedNoteSwitchWidget()) + .child(new NoteTypeWidget()) + .child(new NoteActionsWidget()) + ) + .child(new TabCachingWidget(() => new PromotedAttributesWidget())) + .child(new TabCachingWidget(() => new NoteDetailWidget())) + ) + .child(new SidePaneContainer('right') + .child(new NoteInfoWidget()) + .child(new TabCachingWidget(() => new CalendarWidget())) + .child(new TabCachingWidget(() => new AttributesWidget())) + .child(new TabCachingWidget(() => new LinkMapWidget())) + .child(new TabCachingWidget(() => new NoteRevisionsWidget())) + .child(new TabCachingWidget(() => new SimilarNotesWidget())) + .child(new TabCachingWidget(() => new WhatLinksHereWidget())) + ) + .child(new SidePaneToggles()) + ); root.setParent(appContext); diff --git a/src/public/javascripts/widgets/note_detail.js b/src/public/javascripts/widgets/note_detail.js index dfdbd2b17..b2e12de04 100644 --- a/src/public/javascripts/widgets/note_detail.js +++ b/src/public/javascripts/widgets/note_detail.js @@ -43,8 +43,8 @@ const typeWidgetClasses = { }; export default class NoteDetailWidget extends TabAwareWidget { - constructor(parent) { - super(parent); + constructor() { + super(); this.typeWidgets = {}; @@ -107,10 +107,10 @@ export default class NoteDetailWidget extends TabAwareWidget { if (!(this.type in this.typeWidgets)) { const clazz = typeWidgetClasses[this.type]; - const typeWidget = this.typeWidgets[this.type] = new clazz(this); + const typeWidget = this.typeWidgets[this.type] = new clazz(); typeWidget.spacedUpdate = this.spacedUpdate; - this.children.push(typeWidget); + this.child(typeWidget); const $renderedWidget = typeWidget.render(); keyboardActionsService.updateDisplayedShortcuts($renderedWidget); diff --git a/src/public/javascripts/widgets/note_title.js b/src/public/javascripts/widgets/note_title.js index 2c7c8316c..f4d167d36 100644 --- a/src/public/javascripts/widgets/note_title.js +++ b/src/public/javascripts/widgets/note_title.js @@ -23,8 +23,8 @@ const TPL = `
`; export default class NoteTitleWidget extends TabAwareWidget { - constructor(parent) { - super(parent); + constructor() { + super(); this.spacedUpdate = new SpacedUpdate(async () => { const title = this.$noteTitle.val(); diff --git a/src/public/javascripts/widgets/side_pane_container.js b/src/public/javascripts/widgets/side_pane_container.js index 5fc8e5e71..309e5b8b6 100644 --- a/src/public/javascripts/widgets/side_pane_container.js +++ b/src/public/javascripts/widgets/side_pane_container.js @@ -2,10 +2,13 @@ import options from "../services/options.js"; import FlexContainer from "./flex_container.js"; export default class SidePaneContainer extends FlexContainer { - constructor(parent, side, widgetFactories) { - super(parent, {id: side + '-pane', 'flex-direction': 'column', 'height': '100%'}, widgetFactories); + constructor(side) { + super('column'); this.side = side; + + this.id(side + '-pane'); + this.css('height', '100%'); } isEnabled() { diff --git a/src/public/javascripts/widgets/side_pane_toggles.js b/src/public/javascripts/widgets/side_pane_toggles.js index 4f5ff3e66..59d54166c 100644 --- a/src/public/javascripts/widgets/side_pane_toggles.js +++ b/src/public/javascripts/widgets/side_pane_toggles.js @@ -29,8 +29,8 @@ const TPL = ` `; export default class SidePaneToggles extends BasicWidget { - constructor(parent) { - super(parent); + constructor() { + super(); this.paneVisible = {}; } diff --git a/src/public/javascripts/widgets/type_widgets/type_widget.js b/src/public/javascripts/widgets/type_widgets/type_widget.js index 6bedc0bd3..c1b37c327 100644 --- a/src/public/javascripts/widgets/type_widgets/type_widget.js +++ b/src/public/javascripts/widgets/type_widgets/type_widget.js @@ -1,13 +1,6 @@ import TabAwareWidget from "../tab_aware_widget.js"; export default class TypeWidget extends TabAwareWidget { - constructor(parent) { - super(parent); - - /** @var {NoteDetailWidget} */ - this.noteDetailWidget = parent; - } - // for overriding static getType() {} @@ -18,7 +11,7 @@ export default class TypeWidget extends TabAwareWidget { async refresh() { const thisWidgetType = this.constructor.getType(); - const noteWidgetType = await this.noteDetailWidget.getWidgetType(); + const noteWidgetType = await this.parent.getWidgetType(); if (thisWidgetType !== noteWidgetType) { this.toggle(false);