diff --git a/js/notebook/src/tree.js b/js/notebook/src/tree.js index 61fcf67c66..e7807111c7 100644 --- a/js/notebook/src/tree.js +++ b/js/notebook/src/tree.js @@ -28,13 +28,19 @@ define([ function load() { var TreeWidget = require('./tree/TreeWidget').default; - var options = { + var bxTabPane = $('
', { + class: 'tab-pane', + id: 'beakerx-tree' + }).appendTo($('.tab-content')).get(0); + + var widgetOptions = { baseUrl: (Jupyter.notebook_list || Jupyter.notebook).base_url, isLab: false }; - var bxWidget = new TreeWidget(options); + var bxWidget = new TreeWidget(widgetOptions); + + Widget.attach(bxWidget, bxTabPane); - Widget.attach(bxWidget, $('.tab-content').get(0)); $('#tabs').append( $('
  • ').append( $('', { @@ -43,15 +49,20 @@ define([ 'data-toggle': 'tab', text: 'BeakerX' }).on('click', function (e) { + if (false === $(e.currentTarget).parents('li').hasClass('active')) { + bxWidget.update(); + } if (window.location.hash === '#beakerx-tree') { return; } - window.history.pushState(null, '', '#beakerx-tree'); - bxWidget.update(); }) ) ); + + $(window).on('resize', function() { + bxWidget.update(); + }); } return { diff --git a/js/notebook/src/tree/Messages.ts b/js/notebook/src/tree/Messages.ts index b55faf729c..8fa6a8703c 100644 --- a/js/notebook/src/tree/Messages.ts +++ b/js/notebook/src/tree/Messages.ts @@ -110,4 +110,14 @@ export namespace Messages { } } + export const TYPE_SIZE_CHANGED = 'size-changed'; + + export class SizeChangedMessage extends Message { + + constructor() { + super(TYPE_SIZE_CHANGED); + } + + } + } diff --git a/js/notebook/src/tree/Models/TreeWidgetModel.ts b/js/notebook/src/tree/Models/TreeWidgetModel.ts index dd54b4c694..27ba1c7e3f 100644 --- a/js/notebook/src/tree/Models/TreeWidgetModel.ts +++ b/js/notebook/src/tree/Models/TreeWidgetModel.ts @@ -50,7 +50,7 @@ export default class TreeWidgetModel { .update(data.ui_options); } - this.showResult(data.jvm_options); + this.setResult(data.jvm_options); setTimeout(() => { this.syncEnd() @@ -64,7 +64,7 @@ export default class TreeWidgetModel { let payload: IApiSettingsResponse = this.api.mergeWithDefaults(this._options); - this.showResult(payload.jvm_options); + this.setResult(payload.jvm_options); this.api.saveSettings({ beakerx: payload }) .then(() => { @@ -90,6 +90,19 @@ export default class TreeWidgetModel { this._options.jvm_options = options; } + public showResult() { + if (this._options) { + this.jvmOptionsModel + .update(this._options.jvm_options); + } + + this.syncWidget.show(); + } + + public hideResult() { + this.syncWidget.hide(); + } + private syncStart(): void { this.syncWidget.onSyncStart(); } @@ -98,8 +111,8 @@ export default class TreeWidgetModel { this.syncWidget.onSyncEnd(); } - private showResult(options: IJVMOptions) { - this.syncWidget.showResult(this.buildResult(options)); + private setResult(options: IJVMOptions) { + this.syncWidget.setResult(this.buildResult(options)); } private buildResult(options: IJVMOptions): string { diff --git a/js/notebook/src/tree/TreeWidget.ts b/js/notebook/src/tree/TreeWidget.ts index 8cc4cee873..bc829b21be 100644 --- a/js/notebook/src/tree/TreeWidget.ts +++ b/js/notebook/src/tree/TreeWidget.ts @@ -14,9 +14,7 @@ * limitations under the License. */ -import * as $ from "jquery"; - -import { Panel, Widget } from "@phosphor/widgets"; +import { Panel } from "@phosphor/widgets"; import { Message } from "@phosphor/messaging"; import BeakerXApi from "./Utils/BeakerXApi"; @@ -25,22 +23,11 @@ import { Messages } from "./Messages"; import ITreeWidgetOptions from "./Types/ITreeWidgetOptions"; import TreeWidgetModel from "./Models/TreeWidgetModel"; import SyncIndicatorWidget from "./Widgets/SyncIndicatorWidget"; -import JVMOptionsWidget from "./Widgets/JVMOptionsWidget"; -import {UIOptionsWidget} from "./Widgets/UIOptions/UIOptionsWidget"; -import UIOptionsModel from "./Models/UIOptionsModel"; +import OptionsWidget from "./Widgets/OptionsWidget"; -export default class TreeWidget extends Panel { +import "./styles/tree.css"; - public readonly HTML_ELEMENT_TEMPLATE = ` - -`; +export default class TreeWidget extends Panel { private _model: TreeWidgetModel; @@ -49,45 +36,43 @@ export default class TreeWidget extends Panel { let api = new BeakerXApi(this.options.baseUrl); - this.id = 'beakerx-tree'; - this.title.label = 'BeakerX'; - this.title.closable = true; - this.addClass('tab-pane'); + this.id = 'beakerx-tree-widget'; - this.createWidgetContent(api); - } + if (this.options.isLab) { + this.addClass('isLab'); + } else { + require("./styles/tree-notebook.css"); + } - private createWidgetContent(api: BeakerXApi) { + this.title.label = 'BeakerX'; + this.title.closable = true; + let bannerWidget = new BannerWidget(api); + let optionsWidget = new OptionsWidget(this.options.isLab); let syncIndicatorWidget = new SyncIndicatorWidget(); - let jvmOptionsWidget = new JVMOptionsWidget(); - let uiOptionsWidget; - let uiOptionsModel; - if (false === this.options.isLab) { - uiOptionsWidget = new UIOptionsWidget(); - uiOptionsModel = new UIOptionsModel(uiOptionsWidget); - } this._model = new TreeWidgetModel( api, - jvmOptionsWidget.model, - uiOptionsModel, + optionsWidget.jvmOptionsModel, + optionsWidget.uiOptionsModel, syncIndicatorWidget ); - this.addWidget(new BannerWidget(api)); - this.addWidget(new Widget({ node: document.createElement('br') })); - - if (false === this.options.isLab) { - this.addWidget(uiOptionsWidget); - } - - this.addWidget(jvmOptionsWidget); + this.addWidget(bannerWidget); + this.addWidget(optionsWidget); this.addWidget(syncIndicatorWidget); } public processMessage(msg: Message): void { switch (msg.type) { + case 'show-result': + this._model.clearErrors(); + this._model.showResult(); + break; + case 'hide-result': + this._model.clearErrors(); + this._model.hideResult(); + break; case Messages.TYPE_UI_OPTIONS_CHANGED: this._model.clearErrors(); this._model.setUIOptions((msg as Messages.UIOptionsChangedMessage).options); @@ -108,13 +93,8 @@ export default class TreeWidget extends Panel { } } - public onBeforeAttach(msg: Message): void { - this.createWidgetStylesElement(); + protected onBeforeAttach(msg: Message): void { this._model.load(); } - private createWidgetStylesElement(): void { - $(this.HTML_ELEMENT_TEMPLATE) - .insertBefore(this.node); - } } diff --git a/js/notebook/src/tree/Utils/DOMUtils.ts b/js/notebook/src/tree/Utils/DOMUtils.ts new file mode 100644 index 0000000000..3e570bb5f1 --- /dev/null +++ b/js/notebook/src/tree/Utils/DOMUtils.ts @@ -0,0 +1,34 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as $ from "jquery"; + +export default class DOMUtils { + + public static getRealElementHeight(el: HTMLElement): number { + let copyEl = $(el).clone() + .attr("id", null) + .css({ + visibility:"hidden", + display:"block", + position:"absolute" + }); + $('body').append(copyEl); + let h = copyEl.outerHeight(); + copyEl.remove(); + return h; + } +} diff --git a/js/notebook/src/tree/Widgets/BannerWidget.ts b/js/notebook/src/tree/Widgets/BannerWidget.ts index 71b5f92f24..5bbb6dce44 100644 --- a/js/notebook/src/tree/Widgets/BannerWidget.ts +++ b/js/notebook/src/tree/Widgets/BannerWidget.ts @@ -15,11 +15,11 @@ */ import * as $ from 'jquery'; + import { Widget } from "@phosphor/widgets"; import BeakerXApi from "../Utils/BeakerXApi"; - export default class BannerWidget extends Widget { static readonly GITHUB_RELEASE_TAG_BASE_URL = 'https://github.com/twosigma/beakerx/releases/tag/'; @@ -41,90 +41,51 @@ export default class BannerWidget extends Widget { -`; - - static readonly BASIC_HTML_ELEMENT_TEMPLATE = ` - -
    - -
    `; constructor(api: BeakerXApi) { super(); + + this.addClass('bx-banner-widget'); + api .getVersion() .then((version) => { - this.buildWidget(version); + this.createBanner(version); }); } - private buildWidget(version: string) { - this.createBaseElement(); - this.createBanner(version); - } - - private createBaseElement(): void { - $(BannerWidget.BASIC_HTML_ELEMENT_TEMPLATE) - .appendTo(this.node); - } - private createBanner(version: string): void { $(this.node) - .find('#beakerx_info') .empty() .append( this.createLinkedBannerElementRow(), - this.createLinkedVersionElementRow(version), - this.createLinkedFromElementRow() - ); - } + document.createTextNode(' version '), - private createLinkedFromElementRow() { - return $('
    ', { - text: 'from ' - }).append( - $('', { - target: '_blank', - href: 'http://opensource.twosigma.com/', - text: 'Two Sigma Open Source', - }) - ); - } + $('', { + target: '_blank', + href: `${BannerWidget.GITHUB_RELEASE_TAG_BASE_URL}${version}`, + text: version + }), - private createLinkedBannerElementRow() { - return $('
    ').append( - $('', { - class: 'beakerx_site_link', - target: '_blank', - href: 'http://BeakerX.com', - }).append( - $(`${BannerWidget.SVG_LOGO}`) - ) - ); + document.createTextNode(', from '), + + $('', { + target: '_blank', + href: 'http://opensource.twosigma.com/', + text: 'Two Sigma Open Source', + }), + + ); } - private createLinkedVersionElementRow(version: string) { - return $('
    ', { - text: 'version ' + private createLinkedBannerElementRow() { + return $('', { + class: 'beakerx_site_link', + target: '_blank', + href: 'http://BeakerX.com', }).append( - $('', { - target: '_blank', - href: `${BannerWidget.GITHUB_RELEASE_TAG_BASE_URL}${version}`, - text: version - }) + $(`${BannerWidget.SVG_LOGO}`) ); } diff --git a/js/notebook/src/tree/Widgets/JVMOptions/DefaultOptionsWidget.ts b/js/notebook/src/tree/Widgets/JVMOptions/DefaultOptionsWidget.ts index 47f53b5ad6..cbb6c70c12 100644 --- a/js/notebook/src/tree/Widgets/JVMOptions/DefaultOptionsWidget.ts +++ b/js/notebook/src/tree/Widgets/JVMOptions/DefaultOptionsWidget.ts @@ -16,8 +16,10 @@ import * as $ from "jquery"; import * as _ from "underscore"; + import { Widget } from "@phosphor/widgets"; import { MessageLoop } from "@phosphor/messaging"; + import DefaultOptionsWidgetInterface from "./DefaultOptionsWidgetInterface"; import HeapGBValidator from "../../Utils/HeapGBValidator"; import { Messages } from "../../Messages"; @@ -27,8 +29,7 @@ export default class DefaultOptionsWidget extends Widget implements DefaultOptio public readonly HEAP_GB_SELECTOR = '#heap_GB'; public readonly HTML_ELEMENT_TEMPLATE = ` -
    - JVM Options: +
    @@ -38,7 +39,7 @@ export default class DefaultOptionsWidget extends Widget implements DefaultOptio
    -
    +
    `; public get $node(): JQuery { diff --git a/js/notebook/src/tree/Widgets/JVMOptions/OtherOptionsWidget.ts b/js/notebook/src/tree/Widgets/JVMOptions/OtherOptionsWidget.ts index 194c452d32..8a1a3d8037 100644 --- a/js/notebook/src/tree/Widgets/JVMOptions/OtherOptionsWidget.ts +++ b/js/notebook/src/tree/Widgets/JVMOptions/OtherOptionsWidget.ts @@ -16,8 +16,10 @@ import * as $ from "jquery"; import * as _ from "underscore"; + import { Widget } from "@phosphor/widgets"; import { MessageLoop, Message } from "@phosphor/messaging"; + import OtherOptionsWidgetInterface from "./OtherOptionsWidgetInterface"; import { IOtherJVMOptions } from "../../Types/IJVMOptions"; import { Messages } from "../../Messages"; @@ -28,11 +30,6 @@ export default class OtherOptionsWidget extends Widget implements OtherOptionsWi public readonly PANEL_SELECTOR = '#other_property'; public readonly HTML_ELEMENT_TEMPLATE = ` -
    @@ -67,12 +64,13 @@ export default class OtherOptionsWidget extends Widget implements OtherOptionsWi } private clear() { + this._options = []; this.$node.find(this.PANEL_SELECTOR).empty(); + MessageLoop.sendMessage(this.parent, new Messages.SizeChangedMessage()); } public onLoad(otherOptions: IOtherJVMOptions) { this.clear(); - this._options = []; for (let option of otherOptions) { this.addFormElement(option); } @@ -113,6 +111,7 @@ export default class OtherOptionsWidget extends Widget implements OtherOptionsWi element.appendTo(this.$node.find(this.PANEL_SELECTOR)) MessageLoop.sendMessage(this, new Private.ElementAddedMessage(element)); + MessageLoop.sendMessage(this.parent, new Messages.SizeChangedMessage()); } public processMessage(msg: Message): void { @@ -150,6 +149,7 @@ export default class OtherOptionsWidget extends Widget implements OtherOptionsWi let el: JQuery = evt.data.el; el.remove(); MessageLoop.sendMessage(this, new Private.ElementRemovedMessage(el)); + MessageLoop.sendMessage(this.parent, new Messages.SizeChangedMessage()); } private inputChangedHandler(evt): void { diff --git a/js/notebook/src/tree/Widgets/JVMOptions/PropertiesWidget.ts b/js/notebook/src/tree/Widgets/JVMOptions/PropertiesWidget.ts index 4e465ea606..7a3ffae93c 100644 --- a/js/notebook/src/tree/Widgets/JVMOptions/PropertiesWidget.ts +++ b/js/notebook/src/tree/Widgets/JVMOptions/PropertiesWidget.ts @@ -19,8 +19,9 @@ import * as _ from "underscore"; import { Widget } from "@phosphor/widgets"; import { MessageLoop, Message } from "@phosphor/messaging"; -import {IPropertiesJVMOptions} from "../../Types/IJVMOptions"; -import {Messages} from "../../Messages"; + +import { IPropertiesJVMOptions } from "../../Types/IJVMOptions"; +import { Messages } from "../../Messages"; export default class PropertiesWidget extends Widget { @@ -28,11 +29,6 @@ export default class PropertiesWidget extends Widget { public readonly PROPERTIES_PANEL_SELECTOR: string = '#properties_property'; public readonly HTML_ELEMENT_TEMPLATE = ` -
    @@ -92,6 +88,7 @@ export default class PropertiesWidget extends Widget { private clear() { this._elements = []; this.$node.find(this.PROPERTIES_PANEL_SELECTOR).empty(); + MessageLoop.sendMessage(this.parent, new Messages.SizeChangedMessage()); } private addPropertyButtonClickedHandler(evt) { @@ -109,30 +106,7 @@ export default class PropertiesWidget extends Widget { element.appendTo(this.$node.find(this.PROPERTIES_PANEL_SELECTOR)); MessageLoop.sendMessage(this, new Private.ElementAddedMessage(element)); - } - - private createFormRowElement(): JQuery { - return $('
    ', { - class: 'form-group form-inline bko-spacing' - }); - } - - private createInputElement(placeholder: string, val: string = ''): JQuery { - return $('', { - class: 'form-control', - type: 'text', - placeholder: placeholder, - }).val(val) - .data('val', val); - } - - private createRemoveButtonElement(): JQuery { - return $('
    `; public get $node(): JQuery { @@ -70,6 +67,11 @@ export class UIOptionsWidget extends Widget implements UIOptionsWidgetInterface constructor() { super(); + this.addClass('bx-ui-options-widget'); + + this.title.label = 'UI Options'; + this.title.closable = false; + $(this.HTML_ELEMENT_TEMPLATE).appendTo(this.node); this.$node @@ -93,11 +95,23 @@ export class UIOptionsWidget extends Widget implements UIOptionsWidgetInterface this.setAutoSave(options.auto_save); } + protected onActivateRequest(): void { + this._updateSize(); + } + + private _updateSize(): void { + let h = DOMUtils.getRealElementHeight(this.$node.find('#ui_options').get(0)); + + $(this.node).height(h); + $(this.parent.node).height(h); + (this.parent.parent as OptionsWidget).updateDimensions(); + } + private optionsChangedHandler(evt): void { this._options[evt.currentTarget.id] = evt.currentTarget.checked; MessageLoop.sendMessage( - this.parent, + this.parent!.parent, new Messages.UIOptionsChangedMessage(this._options) ); } diff --git a/js/notebook/src/tree/styles/tree-notebook.css b/js/notebook/src/tree/styles/tree-notebook.css new file mode 100644 index 0000000000..701b7bc061 --- /dev/null +++ b/js/notebook/src/tree/styles/tree-notebook.css @@ -0,0 +1,21 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + /* + this styles are needed in notebook, + but must not be loaded in lab - breaks whole lab UI + */ +@import '~@phosphor/widgets/style/index.css'; \ No newline at end of file diff --git a/js/notebook/src/tree/styles/tree.css b/js/notebook/src/tree/styles/tree.css new file mode 100644 index 0000000000..d17c28048f --- /dev/null +++ b/js/notebook/src/tree/styles/tree.css @@ -0,0 +1,107 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#beakerx-tree-widget.isLab { + width: 100%; + height: 100%; + overflow: auto; + box-sizing: border-box; +} + +#beakerx-tree-widget .p-TabBar { + min-height: 30px; + max-height: 30px; +} + +#beakerx-tree-widget .p-TabBar-content { + min-width: 0; + align-items: flex-end; + border-bottom: 1px solid #ddd; + padding: 0 15px; +} + +#beakerx-tree-widget .p-TabBar-tab { + flex: 0 1 auto; + min-height: 26px; + max-height: 26px; + min-width: 35px; + margin-left: -1px; + border: 1px solid transparent; + padding: 4px 15px; +} + +#beakerx-tree-widget .p-TabBar-tab:first-child { + margin-left: 0; +} + +#beakerx-tree-widget .p-TabBar-tab.p-mod-current { + background-color: #fff; + border-color: #ddd; + border-bottom-color: transparent; + transform: translateY(1px); +} + +#beakerx-tree-widget .p-TabBar-tab:hover:not(.p-mod-current) { + background: #F0F0F0; +} + +.bx-banner-widget { + padding: 15px; +} + +.bx-banner-widget .beakerx_site_link { + display: inline-block; + margin: 0 75px 0 0; +} + +.bx-banner-widget svg { + width: 206px; + height: 59px; + transform: translateY(5px); +} + +.bx-options-widget { + padding: 15px 0; + display: flex; +} + +.bx-options-widget .p-TabPanel-stackedPanel { + left: 15px; + right: 15px; +} + +.bx-ui-options-widget, +.bx-jvm-options-widget { + left: 10px; + right: 10px; + padding: 15px; +} + +.bx-sync-indicator-widget { + padding: 15px; +} +.bx-sync-indicator-widget .saving { color: #f0ad4e; } +.bx-sync-indicator-widget .saved { color: #5cb85c; } + +.bx-sync-indicator-widget .errors-wrapper { color: #ff0000; } + +#properties_property .form-control { + margin-right: 10px; +} + +#other_property .form-control { + margin-right: 10px; +}