mirror of
https://github.com/nasa/openmct.git
synced 2025-02-22 02:06:49 +00:00
* [Menu API] All our drop down menu's now use the new menu api #3607 Co-authored-by: charlesh88 <charles.f.hacskaylo@nasa.gov> Co-authored-by: Andrew Henry <akhenry@gmail.com>
This commit is contained in:
parent
cf3566742b
commit
f9bd31deee
@ -20,7 +20,25 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import Menu from './menu.js';
|
import Menu, { MENU_PLACEMENT } from './menu.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Popup Menu options
|
||||||
|
* @typedef {Object} MenuOptions
|
||||||
|
* @property {String} menuClass Class for popup menu
|
||||||
|
* @property {MENU_PLACEMENT} placement Placement for menu relative to click
|
||||||
|
* @property {Function} onDestroy callback function: invoked when menu is destroyed
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Popup Menu Item/action
|
||||||
|
* @typedef {Object} Action
|
||||||
|
* @property {String} cssClass Class for menu item
|
||||||
|
* @property {Boolean} isDisabled adds disable class if true
|
||||||
|
* @property {String} name Menu item text
|
||||||
|
* @property {String} description Menu item description
|
||||||
|
* @property {Function} callBack callback function: invoked when item is clicked
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The MenuAPI allows the addition of new context menu actions, and for the context menu to be launched from
|
* The MenuAPI allows the addition of new context menu actions, and for the context menu to be launched from
|
||||||
@ -33,12 +51,46 @@ class MenuAPI {
|
|||||||
constructor(openmct) {
|
constructor(openmct) {
|
||||||
this.openmct = openmct;
|
this.openmct = openmct;
|
||||||
|
|
||||||
|
this.menuPlacement = MENU_PLACEMENT;
|
||||||
this.showMenu = this.showMenu.bind(this);
|
this.showMenu = this.showMenu.bind(this);
|
||||||
|
this.showSuperMenu = this.showSuperMenu.bind(this);
|
||||||
|
|
||||||
this._clearMenuComponent = this._clearMenuComponent.bind(this);
|
this._clearMenuComponent = this._clearMenuComponent.bind(this);
|
||||||
this._showObjectMenu = this._showObjectMenu.bind(this);
|
this._showObjectMenu = this._showObjectMenu.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
showMenu(x, y, actions, onDestroy) {
|
/**
|
||||||
|
* Show popup menu
|
||||||
|
* @param {number} x x-coordinates for popup
|
||||||
|
* @param {number} y x-coordinates for popup
|
||||||
|
* @param {Array.<Action>|Array.<Array.<Action>>} actions collection of actions{@link Action} or collection of groups of actions {@link Action}
|
||||||
|
* @param {MenuOptions} [menuOptions] [Optional] The {@link MenuOptions} options for Menu
|
||||||
|
*/
|
||||||
|
showMenu(x, y, actions, menuOptions) {
|
||||||
|
this._createMenuComponent(x, y, actions, menuOptions);
|
||||||
|
|
||||||
|
this.menuComponent.showMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show popup menu with description of item on hover
|
||||||
|
* @param {number} x x-coordinates for popup
|
||||||
|
* @param {number} y x-coordinates for popup
|
||||||
|
* @param {Array.<Action>|Array.<Array.<Action>>} actions collection of actions {@link Action} or collection of groups of actions {@link Action}
|
||||||
|
* @param {MenuOptions} [menuOptions] [Optional] The {@link MenuOptions} options for Menu
|
||||||
|
*/
|
||||||
|
showSuperMenu(x, y, actions, menuOptions) {
|
||||||
|
this._createMenuComponent(x, y, actions, menuOptions);
|
||||||
|
|
||||||
|
this.menuComponent.showSuperMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
_clearMenuComponent() {
|
||||||
|
this.menuComponent = undefined;
|
||||||
|
delete this.menuComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
_createMenuComponent(x, y, actions, menuOptions = {}) {
|
||||||
if (this.menuComponent) {
|
if (this.menuComponent) {
|
||||||
this.menuComponent.dismiss();
|
this.menuComponent.dismiss();
|
||||||
}
|
}
|
||||||
@ -47,18 +99,13 @@ class MenuAPI {
|
|||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
actions,
|
actions,
|
||||||
onDestroy
|
...menuOptions
|
||||||
};
|
};
|
||||||
|
|
||||||
this.menuComponent = new Menu(options);
|
this.menuComponent = new Menu(options);
|
||||||
this.menuComponent.once('destroy', this._clearMenuComponent);
|
this.menuComponent.once('destroy', this._clearMenuComponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
_clearMenuComponent() {
|
|
||||||
this.menuComponent = undefined;
|
|
||||||
delete this.menuComponent;
|
|
||||||
}
|
|
||||||
|
|
||||||
_showObjectMenu(objectPath, x, y, actionsToBeIncluded) {
|
_showObjectMenu(objectPath, x, y, actionsToBeIncluded) {
|
||||||
let applicableActions = this.openmct.actions._groupedAndSortedObjectActions(objectPath, actionsToBeIncluded);
|
let applicableActions = this.openmct.actions._groupedAndSortedObjectActions(objectPath, actionsToBeIncluded);
|
||||||
|
|
||||||
|
@ -22,10 +22,11 @@
|
|||||||
|
|
||||||
import MenuAPI from './MenuAPI';
|
import MenuAPI from './MenuAPI';
|
||||||
import Menu from './menu';
|
import Menu from './menu';
|
||||||
import { createOpenMct, resetApplicationState } from '../../utils/testing';
|
import { createOpenMct, createMouseEvent, resetApplicationState } from '../../utils/testing';
|
||||||
|
|
||||||
describe ('The Menu API', () => {
|
describe ('The Menu API', () => {
|
||||||
let openmct;
|
let openmct;
|
||||||
|
let element;
|
||||||
let menuAPI;
|
let menuAPI;
|
||||||
let actionsArray;
|
let actionsArray;
|
||||||
let x;
|
let x;
|
||||||
@ -33,21 +34,37 @@ describe ('The Menu API', () => {
|
|||||||
let result;
|
let result;
|
||||||
let onDestroy;
|
let onDestroy;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach((done) => {
|
||||||
|
const appHolder = document.createElement('div');
|
||||||
|
appHolder.style.display = 'block';
|
||||||
|
appHolder.style.width = '1920px';
|
||||||
|
appHolder.style.height = '1080px';
|
||||||
|
|
||||||
openmct = createOpenMct();
|
openmct = createOpenMct();
|
||||||
|
|
||||||
|
element = document.createElement('div');
|
||||||
|
element.style.display = 'block';
|
||||||
|
element.style.width = '1920px';
|
||||||
|
element.style.height = '1080px';
|
||||||
|
|
||||||
|
openmct.on('start', done);
|
||||||
|
openmct.startHeadless(appHolder);
|
||||||
|
|
||||||
menuAPI = new MenuAPI(openmct);
|
menuAPI = new MenuAPI(openmct);
|
||||||
actionsArray = [
|
actionsArray = [
|
||||||
{
|
{
|
||||||
|
key: 'test-css-class-1',
|
||||||
name: 'Test Action 1',
|
name: 'Test Action 1',
|
||||||
cssClass: 'test-css-class-1',
|
cssClass: 'icon-clock',
|
||||||
description: 'This is a test action',
|
description: 'This is a test action',
|
||||||
callBack: () => {
|
callBack: () => {
|
||||||
result = 'Test Action 1 Invoked';
|
result = 'Test Action 1 Invoked';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
key: 'test-css-class-2',
|
||||||
name: 'Test Action 2',
|
name: 'Test Action 2',
|
||||||
cssClass: 'test-css-class-2',
|
cssClass: 'icon-clock',
|
||||||
description: 'This is a test action',
|
description: 'This is a test action',
|
||||||
callBack: () => {
|
callBack: () => {
|
||||||
result = 'Test Action 2 Invoked';
|
result = 'Test Action 2 Invoked';
|
||||||
@ -76,7 +93,11 @@ describe ('The Menu API', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
onDestroy = jasmine.createSpy('onDestroy');
|
onDestroy = jasmine.createSpy('onDestroy');
|
||||||
|
|
||||||
menuAPI.showMenu(x, y, actionsArray, onDestroy);
|
const menuOptions = {
|
||||||
|
onDestroy
|
||||||
|
};
|
||||||
|
|
||||||
|
menuAPI.showMenu(x, y, actionsArray, menuOptions);
|
||||||
vueComponent = menuAPI.menuComponent.component;
|
vueComponent = menuAPI.menuComponent.component;
|
||||||
menuComponent = document.querySelector(".c-menu");
|
menuComponent = document.querySelector(".c-menu");
|
||||||
|
|
||||||
@ -131,4 +152,62 @@ describe ('The Menu API', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("superMenu method", () => {
|
||||||
|
it("creates a superMenu", () => {
|
||||||
|
menuAPI.showSuperMenu(x, y, actionsArray);
|
||||||
|
|
||||||
|
const superMenu = document.querySelector('.c-super-menu__menu');
|
||||||
|
|
||||||
|
expect(superMenu).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Mouse over a superMenu shows correct description", (done) => {
|
||||||
|
menuAPI.showSuperMenu(x, y, actionsArray);
|
||||||
|
|
||||||
|
const superMenu = document.querySelector('.c-super-menu__menu');
|
||||||
|
const superMenuItem = superMenu.querySelector('li');
|
||||||
|
const mouseOverEvent = createMouseEvent('mouseover');
|
||||||
|
|
||||||
|
superMenuItem.dispatchEvent(mouseOverEvent);
|
||||||
|
const itemDescription = document.querySelector('.l-item-description__description');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(itemDescription.innerText).toEqual(actionsArray[0].description);
|
||||||
|
expect(superMenu).not.toBeNull();
|
||||||
|
done();
|
||||||
|
}, 300);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Menu Placements", () => {
|
||||||
|
it("default menu position BOTTOM_RIGHT", () => {
|
||||||
|
menuAPI.showMenu(x, y, actionsArray);
|
||||||
|
|
||||||
|
const menu = document.querySelector('.c-menu');
|
||||||
|
|
||||||
|
const boundingClientRect = menu.getBoundingClientRect();
|
||||||
|
const left = boundingClientRect.left;
|
||||||
|
const top = boundingClientRect.top;
|
||||||
|
|
||||||
|
expect(left).toEqual(x);
|
||||||
|
expect(top).toEqual(y);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("menu position BOTTOM_RIGHT", () => {
|
||||||
|
const menuOptions = {
|
||||||
|
placement: openmct.menus.menuPlacement.BOTTOM_RIGHT
|
||||||
|
};
|
||||||
|
|
||||||
|
menuAPI.showMenu(x, y, actionsArray, menuOptions);
|
||||||
|
|
||||||
|
const menu = document.querySelector('.c-menu');
|
||||||
|
const boundingClientRect = menu.getBoundingClientRect();
|
||||||
|
const left = boundingClientRect.left;
|
||||||
|
const top = boundingClientRect.top;
|
||||||
|
|
||||||
|
expect(left).toEqual(x);
|
||||||
|
expect(top).toEqual(y);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="c-menu">
|
<div class="c-menu"
|
||||||
<ul v-if="actions.length && actions[0].length">
|
:class="options.menuClass"
|
||||||
|
>
|
||||||
|
<ul v-if="options.actions.length && options.actions[0].length">
|
||||||
<template
|
<template
|
||||||
v-for="(actionGroups, index) in actions"
|
v-for="(actionGroups, index) in options.actions"
|
||||||
>
|
>
|
||||||
<li
|
<li
|
||||||
v-for="action in actionGroups"
|
v-for="action in actionGroups"
|
||||||
@ -14,7 +16,7 @@
|
|||||||
{{ action.name }}
|
{{ action.name }}
|
||||||
</li>
|
</li>
|
||||||
<div
|
<div
|
||||||
v-if="index !== actions.length - 1"
|
v-if="index !== options.actions.length - 1"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="c-menu__section-separator"
|
class="c-menu__section-separator"
|
||||||
>
|
>
|
||||||
@ -30,7 +32,7 @@
|
|||||||
|
|
||||||
<ul v-else>
|
<ul v-else>
|
||||||
<li
|
<li
|
||||||
v-for="action in actions"
|
v-for="action in options.actions"
|
||||||
:key="action.name"
|
:key="action.name"
|
||||||
:class="action.cssClass"
|
:class="action.cssClass"
|
||||||
:title="action.description"
|
:title="action.description"
|
||||||
@ -38,7 +40,7 @@
|
|||||||
>
|
>
|
||||||
{{ action.name }}
|
{{ action.name }}
|
||||||
</li>
|
</li>
|
||||||
<li v-if="actions.length === 0">
|
<li v-if="options.actions.length === 0">
|
||||||
No actions defined.
|
No actions defined.
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -47,6 +49,6 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
inject: ['actions']
|
inject: ['options']
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
88
src/api/menu/components/SuperMenu.vue
Normal file
88
src/api/menu/components/SuperMenu.vue
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<template>
|
||||||
|
<div class="c-menu"
|
||||||
|
:class="[options.menuClass, 'c-super-menu']"
|
||||||
|
>
|
||||||
|
<ul v-if="options.actions.length && options.actions[0].length"
|
||||||
|
class="c-super-menu__menu"
|
||||||
|
>
|
||||||
|
<template
|
||||||
|
v-for="(actionGroups, index) in options.actions"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
v-for="action in actionGroups"
|
||||||
|
:key="action.name"
|
||||||
|
:class="[action.cssClass, action.isDisabled ? 'disabled' : '']"
|
||||||
|
:title="action.description"
|
||||||
|
@click="action.callBack"
|
||||||
|
@mouseover="toggleItemDescription(action)"
|
||||||
|
@mouseleave="toggleItemDescription()"
|
||||||
|
>
|
||||||
|
{{ action.name }}
|
||||||
|
</li>
|
||||||
|
<div
|
||||||
|
v-if="index !== options.actions.length - 1"
|
||||||
|
:key="index"
|
||||||
|
class="c-menu__section-separator"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<li
|
||||||
|
v-if="actionGroups.length === 0"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
No actions defined.
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<ul v-else
|
||||||
|
class="c-super-menu__menu"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
v-for="action in options.actions"
|
||||||
|
:key="action.name"
|
||||||
|
:class="action.cssClass"
|
||||||
|
:title="action.description"
|
||||||
|
@click="action.callBack"
|
||||||
|
@mouseover="toggleItemDescription(action)"
|
||||||
|
@mouseleave="toggleItemDescription()"
|
||||||
|
>
|
||||||
|
{{ action.name }}
|
||||||
|
</li>
|
||||||
|
<li v-if="options.actions.length === 0">
|
||||||
|
No actions defined.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="c-super-menu__item-description">
|
||||||
|
<div :class="['l-item-description__icon', 'bg-' + hoveredItem.cssClass]"></div>
|
||||||
|
<div class="l-item-description__name">
|
||||||
|
{{ hoveredItem.name }}
|
||||||
|
</div>
|
||||||
|
<div class="l-item-description__description">
|
||||||
|
{{ hoveredItem.description }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
inject: ['options'],
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
hoveredItem: {}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleItemDescription(action = {}) {
|
||||||
|
const hoveredItem = {
|
||||||
|
name: action.name,
|
||||||
|
description: action.description,
|
||||||
|
cssClass: action.cssClass
|
||||||
|
};
|
||||||
|
|
||||||
|
this.hoveredItem = Object.assign({}, this.hoveredItem, hoveredItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
@ -21,32 +21,33 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
import EventEmitter from 'EventEmitter';
|
import EventEmitter from 'EventEmitter';
|
||||||
import MenuComponent from './components/Menu.vue';
|
import MenuComponent from './components/Menu.vue';
|
||||||
|
import SuperMenuComponent from './components/SuperMenu.vue';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
export const MENU_PLACEMENT = {
|
||||||
|
TOP: 'top',
|
||||||
|
TOP_LEFT: 'top-left',
|
||||||
|
TOP_RIGHT: 'top-right',
|
||||||
|
BOTTOM: 'bottom',
|
||||||
|
BOTTOM_LEFT: 'bottom-left',
|
||||||
|
BOTTOM_RIGHT: 'bottom-right',
|
||||||
|
LEFT: 'left',
|
||||||
|
RIGHT: 'right'
|
||||||
|
};
|
||||||
|
|
||||||
class Menu extends EventEmitter {
|
class Menu extends EventEmitter {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.options = options;
|
this.options = options;
|
||||||
|
|
||||||
this.component = new Vue({
|
|
||||||
components: {
|
|
||||||
MenuComponent
|
|
||||||
},
|
|
||||||
provide: {
|
|
||||||
actions: options.actions
|
|
||||||
},
|
|
||||||
template: '<menu-component />'
|
|
||||||
});
|
|
||||||
|
|
||||||
if (options.onDestroy) {
|
if (options.onDestroy) {
|
||||||
this.once('destroy', options.onDestroy);
|
this.once('destroy', options.onDestroy);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dismiss = this.dismiss.bind(this);
|
this.dismiss = this.dismiss.bind(this);
|
||||||
this.show = this.show.bind(this);
|
this.show = this.show.bind(this);
|
||||||
|
this.showMenu = this.showMenu.bind(this);
|
||||||
this.show();
|
this.showSuperMenu = this.showSuperMenu.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
dismiss() {
|
dismiss() {
|
||||||
@ -60,7 +61,7 @@ class Menu extends EventEmitter {
|
|||||||
this.component.$mount();
|
this.component.$mount();
|
||||||
document.body.appendChild(this.component.$el);
|
document.body.appendChild(this.component.$el);
|
||||||
|
|
||||||
let position = this._calculatePopupPosition(this.options.x, this.options.y, this.component.$el);
|
let position = this._calculatePopupPosition(this.component.$el);
|
||||||
|
|
||||||
this.component.$el.style.left = `${position.x}px`;
|
this.component.$el.style.left = `${position.x}px`;
|
||||||
this.component.$el.style.top = `${position.y}px`;
|
this.component.$el.style.top = `${position.y}px`;
|
||||||
@ -68,11 +69,97 @@ class Menu extends EventEmitter {
|
|||||||
document.addEventListener('click', this.dismiss);
|
document.addEventListener('click', this.dismiss);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showMenu() {
|
||||||
|
this.component = new Vue({
|
||||||
|
provide: {
|
||||||
|
options: this.options
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
MenuComponent
|
||||||
|
},
|
||||||
|
template: '<menu-component />'
|
||||||
|
});
|
||||||
|
|
||||||
|
this.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
showSuperMenu() {
|
||||||
|
this.component = new Vue({
|
||||||
|
provide: {
|
||||||
|
options: this.options
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
SuperMenuComponent
|
||||||
|
},
|
||||||
|
template: '<super-menu-component />'
|
||||||
|
});
|
||||||
|
|
||||||
|
this.show();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_calculatePopupPosition(eventPosX, eventPosY, menuElement) {
|
_calculatePopupPosition(menuElement) {
|
||||||
let menuDimensions = menuElement.getBoundingClientRect();
|
let menuDimensions = menuElement.getBoundingClientRect();
|
||||||
|
|
||||||
|
if (!this.options.placement) {
|
||||||
|
this.options.placement = MENU_PLACEMENT.BOTTOM_RIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
const menuPosition = this._getMenuPositionBasedOnPlacement(menuDimensions);
|
||||||
|
|
||||||
|
return this._preventMenuOverflow(menuPosition, menuDimensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_getMenuPositionBasedOnPlacement(menuDimensions) {
|
||||||
|
let eventPosX = this.options.x;
|
||||||
|
let eventPosY = this.options.y;
|
||||||
|
|
||||||
|
// Adjust popup menu based on placement
|
||||||
|
switch (this.options.placement) {
|
||||||
|
case MENU_PLACEMENT.TOP:
|
||||||
|
eventPosX = this.options.x - Math.floor(menuDimensions.width / 2);
|
||||||
|
eventPosY = this.options.y - menuDimensions.height;
|
||||||
|
break;
|
||||||
|
case MENU_PLACEMENT.BOTTOM:
|
||||||
|
eventPosX = this.options.x - Math.floor(menuDimensions.width / 2);
|
||||||
|
break;
|
||||||
|
case MENU_PLACEMENT.LEFT:
|
||||||
|
eventPosX = this.options.x - menuDimensions.width;
|
||||||
|
eventPosY = this.options.y - Math.floor(menuDimensions.height / 2);
|
||||||
|
break;
|
||||||
|
case MENU_PLACEMENT.RIGHT:
|
||||||
|
eventPosY = this.options.y - Math.floor(menuDimensions.height / 2);
|
||||||
|
break;
|
||||||
|
case MENU_PLACEMENT.TOP_LEFT:
|
||||||
|
eventPosX = this.options.x - menuDimensions.width;
|
||||||
|
eventPosY = this.options.y - menuDimensions.height;
|
||||||
|
break;
|
||||||
|
case MENU_PLACEMENT.TOP_RIGHT:
|
||||||
|
eventPosY = this.options.y - menuDimensions.height;
|
||||||
|
break;
|
||||||
|
case MENU_PLACEMENT.BOTTOM_LEFT:
|
||||||
|
eventPosX = this.options.x - menuDimensions.width;
|
||||||
|
break;
|
||||||
|
case MENU_PLACEMENT.BOTTOM_RIGHT:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: eventPosX,
|
||||||
|
y: eventPosY
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_preventMenuOverflow(menuPosition, menuDimensions) {
|
||||||
|
let { x: eventPosX, y: eventPosY } = menuPosition;
|
||||||
let overflowX = (eventPosX + menuDimensions.width) - document.body.clientWidth;
|
let overflowX = (eventPosX + menuDimensions.width) - document.body.clientWidth;
|
||||||
let overflowY = (eventPosY + menuDimensions.height) - document.body.clientHeight;
|
let overflowY = (eventPosY + menuDimensions.height) - document.body.clientHeight;
|
||||||
|
|
||||||
@ -84,6 +171,14 @@ class Menu extends EventEmitter {
|
|||||||
eventPosY = eventPosY - overflowY;
|
eventPosY = eventPosY - overflowY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (eventPosX < 0) {
|
||||||
|
eventPosX = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventPosY < 0) {
|
||||||
|
eventPosY = 0;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
x: eventPosX,
|
x: eventPosX,
|
||||||
y: eventPosY
|
y: eventPosY
|
||||||
|
@ -20,59 +20,27 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
<template>
|
<template>
|
||||||
<div class="c-ctrl-wrapper c-ctrl-wrapper--menus-up">
|
<div ref="historyButton"
|
||||||
<button class="c-button--menu c-history-button icon-history"
|
class="c-ctrl-wrapper c-ctrl-wrapper--menus-up"
|
||||||
@click.prevent="toggle"
|
>
|
||||||
>
|
<div class="c-menu-button c-ctrl-wrapper c-ctrl-wrapper--menus-left">
|
||||||
<span class="c-button__label">History</span>
|
<button
|
||||||
</button>
|
class="c-button--menu c-history-button icon-history"
|
||||||
<div v-if="open"
|
@click.prevent.stop="showHistoryMenu"
|
||||||
class="c-menu c-conductor__history-menu"
|
>
|
||||||
>
|
<span class="c-button__label">History</span>
|
||||||
<ul v-if="hasHistoryPresets">
|
</button>
|
||||||
<li
|
|
||||||
v-for="preset in presets"
|
|
||||||
:key="preset.label"
|
|
||||||
class="icon-clock"
|
|
||||||
@click="selectPresetBounds(preset.bounds)"
|
|
||||||
>
|
|
||||||
{{ preset.label }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div
|
|
||||||
v-if="hasHistoryPresets"
|
|
||||||
class="c-menu__section-separator"
|
|
||||||
></div>
|
|
||||||
|
|
||||||
<div class="c-menu__section-hint">
|
|
||||||
Past timeframes, ordered by latest first
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li
|
|
||||||
v-for="(timespan, index) in historyForCurrentTimeSystem"
|
|
||||||
:key="index"
|
|
||||||
class="icon-history"
|
|
||||||
@click="selectTimespan(timespan)"
|
|
||||||
>
|
|
||||||
{{ formatTime(timespan.start) }} - {{ formatTime(timespan.end) }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import toggleMixin from '../../ui/mixins/toggle-mixin';
|
|
||||||
|
|
||||||
const DEFAULT_DURATION_FORMATTER = 'duration';
|
const DEFAULT_DURATION_FORMATTER = 'duration';
|
||||||
const LOCAL_STORAGE_HISTORY_KEY_FIXED = 'tcHistory';
|
const LOCAL_STORAGE_HISTORY_KEY_FIXED = 'tcHistory';
|
||||||
const LOCAL_STORAGE_HISTORY_KEY_REALTIME = 'tcHistoryRealtime';
|
const LOCAL_STORAGE_HISTORY_KEY_REALTIME = 'tcHistoryRealtime';
|
||||||
const DEFAULT_RECORDS = 10;
|
const DEFAULT_RECORDS = 10;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [toggleMixin],
|
|
||||||
inject: ['openmct', 'configuration'],
|
inject: ['openmct', 'configuration'],
|
||||||
props: {
|
props: {
|
||||||
bounds: {
|
bounds: {
|
||||||
@ -117,9 +85,6 @@ export default {
|
|||||||
isFixed() {
|
isFixed() {
|
||||||
return this.openmct.time.clock() === undefined;
|
return this.openmct.time.clock() === undefined;
|
||||||
},
|
},
|
||||||
hasHistoryPresets() {
|
|
||||||
return this.timeSystem.isUTCBased && this.presets.length;
|
|
||||||
},
|
|
||||||
historyForCurrentTimeSystem() {
|
historyForCurrentTimeSystem() {
|
||||||
const history = this[this.currentHistory][this.timeSystem.key];
|
const history = this[this.currentHistory][this.timeSystem.key];
|
||||||
|
|
||||||
@ -168,6 +133,36 @@ export default {
|
|||||||
this.initializeHistoryIfNoHistory();
|
this.initializeHistoryIfNoHistory();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
getHistoryMenuItems() {
|
||||||
|
const history = this.historyForCurrentTimeSystem.map(timespan => {
|
||||||
|
return {
|
||||||
|
cssClass: 'icon-history',
|
||||||
|
name: `${this.formatTime(timespan.start)} - ${this.formatTime(timespan.end)}`,
|
||||||
|
description: `${this.formatTime(timespan.start)} - ${this.formatTime(timespan.end)}`,
|
||||||
|
callBack: () => this.selectTimespan(timespan)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
history.unshift({
|
||||||
|
cssClass: 'c-menu__section-hint',
|
||||||
|
description: 'Past timeframes, ordered by latest first',
|
||||||
|
isDisabled: true,
|
||||||
|
name: 'Past timeframes, ordered by latest first',
|
||||||
|
callBack: () => {}
|
||||||
|
});
|
||||||
|
|
||||||
|
return history;
|
||||||
|
},
|
||||||
|
getPresetMenuItems() {
|
||||||
|
return this.presets.map(preset => {
|
||||||
|
return {
|
||||||
|
cssClass: 'icon-clock',
|
||||||
|
name: preset.label,
|
||||||
|
description: preset.label,
|
||||||
|
callBack: () => this.selectPresetBounds(preset.bounds)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
getHistoryFromLocalStorage() {
|
getHistoryFromLocalStorage() {
|
||||||
const localStorageHistory = localStorage.getItem(this.storageKey);
|
const localStorageHistory = localStorage.getItem(this.storageKey);
|
||||||
const history = localStorageHistory ? JSON.parse(localStorageHistory) : undefined;
|
const history = localStorageHistory ? JSON.parse(localStorageHistory) : undefined;
|
||||||
@ -265,6 +260,28 @@ export default {
|
|||||||
}).formatter;
|
}).formatter;
|
||||||
|
|
||||||
return (isNegativeOffset ? '-' : '') + formatter.format(time);
|
return (isNegativeOffset ? '-' : '') + formatter.format(time);
|
||||||
|
},
|
||||||
|
showHistoryMenu() {
|
||||||
|
const elementBoundingClientRect = this.$refs.historyButton.getBoundingClientRect();
|
||||||
|
const x = elementBoundingClientRect.x;
|
||||||
|
const y = elementBoundingClientRect.y;
|
||||||
|
|
||||||
|
const menuOptions = {
|
||||||
|
menuClass: 'c-conductor__history-menu',
|
||||||
|
placement: this.openmct.menus.menuPlacement.TOP_RIGHT
|
||||||
|
};
|
||||||
|
|
||||||
|
const menuActions = [];
|
||||||
|
|
||||||
|
const presets = this.getPresetMenuItems();
|
||||||
|
if (presets.length) {
|
||||||
|
menuActions.push(presets);
|
||||||
|
}
|
||||||
|
|
||||||
|
const history = this.getHistoryMenuItems();
|
||||||
|
menuActions.push(history);
|
||||||
|
|
||||||
|
this.openmct.menus.showMenu(x, y, menuActions, menuOptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -20,41 +20,16 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
<template>
|
<template>
|
||||||
<div class="c-ctrl-wrapper c-ctrl-wrapper--menus-up">
|
<div ref="modeButton"
|
||||||
<button
|
class="c-ctrl-wrapper c-ctrl-wrapper--menus-up"
|
||||||
class="c-button--menu c-mode-button"
|
>
|
||||||
@click.prevent="toggle"
|
<div class="c-menu-button c-ctrl-wrapper c-ctrl-wrapper--menus-left">
|
||||||
>
|
<button
|
||||||
<span class="c-button__label">{{ selectedMode.name }}</span>
|
class="c-button--menu c-mode-button"
|
||||||
</button>
|
@click.prevent.stop="showModesMenu"
|
||||||
<div
|
>
|
||||||
v-if="open"
|
<span class="c-button__label">{{ selectedMode.name }}</span>
|
||||||
class="c-menu c-super-menu c-conductor__mode-menu"
|
</button>
|
||||||
>
|
|
||||||
<div class="c-super-menu__menu">
|
|
||||||
<ul>
|
|
||||||
<li
|
|
||||||
v-for="mode in modes"
|
|
||||||
:key="mode.key"
|
|
||||||
class="menu-item-a"
|
|
||||||
:class="mode.cssClass"
|
|
||||||
@click="setOption(mode)"
|
|
||||||
@mouseover="hoveredMode = mode"
|
|
||||||
@mouseleave="hoveredMode = {}"
|
|
||||||
>
|
|
||||||
{{ mode.name }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="c-super-menu__item-description">
|
|
||||||
<div :class="['l-item-description__icon', 'bg-' + hoveredMode.cssClass]"></div>
|
|
||||||
<div class="l-item-description__name">
|
|
||||||
{{ hoveredMode.name }}
|
|
||||||
</div>
|
|
||||||
<div class="l-item-description__description">
|
|
||||||
{{ hoveredMode.description }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -88,6 +63,19 @@ export default {
|
|||||||
this.openmct.time.off('clock', this.setViewFromClock);
|
this.openmct.time.off('clock', this.setViewFromClock);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
showModesMenu() {
|
||||||
|
const elementBoundingClientRect = this.$refs.modeButton.getBoundingClientRect();
|
||||||
|
const x = elementBoundingClientRect.x;
|
||||||
|
const y = elementBoundingClientRect.y;
|
||||||
|
|
||||||
|
const menuOptions = {
|
||||||
|
menuClass: 'c-conductor__mode-menu',
|
||||||
|
placement: this.openmct.menus.menuPlacement.TOP_RIGHT
|
||||||
|
};
|
||||||
|
|
||||||
|
this.openmct.menus.showSuperMenu(x, y, this.modes, menuOptions);
|
||||||
|
},
|
||||||
|
|
||||||
loadClocksFromConfiguration() {
|
loadClocksFromConfiguration() {
|
||||||
let clocks = this.configuration.menuOptions
|
let clocks = this.configuration.menuOptions
|
||||||
.map(menuOption => menuOption.clock)
|
.map(menuOption => menuOption.clock)
|
||||||
@ -109,19 +97,25 @@ export default {
|
|||||||
|
|
||||||
getModeOptionForClock(clock) {
|
getModeOptionForClock(clock) {
|
||||||
if (clock === undefined) {
|
if (clock === undefined) {
|
||||||
|
const key = 'fixed';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
key: 'fixed',
|
key,
|
||||||
name: 'Fixed Timespan',
|
name: 'Fixed Timespan',
|
||||||
description: 'Query and explore data that falls between two fixed datetimes.',
|
description: 'Query and explore data that falls between two fixed datetimes.',
|
||||||
cssClass: 'icon-tabular'
|
cssClass: 'icon-tabular',
|
||||||
|
callBack: () => this.setOption(key)
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
|
const key = clock.key;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
key: clock.key,
|
key,
|
||||||
name: clock.name,
|
name: clock.name,
|
||||||
description: "Monitor streaming data in real-time. The Time "
|
description: "Monitor streaming data in real-time. The Time "
|
||||||
+ "Conductor and displays will automatically advance themselves based on this clock. " + clock.description,
|
+ "Conductor and displays will automatically advance themselves based on this clock. " + clock.description,
|
||||||
cssClass: clock.cssClass || 'icon-clock'
|
cssClass: clock.cssClass || 'icon-clock',
|
||||||
|
callBack: () => this.setOption(key)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -132,8 +126,7 @@ export default {
|
|||||||
})[0];
|
})[0];
|
||||||
},
|
},
|
||||||
|
|
||||||
setOption(option) {
|
setOption(clockKey) {
|
||||||
let clockKey = option.key;
|
|
||||||
if (clockKey === 'fixed') {
|
if (clockKey === 'fixed') {
|
||||||
clockKey = undefined;
|
clockKey = undefined;
|
||||||
}
|
}
|
||||||
@ -181,6 +174,5 @@ export default {
|
|||||||
this.selectedMode = this.getModeOptionForClock(clock);
|
this.selectedMode = this.getModeOptionForClock(clock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -20,40 +20,22 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div v-if="selectedTimeSystem.name"
|
||||||
v-if="selectedTimeSystem.name"
|
ref="timeSystemButton"
|
||||||
class="c-ctrl-wrapper c-ctrl-wrapper--menus-up"
|
class="c-ctrl-wrapper c-ctrl-wrapper--menus-up"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="c-button--menu c-time-system-button"
|
class="c-button--menu c-time-system-button"
|
||||||
:class="selectedTimeSystem.cssClass"
|
:class="selectedTimeSystem.cssClass"
|
||||||
@click.prevent="toggle"
|
@click.prevent.stop="showTimeSystemMenu"
|
||||||
>
|
>
|
||||||
<span class="c-button__label">{{ selectedTimeSystem.name }}</span>
|
<span class="c-button__label">{{ selectedTimeSystem.name }}</span>
|
||||||
</button>
|
</button>
|
||||||
<div
|
|
||||||
v-if="open"
|
|
||||||
class="c-menu"
|
|
||||||
>
|
|
||||||
<ul>
|
|
||||||
<li
|
|
||||||
v-for="timeSystem in timeSystems"
|
|
||||||
:key="timeSystem.key"
|
|
||||||
:class="timeSystem.cssClass"
|
|
||||||
@click="setTimeSystemFromView(timeSystem)"
|
|
||||||
>
|
|
||||||
{{ timeSystem.name }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import toggleMixin from '../../ui/mixins/toggle-mixin';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [toggleMixin],
|
|
||||||
inject: ['openmct', 'configuration'],
|
inject: ['openmct', 'configuration'],
|
||||||
data: function () {
|
data: function () {
|
||||||
let activeClock = this.openmct.time.clock();
|
let activeClock = this.openmct.time.clock();
|
||||||
@ -72,10 +54,26 @@ export default {
|
|||||||
this.openmct.time.on('clock', this.setViewFromClock);
|
this.openmct.time.on('clock', this.setViewFromClock);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
showTimeSystemMenu() {
|
||||||
|
const elementBoundingClientRect = this.$refs.timeSystemButton.getBoundingClientRect();
|
||||||
|
const x = elementBoundingClientRect.x;
|
||||||
|
const y = elementBoundingClientRect.y;
|
||||||
|
|
||||||
|
const menuOptions = {
|
||||||
|
placement: this.openmct.menus.menuPlacement.TOP_RIGHT
|
||||||
|
};
|
||||||
|
|
||||||
|
this.openmct.menus.showMenu(x, y, this.timeSystems, menuOptions);
|
||||||
|
},
|
||||||
getValidTimesystemsForClock(clock) {
|
getValidTimesystemsForClock(clock) {
|
||||||
return this.configuration.menuOptions
|
return this.configuration.menuOptions
|
||||||
.filter(menuOption => menuOption.clock === (clock && clock.key))
|
.filter(menuOption => menuOption.clock === (clock && clock.key))
|
||||||
.map(menuOption => JSON.parse(JSON.stringify(this.openmct.time.timeSystems.get(menuOption.timeSystem))));
|
.map(menuOption => {
|
||||||
|
const timeSystem = JSON.parse(JSON.stringify(this.openmct.time.timeSystems.get(menuOption.timeSystem)));
|
||||||
|
timeSystem.callBack = () => this.setTimeSystemFromView(timeSystem);
|
||||||
|
|
||||||
|
return timeSystem;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
setTimeSystemFromView(timeSystem) {
|
setTimeSystemFromView(timeSystem) {
|
||||||
if (timeSystem.key !== this.selectedTimeSystem.key) {
|
if (timeSystem.key !== this.selectedTimeSystem.key) {
|
||||||
|
@ -1,64 +1,103 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="c-toolbar">
|
<div class="c-toolbar">
|
||||||
<toolbar-select-menu
|
<div ref="fontSizeMenu"
|
||||||
class="menus-to-left menus-no-icon"
|
class="c-menu-button c-ctrl-wrapper c-ctrl-wrapper--menus-left"
|
||||||
:options="fontSizeMenuOptions"
|
>
|
||||||
@change="setFontSize"
|
<button
|
||||||
/>
|
class="c-icon-button c-button--menu icon-font-size"
|
||||||
<div class="c-toolbar__separator"></div>
|
@click.prevent.stop="showFontSizeMenu"
|
||||||
<toolbar-select-menu
|
>
|
||||||
class="menus-to-left menus-no-icon"
|
<span class="c-button__label">{{ fontSizeLabel }}</span>
|
||||||
:options="fontMenuOptions"
|
</button>
|
||||||
@change="setFont"
|
</div>
|
||||||
/>
|
<div ref="fontMenu"
|
||||||
|
class="c-menu-button c-ctrl-wrapper c-ctrl-wrapper--menus-left"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="c-icon-button c-button--menu icon-font"
|
||||||
|
@click.prevent.stop="showFontMenu"
|
||||||
|
>
|
||||||
|
<span class="c-button__label">{{ fontTypeLable }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ToolbarSelectMenu from '@/ui/toolbar/components/toolbar-select-menu.vue';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
FONT_SIZES,
|
FONT_SIZES,
|
||||||
FONTS
|
FONTS
|
||||||
} from '@/ui/inspector/styles/constants';
|
} from '@/ui/inspector/styles/constants';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
|
||||||
ToolbarSelectMenu
|
|
||||||
},
|
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
props: {
|
props: {
|
||||||
fontStyle: {
|
fontStyle: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true,
|
||||||
|
default: () => {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
fontMenuOptions() {
|
fontTypeLable() {
|
||||||
return {
|
const fontType = FONTS.find(f => f.value === this.fontStyle.font);
|
||||||
control: 'select-menu',
|
if (!fontType) {
|
||||||
icon: "icon-font",
|
return '??';
|
||||||
title: "Set font style",
|
}
|
||||||
value: this.fontStyle.font,
|
|
||||||
options: FONTS
|
return fontType.name || fontType.value || FONTS[0].name;
|
||||||
};
|
|
||||||
},
|
},
|
||||||
fontSizeMenuOptions() {
|
fontSizeLabel() {
|
||||||
return {
|
const fontSize = FONT_SIZES.find(f => f.value === this.fontStyle.fontSize);
|
||||||
control: 'select-menu',
|
if (!fontSize) {
|
||||||
icon: "icon-font-size",
|
return '??';
|
||||||
title: "Set font size",
|
}
|
||||||
value: this.fontStyle.fontSize,
|
|
||||||
options: FONT_SIZES
|
return fontSize.name || fontSize.value || FONT_SIZES[0].name;
|
||||||
};
|
},
|
||||||
|
fontMenu() {
|
||||||
|
return FONTS.map(font => {
|
||||||
|
return {
|
||||||
|
cssClass: font.cssClass || '',
|
||||||
|
name: font.name,
|
||||||
|
description: font.name,
|
||||||
|
callBack: () => this.setFont(font.value)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fontSizeMenu() {
|
||||||
|
return FONT_SIZES.map(fontSize => {
|
||||||
|
return {
|
||||||
|
cssClass: fontSize.cssClass || '',
|
||||||
|
name: fontSize.name,
|
||||||
|
description: fontSize.name,
|
||||||
|
callBack: () => this.setFontSize(fontSize.value)
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setFont(font) {
|
setFont(font) {
|
||||||
this.$emit('set-font-property', { font: font });
|
this.$emit('set-font-property', { font });
|
||||||
},
|
},
|
||||||
setFontSize(fontSize) {
|
setFontSize(fontSize) {
|
||||||
this.$emit('set-font-property', { fontSize: fontSize });
|
this.$emit('set-font-property', { fontSize });
|
||||||
|
},
|
||||||
|
showFontMenu() {
|
||||||
|
const elementBoundingClientRect = this.$refs.fontMenu.getBoundingClientRect();
|
||||||
|
const x = elementBoundingClientRect.x;
|
||||||
|
const y = elementBoundingClientRect.bottom;
|
||||||
|
|
||||||
|
this.openmct.menus.showMenu(x, y, this.fontMenu);
|
||||||
|
},
|
||||||
|
showFontSizeMenu() {
|
||||||
|
const elementBoundingClientRect = this.$refs.fontSizeMenu.getBoundingClientRect();
|
||||||
|
const x = elementBoundingClientRect.x;
|
||||||
|
const y = elementBoundingClientRect.bottom;
|
||||||
|
|
||||||
|
this.openmct.menus.showMenu(x, y, this.fontSizeMenu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
export const FONT_SIZES = [
|
export const FONT_SIZES = [
|
||||||
{
|
{
|
||||||
name: 'Default Size',
|
name: 'Default',
|
||||||
value: 'default'
|
value: 'default'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1,41 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="c-create-button--w">
|
<div ref="createButton"
|
||||||
|
class="c-create-button--w"
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
class="c-create-button c-button--menu c-button--major icon-plus"
|
class="c-create-button c-button--menu c-button--major icon-plus"
|
||||||
@click="open"
|
@click.prevent.stop="showCreateMenu"
|
||||||
>
|
>
|
||||||
<span class="c-button__label">Create</span>
|
<span class="c-button__label">Create</span>
|
||||||
</button>
|
</button>
|
||||||
<div
|
|
||||||
v-if="opened"
|
|
||||||
class="c-create-menu c-super-menu"
|
|
||||||
>
|
|
||||||
<div class="c-super-menu__menu">
|
|
||||||
<ul>
|
|
||||||
<li
|
|
||||||
v-for="(item, index) in sortedItems"
|
|
||||||
:key="index"
|
|
||||||
:class="item.class"
|
|
||||||
:aria-label="item.name"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
@mouseover="showItemDescription(item)"
|
|
||||||
@click="create(item)"
|
|
||||||
>
|
|
||||||
{{ item.name }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="c-super-menu__item-description">
|
|
||||||
<div :class="['l-item-description__icon', 'bg-' + selectedMenuItem.class]"></div>
|
|
||||||
<div class="l-item-description__name">
|
|
||||||
{{ selectedMenuItem.name }}
|
|
||||||
</div>
|
|
||||||
<div class="l-item-description__description">
|
|
||||||
{{ selectedMenuItem.title }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -53,10 +25,10 @@ export default {
|
|||||||
|
|
||||||
if (menuItem.creatable) {
|
if (menuItem.creatable) {
|
||||||
let menuItemTemplate = {
|
let menuItemTemplate = {
|
||||||
key: key,
|
cssClass: menuItem.cssClass,
|
||||||
name: menuItem.name,
|
name: menuItem.name,
|
||||||
class: menuItem.cssClass,
|
description: menuItem.description,
|
||||||
title: menuItem.description
|
callBack: () => this.create(key)
|
||||||
};
|
};
|
||||||
|
|
||||||
items.push(menuItemTemplate);
|
items.push(menuItemTemplate);
|
||||||
@ -82,30 +54,19 @@ export default {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
destroyed() {
|
|
||||||
document.removeEventListener('click', this.close);
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
open: function () {
|
showCreateMenu() {
|
||||||
if (this.opened) {
|
const elementBoundingClientRect = this.$refs.createButton.getBoundingClientRect();
|
||||||
return;
|
const x = elementBoundingClientRect.x;
|
||||||
}
|
const y = elementBoundingClientRect.y + elementBoundingClientRect.height;
|
||||||
|
|
||||||
this.opened = true;
|
const menuOptions = {
|
||||||
setTimeout(() => document.addEventListener('click', this.close));
|
menuClass: 'c-create-menu'
|
||||||
},
|
};
|
||||||
close: function () {
|
|
||||||
if (!this.opened) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.opened = false;
|
this.openmct.menus.showSuperMenu(x, y, this.sortedItems, menuOptions);
|
||||||
document.removeEventListener('click', this.close);
|
|
||||||
},
|
},
|
||||||
showItemDescription: function (menuItem) {
|
create(key) {
|
||||||
this.selectedMenuItem = menuItem;
|
|
||||||
},
|
|
||||||
create: function (item) {
|
|
||||||
// Hack for support. TODO: rewrite create action.
|
// Hack for support. TODO: rewrite create action.
|
||||||
// 1. Get contextual object from navigation
|
// 1. Get contextual object from navigation
|
||||||
// 2. Get legacy type from legacy api
|
// 2. Get legacy type from legacy api
|
||||||
@ -114,7 +75,7 @@ export default {
|
|||||||
return this.openmct.objects.get(this.openmct.router.path[0].identifier)
|
return this.openmct.objects.get(this.openmct.router.path[0].identifier)
|
||||||
.then((currentObject) => {
|
.then((currentObject) => {
|
||||||
let legacyContextualParent = this.convertToLegacy(currentObject);
|
let legacyContextualParent = this.convertToLegacy(currentObject);
|
||||||
let legacyType = this.openmct.$injector.get('typeService').getType(item.key);
|
let legacyType = this.openmct.$injector.get('typeService').getType(key);
|
||||||
let context = {
|
let context = {
|
||||||
key: "create",
|
key: "create",
|
||||||
domainObject: legacyContextualParent // should be same as parent object.
|
domainObject: legacyContextualParent // should be same as parent object.
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
.c-create-menu {
|
.c-create-menu {
|
||||||
max-height: 80vh;
|
max-height: 80vh;
|
||||||
max-width: 500px;
|
width: 500px;
|
||||||
min-height: 250px;
|
min-height: 250px;
|
||||||
z-index: 70;
|
z-index: 70;
|
||||||
|
|
||||||
|
@ -40,7 +40,11 @@ export default {
|
|||||||
let actions = actionsCollection.getVisibleActions();
|
let actions = actionsCollection.getVisibleActions();
|
||||||
let sortedActions = this.openmct.actions._groupAndSortActions(actions);
|
let sortedActions = this.openmct.actions._groupAndSortActions(actions);
|
||||||
|
|
||||||
this.openmct.menus.showMenu(event.clientX, event.clientY, sortedActions, this.onContextMenuDestroyed);
|
const menuOptions = {
|
||||||
|
onDestroy: this.onContextMenuDestroyed
|
||||||
|
};
|
||||||
|
|
||||||
|
this.openmct.menus.showMenu(event.clientX, event.clientY, sortedActions, menuOptions);
|
||||||
this.contextClickActive = true;
|
this.contextClickActive = true;
|
||||||
this.$emit('context-click-active', true);
|
this.$emit('context-click-active', true);
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user