Merge pull request #13922 from guerler/replace_multiview

Replace scratchbook with window manager
This commit is contained in:
Dannon
2022-05-26 02:52:47 -07:00
committed by GitHub
27 changed files with 180 additions and 1136 deletions
+1
View File
@@ -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": {
@@ -65,6 +65,7 @@
</template>
<script>
import { getAppRoot } from "onload";
import { backboneRoute, useGalaxy, iframeRedirect } from "components/plugins/legacyNavigation";
import { StatelessTags } from "components/Tags";
import { STATES } from "./model/states";
@@ -137,10 +138,14 @@ export default {
onDisplay() {
const id = this.item.id;
useGalaxy((Galaxy) => {
const url = `${getAppRoot()}datasets/${id}/display/?preview=True`;
if (Galaxy.frame && Galaxy.frame.active) {
Galaxy.frame.addDataset(id);
Galaxy.frame.add({
title: this.name,
url: url,
});
} else {
iframeRedirect(`datasets/${id}/display/?preview=True`);
iframeRedirect(url);
}
});
},
@@ -113,10 +113,12 @@ export default {
this.backboneRoute(`root?job_id=${this.item.creating_job}`);
},
onVisualize() {
const name = this.item.name || "";
const title = `Visualization of ${name}`;
const path = `visualizations?dataset_id=${this.item.id}`;
const redirectParams = {
path: path,
title: "Dataset details",
title: title,
tryIframe: false,
};
if (!this.iframeAdd(redirectParams)) {
+11 -11
View File
@@ -1,7 +1,7 @@
import { default as Masthead } from "./Masthead.vue";
import { mount } from "@vue/test-utils";
import { getLocalVue } from "jest/helpers";
import Scratchbook from "layout/scratchbook";
import { WindowManager } from "layout/window-manager";
import { fetchMenu } from "layout/menu";
import { loadWebhookMenuItems } from "./_webhooks";
@@ -12,7 +12,7 @@ jest.mock("./_webhooks");
describe("Masthead.vue", () => {
let wrapper;
let localVue;
let scratchbook;
let windowManager;
let quotaRendered;
let quotaEl;
let tabs;
@@ -71,15 +71,15 @@ describe("Masthead.vue", () => {
];
const initialActiveTab = "shared";
// scratchbook assumes this is a Backbone collection - mock that out.
// window manager assumes this is a Backbone collection - mock that out.
tabs.add = (x) => {
tabs.push(x);
return x;
};
scratchbook = new Scratchbook({});
windowManager = new WindowManager({});
const mastheadState = {
quotaMeter,
frame: scratchbook,
windowManager,
};
wrapper = mount(Masthead, {
@@ -105,7 +105,7 @@ describe("Masthead.vue", () => {
});
it("should render simple tab item links", () => {
expect(wrapper.findAll("li.nav-item").length).toBe(6);
expect(wrapper.findAll("li.nav-item").length).toBe(5);
// Ensure specified link title respected.
expect(wrapper.find("#analysis a").text()).toBe("Analyze");
expect(wrapper.find("#analysis a").attributes("href")).toBe("/root");
@@ -132,11 +132,11 @@ describe("Masthead.vue", () => {
expect(wrapper.find("#shared").classes("active")).toBe(true);
});
it("should display scratchbook button", async () => {
expect(wrapper.find("#enable-scratchbook a span").classes("fa-th")).toBe(true);
expect(scratchbook.active).toBe(false);
await wrapper.find("#enable-scratchbook a").trigger("click");
expect(scratchbook.active).toBe(true);
it("should display window manager button", async () => {
expect(wrapper.find("#enable-window-manager a span").classes("fa-th")).toBe(true);
expect(windowManager.active).toBe(false);
await wrapper.find("#enable-window-manager a").trigger("click");
expect(windowManager.active).toBe(true);
});
it("should load webhooks on creation", async () => {
+2 -19
View File
@@ -82,8 +82,8 @@ export default {
return brandTitle;
},
tabs() {
const scratchbookTabs = [this.mastheadState.frame.buttonActive, this.mastheadState.frame.buttonLoad];
const tabs = [].concat(this.baseTabs, this.extensionTabs, scratchbookTabs);
const windowTabs = [this.mastheadState.windowManager.buttonActive];
const tabs = [].concat(this.baseTabs, this.extensionTabs, windowTabs);
return tabs.map(this._tabToJson);
},
},
@@ -95,17 +95,6 @@ export default {
mounted() {
this.mastheadState.quotaMeter.setElement(this.$refs["quota-meter-container"]);
this.mastheadState.quotaMeter.render();
const frames = this.mastheadState.frame.getFrames();
frames
.on("add remove", () => {
const tab = this.mastheadState.frame.buttonLoad;
tab.note = String(frames.length());
tab.visible = frames.length() > 0;
tab.show_note = frames.length() > 0;
})
.on("show hide", () => {
this._reflectScratchbookFrames();
});
},
methods: {
addItem(item) {
@@ -127,12 +116,6 @@ export default {
}
return Object.assign({}, defaults, asJson);
},
_reflectScratchbookFrames() {
const frames = this.mastheadState.frame.getFrames();
const tab = this.mastheadState.frame.buttonLoad;
tab.toggle = frames.visible;
tab.icon = (frames.visible && "fa-eye") || "fa-eye-slash";
},
},
};
</script>
@@ -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) != "/") {
@@ -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) {
+3 -3
View File
@@ -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;
}
-1
View File
@@ -239,7 +239,6 @@ export function fetchMenu(options = {}) {
cls: "loggedout-only",
tooltip: _l("Login"),
url: "login",
noscratchbook: true,
};
}
} else {
-1
View File
@@ -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
-247
View File
@@ -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;
}
},
});
+74
View File
@@ -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;
}
}
}
+3 -1
View File
@@ -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();
}
};
-648
View File
@@ -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($("<div/>").addClass("corner frame"));
this.$el
.append(
$("<div/>")
.addClass("f-header corner")
.append($("<div/>").addClass("f-title"))
.append(
$("<div/>")
.addClass("f-icon f-close fa fa-close")
.tooltip({
title: _l("Close"),
placement: "bottom",
})
)
)
.append($("<div/>").addClass("f-content"))
.append($("<div/>").addClass("f-resize f-icon corner fa fa-expand").tooltip({ title: "Resize" }))
.append($("<div/>").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 = $("<div/>").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(
$("<iframe/>")
.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(
$("<div/>")
.addClass("galaxy-frame")
.append($("<div/>").addClass("frame-background"))
.append($("<div/>").addClass("frame-menu frame-scroll-up fa fa-chevron-up fa-2x"))
.append($("<div/>").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: $("<div/>").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,
};
+4 -4
View File
@@ -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
+4 -4
View File
@@ -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
+4 -4
View File
@@ -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
+4 -4
View File
@@ -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
+1 -1
View File
@@ -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";
-155
View File
@@ -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;
}
}
}
+26
View File
@@ -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;
}
+3 -2
View File
@@ -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$": "<rootDir>/tests/jest/__mocks__/config.js",
"utils/localization$": "<rootDir>/tests/jest/__mocks__/localization.js",
"viz/trackster$": "<rootDir>/tests/jest/__mocks__/trackster.js",
"rxjs/internal/scheduler/AsyncScheduler": "<rootDir>/node_modules/rxjs/dist/esm/internal/scheduler/AsyncScheduler.js"
"rxjs/internal/scheduler/AsyncScheduler":
"<rootDir>/node_modules/rxjs/dist/esm/internal/scheduler/AsyncScheduler.js",
},
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
+5
View File
@@ -10163,6 +10163,11 @@ wildcard@^2.0.0:
resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec"
integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==
winbox@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/winbox/-/winbox-0.2.1.tgz#6ca7cd047fa52d9f3324884698ce8a9c9ba9bf27"
integrity sha512-K3PY+y+aWpkCpECGF2TqSi4w+OT+cuJHWbOKBjVerpexmdC6Is5m4y0gZ9rbxU2K+GszE1nH+d2okMAQTG05hg==
word-wrap@^1.2.3, word-wrap@~1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"