diff --git a/client/package.json b/client/package.json index 206ba9df25b..42e72995779 100644 --- a/client/package.json +++ b/client/package.json @@ -92,6 +92,7 @@ "vuex": "^3.6.2", "vuex-cache": "^3.4.0", "vuex-persist": "2.3", + "winbox": "^0.2.1", "xml-beautifier": "^0.5.0" }, "scripts": { diff --git a/client/src/components/History/Content/ContentItem.vue b/client/src/components/History/Content/ContentItem.vue index dd18db8e7b5..76704a5401a 100644 --- a/client/src/components/History/Content/ContentItem.vue +++ b/client/src/components/History/Content/ContentItem.vue @@ -65,6 +65,7 @@ diff --git a/client/src/components/Masthead/MastheadItem.vue b/client/src/components/Masthead/MastheadItem.vue index 848fd9a0e90..ec93a4b5218 100644 --- a/client/src/components/Masthead/MastheadItem.vue +++ b/client/src/components/Masthead/MastheadItem.vue @@ -148,9 +148,6 @@ export default { propogateClick(tab, event) { event.preventDefault(); tab.onclick(); - if (tab.id === "enable-scratchbook") { - this.$emit("updateScratchbookTab", tab); - } }, formatUrl(url) { if (typeof url === "string" && url.indexOf("//") === -1 && url.charAt(0) != "/") { diff --git a/client/src/components/plugins/legacyNavigation.js b/client/src/components/plugins/legacyNavigation.js index 0efac6066b1..516d20d438b 100644 --- a/client/src/components/plugins/legacyNavigation.js +++ b/client/src/components/plugins/legacyNavigation.js @@ -17,13 +17,13 @@ export function backboneRoute(path, ...args) { } } -// wrapper for scratchbook/multiview feature +// wrapper for window manager export function iframeAdd({ path, title = "Galaxy", tryIframe = true }) { const Galaxy = getGalaxyInstance(); if (Galaxy.frame && Galaxy.frame.active) { Galaxy.frame.add({ - url: prependPath(path), title: _l(title), + url: prependPath(path), }); return true; } else if (tryIframe) { diff --git a/client/src/layout/masthead.js b/client/src/layout/masthead.js index ec5bfffaca9..af36d9de6b0 100644 --- a/client/src/layout/masthead.js +++ b/client/src/layout/masthead.js @@ -1,5 +1,5 @@ import $ from "jquery"; -import Scratchbook from "layout/scratchbook"; +import { WindowManager } from "layout/window-manager"; import QuotaMeter from "mvc/user/user-quotameter"; import { getGalaxyInstance } from "app"; import { getAppRoot } from "onload/loadConfig"; @@ -13,7 +13,7 @@ export class MastheadState { constructor(Galaxy = null) { Galaxy = Galaxy || getGalaxyInstance(); - Galaxy.frame = this.frame = new Scratchbook(); + Galaxy.frame = this.windowManager = new WindowManager(); // set up the quota meter (And fetch the current user data from trans) // add quota meter to masthead @@ -35,7 +35,7 @@ export class MastheadState { } }) .on("beforeunload", () => { - const text = this.frame.beforeUnload(); + const text = this.windowManager.beforeUnload(); if (text) { return text; } diff --git a/client/src/layout/menu.js b/client/src/layout/menu.js index 0ed3a76019c..c186a6b8eb8 100644 --- a/client/src/layout/menu.js +++ b/client/src/layout/menu.js @@ -239,7 +239,6 @@ export function fetchMenu(options = {}) { cls: "loggedout-only", tooltip: _l("Login"), url: "login", - noscratchbook: true, }; } } else { diff --git a/client/src/layout/page.js b/client/src/layout/page.js index 4c9f72ccfb5..5897bcd3763 100644 --- a/client/src/layout/page.js +++ b/client/src/layout/page.js @@ -62,7 +62,6 @@ const View = Backbone.View.extend({ this.masthead = mountMasthead(this.$masthead[0], this.config, mastheadState); } this.$center.append(this.center.$el); - this.$el.append(mastheadState.frame.$el); this.$el.append(this.modal.$el); // build panels diff --git a/client/src/layout/scratchbook.js b/client/src/layout/scratchbook.js deleted file mode 100644 index bdbc9534b6b..00000000000 --- a/client/src/layout/scratchbook.js +++ /dev/null @@ -1,247 +0,0 @@ -/** Frame manager uses the ui-frames to create the scratch book masthead icon and functionality **/ -import _ from "underscore"; -import $ from "jquery"; -import Backbone from "backbone"; -import { getAppRoot } from "onload/loadConfig"; -import { getGalaxyInstance } from "app"; -import Frames from "mvc/ui/ui-frames"; -import { Dataset, createTabularDatasetChunkedView, TabularDataset } from "mvc/dataset/data"; -import visualization from "viz/visualization"; -import { TracksterUI } from "viz/trackster"; -import _l from "utils/localization"; - -export default Backbone.View.extend({ - initialize: function (options) { - options = options || {}; - this.frames = new Frames.View({ visible: false }); - this.setElement(this.frames.$el); - this.active = false; - this.buttonActive = { - id: "enable-scratchbook", - icon: "fa-th", - tooltip: _l("Enable/Disable Scratchbook"), - toggle: false, - onclick: () => { - this.active = !this.active; - this.buttonActive.toggle = this.active; - this.buttonActive.show_note = this.active; - this.buttonActive.note_cls = this.active && "fa fa-check"; - if (!this.active) { - this.frames.hide(); - } - }, - }; - this.buttonLoad = { - id: "show-scratchbook", - icon: "fa-eye", - tooltip: _l("Show/Hide Scratchbook"), - show_note: true, - visible: false, - note: "", - onclick: (e) => { - if (this.frames.visible) { - this.frames.hide(); - } else { - this.frames.show(); - } - }, - }; - this.history_cache = {}; - }, - - getFrames() { - // needed for Vue.js integration - return this.frames; - }, - - beforeUnload() { - let confirmText = ""; - if (this.frames.length() > 0) { - confirmText = `You opened ${this.frames.length()} frame(s) which will be lost.`; - } - return confirmText; - }, - - /** Add a dataset to the frames */ - addDataset: function (dataset_id) { - const self = this; - let current_dataset = null; - const Galaxy = getGalaxyInstance(); - if (Galaxy && Galaxy.currHistoryPanel) { - const history_id = Galaxy.currHistoryPanel.model.id; - this.history_cache[history_id] = { - name: Galaxy.currHistoryPanel.model.get("name"), - dataset_ids: [], - }; - Galaxy.currHistoryPanel.collection.each((model) => { - if (!model.get("deleted") && model.get("visible") && model.get("history_content_type") == "dataset") { - self.history_cache[history_id].dataset_ids.push(model.get("id")); - } - }); - } - const _findDataset = (dataset, offset) => { - if (dataset) { - const history_details = self.history_cache[dataset.get("history_id")]; - if (history_details && history_details.dataset_ids) { - const dataset_list = history_details.dataset_ids; - const pos = dataset_list.indexOf(dataset.get("id")); - if (pos !== -1 && pos + offset >= 0 && pos + offset < dataset_list.length) { - return dataset_list[pos + offset]; - } - } - } - }; - const _loadOffset = (dataset, offset, frame) => { - const new_dataset_id = _findDataset(dataset, offset); - if (new_dataset_id) { - self._loadDataset(new_dataset_id, (new_dataset, config) => { - current_dataset = new_dataset; - frame.model.set(config); - }); - } - }; - this._loadDataset(dataset_id, (dataset, config) => { - current_dataset = dataset; - self.add( - _.extend( - { - menu: [ - { - icon: "fa fa-chevron-circle-left", - tooltip: _l("Previous in History"), - onclick: function (frame) { - _loadOffset(current_dataset, -1, frame); - }, - disabled: function () { - return !_findDataset(current_dataset, -1); - }, - }, - { - icon: "fa fa-chevron-circle-right", - tooltip: _l("Next in History"), - onclick: function (frame) { - _loadOffset(current_dataset, 1, frame); - }, - disabled: function () { - return !_findDataset(current_dataset, 1); - }, - }, - ], - }, - config - ) - ); - }); - }, - - _loadDataset: function (dataset_id, callback) { - const self = this; - const dataset = new Dataset({ id: dataset_id }); - $.when(dataset.fetch()).then(() => { - const is_tabular = _.find( - ["tabular", "interval"], - (data_type) => dataset.get("data_type").indexOf(data_type) !== -1 - ); - let title = dataset.get("name"); - const history_details = self.history_cache[dataset.get("history_id")]; - if (history_details) { - title = `${history_details.name}: ${title}`; - } - callback( - dataset, - is_tabular - ? { - title: title, - url: null, - content: createTabularDatasetChunkedView({ - model: new TabularDataset(dataset.toJSON()), - embedded: true, - height: "100%", - }).$el, - } - : { - title: title, - url: `${getAppRoot()}datasets/${dataset_id}/display/?preview=True`, - content: null, - } - ); - }); - }, - - /** Add a trackster visualization to the frames. */ - addTrackster: function (viz_id) { - const self = this; - const viz = new visualization.Visualization({ id: viz_id }); - $.when(viz.fetch()).then(() => { - const ui = new TracksterUI(getAppRoot()); - - // Construct frame config based on dataset's type. - const frame_config = { - title: viz.get("name"), - type: "other", - content: function (parent_elt) { - // Create view config. - const view_config = { - container: parent_elt, - name: viz.get("title"), - id: viz.id, - // FIXME: this will not work with custom builds b/c the dbkey needed to be encoded. - dbkey: viz.get("dbkey"), - stand_alone: false, - }; - - const latest_revision = viz.get("latest_revision"); - const drawables = latest_revision.config.view.drawables; - - // Set up datasets in drawables. - _.each(drawables, (d) => { - d.dataset = { - hda_ldda: d.hda_ldda, - id: d.dataset_id, - }; - }); - ui.create_visualization( - view_config, - latest_revision.config.viewport, - latest_revision.config.view.drawables, - latest_revision.config.bookmarks, - false - ); - }, - }; - self.add(frame_config); - }); - }, - - /** Add and display a new frame/window based on options. */ - add: function (options) { - if (options.target == "_blank") { - window.open(options.url); - } else if (options.target == "_top" || options.target == "_parent" || options.target == "_self") { - window.location = options.url; - } else if (!this.active || options.noscratchbook) { - const $galaxy_main = $(window.parent.document).find("#galaxy_main"); - if (options.target == "galaxy_main" || options.target == "center") { - if ($galaxy_main.length === 0) { - window.location = this._build_url(options.url, { use_panels: true }); - } else { - $galaxy_main.attr("src", options.url); - } - } else { - window.location = options.url; - } - } else { - options.url = this._build_url(options.url, { hide_panels: true, hide_masthead: true }); - this.frames.add(options); - } - }, - - /** Url helper */ - _build_url: function (url, options) { - if (url) { - url += url.indexOf("?") == -1 ? "?" : "&"; - url += $.param(options, true); - return url; - } - }, -}); diff --git a/client/src/layout/window-manager.js b/client/src/layout/window-manager.js new file mode 100644 index 00000000000..99befc0a057 --- /dev/null +++ b/client/src/layout/window-manager.js @@ -0,0 +1,74 @@ +/** Adds window manager masthead icon and functionality **/ +import _l from "utils/localization"; +import WinBox from "winbox/src/js/winbox.js"; +import "winbox/dist/css/winbox.min.css"; + +export class WindowManager { + constructor(options) { + options = options || {}; + this.counter = 0; + this.active = false; + this.buttonActive = { + id: "enable-window-manager", + icon: "fa-th", + tooltip: _l("Enable/Disable Window Manager"), + toggle: false, + onclick: () => { + this.active = !this.active; + this.buttonActive.toggle = this.active; + this.buttonActive.show_note = this.active; + this.buttonActive.note_cls = this.active && "fa fa-check"; + }, + }; + } + + /** Add and display a new window based on options. */ + add(options) { + if (options.target == "_blank") { + window.open(options.url); + } else if (options.target == "_top" || options.target == "_parent" || options.target == "_self") { + window.location = options.url; + } else if (!this.active) { + const $galaxy_main = window.parent.document.getElementById("galaxy_main"); + if (options.target == "galaxy_main" || options.target == "center") { + if ($galaxy_main.length === 0) { + window.location = this._build_url(options.url, { use_panels: true }); + } else { + $galaxy_main.attr("src", options.url); + } + } else { + window.location = options.url; + } + } else { + this.counter++; + const url = this._build_url(options.url, { hide_panels: true, hide_masthead: true }); + WinBox.new({ + title: options.title || "Window", + url: url, + onclose: () => { + this.counter--; + }, + }); + } + } + + /** Called before closing all windows. */ + beforeUnload() { + let confirmText = ""; + if (this.counter > 0) { + confirmText = `You opened ${this.counter} window(s) which will be lost.`; + } + return confirmText; + } + + /** Url helper */ + _build_url(url, options) { + if (url) { + url += url.indexOf("?") == -1 ? "?" : "&"; + Object.entries(options).forEach(([key, value]) => { + url += `${key}=${value}&`; + }); + return url; + } + } +} diff --git a/client/src/mvc/dataset/dataset-li.js b/client/src/mvc/dataset/dataset-li.js index b2c0afcaf1a..7f51996127e 100644 --- a/client/src/mvc/dataset/dataset-li.js +++ b/client/src/mvc/dataset/dataset-li.js @@ -9,6 +9,7 @@ import BASE_MVC from "mvc/base-mvc"; import _l from "utils/localization"; import { mountNametags } from "components/Nametags"; import { Toast } from "ui/toast"; +import { getAppRoot } from "onload/loadConfig"; var logNamespace = "dataset"; /*============================================================================== @@ -187,7 +188,8 @@ export var DatasetListItemView = _super.extend( if (Galaxy.frame && Galaxy.frame.active) { // Add dataset to frames. const identifier = self.model.get("element_id") || self.model.get("id"); - Galaxy.frame.addDataset(identifier); + const url = `${getAppRoot()}datasets/${identifier}/display/?preview=True`; + Galaxy.frame.add({ url }); ev.preventDefault(); } }; diff --git a/client/src/mvc/ui/ui-frames.js b/client/src/mvc/ui/ui-frames.js deleted file mode 100644 index 79ee255792c..00000000000 --- a/client/src/mvc/ui/ui-frames.js +++ /dev/null @@ -1,648 +0,0 @@ -/** Frame view */ -import _ from "underscore"; -import $ from "jquery"; -import Backbone from "backbone"; -import _l from "utils/localization"; -import { getGalaxyInstance } from "app"; - -var FrameView = Backbone.View.extend({ - initialize: function (options) { - this.model = (options && options.model) || new Backbone.Model(options); - this.setElement($("
").addClass("corner frame")); - this.$el - .append( - $("") - .addClass("f-header corner") - .append($("").addClass("f-title")) - .append( - $("") - .addClass("f-icon f-close fa fa-close") - .tooltip({ - title: _l("Close"), - placement: "bottom", - }) - ) - ) - .append($("").addClass("f-content")) - .append($("").addClass("f-resize f-icon corner fa fa-expand").tooltip({ title: "Resize" })) - .append($("").addClass("f-cover")); - this.$header = this.$(".f-header"); - this.$title = this.$(".f-title"); - this.$content = this.$(".f-content"); - this.render(); - this.listenTo(this.model, "change", this.render, this); - }, - - render: function () { - var self = this; - var options = this.model.attributes; - this.$title.html(_.escape(options.title) || ""); - this.$header.find(".f-icon-left").remove(); - _.each(options.menu, (option) => { - var $option = $("").addClass("f-icon-left").addClass(option.icon); - if (_.isFunction(option.disabled) && option.disabled()) { - $option.attr("disabled", true); - } else { - $option - .on("click", () => { - option.onclick(self); - }) - .tooltip({ - title: option.tooltip || "", - placement: "bottom", - }); - } - self.$header.append($option); - }); - if (options.url) { - this.$content.html( - $("") - .addClass("f-iframe") - .attr("scrolling", "auto") - .attr("src", `${options.url + (options.url.indexOf("?") === -1 ? "?" : "&")}widget=True`) - ); - } else if (options.content) { - if (_.isFunction(options.content)) { - options.content(self.$content); - } else { - self.$content.html(options.content); - } - } - }, -}); - -/** Scratchbook viewer */ -var View = Backbone.View.extend({ - defaultOptions: { - frame: { - // default frame size in cells - cols: 6, - rows: 3, - }, - rows: 1000, // maximum number of rows - cell: 130, // cell size in px - margin: 5, // margin between frames - scroll: 5, // scroll speed - top_min: 40, // top margin - frame_max: 9, // maximum number of frames - visible: true, // initial visibility - }, - - cols: 0, // number of columns - top: 0, // scroll/element top - top_max: 0, // viewport scrolling state - frame_z: 0, // frame z-index - frame_counter: 0, // frame counter - frame_uid: 0, // unique frame id counter - frame_list: {}, // list of all frames - frame_shadow: null, // frame shown as placeholder when moving active frames - visible: false, // flag indicating if scratchbook viewer is visible or not - event: {}, // dictionary keeping track of current event - - initialize: function (options) { - var self = this; - this.options = _.defaults(options || {}, this.defaultOptions); - this.visible = this.options.visible; - this.top = this.top_max = this.options.top_min; - this.setElement( - $("") - .addClass("galaxy-frame") - .append($("").addClass("frame-background")) - .append($("").addClass("frame-menu frame-scroll-up fa fa-chevron-up fa-2x")) - .append($("").addClass("frame-menu frame-scroll-down fa fa-chevron-down fa-2x")) - ); - - // initialize shadow to guiding drag/resize events - this.frame_shadow = new Backbone.View({ - el: $("").addClass("corner frame-shadow"), - }); - this.$el.append(this.frame_shadow.$el); - this._frameInit(this.frame_shadow, "#frame-shadow"); - this._frameResize(this.frame_shadow, { width: 0, height: 0 }); - this.frame_list["#frame-shadow"] = this.frame_shadow; - - // initialize panel - if (this.visible) { - this.show(); - } else { - this.hide(); - } - this._panelRefresh(); - $(window).resize(() => { - if (self.visible) { - self._panelRefresh(); - } - }); - }, - - /** Render */ - render: function () { - this.$(".frame-scroll-up")[(this.top != this.options.top_min && "show") || "hide"](); - this.$(".frame-scroll-down")[(this.top != this.top_max && "show") || "hide"](); - }, - - /** - * Adds and displays a new frame. - * - * options: - * url : loaded into an iframe - * content : content is treated as a function or raw HTML, function is passed a single - * argument that is the frame's content DOM element - */ - add: function (options) { - const Galaxy = getGalaxyInstance(); - if (this.frame_counter >= this.options.frame_max) { - Galaxy.modal.show({ - title: _l("Warning"), - body: `You have reached the maximum number of allowed frames (${this.options.frame_max}).`, - buttons: { - Close: function () { - Galaxy.modal.hide(); - }, - }, - }); - } else { - var frame_id = `#frame-${this.frame_uid++}`; - if ($(frame_id).length !== 0) { - Galaxy.modal.show({ - title: _l("Error"), - body: "This frame already exists. This page might contain multiple frame managers.", - buttons: { - Close: function () { - Galaxy.modal.hide(); - }, - }, - }); - } else { - // initialize new frame elements - this.top = this.options.top_min; - var frame = new FrameView(options); - this.$el.append(frame.$el); - - // set dimensions - options.width = this._toPixelCoord("width", this.options.frame.cols); - options.height = this._toPixelCoord("height", this.options.frame.rows); - - // set default z-index and add to ui and frame list - this.frame_z = parseInt(frame.$el.css("z-index")); - this.frame_list[frame_id] = frame; - this.frame_counter++; - this._frameInit(frame, frame_id); - this._frameResize(frame, { - width: options.width, - height: options.height, - }); - this._frameInsert(frame, { top: 0, left: 0 }, true); - if (!this.visible) { - this.show(); - } - this.trigger("add"); - } - } - }, - - /** Remove a frame */ - del: function (frame) { - var self = this; - var $frame = frame.$el; - $frame.fadeOut("fast", () => { - $frame.remove(); - delete self.frame_list[frame.id]; - self.frame_counter--; - self._panelRefresh(true); - self._panelAnimationComplete(); - if (this.frame_counter == 0) { - this._eventHide(); - } - self.trigger("remove"); - }); - }, - - /** Show panel */ - show: function () { - this.visible = true; - this.$el.fadeIn("fast"); - this.trigger("show"); - }, - - /** Hide panel */ - hide: function () { - if (!this.event.type) { - this.visible = false; - this.$el.fadeOut("fast", function () { - $(this).hide(); - }); - this.trigger("hide"); - } - }, - - /** Returns the number of frames */ - length: function () { - return this.frame_counter; - }, - - /* - EVENT HANDLING - */ - events: { - // global frame events - mousemove: "_eventFrameMouseMove", - mouseup: "_eventFrameMouseUp", - mouseleave: "_eventFrameMouseUp", - mousewheel: "_eventPanelScroll", - DOMMouseScroll: "_eventPanelScroll", - - // events fixed to elements - "mousedown .frame": "_eventFrameMouseDown", - "mousedown .frame-background": "_eventHide", - "mousedown .frame-scroll-up": "_eventPanelScroll_up", - "mousedown .frame-scroll-down": "_eventPanelScroll_down", - "mousedown .f-close": "_eventFrameClose", - }, - - /** Start drag/resize event */ - _eventFrameMouseDown: function (e) { - $(".tooltip").hide(); - if (!this.event.type) { - if ($(e.target).hasClass("f-header") || $(e.target).hasClass("f-title")) { - this.event.type = "drag"; - } - if ($(e.target).hasClass("f-resize")) { - this.event.type = "resize"; - } - if (this.event.type) { - e.preventDefault(); - this.event.target = this._frameIdentify(e.target); - this.event.xy = { - x: e.originalEvent.pageX, - y: e.originalEvent.pageY, - }; - this._frameDragStart(this.event.target); - } - } - }, - - /** Processes drag/resize events */ - _eventFrameMouseMove: function (e) { - if (this.event.type) { - // get mouse motion and delta - var event_xy_new = { - x: e.originalEvent.pageX, - y: e.originalEvent.pageY, - }; - var event_xy_delta = { - x: event_xy_new.x - this.event.xy.x, - y: event_xy_new.y - this.event.xy.y, - }; - this.event.xy = event_xy_new; - - // get current screen position and size of frame - var p = this._frameScreen(this.event.target); - - // drag/resize event - if (this.event.type == "resize") { - p.width += event_xy_delta.x; - p.height += event_xy_delta.y; - var min_dim = this.options.cell - this.options.margin - 1; - p.width = Math.max(p.width, min_dim); - p.height = Math.max(p.height, min_dim); - this._frameResize(this.event.target, p); - p.width = this._toGridCoord("width", p.width) + 1; - p.height = this._toGridCoord("height", p.height) + 1; - p.width = this._toPixelCoord("width", p.width); - p.height = this._toPixelCoord("height", p.height); - this._frameResize(this.frame_shadow, p); - this._frameInsert(this.frame_shadow, { - top: this._toGridCoord("top", p.top), - left: this._toGridCoord("left", p.left), - }); - } else if (this.event.type == "drag") { - p.left += event_xy_delta.x; - p.top += event_xy_delta.y; - this._frameOffset(this.event.target, p); - var l = { - top: this._toGridCoord("top", p.top), - left: this._toGridCoord("left", p.left), - }; - if (l.left !== 0) { - l.left++; - } - this._frameInsert(this.frame_shadow, l); - } - } - }, - - /** Stop drag/resize events */ - _eventFrameMouseUp: function (e) { - if (this.event.type) { - this._frameDragStop(this.event.target); - this.event.type = null; - } - }, - - /** Destroy a frame */ - _eventFrameClose: function (e) { - if (!this.event.type) { - e.preventDefault(); - this.del(this._frameIdentify(e.target)); - } - }, - - /** Hide all frames */ - _eventHide: function (e) { - if (!this.event.type) { - this.hide(); - } - }, - - /** Fired when scrolling occurs on panel */ - _eventPanelScroll: function (e) { - if (!this.event.type && this.visible) { - // Stop propagation if scrolling is happening inside a frame. - // TODO: could propagate scrolling if at top/bottom of frame. - var frames = $(e.srcElement).parents(".frame"); - if (frames.length !== 0) { - e.stopPropagation(); - } else { - e.preventDefault(); - this._panelScroll(e.originalEvent.detail ? e.originalEvent.detail : e.originalEvent.wheelDelta / -3); - } - } - }, - - /** Handle scroll up event */ - _eventPanelScroll_up: function (e) { - if (!this.event.type) { - e.preventDefault(); - this._panelScroll(-this.options.scroll); - } - }, - - /** Handle scroll down */ - _eventPanelScroll_down: function (e) { - if (!this.event.type) { - e.preventDefault(); - this._panelScroll(this.options.scroll); - } - }, - - /* - FRAME EVENTS SUPPORT - */ - - /** Identify the target frame */ - _frameIdentify: function (target) { - return this.frame_list[`#${$(target).closest(".frame").attr("id")}`]; - }, - - /** Provides drag support */ - _frameDragStart: function (frame) { - this._frameFocus(frame, true); - var p = this._frameScreen(frame); - this._frameResize(this.frame_shadow, p); - this._frameGrid(this.frame_shadow, frame.grid_location); - frame.grid_location = null; - this.frame_shadow.$el.show(); - $(".f-cover").show(); - }, - - /** Removes drag support */ - _frameDragStop: function (frame) { - this._frameFocus(frame, false); - var p = this._frameScreen(this.frame_shadow); - this._frameResize(frame, p); - this._frameGrid(frame, this.frame_shadow.grid_location, true); - this.frame_shadow.grid_location = null; - this.frame_shadow.$el.hide(); - $(".f-cover").hide(); - this._panelAnimationComplete(); - }, - - /* - GRID/PIXEL CONVERTER - */ - - /** Converts a pixel to a grid dimension */ - _toGridCoord: function (type, px) { - var sign = type == "width" || type == "height" ? 1 : -1; - if (type == "top") { - px -= this.top; - } - return parseInt((px + sign * this.options.margin) / this.options.cell, 10); - }, - - /** Converts a grid to a pixels dimension */ - _toPixelCoord: function (type, g) { - var sign = type == "width" || type == "height" ? 1 : -1; - var px = g * this.options.cell - sign * this.options.margin; - if (type == "top") { - px += this.top; - } - return px; - }, - - /** Converts a pixel to a grid coordinate set */ - _toGrid: function (px) { - return { - top: this._toGridCoord("top", px.top), - left: this._toGridCoord("left", px.left), - width: this._toGridCoord("width", px.width), - height: this._toGridCoord("height", px.height), - }; - }, - - /** Converts a pixel to a grid coordinate set */ - _toPixel: function (g) { - return { - top: this._toPixelCoord("top", g.top), - left: this._toPixelCoord("left", g.left), - width: this._toPixelCoord("width", g.width), - height: this._toPixelCoord("height", g.height), - }; - }, - - /* - COLLISION DETECTION - */ - - /** Check collisions for a grid coordinate set */ - _isCollision: function (g) { - function is_collision_pair(a, b) { - return !( - a.left > b.left + b.width - 1 || - a.left + a.width - 1 < b.left || - a.top > b.top + b.height - 1 || - a.top + a.height - 1 < b.top - ); - } - for (var i in this.frame_list) { - var frame = this.frame_list[i]; - if (frame.grid_location !== null && is_collision_pair(g, frame.grid_location)) { - return true; - } - } - return false; - }, - - /** Return location/grid rank */ - _locationRank: function (loc) { - return loc.top * this.cols + loc.left; - }, - - /* - PANEL/WINDOW FUNCTIONS - */ - - /** Refresh panel */ - _panelRefresh: function (animate) { - this.cols = parseInt($(window).width() / this.options.cell, 10) + 1; - this._frameInsert(null, null, animate); - }, - - /** Complete panel animation / frames not moving */ - _panelAnimationComplete: function () { - var self = this; - $(".frame") - .promise() - .done(() => { - self._panelScroll(0, true); - }); - }, - - /** Scroll panel */ - _panelScroll: function (delta, animate) { - var top_new = this.top - this.options.scroll * delta; - top_new = Math.max(top_new, this.top_max); - top_new = Math.min(top_new, this.options.top_min); - if (this.top != top_new) { - for (var i in this.frame_list) { - var frame = this.frame_list[i]; - if (frame.grid_location !== null) { - var screen_location = { - top: frame.screen_location.top - (this.top - top_new), - left: frame.screen_location.left, - }; - this._frameOffset(frame, screen_location, animate); - } - } - this.top = top_new; - } - this.render(); - }, - - /* - FRAME FUNCTIONS - */ - - /** Initialize a new frame */ - _frameInit: function (frame, id) { - frame.id = id; - frame.screen_location = {}; - frame.grid_location = {}; - frame.grid_rank = null; - frame.$el.attr("id", id.substring(1)); - }, - - /** Insert frame at given location */ - _frameInsert: function (frame, new_loc, animate) { - var self = this; - var place_list = []; - if (frame) { - frame.grid_location = null; - place_list.push([frame, this._locationRank(new_loc)]); - } - _.each(this.frame_list, (f) => { - if (f.grid_location !== null) { - f.grid_location = null; - place_list.push([f, f.grid_rank]); - } - }); - place_list.sort((a, b) => (a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0)); - _.each(place_list, (place) => { - self._framePlace(place[0], animate); - }); - this.top_max = 0; - _.each(this.frame_list, (f) => { - if (f.grid_location !== null) { - self.top_max = Math.max(self.top_max, f.grid_location.top + f.grid_location.height); - } - }); - this.top_max = $(window).height() - this.top_max * this.options.cell - 2 * this.options.margin; - this.top_max = Math.min(this.top_max, this.options.top_min); - this.render(); - }, - - /** Naive frame placement */ - _framePlace: function (frame, animate) { - frame.grid_location = null; - var g = this._toGrid(this._frameScreen(frame)); - var done = false; - for (var i = 0; i < this.options.rows; i++) { - for (var j = 0; j < Math.max(1, this.cols - g.width); j++) { - g.top = i; - g.left = j; - if (!this._isCollision(g)) { - done = true; - break; - } - } - if (done) { - break; - } - } - if (done) { - this._frameGrid(frame, g, animate); - } else { - console.log("Grid dimensions exceeded."); - } - }, - - /** Handle frame focussing */ - _frameFocus: function (frame, has_focus) { - frame.$el.css("z-index", this.frame_z + (has_focus ? 1 : 0)); - }, - - /** New left/top position frame */ - _frameOffset: function (frame, p, animate) { - frame.screen_location.left = p.left; - frame.screen_location.top = p.top; - if (animate) { - this._frameFocus(frame, true); - var self = this; - frame.$el.animate({ top: p.top, left: p.left }, "fast", () => { - self._frameFocus(frame, false); - }); - } else { - frame.$el.css({ top: p.top, left: p.left }); - } - }, - - /** Resize frame */ - _frameResize: function (frame, p) { - frame.$el.css({ width: p.width, height: p.height }); - frame.screen_location.width = p.width; - frame.screen_location.height = p.height; - }, - - /** Push frame to new grid location */ - _frameGrid: function (frame, l, animate) { - frame.grid_location = l; - this._frameOffset(frame, this._toPixel(l), animate); - frame.grid_rank = this._locationRank(l); - }, - - /** Get frame dimensions */ - _frameScreen: function (frame) { - var p = frame.screen_location; - return { - top: p.top, - left: p.left, - width: p.width, - height: p.height, - }; - }, -}); - -export default { - View: View, -}; diff --git a/client/src/nls/es/locale.js b/client/src/nls/es/locale.js index 61854513f1b..2da775f856f 100644 --- a/client/src/nls/es/locale.js +++ b/client/src/nls/es/locale.js @@ -507,9 +507,9 @@ define({ "Local data": "Datos locales", "Form Definitions": "Definiciones de formulario", - // ---------------------------------------------------------------------------- Scratchbook - "Enable/Disable Scratchbook": "Habilitar/Deshabilitar Scratchbook", - "Show/Hide Scratchbook": "Mostrar/Ocultar Scratchbook", + // ---------------------------------------------------------------------------- Window Manager + "Enable/Disable Window Manager": "Habilitar/Deshabilitar Window Manager", + "Show/Hide Window Manager": "Mostrar/Ocultar Window Manager", // ---------------------------------------------------------------------------- misc. MVC @@ -793,7 +793,7 @@ define({ "Create new Build": "Crear nueva construcción", "Delete custom build.": "Eliminar construcción personalizada", "Provide the data source.": "Proporcionar la fuente de datos", - // ---------------------------------------------------------------------------- scratchbook + // ---------------------------------------------------------------------------- Window Manager "Next in History": "Siguiente en historial", "Previous in History": "Anterior en historial", // ---------------------------------------------------------------------------- generic-nav-view diff --git a/client/src/nls/fr/locale.js b/client/src/nls/fr/locale.js index e174aa641c3..a62bff7dd34 100644 --- a/client/src/nls/fr/locale.js +++ b/client/src/nls/fr/locale.js @@ -230,9 +230,9 @@ define({ "Local data": "Données locales", "Form Definitions": "Définitions de formulaires", - // ---------------------------------------------------------------------------- Scratchbook - "Enable/Disable Scratchbook": "Activer/Désactiver la visu Scratchbook", - "Show/Hide Scratchbook": "Montrer/cacher la visu Scratchbook", + // ---------------------------------------------------------------------------- Window Manager + "Enable/Disable Window Manager": "Activer/Désactiver la visu Window Manager", + "Show/Hide Window Manager": "Montrer/cacher la visu Window Manager", // ---------------------------------------------------------------------------- misc. MVC Tags: "Mots-clés", @@ -437,7 +437,7 @@ define({ "Create new Build": false, "Delete custom build.": false, "Provide the data source.": false, - // ---------------------------------------------------------------------------- scratchbook + // ---------------------------------------------------------------------------- Window Manager "Next in History": false, "Previous in History": false, // ---------------------------------------------------------------------------- generic-nav-view diff --git a/client/src/nls/locale.js b/client/src/nls/locale.js index 2b712a7bf28..5e0e2e04977 100644 --- a/client/src/nls/locale.js +++ b/client/src/nls/locale.js @@ -230,9 +230,9 @@ define({ "Local data": false, "Form Definitions": false, - // ---------------------------------------------------------------------------- Scratchbook - "Enable/Disable Scratchbook": false, - "Show/Hide Scratchbook": false, + // ---------------------------------------------------------------------------- Window Manager + "Enable/Disable Window Manager": false, + "Show/Hide Window Manager": false, // ---------------------------------------------------------------------------- misc. MVC Tags: false, @@ -435,7 +435,7 @@ define({ "Create new Build": false, "Delete custom build.": false, "Provide the data source.": false, - // ---------------------------------------------------------------------------- scratchbook + // ---------------------------------------------------------------------------- Window Manager "Next in History": false, "Previous in History": false, // ---------------------------------------------------------------------------- generic-nav-view diff --git a/client/src/nls/zh/locale.js b/client/src/nls/zh/locale.js index d14bb6b878f..1e5b2adc781 100644 --- a/client/src/nls/zh/locale.js +++ b/client/src/nls/zh/locale.js @@ -239,9 +239,9 @@ define({ "Local data": false, "Form Definitions": false, - // ---------------------------------------------------------------------------- Scratchbook - "Enable/Disable Scratchbook": "启用/禁用 Scratchbook", - "Show/Hide Scratchbook": "显示/隐藏 Scratchbook", + // ---------------------------------------------------------------------------- Window Manager + "Enable/Disable Window Manager": "启用/禁用 Window Manager", + "Show/Hide Window Manager": "显示/隐藏 Window Manager", // ---------------------------------------------------------------------------- misc. MVC Tags: "标签", @@ -451,7 +451,7 @@ define({ "Create new Build": "创建新的构建集", "Delete custom build.": "删除自定义的构建集。", "Provide the data source.": "提供数据源。", - // ---------------------------------------------------------------------------- scratchbook + // ---------------------------------------------------------------------------- Window Manager "Next in History": "下一个历史记录", "Previous in History": "上一个历史记录", // ---------------------------------------------------------------------------- generic-nav-view diff --git a/client/src/style/scss/base.scss b/client/src/style/scss/base.scss index 6f25089e965..fe61dd9854b 100644 --- a/client/src/style/scss/base.scss +++ b/client/src/style/scss/base.scss @@ -24,7 +24,7 @@ $fa-font-path: "../../../node_modules/@fortawesome/fontawesome-free/webfonts/"; // galaxy sub-components @import "reports"; -@import "frame.scss"; +@import "windows.scss"; @import "upload.scss"; @import "ui.scss"; @import "library.scss"; diff --git a/client/src/style/scss/frame.scss b/client/src/style/scss/frame.scss deleted file mode 100644 index 33fedbc8312..00000000000 --- a/client/src/style/scss/frame.scss +++ /dev/null @@ -1,155 +0,0 @@ -$frame-zindex: 1000; -$frame-default-border: $border-color; - -.galaxy-frame { - .corner { - -moz-border-radius: $border-radius-large; - border-radius: $border-radius-large; - } - - .toggle { - color: $dropdown-link-active-color; - } - - .frame-background { - z-index: $frame-zindex; - position: absolute; - top: 0px; - left: 0px; - height: 100%; - width: 100%; - opacity: 0.5; - background: $brand-dark; - overflow: auto; - } - - .frame-shadow { - z-index: $frame-zindex + 1; - position: absolute; - display: none; - top: 0px; - left: 0px; - opacity: 0.5; - background: $brand-dark; - border: 1px solid $frame-default-border; - } - - /* - panel menu button - */ - .frame-menu { - z-index: $frame-zindex + 5; - position: absolute; - cursor: pointer; - color: $dropdown-link-active-color; - right: 10px; - } - - .frame-scroll-up { - top: 50px; - } - - .frame-scroll-down { - bottom: 20px; - } - - /* - frame components - */ - - .frame { - z-index: $frame-zindex + 2; - overflow: hidden; - position: absolute; - background: $white; - border: 1px solid $frame-default-border; - -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, 0.3); - - .f-content { - position: absolute; - overflow: hidden; - background: $white; - border: none; - top: 24px; - bottom: 3px; - left: 3px; - right: 3px; - } - - .f-cover { - position: absolute; - display: none; - top: 0px; - left: 0px; - height: 100%; - width: 100%; - opacity: 0; - background: $white; - } - - .f-iframe { - border: none; - width: 100%; - height: 100%; - } - - .f-header { - height: 17px; - margin: 2px; - cursor: pointer; - border: 1px solid $black; - background: $brand-dark; - color: $white; - .f-icon-left { - cursor: pointer; - font-size: 15px; - margin-left: 3px; - float: left; - &[disabled] { - opacity: 0.25; - } - } - } - - .f-title { - position: absolute; - top: 2px; - left: 50px; - right: 50px; - font-size: 12px; - font-family: $font-family-sans-serif; - text-align: center; - } - - /* - frame icons - */ - - .f-not-allowed { - cursor: not-allowed; - } - - .f-close { - cursor: pointer; - position: absolute; - font-size: 15px; - right: 5px; - top: 2px; - } - - .f-resize { - cursor: pointer; - position: absolute; - font-size: 15px; - right: 0px; - bottom: 0px; - background: $white; - width: 16px; - height: 16px; - color: $brand-dark; - text-align: center; - line-height: 16px; - border: 0px; - } - } -} diff --git a/client/src/style/scss/windows.scss b/client/src/style/scss/windows.scss new file mode 100644 index 00000000000..15b7970ea45 --- /dev/null +++ b/client/src/style/scss/windows.scss @@ -0,0 +1,26 @@ +.winbox { + border-radius: $border-radius-base; + margin-top: 4rem; + margin-left: 1rem; +} + +.winbox.min { + @extend .m-0; +} + +.wb-header { + border-radius: $border-radius-base; + background: $brand-info; +} + +.wb-icon * { + opacity: 0.65; +} + +.wb-icon *:hover { + opacity: 1; +} + +.wb-full { + display: none !important; +} diff --git a/client/tests/jest/jest.config.js b/client/tests/jest/jest.config.js index 9ac271decef..c7159c8cd52 100644 --- a/client/tests/jest/jest.config.js +++ b/client/tests/jest/jest.config.js @@ -3,7 +3,7 @@ const path = require("path"); // For a detailed explanation regarding each configuration property, visit: // https://jestjs.io/docs/en/configuration.html -const modulesToTransform = ["bootstrap-vue", "rxjs", "@hirez_io"].join("|"); +const modulesToTransform = ["bootstrap-vue", "rxjs", "@hirez_io", "winbox"].join("|"); module.exports = { // All imported modules in your tests should be mocked automatically @@ -81,7 +81,8 @@ module.exports = { "^config$": "Take an interactive tour: Galaxy UI History - Scratchbook + Window Manager