Migrate to Vue 3 Migration Build (#6767)

* Replacing all instances of the new Vue() component creation pattern
* In Vue 3, components cannot be created on the fly and mounted off-DOM. The suggested fix from Vue is to use createApp, but in the context of Open MCT this means dozens of Vue apps being created and destroyed at any given moment. Instead, we have used a community hack for creating individual components.
* beforeDestroy() -> beforeUnmount()
* destroyed() -> unmounted()
* The addition of deep: true option on Array listeners is now required to detect Array changes
* Open MCT is now mounted on a child div instead of directly on document.body


---------

Co-authored-by: Scott Bell <scott@traclabs.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
This commit is contained in:
Jesse Mazzella 2023-07-19 11:22:23 -07:00 committed by GitHub
parent 42b545917c
commit 4885c816dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
248 changed files with 2688 additions and 2121 deletions

View File

@ -28,6 +28,8 @@ module.exports = {
} }
}, },
rules: { rules: {
'vue/no-v-for-template-key': 'off',
'vue/no-v-for-template-key-on-child': 'error',
'prettier/prettier': 'error', 'prettier/prettier': 'error',
'you-dont-need-lodash-underscore/omit': 'off', 'you-dont-need-lodash-underscore/omit': 'off',
'you-dont-need-lodash-underscore/throttle': 'off', 'you-dont-need-lodash-underscore/throttle': 'off',

View File

@ -67,7 +67,8 @@ const config = {
MCT: path.join(projectRootDir, 'src/MCT'), MCT: path.join(projectRootDir, 'src/MCT'),
testUtils: path.join(projectRootDir, 'src/utils/testUtils.js'), testUtils: path.join(projectRootDir, 'src/utils/testUtils.js'),
objectUtils: path.join(projectRootDir, 'src/api/objects/object-utils.js'), objectUtils: path.join(projectRootDir, 'src/api/objects/object-utils.js'),
utils: path.join(projectRootDir, 'src/utils') utils: path.join(projectRootDir, 'src/utils'),
vue: path.join(projectRootDir, 'node_modules/@vue/compat/dist/vue.esm-bundler.js'),
} }
}, },
plugins: [ plugins: [
@ -121,7 +122,15 @@ const config = {
}, },
{ {
test: /\.vue$/, test: /\.vue$/,
use: 'vue-loader' loader: 'vue-loader',
options: {
compilerOptions: {
whitespace: 'preserve',
compatConfig: {
MODE: 2
}
}
}
}, },
{ {
test: /\.html$/, test: /\.html$/,

View File

@ -25,11 +25,6 @@ module.exports = merge(common, {
'**/.*' // dotfiles and dotfolders '**/.*' // dotfiles and dotfolders
] ]
}, },
resolve: {
alias: {
vue: path.join(projectRootDir, 'node_modules/vue/dist/vue.js')
}
},
plugins: [ plugins: [
new webpack.DefinePlugin({ new webpack.DefinePlugin({
__OPENMCT_ROOT_RELATIVE__: '"dist/"' __OPENMCT_ROOT_RELATIVE__: '"dist/"'

View File

@ -13,11 +13,6 @@ const projectRootDir = path.resolve(__dirname, '..');
module.exports = merge(common, { module.exports = merge(common, {
mode: 'production', mode: 'production',
resolve: {
alias: {
vue: path.join(projectRootDir, 'node_modules/vue/dist/vue.min.js')
}
},
plugins: [ plugins: [
new webpack.DefinePlugin({ new webpack.DefinePlugin({
__OPENMCT_ROOT_RELATIVE__: '""' __OPENMCT_ROOT_RELATIVE__: '""'

View File

@ -41,7 +41,7 @@ test.describe('Form Validation Behavior', () => {
await page.goto('./', { waitUntil: 'domcontentloaded' }); await page.goto('./', { waitUntil: 'domcontentloaded' });
await page.click('button:has-text("Create")'); await page.click('button:has-text("Create")');
await page.click(':nth-match(:text("Folder"), 2)'); await page.getByRole('menuitem', { name: ' Folder' }).click();
// Fill in empty string into title and trigger validation with 'Tab' // Fill in empty string into title and trigger validation with 'Tab'
await page.click('text=Properties Title Notes >> input[type="text"]'); await page.click('text=Properties Title Notes >> input[type="text"]');

View File

@ -24,7 +24,7 @@ function SimpleVuePlugin() {
container.appendChild(vm.$mount().$el); container.appendChild(vm.$mount().$el);
}, },
destroy: function (container) { destroy: function (container) {
vm.$destroy(); //vm.$destroy();
} }
}; };
} }

View File

@ -92,7 +92,9 @@
} }
</style> </style>
</head> </head>
<body></body> <body>
<div id="app"></div>
</body>
<script> <script>
const THIRTY_SECONDS = 30 * 1000; const THIRTY_SECONDS = 30 * 1000;
const ONE_MINUTE = THIRTY_SECONDS * 2; const ONE_MINUTE = THIRTY_SECONDS * 2;

View File

@ -12,6 +12,8 @@
"@types/eventemitter3": "1.2.0", "@types/eventemitter3": "1.2.0",
"@types/jasmine": "4.3.4", "@types/jasmine": "4.3.4",
"@types/lodash": "4.14.192", "@types/lodash": "4.14.192",
"@vue/compat": "^3.1.0",
"@vue/compiler-sfc": "^3.1.0",
"babel-loader": "9.1.0", "babel-loader": "9.1.0",
"babel-plugin-istanbul": "6.1.1", "babel-plugin-istanbul": "6.1.1",
"codecov": "3.8.3", "codecov": "3.8.3",
@ -22,8 +24,8 @@
"d3-scale": "3.3.0", "d3-scale": "3.3.0",
"d3-selection": "3.0.0", "d3-selection": "3.0.0",
"eslint": "8.43.0", "eslint": "8.43.0",
"eslint-plugin-compat": "4.1.4",
"eslint-config-prettier": "8.8.0", "eslint-config-prettier": "8.8.0",
"eslint-plugin-compat": "4.1.4",
"eslint-plugin-playwright": "0.12.0", "eslint-plugin-playwright": "0.12.0",
"eslint-plugin-prettier": "4.2.1", "eslint-plugin-prettier": "4.2.1",
"eslint-plugin-vue": "9.15.0", "eslint-plugin-vue": "9.15.0",
@ -66,11 +68,10 @@
"style-loader": "3.3.3", "style-loader": "3.3.3",
"typescript": "5.1.3", "typescript": "5.1.3",
"uuid": "9.0.0", "uuid": "9.0.0",
"vue": "2.6.14", "vue": "^3.1.0",
"vue-eslint-parser": "9.3.1", "vue-eslint-parser": "9.3.1",
"vue-loader": "15.9.8",
"vue-template-compiler": "2.6.14",
"webpack": "5.88.0", "webpack": "5.88.0",
"vue-loader": "^16.0.0",
"webpack-cli": "5.1.1", "webpack-cli": "5.1.1",
"webpack-dev-server": "4.15.1", "webpack-dev-server": "4.15.1",
"webpack-merge": "5.9.0" "webpack-merge": "5.9.0"

View File

@ -343,7 +343,17 @@ define([
* @param {HTMLElement} [domElement] the DOM element in which to run * @param {HTMLElement} [domElement] the DOM element in which to run
* MCT; if undefined, MCT will be run in the body of the document * MCT; if undefined, MCT will be run in the body of the document
*/ */
MCT.prototype.start = function (domElement = document.body, isHeadlessMode = false) { MCT.prototype.start = function (
domElement = document.body.firstElementChild,
isHeadlessMode = false
) {
// Create element to mount Layout if it doesn't exist
if (domElement === null) {
domElement = document.createElement('div');
document.body.appendChild(domElement);
}
domElement.id = 'openmct-app';
if (this.types.get('layout') === undefined) { if (this.types.get('layout') === undefined) {
this.install( this.install(
this.plugins.DisplayLayout({ this.plugins.DisplayLayout({
@ -370,25 +380,30 @@ define([
*/ */
if (!isHeadlessMode) { if (!isHeadlessMode) {
const appLayout = new Vue({ const appLayout = Vue.createApp({
components: { components: {
Layout: Layout.default Layout: Layout.default
}, },
provide: { provide: {
openmct: this openmct: Vue.markRaw(this)
}, },
template: '<Layout ref="layout"></Layout>' template: '<Layout ref="layout"></Layout>'
}); });
domElement.appendChild(appLayout.$mount().$el); const component = appLayout.mount(domElement);
component.$nextTick(() => {
this.layout = appLayout.$refs.layout; this.layout = component.$refs.layout;
this.app = appLayout;
Browse(this); Browse(this);
} window.addEventListener('beforeunload', this.destroy);
this.router.start();
this.emit('start');
});
} else {
window.addEventListener('beforeunload', this.destroy); window.addEventListener('beforeunload', this.destroy);
this.router.start(); this.router.start();
this.emit('start'); this.emit('start');
}
}; };
MCT.prototype.startHeadless = function () { MCT.prototype.startHeadless = function () {

View File

@ -21,6 +21,7 @@
*****************************************************************************/ *****************************************************************************/
import objectUtils from '../objects/object-utils'; import objectUtils from '../objects/object-utils';
import CompositionProvider from './CompositionProvider'; import CompositionProvider from './CompositionProvider';
import { toRaw } from 'vue';
/** /**
* @typedef {import('../objects/ObjectAPI').DomainObject} DomainObject * @typedef {import('../objects/ObjectAPI').DomainObject} DomainObject
@ -167,7 +168,7 @@ export default class DefaultCompositionProvider extends CompositionProvider {
*/ */
add(parent, childId) { add(parent, childId) {
if (!this.includes(parent, childId)) { if (!this.includes(parent, childId)) {
const composition = structuredClone(parent.composition); const composition = structuredClone(toRaw(parent.composition));
composition.push(childId); composition.push(childId);
this.publicAPI.objects.mutate(parent, 'composition', composition); this.publicAPI.objects.mutate(parent, 'composition', composition);
} }

View File

@ -10,8 +10,7 @@ import TextAreaField from './components/controls/TextAreaField.vue';
import TextField from './components/controls/TextField.vue'; import TextField from './components/controls/TextField.vue';
import ToggleSwitchField from './components/controls/ToggleSwitchField.vue'; import ToggleSwitchField from './components/controls/ToggleSwitchField.vue';
import Vue from 'vue'; import mount from 'utils/mount';
export const DEFAULT_CONTROLS_MAP = { export const DEFAULT_CONTROLS_MAP = {
autocomplete: AutoCompleteField, autocomplete: AutoCompleteField,
checkbox: CheckBoxField, checkbox: CheckBoxField,
@ -69,11 +68,12 @@ export default class FormControl {
*/ */
_getControlViewProvider(control) { _getControlViewProvider(control) {
const self = this; const self = this;
let rowComponent; let _destroy = null;
return { return {
show(element, model, onChange) { show(element, model, onChange) {
rowComponent = new Vue({ const { vNode, destroy } = mount(
{
el: element, el: element,
components: { components: {
FormControlComponent: DEFAULT_CONTROLS_MAP[control] FormControlComponent: DEFAULT_CONTROLS_MAP[control]
@ -88,12 +88,20 @@ export default class FormControl {
}; };
}, },
template: `<FormControlComponent :model="model" @onChange="onChange"></FormControlComponent>` template: `<FormControlComponent :model="model" @onChange="onChange"></FormControlComponent>`
}); },
{
element,
app: self.openmct.app
}
);
_destroy = destroy;
return rowComponent; return vNode;
}, },
destroy() { destroy() {
rowComponent.$destroy(); if (_destroy) {
_destroy();
}
} }
}; };
} }

View File

@ -23,8 +23,8 @@
import FormController from './FormController'; import FormController from './FormController';
import FormProperties from './components/FormProperties.vue'; import FormProperties from './components/FormProperties.vue';
import Vue from 'vue';
import _ from 'lodash'; import _ from 'lodash';
import mount from 'utils/mount';
export default class FormsAPI { export default class FormsAPI {
constructor(openmct) { constructor(openmct) {
@ -156,7 +156,8 @@ export default class FormsAPI {
formCancel = onFormAction(reject); formCancel = onFormAction(reject);
}); });
const vm = new Vue({ const { destroy } = mount(
{
components: { FormProperties }, components: { FormProperties },
provide: { provide: {
openmct: self.openmct openmct: self.openmct
@ -171,10 +172,12 @@ export default class FormsAPI {
}, },
template: template:
'<FormProperties :model="formStructure" @onChange="onChange" @onCancel="onCancel" @onSave="onSave"></FormProperties>' '<FormProperties :model="formStructure" @onChange="onChange" @onCancel="onCancel" @onSave="onSave"></FormProperties>'
}).$mount(); },
{
const formElement = vm.$el; element,
element.append(formElement); app: self.openmct.app
}
);
function onFormPropertyChange(data) { function onFormPropertyChange(data) {
if (onChange) { if (onChange) {
@ -195,8 +198,7 @@ export default class FormsAPI {
function onFormAction(callback) { function onFormAction(callback) {
return () => { return () => {
formElement.remove(); destroy();
vm.$destroy();
if (callback) { if (callback) {
callback(changes); callback(changes);

View File

@ -141,7 +141,7 @@ export default {
}, },
methods: { methods: {
onChange(data) { onChange(data) {
this.$set(this.invalidProperties, data.model.key, data.invalid); this.invalidProperties[data.model.key] = data.invalid;
this.$emit('onChange', data); this.$emit('onChange', data);
}, },

View File

@ -26,9 +26,7 @@
{{ row.name }} {{ row.name }}
</div> </div>
<div class="c-form-row__state-indicator" :class="reqClass"></div> <div class="c-form-row__state-indicator" :class="reqClass"></div>
<div v-if="row.control" class="c-form-row__controls"> <div v-if="row.control" ref="rowElement" class="c-form-row__controls"></div>
<div ref="rowElement"></div>
</div>
</div> </div>
</template> </template>
@ -91,7 +89,7 @@ export default {
this.formControl.show(this.$refs.rowElement, this.row, this.onChange); this.formControl.show(this.$refs.rowElement, this.row, this.onChange);
}, },
destroyed() { unmounted() {
const destroy = this.formControl.destroy; const destroy = this.formControl.destroy;
if (destroy) { if (destroy) {
destroy(); destroy();

View File

@ -166,7 +166,7 @@ export default {
this.options = this.model.options; this.options = this.model.options;
} }
}, },
destroyed() { unmounted() {
document.body.removeEventListener('click', this.handleOutsideClick); document.body.removeEventListener('click', this.handleOutsideClick);
}, },
methods: { methods: {

View File

@ -20,17 +20,17 @@
at runtime from the About dialog for additional information. at runtime from the About dialog for additional information.
--> -->
<template> <template>
<div class="c-menu" :class="options.menuClass"> <div class="c-menu" :class="options.menuClass" :style="styleObject">
<ul v-if="options.actions.length && options.actions[0].length" role="menu"> <ul v-if="options.actions.length && options.actions[0].length" role="menu">
<template v-for="(actionGroups, index) in options.actions"> <template v-for="(actionGroups, index) in options.actions" :key="index">
<div :key="index" role="group"> <div role="group">
<li <li
v-for="action in actionGroups" v-for="action in actionGroups"
:key="action.name" :key="action.name"
role="menuitem" role="menuitem"
:class="[action.cssClass, action.isDisabled ? 'disabled' : '']" :class="[action.cssClass, action.isDisabled ? 'disabled' : '']"
:title="action.description" :title="action.description"
:data-testid="action.testId || false" :data-testid="action.testId || null"
@click="action.onItemClicked" @click="action.onItemClicked"
> >
{{ action.name }} {{ action.name }}
@ -42,8 +42,8 @@
class="c-menu__section-separator" class="c-menu__section-separator"
></div> ></div>
<li v-if="actionGroups.length === 0" :key="index">No actions defined.</li> <li v-if="actionGroups.length === 0" :key="index">No actions defined.</li>
</div></template </div>
> </template>
</ul> </ul>
<ul v-else role="menu"> <ul v-else role="menu">
@ -53,7 +53,7 @@
role="menuitem" role="menuitem"
:class="[action.cssClass, action.isDisabled ? 'disabled' : '']" :class="[action.cssClass, action.isDisabled ? 'disabled' : '']"
:title="action.description" :title="action.description"
:data-testid="action.testId || false" :data-testid="action.testId || null"
@click="action.onItemClicked" @click="action.onItemClicked"
> >
{{ action.name }} {{ action.name }}
@ -64,7 +64,9 @@
</template> </template>
<script> <script>
import popupMenuMixin from '../mixins/popupMenuMixin';
export default { export default {
mixins: [popupMenuMixin],
inject: ['options'] inject: ['options']
}; };
</script> </script>

View File

@ -20,21 +20,21 @@
at runtime from the About dialog for additional information. at runtime from the About dialog for additional information.
--> -->
<template> <template>
<div class="c-menu" :class="[options.menuClass, 'c-super-menu']"> <div class="c-menu" :class="[options.menuClass, 'c-super-menu']" :style="styleObject">
<ul <ul
v-if="options.actions.length && options.actions[0].length" v-if="options.actions.length && options.actions[0].length"
role="menu" role="menu"
class="c-super-menu__menu" class="c-super-menu__menu"
> >
<template v-for="(actionGroups, index) in options.actions"> <template v-for="(actionGroups, index) in options.actions" :key="index">
<div :key="index" role="group"> <div role="group">
<li <li
v-for="action in actionGroups" v-for="action in actionGroups"
:key="action.name" :key="action.name"
role="menuitem" role="menuitem"
:class="[action.cssClass, action.isDisabled ? 'disabled' : '']" :class="[action.cssClass, action.isDisabled ? 'disabled' : '']"
:title="action.description" :title="action.description"
:data-testid="action.testId || false" :data-testid="action.testId || null"
@click="action.onItemClicked" @click="action.onItemClicked"
@mouseover="toggleItemDescription(action)" @mouseover="toggleItemDescription(action)"
@mouseleave="toggleItemDescription()" @mouseleave="toggleItemDescription()"
@ -58,7 +58,8 @@
:key="action.name" :key="action.name"
role="menuitem" role="menuitem"
:class="action.cssClass" :class="action.cssClass"
:data-testid="action.testId || false" :title="action.description"
:data-testid="action.testId || null"
@click="action.onItemClicked" @click="action.onItemClicked"
@mouseover="toggleItemDescription(action)" @mouseover="toggleItemDescription(action)"
@mouseleave="toggleItemDescription()" @mouseleave="toggleItemDescription()"
@ -79,9 +80,10 @@
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import popupMenuMixin from '../mixins/popupMenuMixin';
export default { export default {
mixins: [popupMenuMixin],
inject: ['options'], inject: ['options'],
data: function () { data: function () {
return { return {

View File

@ -22,7 +22,8 @@
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 SuperMenuComponent from './components/SuperMenu.vue';
import Vue from 'vue'; import { h } from 'vue';
import mount from 'utils/mount';
export const MENU_PLACEMENT = { export const MENU_PLACEMENT = {
TOP: 'top', TOP: 'top',
@ -52,137 +53,67 @@ class Menu extends EventEmitter {
dismiss() { dismiss() {
this.emit('destroy'); this.emit('destroy');
document.body.removeChild(this.component.$el); if (this.destroy) {
document.removeEventListener('click', this.dismiss); this.destroy();
this.component.$destroy(); this.destroy = null;
} }
document.removeEventListener('click', this.dismiss);
show() {
this.component.$mount();
document.body.appendChild(this.component.$el);
let position = this._calculatePopupPosition(this.component.$el);
this.component.$el.style.left = `${position.x}px`;
this.component.$el.style.top = `${position.y}px`;
document.addEventListener('click', this.dismiss);
} }
showMenu() { showMenu() {
this.component = new Vue({ if (this.destroy) {
components: { return;
MenuComponent }
const { vNode, destroy } = mount({
render() {
return h(MenuComponent);
}, },
provide: { provide: {
options: this.options options: this.options
}, },
template: '<menu-component />' // TODO: Remove this exception upon full migration to Vue 3
// https://v3-migration.vuejs.org/breaking-changes/render-function-api.html#render-function-argument
compatConfig: {
RENDER_FUNCTION: false
}
}); });
this.el = vNode.el;
this.destroy = destroy;
this.show(); this.show();
} }
showSuperMenu() { showSuperMenu() {
this.component = new Vue({ const { vNode, destroy } = mount({
components: { data() {
SuperMenuComponent return {
top: '0px',
left: '0px'
};
},
render() {
return h(SuperMenuComponent);
}, },
provide: { provide: {
options: this.options options: this.options
}, },
template: '<super-menu-component />' // TODO: Remove this exception upon full migration to Vue 3
// https://v3-migration.vuejs.org/breaking-changes/render-function-api.html#render-function-argument
compatConfig: {
RENDER_FUNCTION: false
}
}); });
this.el = vNode.el;
this.destroy = destroy;
this.show(); this.show();
} }
/** show() {
* @private document.body.appendChild(this.el);
*/ document.addEventListener('click', this.dismiss);
_calculatePopupPosition(menuElement) {
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 overflowY = eventPosY + menuDimensions.height - document.body.clientHeight;
if (overflowX > 0) {
eventPosX = eventPosX - overflowX;
}
if (overflowY > 0) {
eventPosY = eventPosY - overflowY;
}
if (eventPosX < 0) {
eventPosX = 0;
}
if (eventPosY < 0) {
eventPosY = 0;
}
return {
x: eventPosX,
y: eventPosY
};
} }
} }

View File

@ -0,0 +1,111 @@
import { MENU_PLACEMENT } from '../menu';
export default {
methods: {
/**
* @private
*/
_calculatePopupPosition(menuElement) {
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 overflowY = eventPosY + menuDimensions.height - document.body.clientHeight;
if (overflowX > 0) {
eventPosX = eventPosX - overflowX;
}
if (overflowY > 0) {
eventPosY = eventPosY - overflowY;
}
if (eventPosX < 0) {
eventPosX = 0;
}
if (eventPosY < 0) {
eventPosY = 0;
}
return {
x: eventPosX,
y: eventPosY
};
}
},
mounted() {
this.$nextTick(() => {
const position = this._calculatePopupPosition(this.$el);
this.top = position.y;
this.left = position.x;
});
},
data() {
return {
top: '0px',
left: '0px'
};
},
computed: {
styleObject() {
return {
top: `${this.top}px`,
left: `${this.left}px`
};
}
}
};

View File

@ -159,6 +159,9 @@ class InMemorySearchProvider {
return pendingQuery.promise; return pendingQuery.promise;
} }
/**
* @private
*/
#localQueryFallBack({ queryId, searchType, query, maxResults }) { #localQueryFallBack({ queryId, searchType, query, maxResults }) {
if (searchType === this.searchTypes.OBJECTS) { if (searchType === this.searchTypes.OBJECTS) {
return this.localSearchForObjects(queryId, query, maxResults); return this.localSearchForObjects(queryId, query, maxResults);

View File

@ -96,12 +96,26 @@ class MutableDomainObject {
//Emit events specific to properties affected //Emit events specific to properties affected
let parentPropertiesList = path.split('.'); let parentPropertiesList = path.split('.');
for (let index = parentPropertiesList.length; index > 0; index--) { for (let index = parentPropertiesList.length; index > 0; index--) {
let pathToThisProperty = parentPropertiesList.slice(0, index);
let parentPropertyPath = parentPropertiesList.slice(0, index).join('.'); let parentPropertyPath = parentPropertiesList.slice(0, index).join('.');
this._globalEventEmitter.emit( this._globalEventEmitter.emit(
qualifiedEventName(this, parentPropertyPath), qualifiedEventName(this, parentPropertyPath),
_.get(this, parentPropertyPath), _.get(this, parentPropertyPath),
_.get(oldModel, parentPropertyPath) _.get(oldModel, parentPropertyPath)
); );
const lastPathElement = parentPropertiesList[index - 1];
// Also emit an event for the array whose element has changed so developers do not need to listen to every element of the array.
if (lastPathElement.endsWith(']')) {
const arrayPathElement = lastPathElement.substring(0, lastPathElement.lastIndexOf('['));
pathToThisProperty[index - 1] = arrayPathElement;
const pathToArrayString = pathToThisProperty.join('.');
this._globalEventEmitter.emit(
qualifiedEventName(this, pathToArrayString),
_.get(this, pathToArrayString),
_.get(oldModel, pathToArrayString)
);
}
} }
//TODO: Emit events for listeners of child properties when parent changes. //TODO: Emit events for listeners of child properties when parent changes.

View File

@ -1,10 +1,10 @@
import DialogComponent from './components/DialogComponent.vue'; import DialogComponent from './components/DialogComponent.vue';
import Overlay from './Overlay'; import Overlay from './Overlay';
import Vue from 'vue'; import mount from 'utils/mount';
class Dialog extends Overlay { class Dialog extends Overlay {
constructor({ iconClass, message, title, hint, timestamp, ...options }) { constructor({ iconClass, message, title, hint, timestamp, ...options }) {
let component = new Vue({ const { vNode, destroy } = mount({
components: { components: {
DialogComponent: DialogComponent DialogComponent: DialogComponent
}, },
@ -16,17 +16,17 @@ class Dialog extends Overlay {
timestamp timestamp
}, },
template: '<dialog-component></dialog-component>' template: '<dialog-component></dialog-component>'
}).$mount(); });
super({ super({
element: component.$el, element: vNode.el,
size: 'fit', size: 'fit',
dismissable: false, dismissable: false,
...options ...options
}); });
this.once('destroy', () => { this.once('destroy', () => {
component.$destroy(); destroy();
}); });
} }
} }

View File

@ -1,6 +1,6 @@
import OverlayComponent from './components/OverlayComponent.vue'; import OverlayComponent from './components/OverlayComponent.vue';
import EventEmitter from 'EventEmitter'; import EventEmitter from 'EventEmitter';
import Vue from 'vue'; import mount from 'utils/mount';
const cssClasses = { const cssClasses = {
large: 'l-overlay-large', large: 'l-overlay-large',
@ -28,7 +28,8 @@ class Overlay extends EventEmitter {
this.autoHide = autoHide; this.autoHide = autoHide;
this.dismissable = dismissable !== false; this.dismissable = dismissable !== false;
this.component = new Vue({ const { destroy } = mount(
{
components: { components: {
OverlayComponent: OverlayComponent OverlayComponent: OverlayComponent
}, },
@ -39,7 +40,13 @@ class Overlay extends EventEmitter {
dismissable: this.dismissable dismissable: this.dismissable
}, },
template: '<overlay-component></overlay-component>' template: '<overlay-component></overlay-component>'
}); },
{
element: this.container
}
);
this.destroy = destroy;
if (onDestroy) { if (onDestroy) {
this.once('destroy', onDestroy); this.once('destroy', onDestroy);
@ -53,7 +60,7 @@ class Overlay extends EventEmitter {
dismiss() { dismiss() {
this.emit('destroy'); this.emit('destroy');
document.body.removeChild(this.container); document.body.removeChild(this.container);
this.component.$destroy(); this.destroy();
} }
//Ensures that any callers are notified that the overlay is dismissed //Ensures that any callers are notified that the overlay is dismissed
@ -67,7 +74,6 @@ class Overlay extends EventEmitter {
**/ **/
show() { show() {
document.body.appendChild(this.container); document.body.appendChild(this.container);
this.container.appendChild(this.component.$mount().$el);
} }
} }

View File

@ -1,9 +1,8 @@
import ProgressDialogComponent from './components/ProgressDialogComponent.vue'; import ProgressDialogComponent from './components/ProgressDialogComponent.vue';
import Overlay from './Overlay'; import Overlay from './Overlay';
import Vue from 'vue'; import mount from 'utils/mount';
let component; let component;
class ProgressDialog extends Overlay { class ProgressDialog extends Overlay {
constructor({ constructor({
progressPerc, progressPerc,
@ -15,7 +14,7 @@ class ProgressDialog extends Overlay {
timestamp, timestamp,
...options ...options
}) { }) {
component = new Vue({ const { vNode, destroy } = mount({
components: { components: {
ProgressDialogComponent: ProgressDialogComponent ProgressDialogComponent: ProgressDialogComponent
}, },
@ -35,17 +34,18 @@ class ProgressDialog extends Overlay {
}; };
}, },
template: '<progress-dialog-component :model="model"></progress-dialog-component>' template: '<progress-dialog-component :model="model"></progress-dialog-component>'
}).$mount(); });
component = vNode.componentInstance;
super({ super({
element: component.$el, element: vNode.el,
size: 'fit', size: 'fit',
dismissable: false, dismissable: false,
...options ...options
}); });
this.once('destroy', () => { this.once('destroy', () => {
component.$destroy(); destroy();
}); });
} }

View File

@ -22,7 +22,7 @@
import SelectionComponent from './components/SelectionComponent.vue'; import SelectionComponent from './components/SelectionComponent.vue';
import Overlay from './Overlay'; import Overlay from './Overlay';
import Vue from 'vue'; import mount from 'utils/mount';
class Selection extends Overlay { class Selection extends Overlay {
constructor({ constructor({
@ -34,7 +34,7 @@ class Selection extends Overlay {
currentSelection, currentSelection,
...options ...options
}) { }) {
let component = new Vue({ const { vNode, destroy } = mount({
components: { components: {
SelectionComponent: SelectionComponent SelectionComponent: SelectionComponent
}, },
@ -47,7 +47,9 @@ class Selection extends Overlay {
currentSelection currentSelection
}, },
template: '<selection-component></selection-component>' template: '<selection-component></selection-component>'
}).$mount(); });
const component = vNode.componentInstance;
super({ super({
element: component.$el, element: component.$el,
@ -59,7 +61,7 @@ class Selection extends Overlay {
}); });
this.once('destroy', () => { this.once('destroy', () => {
component.$destroy(); destroy();
}); });
} }
} }

View File

@ -22,7 +22,7 @@
import TooltipComponent from './components/TooltipComponent.vue'; import TooltipComponent from './components/TooltipComponent.vue';
import EventEmitter from 'EventEmitter'; import EventEmitter from 'EventEmitter';
import Vue from 'vue'; import mount from 'utils/mount';
class Tooltip extends EventEmitter { class Tooltip extends EventEmitter {
constructor( constructor(
@ -34,9 +34,7 @@ class Tooltip extends EventEmitter {
) { ) {
super(); super();
this.container = document.createElement('div'); const { vNode, destroy } = mount({
this.component = new Vue({
components: { components: {
TooltipComponent: TooltipComponent TooltipComponent: TooltipComponent
}, },
@ -48,6 +46,9 @@ class Tooltip extends EventEmitter {
template: '<tooltip-component toolTipText="toolTipText"></tooltip-component>' template: '<tooltip-component toolTipText="toolTipText"></tooltip-component>'
}); });
this.component = vNode.componentInstance;
this._destroy = destroy;
this.isActive = null; this.isActive = null;
} }
@ -55,8 +56,7 @@ class Tooltip extends EventEmitter {
if (!this.isActive) { if (!this.isActive) {
return; return;
} }
document.body.removeChild(this.container); this._destroy();
this.component.$destroy();
this.isActive = false; this.isActive = false;
} }
@ -64,8 +64,7 @@ class Tooltip extends EventEmitter {
* @private * @private
**/ **/
show() { show() {
document.body.appendChild(this.container); document.body.appendChild(this.component.$el);
this.container.appendChild(this.component.$mount().$el);
this.isActive = true; this.isActive = true;
} }
} }

View File

@ -1,5 +1,7 @@
.c-tooltip-wrapper { .c-tooltip-wrapper {
max-width: 200px; max-width: 200px;
height: auto;
width: auto;
padding: $interiorMargin; padding: $interiorMargin;
} }

View File

@ -21,13 +21,16 @@
*****************************************************************************/ *****************************************************************************/
import EventEmitter from 'EventEmitter'; import EventEmitter from 'EventEmitter';
import { markRaw } from 'vue';
export default class LADTableConfiguration extends EventEmitter { export default class LADTableConfiguration extends EventEmitter {
constructor(domainObject, openmct) { constructor(domainObject, openmct) {
super(); super();
this.domainObject = domainObject; this.domainObject = domainObject;
this.openmct = openmct;
// Prevent Vue from making this a Proxy, otherwise
// it cannot access any private methods (like #mutate()).
this.openmct = markRaw(openmct);
this.objectMutated = this.objectMutated.bind(this); this.objectMutated = this.objectMutated.bind(this);
this.unlistenFromMutation = openmct.objects.observe( this.unlistenFromMutation = openmct.objects.observe(

View File

@ -21,7 +21,7 @@
*****************************************************************************/ *****************************************************************************/
import LADTableConfigurationComponent from './components/LADTableConfiguration.vue'; import LADTableConfigurationComponent from './components/LADTableConfiguration.vue';
import Vue from 'vue'; import mount from 'utils/mount';
export default function LADTableConfigurationViewProvider(openmct) { export default function LADTableConfigurationViewProvider(openmct) {
return { return {
@ -37,11 +37,12 @@ export default function LADTableConfigurationViewProvider(openmct) {
return object?.type === 'LadTable' || object?.type === 'LadTableSet'; return object?.type === 'LadTable' || object?.type === 'LadTableSet';
}, },
view(selection) { view(selection) {
let component; let _destroy = null;
return { return {
show(element) { show(element) {
component = new Vue({ const { destroy } = mount(
{
el: element, el: element,
components: { components: {
LADTableConfiguration: LADTableConfigurationComponent LADTableConfiguration: LADTableConfigurationComponent
@ -50,15 +51,20 @@ export default function LADTableConfigurationViewProvider(openmct) {
openmct openmct
}, },
template: '<LADTableConfiguration />' template: '<LADTableConfiguration />'
}); },
{
app: openmct.app,
element
}
);
_destroy = destroy;
}, },
priority() { priority() {
return 1; return 1;
}, },
destroy() { destroy() {
if (component) { if (_destroy) {
component.$destroy(); _destroy();
component = undefined;
} }
} }
}; };

View File

@ -22,20 +22,22 @@
import LadTable from './components/LADTable.vue'; import LadTable from './components/LADTable.vue';
import LADTableConfiguration from './LADTableConfiguration'; import LADTableConfiguration from './LADTableConfiguration';
import Vue from 'vue'; import mount from 'utils/mount';
export default class LADTableView { export default class LADTableView {
constructor(openmct, domainObject, objectPath) { constructor(openmct, domainObject, objectPath) {
this.openmct = openmct; this.openmct = openmct;
this.domainObject = domainObject; this.domainObject = domainObject;
this.objectPath = objectPath; this.objectPath = objectPath;
this.component = undefined; this.component = null;
this._destroy = null;
} }
show(element) { show(element) {
let ladTableConfiguration = new LADTableConfiguration(this.domainObject, this.openmct); let ladTableConfiguration = new LADTableConfiguration(this.domainObject, this.openmct);
this.component = new Vue({ const { vNode, destroy } = mount(
{
el: element, el: element,
components: { components: {
LadTable LadTable
@ -53,7 +55,14 @@ export default class LADTableView {
}, },
template: template:
'<lad-table ref="ladTable" :domain-object="domainObject" :object-path="objectPath"></lad-table>' '<lad-table ref="ladTable" :domain-object="domainObject" :object-path="objectPath"></lad-table>'
}); },
{
app: this.openmct.app,
element
}
);
this.component = vNode.componentInstance;
this._destroy = destroy;
} }
getViewContext() { getViewContext() {
@ -64,8 +73,9 @@ export default class LADTableView {
return this.component.$refs.ladTable.getViewContext(); return this.component.$refs.ladTable.getViewContext();
} }
destroy(element) { destroy() {
this.component.$destroy(); if (this._destroy) {
this.component = undefined; this._destroy();
}
} }
} }

View File

@ -22,20 +22,22 @@
import LadTableSet from './components/LadTableSet.vue'; import LadTableSet from './components/LadTableSet.vue';
import LADTableConfiguration from './LADTableConfiguration'; import LADTableConfiguration from './LADTableConfiguration';
import Vue from 'vue'; import mount from 'utils/mount';
export default class LadTableSetView { export default class LadTableSetView {
constructor(openmct, domainObject, objectPath) { constructor(openmct, domainObject, objectPath) {
this.openmct = openmct; this.openmct = openmct;
this.domainObject = domainObject; this.domainObject = domainObject;
this.objectPath = objectPath; this.objectPath = objectPath;
this.component = undefined; this._destroy = null;
this.component = null;
} }
show(element) { show(element) {
let ladTableConfiguration = new LADTableConfiguration(this.domainObject, this.openmct); let ladTableConfiguration = new LADTableConfiguration(this.domainObject, this.openmct);
this.component = new Vue({ const { vNode, destroy } = mount(
{
el: element, el: element,
components: { components: {
LadTableSet LadTableSet
@ -52,7 +54,14 @@ export default class LadTableSetView {
}; };
}, },
template: '<lad-table-set ref="ladTableSet" :domain-object="domainObject"></lad-table-set>' template: '<lad-table-set ref="ladTableSet" :domain-object="domainObject"></lad-table-set>'
}); },
{
app: this.openmct.app,
element
}
);
this._destroy = destroy;
this.component = vNode.componentInstance;
} }
getViewContext() { getViewContext() {
@ -63,8 +72,9 @@ export default class LadTableSetView {
return this.component.$refs.ladTableSet.getViewContext(); return this.component.$refs.ladTableSet.getViewContext();
} }
destroy(element) { destroy() {
this.component.$destroy(); if (this._destroy) {
this.component = undefined; this._destroy();
}
} }
} }

View File

@ -190,7 +190,7 @@ export default {
this.previewAction = new PreviewAction(this.openmct); this.previewAction = new PreviewAction(this.openmct);
this.previewAction.on('isVisible', this.togglePreviewState); this.previewAction.on('isVisible', this.togglePreviewState);
}, },
destroyed() { unmounted() {
this.openmct.time.off('timeSystem', this.updateTimeSystem); this.openmct.time.off('timeSystem', this.updateTimeSystem);
this.telemetryCollection.off('add', this.setLatestValues); this.telemetryCollection.off('add', this.setLatestValues);
this.telemetryCollection.off('clear', this.resetValues); this.telemetryCollection.off('clear', this.resetValues);

View File

@ -49,7 +49,7 @@
</template> </template>
<script> <script>
import Vue from 'vue'; import Vue, { toRaw } from 'vue';
import LadRow from './LADRow.vue'; import LadRow from './LADRow.vue';
import StalenessUtils from '@/utils/staleness'; import StalenessUtils from '@/utils/staleness';
@ -139,7 +139,7 @@ export default {
); );
this.initializeViewActions(); this.initializeViewActions();
}, },
destroyed() { unmounted() {
this.ladTableConfiguration.off('change', this.handleConfigurationChange); this.ladTableConfiguration.off('change', this.handleConfigurationChange);
this.composition.off('add', this.addItem); this.composition.off('add', this.addItem);
@ -191,7 +191,7 @@ export default {
reorder(reorderPlan) { reorder(reorderPlan) {
const oldItems = this.items.slice(); const oldItems = this.items.slice();
reorderPlan.forEach((reorderEvent) => { reorderPlan.forEach((reorderEvent) => {
this.$set(this.items, reorderEvent.newIndex, oldItems[reorderEvent.oldIndex]); this.items[reorderEvent.newIndex] = oldItems[reorderEvent.oldIndex];
}); });
}, },
metadataHasUnits(valueMetadatas) { metadataHasUnits(valueMetadatas) {
@ -230,7 +230,7 @@ export default {
}; };
}, },
toggleFixedLayout() { toggleFixedLayout() {
const config = structuredClone(this.configuration); const config = structuredClone(toRaw(this.configuration));
config.isFixedLayout = !this.configuration.isFixedLayout; config.isFixedLayout = !this.configuration.isFixedLayout;
this.ladTableConfiguration.updateConfiguration(config); this.ladTableConfiguration.updateConfiguration(config);

View File

@ -87,7 +87,7 @@ export default {
this.composition.load(); this.composition.load();
}, },
destroyed() { unmounted() {
this.ladTableConfiguration.destroy(); this.ladTableConfiguration.destroy();
this.openmct.editor.off('isEditing', this.toggleEdit); this.openmct.editor.off('isEditing', this.toggleEdit);
@ -126,7 +126,7 @@ export default {
ladTable.domainObject = domainObject; ladTable.domainObject = domainObject;
ladTable.key = this.openmct.objects.makeKeyString(domainObject.identifier); ladTable.key = this.openmct.objects.makeKeyString(domainObject.identifier);
this.$set(this.ladTelemetryObjects, ladTable.key, []); this.ladTelemetryObjects[ladTable.key] = [];
this.ladTableObjects.push(ladTable); this.ladTableObjects.push(ladTable);
const composition = this.openmct.composition.get(ladTable.domainObject); const composition = this.openmct.composition.get(ladTable.domainObject);
@ -165,7 +165,7 @@ export default {
const telemetryObjects = this.ladTelemetryObjects[ladTable.key]; const telemetryObjects = this.ladTelemetryObjects[ladTable.key];
telemetryObjects.push(telemetryObject); telemetryObjects.push(telemetryObject);
this.$set(this.ladTelemetryObjects, ladTable.key, telemetryObjects); this.ladTelemetryObjects[ladTable.key] = telemetryObjects;
this.shouldShowUnitsCheckbox(); this.shouldShowUnitsCheckbox();
}; };
@ -179,7 +179,7 @@ export default {
); );
telemetryObjects.splice(index, 1); telemetryObjects.splice(index, 1);
this.$set(this.ladTelemetryObjects, ladTable.key, telemetryObjects); this.ladTelemetryObjects[ladTable.key] = telemetryObjects;
this.shouldShowUnitsCheckbox(); this.shouldShowUnitsCheckbox();
}; };
@ -220,7 +220,7 @@ export default {
} }
if (showUnitsCheckbox && this.headers.units === undefined) { if (showUnitsCheckbox && this.headers.units === undefined) {
this.$set(this.headers, 'units', 'Units'); this.headers.units = 'Units';
} }
if (!showUnitsCheckbox && this.headers?.units) { if (!showUnitsCheckbox && this.headers?.units) {

View File

@ -33,8 +33,8 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<template v-for="ladTable in ladTableObjects"> <template v-for="ladTable in ladTableObjects" :key="ladTable.key">
<tr :key="ladTable.key" class="c-table__group-header js-lad-table-set__table-headers"> <tr class="c-table__group-header js-lad-table-set__table-headers">
<td colspan="10"> <td colspan="10">
{{ ladTable.domainObject.name }} {{ ladTable.domainObject.name }}
</td> </td>
@ -125,7 +125,7 @@ export default {
this.stalenessSubscription = {}; this.stalenessSubscription = {};
}, },
destroyed() { unmounted() {
this.ladTableConfiguration.off('change', this.handleConfigurationChange); this.ladTableConfiguration.off('change', this.handleConfigurationChange);
this.composition.off('add', this.addLadTable); this.composition.off('add', this.addLadTable);
this.composition.off('remove', this.removeLadTable); this.composition.off('remove', this.removeLadTable);
@ -147,7 +147,7 @@ export default {
ladTable.key = this.openmct.objects.makeKeyString(domainObject.identifier); ladTable.key = this.openmct.objects.makeKeyString(domainObject.identifier);
ladTable.objectPath = [domainObject, ...this.objectPath]; ladTable.objectPath = [domainObject, ...this.objectPath];
this.$set(this.ladTelemetryObjects, ladTable.key, []); this.ladTelemetryObjects[ladTable.key] = [];
this.ladTableObjects.push(ladTable); this.ladTableObjects.push(ladTable);
let composition = this.openmct.composition.get(ladTable.domainObject); let composition = this.openmct.composition.get(ladTable.domainObject);
@ -201,7 +201,7 @@ export default {
const telemetryObjects = this.ladTelemetryObjects[ladTable.key]; const telemetryObjects = this.ladTelemetryObjects[ladTable.key];
telemetryObjects.push(telemetryObject); telemetryObjects.push(telemetryObject);
this.$set(this.ladTelemetryObjects, ladTable.key, telemetryObjects); this.ladTelemetryObjects[ladTable.key] = telemetryObjects;
this.stalenessSubscription[combinedKey] = {}; this.stalenessSubscription[combinedKey] = {};
this.stalenessSubscription[combinedKey].stalenessUtils = new StalenessUtils( this.stalenessSubscription[combinedKey].stalenessUtils = new StalenessUtils(
@ -236,7 +236,7 @@ export default {
this.unwatchStaleness(combinedKey); this.unwatchStaleness(combinedKey);
telemetryObjects.splice(index, 1); telemetryObjects.splice(index, 1);
this.$set(this.ladTelemetryObjects, ladTable.key, telemetryObjects); this.ladTelemetryObjects[ladTable.key] = telemetryObjects;
}; };
}, },
unwatchStaleness(combinedKey) { unwatchStaleness(combinedKey) {

View File

@ -84,7 +84,7 @@ define([
rowCount: 'reflow' rowCount: 'reflow'
}, },
template: autoflowTemplate, template: autoflowTemplate,
destroyed: function () { unmounted: function () {
controller.destroy(); controller.destroy();
if (interval) { if (interval) {
@ -109,7 +109,7 @@ define([
}); });
} }
AutoflowTabularView.prototype = Object.create(VueView.prototype); AutoflowTabularView.prototype = Object.create(VueView.default.prototype);
return AutoflowTabularView; return AutoflowTabularView;
}); });

View File

@ -20,15 +20,15 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define(['vue'], function (Vue) { import mount from 'utils/mount';
function VueView(options) { export default function () {
const vm = new Vue(options); return function VueView(options) {
const { vNode, destroy } = mount(options);
this.show = function (container) { this.show = function (container) {
container.appendChild(vm.$mount().$el); container.appendChild(vNode.el);
}; };
this.destroy = vm.$destroy.bind(vm); this.destroy = destroy;
} };
}
return VueView;
});

View File

@ -93,7 +93,7 @@ export default {
}); });
this.registerListeners(); this.registerListeners();
}, },
beforeDestroy() { beforeUnmount() {
if (this.plotResizeObserver) { if (this.plotResizeObserver) {
this.plotResizeObserver.unobserve(this.$refs.plotWrapper); this.plotResizeObserver.unobserve(this.$refs.plotWrapper);
clearTimeout(this.resizeTimer); clearTimeout(this.resizeTimer);

View File

@ -83,7 +83,7 @@ export default {
this.refreshData this.refreshData
); );
}, },
beforeDestroy() { beforeUnmount() {
this.stopFollowingTimeContext(); this.stopFollowingTimeContext();
this.removeAllSubscriptions(); this.removeAllSubscriptions();

View File

@ -22,7 +22,7 @@
import BarGraphView from './BarGraphView.vue'; import BarGraphView from './BarGraphView.vue';
import { BAR_GRAPH_KEY, BAR_GRAPH_VIEW } from './BarGraphConstants'; import { BAR_GRAPH_KEY, BAR_GRAPH_VIEW } from './BarGraphConstants';
import Vue from 'vue'; import mount from 'utils/mount';
export default function BarGraphViewProvider(openmct) { export default function BarGraphViewProvider(openmct) {
function isCompactView(objectPath) { function isCompactView(objectPath) {
@ -44,12 +44,15 @@ export default function BarGraphViewProvider(openmct) {
}, },
view: function (domainObject, objectPath) { view: function (domainObject, objectPath) {
let component; let _destroy = null;
let component = null;
return { return {
show: function (element) { show: function (element) {
let isCompact = isCompactView(objectPath); let isCompact = isCompactView(objectPath);
component = new Vue({
const { vNode, destroy } = mount(
{
el: element, el: element,
components: { components: {
BarGraphView BarGraphView
@ -67,11 +70,17 @@ export default function BarGraphViewProvider(openmct) {
}; };
}, },
template: '<bar-graph-view ref="graphComponent" :options="options"></bar-graph-view>' template: '<bar-graph-view ref="graphComponent" :options="options"></bar-graph-view>'
}); },
{
app: openmct.app,
element
}
);
_destroy = destroy;
component = vNode.componentInstance;
}, },
destroy: function () { destroy: function () {
component.$destroy(); _destroy();
component = undefined;
}, },
onClearData() { onClearData() {
component.$refs.graphComponent.refreshData(); component.$refs.graphComponent.refreshData();

View File

@ -1,6 +1,6 @@
import { BAR_GRAPH_INSPECTOR_KEY, BAR_GRAPH_KEY } from '../BarGraphConstants'; import { BAR_GRAPH_INSPECTOR_KEY, BAR_GRAPH_KEY } from '../BarGraphConstants';
import Vue from 'vue';
import BarGraphOptions from './BarGraphOptions.vue'; import BarGraphOptions from './BarGraphOptions.vue';
import mount from 'utils/mount';
export default function BarGraphInspectorViewProvider(openmct) { export default function BarGraphInspectorViewProvider(openmct) {
return { return {
@ -16,11 +16,12 @@ export default function BarGraphInspectorViewProvider(openmct) {
return object && object.type === BAR_GRAPH_KEY; return object && object.type === BAR_GRAPH_KEY;
}, },
view: function (selection) { view: function (selection) {
let component; let _destroy = null;
return { return {
show: function (element) { show: function (element) {
component = new Vue({ const { destroy } = mount(
{
el: element, el: element,
components: { components: {
BarGraphOptions BarGraphOptions
@ -30,15 +31,20 @@ export default function BarGraphInspectorViewProvider(openmct) {
domainObject: selection[0][0].context.item domainObject: selection[0][0].context.item
}, },
template: '<bar-graph-options></bar-graph-options>' template: '<bar-graph-options></bar-graph-options>'
}); },
{
app: openmct.app,
element
}
);
_destroy = destroy;
}, },
priority: function () { priority: function () {
return openmct.priority.HIGH + 1; return openmct.priority.HIGH + 1;
}, },
destroy: function () { destroy: function () {
if (component) { if (_destroy) {
component.$destroy(); _destroy();
component = undefined;
} }
} }
}; };

View File

@ -167,7 +167,7 @@ export default {
this.registerListeners(); this.registerListeners();
this.composition.load(); this.composition.load();
}, },
beforeDestroy() { beforeUnmount() {
this.openmct.editor.off('isEditing', this.setEditState); this.openmct.editor.off('isEditing', this.setEditState);
this.stopListening(); this.stopListening();
}, },
@ -192,7 +192,7 @@ export default {
} }
}, },
addSeries(series, index) { addSeries(series, index) {
this.$set(this.plotSeries, this.plotSeries.length, series); this.plotSeries.push(series);
this.setupOptions(); this.setupOptions();
}, },
removeSeries(seriesIdentifier) { removeSeries(seriesIdentifier) {

View File

@ -115,7 +115,7 @@ export default {
this.initColorAndName this.initColorAndName
); );
}, },
beforeDestroy() { beforeUnmount() {
if (this.removeBarStylesListener) { if (this.removeBarStylesListener) {
this.removeBarStylesListener(); this.removeBarStylesListener();
} }

View File

@ -77,7 +77,7 @@ export default {
this.reloadTelemetry this.reloadTelemetry
); );
}, },
beforeDestroy() { beforeUnmount() {
this.stopFollowingTimeContext(); this.stopFollowingTimeContext();
if (!this.composition) { if (!this.composition) {

View File

@ -22,7 +22,7 @@
import ScatterPlotView from './ScatterPlotView.vue'; import ScatterPlotView from './ScatterPlotView.vue';
import { SCATTER_PLOT_KEY, SCATTER_PLOT_VIEW, TIME_STRIP_KEY } from './scatterPlotConstants.js'; import { SCATTER_PLOT_KEY, SCATTER_PLOT_VIEW, TIME_STRIP_KEY } from './scatterPlotConstants.js';
import Vue from 'vue'; import mount from 'utils/mount';
export default function ScatterPlotViewProvider(openmct) { export default function ScatterPlotViewProvider(openmct) {
function isCompactView(objectPath) { function isCompactView(objectPath) {
@ -44,12 +44,13 @@ export default function ScatterPlotViewProvider(openmct) {
}, },
view: function (domainObject, objectPath) { view: function (domainObject, objectPath) {
let component; let _destroy = null;
return { return {
show: function (element) { show: function (element) {
let isCompact = isCompactView(objectPath); const isCompact = isCompactView(objectPath);
component = new Vue({ const { destroy } = mount(
{
el: element, el: element,
components: { components: {
ScatterPlotView ScatterPlotView
@ -67,11 +68,18 @@ export default function ScatterPlotViewProvider(openmct) {
}; };
}, },
template: '<scatter-plot-view :options="options"></scatter-plot-view>' template: '<scatter-plot-view :options="options"></scatter-plot-view>'
}); },
{
app: openmct.app,
element
}
);
_destroy = destroy;
}, },
destroy: function () { destroy: function () {
component.$destroy(); if (_destroy) {
component = undefined; _destroy();
}
} }
}; };
} }

View File

@ -87,7 +87,10 @@ export default {
watch: { watch: {
data: { data: {
immediate: false, immediate: false,
handler: 'updateData' handler() {
this.updateData();
},
deep: true
} }
}, },
mounted() { mounted() {
@ -106,7 +109,7 @@ export default {
this.$refs.plot.on('plotly_relayout', this.zoom); this.$refs.plot.on('plotly_relayout', this.zoom);
}, },
beforeDestroy() { beforeUnmount() {
if (this.$refs.plot && this.$refs.plot.off) { if (this.$refs.plot && this.$refs.plot.off) {
this.$refs.plot.off('plotly_relayout', this.zoom); this.$refs.plot.off('plotly_relayout', this.zoom);
} }

View File

@ -52,7 +52,7 @@ export default {
mounted() { mounted() {
this.openmct.editor.on('isEditing', this.setEditState); this.openmct.editor.on('isEditing', this.setEditState);
}, },
beforeDestroy() { beforeUnmount() {
this.openmct.editor.off('isEditing', this.setEditState); this.openmct.editor.off('isEditing', this.setEditState);
}, },
methods: { methods: {

View File

@ -64,7 +64,7 @@ export default {
this.registerListeners(); this.registerListeners();
this.composition.load(); this.composition.load();
}, },
beforeDestroy() { beforeUnmount() {
this.stopListening(); this.stopListening();
}, },
methods: { methods: {
@ -102,7 +102,7 @@ export default {
} }
}, },
addSeries(series, index) { addSeries(series, index) {
this.$set(this.plotSeries, this.plotSeries.length, series); this.plotSeries.push(series);
this.setAxesLabels(); this.setAxesLabels();
}, },
removeSeries(seriesKey) { removeSeries(seriesKey) {

View File

@ -89,7 +89,7 @@ export default {
this.registerListeners(); this.registerListeners();
this.composition.load(); this.composition.load();
}, },
beforeDestroy() { beforeUnmount() {
this.stopListening(); this.stopListening();
}, },
methods: { methods: {
@ -135,7 +135,7 @@ export default {
} }
}, },
addSeries(series, index) { addSeries(series, index) {
this.$set(this.plotSeries, this.plotSeries.length, series); this.plotSeries.push(series);
this.setupOptions(); this.setupOptions();
}, },
removeSeries(seriesIdentifier) { removeSeries(seriesIdentifier) {

View File

@ -1,5 +1,5 @@
import { SCATTER_PLOT_INSPECTOR_KEY, SCATTER_PLOT_KEY } from '../scatterPlotConstants'; import { SCATTER_PLOT_INSPECTOR_KEY, SCATTER_PLOT_KEY } from '../scatterPlotConstants';
import Vue from 'vue'; import mount from 'utils/mount';
import PlotOptions from './PlotOptions.vue'; import PlotOptions from './PlotOptions.vue';
export default function ScatterPlotInspectorViewProvider(openmct) { export default function ScatterPlotInspectorViewProvider(openmct) {
@ -16,11 +16,11 @@ export default function ScatterPlotInspectorViewProvider(openmct) {
return object && object.type === SCATTER_PLOT_KEY; return object && object.type === SCATTER_PLOT_KEY;
}, },
view: function (selection) { view: function (selection) {
let component; let _destroy = null;
return { return {
show: function (element) { show: function (element) {
component = new Vue({ const { destroy } = mount(
{
el: element, el: element,
components: { components: {
PlotOptions PlotOptions
@ -30,15 +30,20 @@ export default function ScatterPlotInspectorViewProvider(openmct) {
domainObject: selection[0][0].context.item domainObject: selection[0][0].context.item
}, },
template: '<plot-options></plot-options>' template: '<plot-options></plot-options>'
}); },
{
app: openmct.app,
element
}
);
_destroy = destroy;
}, },
priority: function () { priority: function () {
return openmct.priority.HIGH + 1; return openmct.priority.HIGH + 1;
}, },
destroy: function () { destroy: function () {
if (component) { if (_destroy) {
component.$destroy(); _destroy();
component = undefined;
} }
} }
}; };

View File

@ -23,8 +23,8 @@ import { SCATTER_PLOT_KEY } from './scatterPlotConstants.js';
import ScatterPlotViewProvider from './ScatterPlotViewProvider'; import ScatterPlotViewProvider from './ScatterPlotViewProvider';
import ScatterPlotInspectorViewProvider from './inspector/ScatterPlotInspectorViewProvider'; import ScatterPlotInspectorViewProvider from './inspector/ScatterPlotInspectorViewProvider';
import ScatterPlotCompositionPolicy from './ScatterPlotCompositionPolicy'; import ScatterPlotCompositionPolicy from './ScatterPlotCompositionPolicy';
import Vue from 'vue';
import ScatterPlotForm from './ScatterPlotForm.vue'; import ScatterPlotForm from './ScatterPlotForm.vue';
import mount from 'utils/mount';
export default function () { export default function () {
return function install(openmct) { return function install(openmct) {
@ -100,7 +100,8 @@ export default function () {
function getScatterPlotFormControl(openmct) { function getScatterPlotFormControl(openmct) {
return { return {
show(element, model, onChange) { show(element, model, onChange) {
const rowComponent = new Vue({ const { vNode } = mount(
{
el: element, el: element,
components: { components: {
ScatterPlotForm ScatterPlotForm
@ -115,9 +116,14 @@ export default function () {
}; };
}, },
template: `<scatter-plot-form :model="model" @onChange="onChange"></scatter-plot-form>` template: `<scatter-plot-form :model="model" @onChange="onChange"></scatter-plot-form>`
}); },
{
app: openmct.app,
element
}
);
return rowComponent; return vNode;
} }
}; };
} }

View File

@ -19,31 +19,35 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
import ClearDataAction from './ClearDataAction';
import GlobalClearIndicator from './components/globalClearIndicator.vue';
import mount from 'utils/mount';
define(['./components/globalClearIndicator.vue', './ClearDataAction', 'vue'], function ( export default function plugin(appliesToObjects, options = { indicator: true }) {
GlobaClearIndicator,
ClearDataAction,
Vue
) {
return function plugin(appliesToObjects, options = { indicator: true }) {
let installIndicator = options.indicator; let installIndicator = options.indicator;
appliesToObjects = appliesToObjects || []; appliesToObjects = appliesToObjects || [];
return function install(openmct) { return function install(openmct) {
if (installIndicator) { if (installIndicator) {
let component = new Vue({ const { vNode } = mount(
{
components: { components: {
GlobalClearIndicator: GlobaClearIndicator.default GlobalClearIndicator
}, },
provide: { provide: {
openmct openmct
}, },
template: '<GlobalClearIndicator></GlobalClearIndicator>' template: '<GlobalClearIndicator></GlobalClearIndicator>'
}); },
{
app: openmct.app,
element: document.createElement('div')
}
);
let indicator = { let indicator = {
element: component.$mount().$el, element: vNode.el,
key: 'global-clear-indicator', key: 'global-clear-indicator',
priority: openmct.priority.DEFAULT priority: openmct.priority.DEFAULT
}; };
@ -51,7 +55,6 @@ define(['./components/globalClearIndicator.vue', './ClearDataAction', 'vue'], fu
openmct.indicators.add(indicator); openmct.indicators.add(indicator);
} }
openmct.actions.register(new ClearDataAction.default(openmct, appliesToObjects)); openmct.actions.register(new ClearDataAction(openmct, appliesToObjects));
}; };
}; }
});

View File

@ -21,7 +21,7 @@
*****************************************************************************/ *****************************************************************************/
import Clock from './components/Clock.vue'; import Clock from './components/Clock.vue';
import Vue from 'vue'; import mount from 'utils/mount';
export default function ClockViewProvider(openmct) { export default function ClockViewProvider(openmct) {
return { return {
@ -33,11 +33,12 @@ export default function ClockViewProvider(openmct) {
}, },
view: function (domainObject) { view: function (domainObject) {
let component; let _destroy = null;
return { return {
show: function (element) { show: function (element) {
component = new Vue({ const { destroy } = mount(
{
el: element, el: element,
components: { components: {
Clock Clock
@ -47,11 +48,18 @@ export default function ClockViewProvider(openmct) {
domainObject domainObject
}, },
template: '<clock />' template: '<clock />'
}); },
{
app: openmct.app,
element
}
);
_destroy = destroy;
}, },
destroy: function () { destroy: function () {
component.$destroy(); if (_destroy) {
component = undefined; _destroy();
}
} }
}; };
} }

View File

@ -21,7 +21,6 @@
--> -->
<template> <template>
<div class="l-angular-ov-wrapper">
<div class="u-contents"> <div class="u-contents">
<div class="c-clock l-time-display u-style-receiver js-style-receiver"> <div class="c-clock l-time-display u-style-receiver js-style-receiver">
<div class="c-clock__timezone"> <div class="c-clock__timezone">
@ -35,7 +34,6 @@
</div> </div>
</div> </div>
</div> </div>
</div>
</template> </template>
<script> <script>
@ -47,13 +45,11 @@ export default {
inject: ['openmct', 'domainObject'], inject: ['openmct', 'domainObject'],
data() { data() {
return { return {
configuration: this.domainObject.configuration,
lastTimestamp: this.openmct.time.now() lastTimestamp: this.openmct.time.now()
}; };
}, },
computed: { computed: {
configuration() {
return this.domainObject.configuration;
},
baseFormat() { baseFormat() {
return this.configuration.baseFormat; return this.configuration.baseFormat;
}, },
@ -85,10 +81,21 @@ export default {
} }
}, },
mounted() { mounted() {
this.unobserve = this.openmct.objects.observe(
this.domainObject,
'configuration',
(configuration) => {
this.configuration = configuration;
}
);
this.tick = raf(this.tick); this.tick = raf(this.tick);
this.openmct.time.on('tick', this.tick); this.openmct.time.on('tick', this.tick);
}, },
beforeDestroy() { beforeUnmount() {
if (this.unobserve) {
this.unobserve();
}
this.openmct.time.off('tick', this.tick); this.openmct.time.off('tick', this.tick);
}, },
methods: { methods: {

View File

@ -50,7 +50,7 @@ export default {
this.openmct.time.on('tick', this.tick); this.openmct.time.on('tick', this.tick);
this.tick(this.timeTextValue); this.tick(this.timeTextValue);
}, },
beforeDestroy() { beforeUnmount() {
this.openmct.time.off('tick', this.tick); this.openmct.time.off('tick', this.tick);
}, },
methods: { methods: {

View File

@ -24,7 +24,7 @@ import ClockViewProvider from './ClockViewProvider';
import ClockIndicator from './components/ClockIndicator.vue'; import ClockIndicator from './components/ClockIndicator.vue';
import momentTimezone from 'moment-timezone'; import momentTimezone from 'moment-timezone';
import Vue from 'vue'; import mount from 'utils/mount';
export default function ClockPlugin(options) { export default function ClockPlugin(options) {
return function install(openmct) { return function install(openmct) {
@ -93,7 +93,10 @@ export default function ClockPlugin(options) {
openmct.objectViews.addProvider(new ClockViewProvider(openmct)); openmct.objectViews.addProvider(new ClockViewProvider(openmct));
if (options && options.enableClockIndicator === true) { if (options && options.enableClockIndicator === true) {
const clockIndicator = new Vue({ const element = document.createElement('div');
const { vNode } = mount(
{
components: { components: {
ClockIndicator ClockIndicator
}, },
@ -106,13 +109,17 @@ export default function ClockPlugin(options) {
}; };
}, },
template: '<ClockIndicator :indicator-format="indicatorFormat" />' template: '<ClockIndicator :indicator-format="indicatorFormat" />'
}); },
{
app: openmct.app,
element
}
);
const indicator = { const indicator = {
element: clockIndicator.$mount().$el, element: vNode.el,
key: 'clock-indicator', key: 'clock-indicator',
priority: openmct.priority.LOW priority: openmct.priority.LOW
}; };
openmct.indicators.add(indicator); openmct.indicators.add(indicator);
} }

View File

@ -21,7 +21,7 @@
*****************************************************************************/ *****************************************************************************/
import ConditionSet from './components/ConditionSet.vue'; import ConditionSet from './components/ConditionSet.vue';
import Vue from 'vue'; import mount from 'utils/mount';
const DEFAULT_VIEW_PRIORITY = 100; const DEFAULT_VIEW_PRIORITY = 100;
@ -46,18 +46,19 @@ export default class ConditionSetViewProvider {
} }
view(domainObject, objectPath) { view(domainObject, objectPath) {
let component; let _destroy = null;
const openmct = this.openmct; let component = null;
return { return {
show: (container, isEditing) => { show: (container, isEditing) => {
component = new Vue({ const { vNode, destroy } = mount(
{
el: container, el: container,
components: { components: {
ConditionSet ConditionSet
}, },
provide: { provide: {
openmct, openmct: this.openmct,
domainObject, domainObject,
objectPath objectPath
}, },
@ -67,14 +68,22 @@ export default class ConditionSetViewProvider {
}; };
}, },
template: '<condition-set :isEditing="isEditing"></condition-set>' template: '<condition-set :isEditing="isEditing"></condition-set>'
}); },
{
app: this.openmct.app,
element: container
}
);
_destroy = destroy;
component = vNode.componentInstance;
}, },
onEditModeChange: (isEditing) => { onEditModeChange: (isEditing) => {
component.isEditing = isEditing; component.isEditing = isEditing;
}, },
destroy: () => { destroy: () => {
component.$destroy(); if (_destroy) {
component = undefined; _destroy();
}
} }
}; };
} }

View File

@ -270,7 +270,7 @@ export default {
return false; return false;
} }
}, },
destroyed() { unmounted() {
this.destroy(); this.destroy();
}, },
mounted() { mounted() {

View File

@ -122,7 +122,7 @@ export default {
deep: true deep: true
} }
}, },
destroyed() { unmounted() {
this.composition.off('add', this.addTelemetryObject); this.composition.off('add', this.addTelemetryObject);
this.composition.off('remove', this.removeTelemetryObject); this.composition.off('remove', this.removeTelemetryObject);
if (this.conditionManager) { if (this.conditionManager) {

View File

@ -154,7 +154,7 @@ export default {
deep: true deep: true
} }
}, },
beforeDestroy() { beforeUnmount() {
this.resetApplied(); this.resetApplied();
}, },
mounted() { mounted() {

View File

@ -229,7 +229,7 @@ export default {
return this.styleableFontItems.length && this.allowEditing; return this.styleableFontItems.length && this.allowEditing;
} }
}, },
destroyed() { unmounted() {
this.removeListeners(); this.removeListeners();
this.openmct.editor.off('isEditing', this.setEditState); this.openmct.editor.off('isEditing', this.setEditState);
this.stylesManager.off('styleSelected', this.applyStyleToSelection); this.stylesManager.off('styleSelected', this.applyStyleToSelection);
@ -878,8 +878,8 @@ export default {
const fontSize = hasConsolidatedFontSize ? styles[0].fontSize : NON_SPECIFIC; const fontSize = hasConsolidatedFontSize ? styles[0].fontSize : NON_SPECIFIC;
const font = hasConsolidatedFont ? styles[0].font : NON_SPECIFIC; const font = hasConsolidatedFont ? styles[0].font : NON_SPECIFIC;
this.$set(this.consolidatedFontStyle, 'fontSize', fontSize); this.consolidatedFontStyle.fontSize = fontSize;
this.$set(this.consolidatedFontStyle, 'font', font); this.consolidatedFontStyle.font = font;
} }
}, },
getFontStyle(selectionPath) { getFontStyle(selectionPath) {
@ -934,7 +934,7 @@ export default {
} }
// sync vue component on font update // sync vue component on font update
this.$set(this.consolidatedFontStyle, property, value); this.consolidatedFontStyle[property] = value;
}, },
isLayoutObject(selectionPath) { isLayoutObject(selectionPath) {
const layoutItemType = const layoutItemType =

View File

@ -21,7 +21,7 @@
*****************************************************************************/ *****************************************************************************/
import ConditionWidgetComponent from './components/ConditionWidget.vue'; import ConditionWidgetComponent from './components/ConditionWidget.vue';
import Vue from 'vue'; import mount from 'utils/mount';
export default function ConditionWidget(openmct) { export default function ConditionWidget(openmct) {
return { return {
@ -35,11 +35,12 @@ export default function ConditionWidget(openmct) {
return domainObject.type === 'conditionWidget'; return domainObject.type === 'conditionWidget';
}, },
view: function (domainObject) { view: function (domainObject) {
let component; let _destroy = null;
return { return {
show: function (element) { show: function (element) {
component = new Vue({ const { destroy } = mount(
{
el: element, el: element,
components: { components: {
ConditionWidgetComponent: ConditionWidgetComponent ConditionWidgetComponent: ConditionWidgetComponent
@ -49,11 +50,18 @@ export default function ConditionWidget(openmct) {
domainObject domainObject
}, },
template: '<condition-widget-component></condition-widget-component>' template: '<condition-widget-component></condition-widget-component>'
});
}, },
destroy: function (element) { {
component.$destroy(); app: openmct.app,
component = undefined; element
}
);
_destroy = destroy;
},
destroy: function () {
if (_destroy) {
_destroy();
}
} }
}; };
}, },

View File

@ -81,7 +81,7 @@ export default {
this.listenToConditionSetChanges(); this.listenToConditionSetChanges();
} }
}, },
beforeDestroy() { beforeUnmount() {
this.stopListeningToConditionSetChanges(); this.stopListeningToConditionSetChanges();
}, },
methods: { methods: {

View File

@ -22,20 +22,21 @@
import AlphanumericFormat from './components/AlphanumericFormat.vue'; import AlphanumericFormat from './components/AlphanumericFormat.vue';
import Vue from 'vue'; import mount from 'utils/mount';
class AlphanumericFormatView { class AlphanumericFormatView {
constructor(openmct, domainObject, objectPath) { constructor(openmct, domainObject, objectPath) {
this.openmct = openmct; this.openmct = openmct;
this.domainObject = domainObject; this.domainObject = domainObject;
this.objectPath = objectPath; this.objectPath = objectPath;
this.component = undefined; this._destroy = null;
this.component = null;
} }
show(element) { show(element) {
this.component = new Vue({ const { vNode, destroy } = mount(
{
el: element, el: element,
name: 'AlphanumericFormat',
components: { components: {
AlphanumericFormat AlphanumericFormat
}, },
@ -44,8 +45,15 @@ class AlphanumericFormatView {
objectPath: this.objectPath, objectPath: this.objectPath,
currentView: this currentView: this
}, },
template: '<alphanumeric-format ref="alphanumericFormat"></alphanumeric-format>' template: '<AlphanumericFormat ref="alphanumericFormat" />'
}); },
{
app: this.openmct.app,
element
}
);
this.component = vNode.componentInstance;
this._destroy = destroy;
} }
getViewContext() { getViewContext() {
@ -61,8 +69,9 @@ class AlphanumericFormatView {
} }
destroy() { destroy() {
this.component.$destroy(); if (this._destroy) {
this.component = undefined; this._destroy();
}
} }
} }

View File

@ -61,7 +61,7 @@ export default {
this.openmct.selection.on('change', this.handleSelection); this.openmct.selection.on('change', this.handleSelection);
this.handleSelection(this.openmct.selection.get()); this.handleSelection(this.openmct.selection.get());
}, },
destroyed() { unmounted() {
this.openmct.editor.off('isEditing', this.toggleEdit); this.openmct.editor.off('isEditing', this.toggleEdit);
this.openmct.selection.off('change', this.handleSelection); this.openmct.selection.off('change', this.handleSelection);
}, },

View File

@ -115,7 +115,7 @@ export default {
this.initSelect this.initSelect
); );
}, },
destroyed() { unmounted() {
if (this.removeSelectable) { if (this.removeSelectable) {
this.removeSelectable(); this.removeSelectable();
} }

View File

@ -55,7 +55,7 @@
:grid-size="gridSize" :grid-size="gridSize"
:init-select="initSelectIndex === index" :init-select="initSelectIndex === index"
:index="index" :index="index"
:multi-select="selectedLayoutItems.length > 1" :multi-select="selectedLayoutItems.length > 1 || null"
:is-editing="isEditing" :is-editing="isEditing"
@contextClick="updateViewContext" @contextClick="updateViewContext"
@move="move" @move="move"
@ -161,16 +161,14 @@ export default {
selection: [], selection: [],
showGrid: true, showGrid: true,
viewContext: {}, viewContext: {},
gridDimensions: [0, 0] gridDimensions: [0, 0],
layoutItems: this.domainObject.configuration.items
}; };
}, },
computed: { computed: {
gridSize() { gridSize() {
return this.domainObject.configuration.layoutGrid.map(Number); return this.domainObject.configuration.layoutGrid.map(Number);
}, },
layoutItems() {
return this.domainObject.configuration.items;
},
selectedLayoutItems() { selectedLayoutItems() {
return this.layoutItems.filter((item) => { return this.layoutItems.filter((item) => {
return this.itemIsInCurrentSelection(item); return this.itemIsInCurrentSelection(item);
@ -223,9 +221,13 @@ export default {
this.composition.load(); this.composition.load();
this.gridDimensions = [this.$el.offsetWidth, this.$el.scrollHeight]; this.gridDimensions = [this.$el.offsetWidth, this.$el.scrollHeight];
this.openmct.objects.observe(this.domainObject, 'configuration.items', (items) => {
this.layoutItems = items;
});
this.watchDisplayResize(); this.watchDisplayResize();
}, },
destroyed: function () { unmounted: function () {
this.openmct.selection.off('change', this.setSelection); this.openmct.selection.off('change', this.setSelection);
this.composition.off('add', this.addChild); this.composition.off('add', this.addChild);
this.composition.off('remove', this.removeChild); this.composition.off('remove', this.removeChild);

View File

@ -115,7 +115,7 @@ export default {
this.initSelect this.initSelect
); );
}, },
destroyed() { unmounted() {
if (this.removeSelectable) { if (this.removeSelectable) {
this.removeSelectable(); this.removeSelectable();
} }

View File

@ -118,7 +118,7 @@ export default {
this.initSelect this.initSelect
); );
}, },
destroyed() { unmounted() {
if (this.removeSelectable) { if (this.removeSelectable) {
this.removeSelectable(); this.removeSelectable();
} }

View File

@ -24,13 +24,13 @@
<div class="l-layout__frame c-frame no-frame c-line-view" :class="[styleClass]" :style="style"> <div class="l-layout__frame c-frame no-frame c-line-view" :class="[styleClass]" :style="style">
<svg width="100%" height="100%"> <svg width="100%" height="100%">
<line <line
class="c-line-view__hover-indicator"
v-bind="linePosition" v-bind="linePosition"
class="c-line-view__hover-indicator"
vector-effect="non-scaling-stroke" vector-effect="non-scaling-stroke"
/> />
<line <line
class="c-line-view__line"
v-bind="linePosition" v-bind="linePosition"
class="c-line-view__line"
:stroke="stroke" :stroke="stroke"
vector-effect="non-scaling-stroke" vector-effect="non-scaling-stroke"
/> />
@ -257,7 +257,7 @@ export default {
this.initSelect this.initSelect
); );
}, },
destroyed() { unmounted() {
if (this.removeSelectable) { if (this.removeSelectable) {
this.removeSelectable(); this.removeSelectable();
} }

View File

@ -133,7 +133,7 @@ export default {
this.openmct.objects.get(this.item.identifier).then(this.setObject); this.openmct.objects.get(this.item.identifier).then(this.setObject);
} }
}, },
beforeDestroy() { beforeUnmount() {
if (this.removeSelectable) { if (this.removeSelectable) {
this.removeSelectable(); this.removeSelectable();
} }

View File

@ -238,7 +238,7 @@ export default {
this.status = this.openmct.status.get(this.item.identifier); this.status = this.openmct.status.get(this.item.identifier);
this.removeStatusListener = this.openmct.status.observe(this.item.identifier, this.setStatus); this.removeStatusListener = this.openmct.status.observe(this.item.identifier, this.setStatus);
}, },
beforeDestroy() { beforeUnmount() {
this.removeStatusListener(); this.removeStatusListener();
if (this.removeSelectable) { if (this.removeSelectable) {

View File

@ -127,7 +127,7 @@ export default {
this.initSelect this.initSelect
); );
}, },
destroyed() { unmounted() {
if (this.removeSelectable) { if (this.removeSelectable) {
this.removeSelectable(); this.removeSelectable();
} }

View File

@ -17,11 +17,6 @@
flex-direction: column; flex-direction: column;
overflow: auto; overflow: auto;
&__grid-holder,
&__dimensions {
display: none;
}
&__dimensions { &__dimensions {
$b: 1px dashed $editDimensionsColor; $b: 1px dashed $editDimensionsColor;
border-right: $b; border-right: $b;

View File

@ -1,6 +1,5 @@
.c-frame-edit { .c-frame-edit {
// In Layouts, this is the editing rect and handles // In Layouts, this is the editing rect and handles
display: none; // Set to display: block in DisplayLayout.vue
pointer-events: none; pointer-events: none;
@include abs(); @include abs();
border: $editMarqueeBorder; border: $editMarqueeBorder;

View File

@ -40,7 +40,7 @@ export default {
); );
this.initObjectStyles(); this.initObjectStyles();
}, },
beforeDestroy() { beforeUnmount() {
if (this.stopListeningObjectStyles) { if (this.stopListeningObjectStyles) {
this.stopListeningObjectStyles(); this.stopListeningObjectStyles();
} }

View File

@ -26,10 +26,8 @@ import DisplayLayout from './components/DisplayLayout.vue';
import DisplayLayoutToolbar from './DisplayLayoutToolbar.js'; import DisplayLayoutToolbar from './DisplayLayoutToolbar.js';
import DisplayLayoutType from './DisplayLayoutType.js'; import DisplayLayoutType from './DisplayLayoutType.js';
import DisplayLayoutDrawingObjectTypes from './DrawingObjectTypes.js'; import DisplayLayoutDrawingObjectTypes from './DrawingObjectTypes.js';
import objectUtils from 'objectUtils'; import objectUtils from 'objectUtils';
import mount from 'utils/mount';
import Vue from 'vue';
class DisplayLayoutView { class DisplayLayoutView {
constructor(openmct, domainObject, objectPath, options) { constructor(openmct, domainObject, objectPath, options) {
@ -38,11 +36,13 @@ class DisplayLayoutView {
this.objectPath = objectPath; this.objectPath = objectPath;
this.options = options; this.options = options;
this.component = undefined; this.component = null;
this.app = null;
} }
show(container, isEditing) { show(container, isEditing) {
this.component = new Vue({ const { vNode, destroy } = mount(
{
el: container, el: container,
components: { components: {
DisplayLayout DisplayLayout
@ -62,7 +62,14 @@ class DisplayLayoutView {
}, },
template: template:
'<display-layout ref="displayLayout" :domain-object="domainObject" :is-editing="isEditing"></display-layout>' '<display-layout ref="displayLayout" :domain-object="domainObject" :is-editing="isEditing"></display-layout>'
}); },
{
app: this.openmct.app,
element: container
}
);
this._destroy = destroy;
this.component = vNode.componentInstance;
} }
getViewContext() { getViewContext() {
@ -95,8 +102,9 @@ class DisplayLayoutView {
} }
destroy() { destroy() {
this.component.$destroy(); if (this._destroy) {
this.component = undefined; this._destroy();
}
} }
} }

View File

@ -22,7 +22,7 @@
import FaultManagementInspector from './FaultManagementInspector.vue'; import FaultManagementInspector from './FaultManagementInspector.vue';
import Vue from 'vue'; import mount from 'utils/mount';
import { FAULT_MANAGEMENT_INSPECTOR, FAULT_MANAGEMENT_TYPE } from './constants'; import { FAULT_MANAGEMENT_INSPECTOR, FAULT_MANAGEMENT_TYPE } from './constants';
@ -41,11 +41,12 @@ export default function FaultManagementInspectorViewProvider(openmct) {
return object && object.type === FAULT_MANAGEMENT_TYPE; return object && object.type === FAULT_MANAGEMENT_TYPE;
}, },
view: (selection) => { view: (selection) => {
let component; let _destroy = null;
return { return {
show: function (element) { show: function (element) {
component = new Vue({ const { destroy } = mount(
{
el: element, el: element,
components: { components: {
FaultManagementInspector FaultManagementInspector
@ -54,15 +55,20 @@ export default function FaultManagementInspectorViewProvider(openmct) {
openmct openmct
}, },
template: '<FaultManagementInspector></FaultManagementInspector>' template: '<FaultManagementInspector></FaultManagementInspector>'
}); },
{
app: openmct.app,
element
}
);
_destroy = destroy;
}, },
priority: function () { priority: function () {
return openmct.priority.HIGH + 1; return openmct.priority.HIGH + 1;
}, },
destroy: function () { destroy: function () {
if (component) { if (_destroy) {
component.$destroy(); _destroy();
component = undefined;
} }
} }
}; };

View File

@ -167,7 +167,7 @@ export default {
}, },
toggleSelected({ fault, selected = false }) { toggleSelected({ fault, selected = false }) {
if (selected) { if (selected) {
this.$set(this.selectedFaults, fault.id, fault); this.selectedFaults[fault.id] = fault;
} else { } else {
this.$delete(this.selectedFaults, fault.id); this.$delete(this.selectedFaults, fault.id);
} }

View File

@ -41,7 +41,7 @@ export default {
mounted() { mounted() {
this.unsubscribe = this.openmct.faults.subscribe(this.domainObject, this.updateFault); this.unsubscribe = this.openmct.faults.subscribe(this.domainObject, this.updateFault);
}, },
beforeDestroy() { beforeUnmount() {
if (this.unsubscribe) { if (this.unsubscribe) {
this.unsubscribe(); this.unsubscribe();
} }
@ -53,7 +53,7 @@ export default {
} else if (type === FAULT_MANAGEMENT_ALARMS) { } else if (type === FAULT_MANAGEMENT_ALARMS) {
this.faultsList.forEach((faultValue, i) => { this.faultsList.forEach((faultValue, i) => {
if (fault.id === faultValue.id) { if (fault.id === faultValue.id) {
this.$set(this.faultsList, i, fault); this.faultsList[i] = fault;
} }
}); });
} }

View File

@ -22,7 +22,7 @@
import FaultManagementView from './FaultManagementView.vue'; import FaultManagementView from './FaultManagementView.vue';
import { FAULT_MANAGEMENT_TYPE, FAULT_MANAGEMENT_VIEW } from './constants'; import { FAULT_MANAGEMENT_TYPE, FAULT_MANAGEMENT_VIEW } from './constants';
import Vue from 'vue'; import mount from 'utils/mount';
export default class FaultManagementViewProvider { export default class FaultManagementViewProvider {
constructor(openmct) { constructor(openmct) {
@ -39,12 +39,13 @@ export default class FaultManagementViewProvider {
} }
view(domainObject) { view(domainObject) {
let component;
const openmct = this.openmct; const openmct = this.openmct;
let _destroy = null;
return { return {
show: (element) => { show: (element) => {
component = new Vue({ const { destroy } = mount(
{
el: element, el: element,
components: { components: {
FaultManagementView FaultManagementView
@ -54,15 +55,18 @@ export default class FaultManagementViewProvider {
domainObject domainObject
}, },
template: '<FaultManagementView></FaultManagementView>' template: '<FaultManagementView></FaultManagementView>'
}); },
{
app: openmct.app,
element
}
);
_destroy = destroy;
}, },
destroy: () => { destroy: () => {
if (!component) { if (_destroy) {
return; _destroy();
} }
component.$destroy();
component = undefined;
} }
}; };
} }

View File

@ -20,33 +20,49 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define(['./components/FiltersView.vue', 'vue'], function (FiltersView, Vue) { import mount from 'utils/mount';
function FiltersInspectorViewProvider(openmct, supportedObjectTypesArray) { import FiltersView from './components/FiltersView.vue';
return {
key: 'filters-inspector', const FILTERS_INSPECTOR_KEY = 'filters-inspector';
name: 'Filters', export default class FiltersInspectorViewProvider {
canView: function (selection) { constructor(openmct, supportedObjectTypesArray) {
this.openmct = openmct;
this.supportedObjectTypesArray = supportedObjectTypesArray;
this.key = FILTERS_INSPECTOR_KEY;
this.name = 'Filters';
}
canView(selection) {
const domainObject = selection?.[0]?.[0]?.context?.item; const domainObject = selection?.[0]?.[0]?.context?.item;
return domainObject && supportedObjectTypesArray.some((type) => domainObject.type === type); return (
}, domainObject && this.supportedObjectTypesArray.some((type) => domainObject.type === type)
view: function (selection) { );
let component; }
view(selection) {
let openmct = this.openmct;
let _destroy = null;
const domainObject = selection?.[0]?.[0]?.context?.item; const domainObject = selection?.[0]?.[0]?.context?.item;
return { return {
show: function (element) { show: function (element) {
component = new Vue({ const { destroy } = mount(
{
el: element, el: element,
components: { components: {
FiltersView: FiltersView.default FiltersView
}, },
provide: { provide: {
openmct openmct: openmct
}, },
template: '<filters-view></filters-view>' template: '<filters-view></filters-view>'
}); },
{
app: openmct.app,
element
}
);
_destroy = destroy;
}, },
showTab: function (isEditing) { showTab: function (isEditing) {
if (isEditing) { if (isEditing) {
@ -64,15 +80,10 @@ define(['./components/FiltersView.vue', 'vue'], function (FiltersView, Vue) {
return openmct.priority.DEFAULT; return openmct.priority.DEFAULT;
}, },
destroy: function () { destroy: function () {
if (component) { if (_destroy) {
component.$destroy(); _destroy();
component = undefined;
} }
} }
}; };
} }
}; }
}
return FiltersInspectorViewProvider;
});

View File

@ -133,7 +133,7 @@ export default {
mounted() { mounted() {
this.openmct.editor.on('isEditing', this.toggleIsEditing); this.openmct.editor.on('isEditing', this.toggleIsEditing);
}, },
beforeDestroy() { beforeUnmount() {
this.openmct.editor.off('isEditing', this.toggleIsEditing); this.openmct.editor.off('isEditing', this.toggleIsEditing);
}, },
methods: { methods: {

View File

@ -136,7 +136,7 @@ export default {
this.objectCssClass = type.definition.cssClass; this.objectCssClass = type.definition.cssClass;
this.openmct.editor.on('isEditing', this.toggleIsEditing); this.openmct.editor.on('isEditing', this.toggleIsEditing);
}, },
beforeDestroy() { beforeUnmount() {
this.openmct.editor.off('isEditing', this.toggleIsEditing); this.openmct.editor.off('isEditing', this.toggleIsEditing);
}, },
methods: { methods: {
@ -151,32 +151,32 @@ export default {
filterValue[comparator].push(valueName); filterValue[comparator].push(valueName);
} else { } else {
if (filterValue[comparator].length === 1) { if (filterValue[comparator].length === 1) {
this.$set(this.updatedFilters, key, {}); this.updatedFilters[key] = {};
} else { } else {
filterValue[comparator] = filterValue[comparator].filter((v) => v !== valueName); filterValue[comparator] = filterValue[comparator].filter((v) => v !== valueName);
} }
} }
} else { } else {
this.$set(this.updatedFilters[key], comparator, [valueName]); this.updatedFilters[key][comparator] = [valueName];
} }
this.$emit('updateFilters', this.keyString, this.updatedFilters); this.$emit('updateFilters', this.keyString, this.updatedFilters);
}, },
clearFilters(key) { clearFilters(key) {
this.$set(this.updatedFilters, key, {}); this.updatedFilters[key] = {};
this.$emit('updateFilters', this.keyString, this.updatedFilters); this.$emit('updateFilters', this.keyString, this.updatedFilters);
}, },
updateFiltersWithTextValue(key, comparator, value) { updateFiltersWithTextValue(key, comparator, value) {
if (value.trim() === '') { if (value.trim() === '') {
this.$set(this.updatedFilters, key, {}); this.updatedFilters[key] = {};
} else { } else {
this.$set(this.updatedFilters[key], comparator, value); this.updatedFilters[key][comparator] = value;
} }
this.$emit('updateFilters', this.keyString, this.updatedFilters); this.$emit('updateFilters', this.keyString, this.updatedFilters);
}, },
updateSingleSelection(key, comparator, value) { updateSingleSelection(key, comparator, value) {
this.$set(this.updatedFilters[key], comparator, [value]); this.updatedFilters[key][comparator] = [value];
this.$emit('updateFilters', this.keyString, this.updatedFilters); this.$emit('updateFilters', this.keyString, this.updatedFilters);
}, },
useGlobalFilter(checked) { useGlobalFilter(checked) {

View File

@ -114,7 +114,7 @@ export default {
this.updateGlobalFilters this.updateGlobalFilters
); );
}, },
beforeDestroy() { beforeUnmount() {
this.composition.off('add', this.addChildren); this.composition.off('add', this.addChildren);
this.composition.off('remove', this.removeChildren); this.composition.off('remove', this.removeChildren);
this.unobserve(); this.unobserve();
@ -136,21 +136,21 @@ export default {
}; };
if (metadataWithFilters.length) { if (metadataWithFilters.length) {
this.$set(this.children, keyString, childObject); this.children[keyString] = childObject;
metadataWithFilters.forEach((metadatum) => { metadataWithFilters.forEach((metadatum) => {
if (!this.globalFilters[metadatum.key]) { if (!this.globalFilters[metadatum.key]) {
this.$set(this.globalFilters, metadatum.key, {}); this.globalFilters[metadatum.key] = {};
} }
if (!this.globalMetadata[metadatum.key]) { if (!this.globalMetadata[metadatum.key]) {
this.$set(this.globalMetadata, metadatum.key, metadatum); this.globalMetadata[metadatum.key] = metadatum;
} }
if (!hasFiltersWithKeyString) { if (!hasFiltersWithKeyString) {
if (!this.persistedFilters[keyString]) { if (!this.persistedFilters[keyString]) {
this.$set(this.persistedFilters, keyString, {}); this.persistedFilters[keyString] = {};
this.$set(this.persistedFilters[keyString], 'useGlobal', true); this.persistedFilters[keyString].useGlobal = true;
mutateFilters = true; mutateFilters = true;
} }
@ -239,10 +239,10 @@ export default {
this.containsField(keyString, key) this.containsField(keyString, key)
) { ) {
if (!this.persistedFilters[keyString][key]) { if (!this.persistedFilters[keyString][key]) {
this.$set(this.persistedFilters[keyString], key, {}); this.persistedFilters[keyString][key] = {};
} }
this.$set(this.persistedFilters[keyString], key, filters[key]); this.persistedFilters[keyString][key] = filters[key];
mutateFilters = true; mutateFilters = true;
} }
}); });

View File

@ -101,7 +101,7 @@ export default {
this.expanded = !this.expanded; this.expanded = !this.expanded;
}, },
clearFilters(key) { clearFilters(key) {
this.$set(this.updatedFilters, key, {}); this.updatedFilters[key] = {};
this.$emit('persistGlobalFilters', key, this.updatedFilters); this.$emit('persistGlobalFilters', key, this.updatedFilters);
}, },
updateFiltersWithSelectedValue(key, comparator, valueName, value) { updateFiltersWithSelectedValue(key, comparator, valueName, value) {
@ -112,26 +112,26 @@ export default {
filterValue[comparator].push(valueName); filterValue[comparator].push(valueName);
} else { } else {
if (filterValue[comparator].length === 1) { if (filterValue[comparator].length === 1) {
this.$set(this.updatedFilters, key, {}); this.updatedFilters[key] = {};
} else { } else {
filterValue[comparator] = filterValue[comparator].filter((v) => v !== valueName); filterValue[comparator] = filterValue[comparator].filter((v) => v !== valueName);
} }
} }
} else { } else {
this.$set(this.updatedFilters[key], comparator, [valueName]); this.updatedFilters[key][comparator] = [valueName];
} }
this.$emit('persistGlobalFilters', key, this.updatedFilters); this.$emit('persistGlobalFilters', key, this.updatedFilters);
}, },
updateSingleSelection(key, comparator, value) { updateSingleSelection(key, comparator, value) {
this.$set(this.updatedFilters[key], comparator, [value]); this.updatedFilters[key][comparator] = [value];
this.$emit('persistGlobalFilters', key, this.updatedFilters); this.$emit('persistGlobalFilters', key, this.updatedFilters);
}, },
updateFiltersWithTextValue(key, comparator, value) { updateFiltersWithTextValue(key, comparator, value) {
if (value.trim() === '') { if (value.trim() === '') {
this.$set(this.updatedFilters, key, {}); this.updatedFilters[key] = {};
} else { } else {
this.$set(this.updatedFilters[key], comparator, value); this.updatedFilters[key][comparator] = value;
} }
this.$emit('persistGlobalFilters', key, this.updatedFilters); this.$emit('persistGlobalFilters', key, this.updatedFilters);

View File

@ -24,7 +24,7 @@ define(['./FiltersInspectorViewProvider'], function (FiltersInspectorViewProvide
return function plugin(supportedObjectTypesArray) { return function plugin(supportedObjectTypesArray) {
return function install(openmct) { return function install(openmct) {
openmct.inspectorViews.addProvider( openmct.inspectorViews.addProvider(
new FiltersInspectorViewProvider(openmct, supportedObjectTypesArray) new FiltersInspectorViewProvider.default(openmct, supportedObjectTypesArray)
); );
}; };
}; };

View File

@ -43,9 +43,8 @@
/> />
<div class="c-fl-container__frames-holder"> <div class="c-fl-container__frames-holder">
<template v-for="(frame, i) in frames"> <template v-for="(frame, i) in frames" :key="frame.id">
<frame-component <frame-component
:key="frame.id"
class="c-fl-container__frame" class="c-fl-container__frame"
:frame="frame" :frame="frame"
:index="i" :index="i"
@ -55,7 +54,6 @@
/> />
<drop-hint <drop-hint
:key="'hint-' + i"
class="c-fl-frame__drop-hint" class="c-fl-frame__drop-hint"
:index="i" :index="i"
:allow-drop="allowDrop" :allow-drop="allowDrop"
@ -64,7 +62,6 @@
<resize-handle <resize-handle
v-if="i !== frames.length - 1" v-if="i !== frames.length - 1"
:key="'handle-' + i"
:index="i" :index="i"
:orientation="rowsLayout ? 'horizontal' : 'vertical'" :orientation="rowsLayout ? 'horizontal' : 'vertical'"
:is-editing="isEditing" :is-editing="isEditing"
@ -132,7 +129,7 @@ export default {
this.unsubscribeSelection = this.openmct.selection.selectable(this.$el, context, false); this.unsubscribeSelection = this.openmct.selection.selectable(this.$el, context, false);
}, },
beforeDestroy() { beforeUnmount() {
this.unsubscribeSelection(); this.unsubscribeSelection();
}, },
methods: { methods: {

View File

@ -56,7 +56,7 @@ export default {
document.addEventListener('dragend', this.dragend); document.addEventListener('dragend', this.dragend);
document.addEventListener('drop', this.dragend); document.addEventListener('drop', this.dragend);
}, },
destroyed() { unmounted() {
document.removeEventListener('dragstart', this.dragstart); document.removeEventListener('dragstart', this.dragstart);
document.removeEventListener('dragend', this.dragend); document.removeEventListener('dragend', this.dragend);
document.removeEventListener('drop', this.dragend); document.removeEventListener('drop', this.dragend);

View File

@ -34,7 +34,7 @@
'c-fl--rows': rowsLayout === true 'c-fl--rows': rowsLayout === true
}" }"
> >
<template v-for="(container, index) in containers"> <template v-for="(container, index) in containers" :key="`component-${container.id}`">
<drop-hint <drop-hint
v-if="index === 0 && containers.length > 1" v-if="index === 0 && containers.length > 1"
:key="`hint-top-${container.id}`" :key="`hint-top-${container.id}`"
@ -45,7 +45,6 @@
/> />
<container-component <container-component
:key="`component-${container.id}`"
class="c-fl__container" class="c-fl__container"
:index="index" :index="index"
:container="container" :container="container"
@ -174,7 +173,7 @@ export default {
this.composition.on('add', this.addFrame); this.composition.on('add', this.addFrame);
this.composition.load(); this.composition.load();
}, },
beforeDestroy() { beforeUnmount() {
this.composition.off('remove', this.removeChildObject); this.composition.off('remove', this.removeChildObject);
this.composition.off('add', this.addFrame); this.composition.off('add', this.addFrame);
}, },

View File

@ -114,7 +114,7 @@ export default {
this.dragGhost = document.getElementById('js-fl-drag-ghost'); this.dragGhost = document.getElementById('js-fl-drag-ghost');
}, },
beforeDestroy() { beforeUnmount() {
if (this.domainObjectPromise) { if (this.domainObjectPromise) {
this.domainObjectPromise.then(() => { this.domainObjectPromise.then(() => {
if (this?.domainObject?.isMutable) { if (this?.domainObject?.isMutable) {

View File

@ -56,7 +56,7 @@ export default {
document.addEventListener('dragend', this.unsetDragging); document.addEventListener('dragend', this.unsetDragging);
document.addEventListener('drop', this.unsetDragging); document.addEventListener('drop', this.unsetDragging);
}, },
destroyed() { unmounted() {
document.removeEventListener('dragstart', this.setDragging); document.removeEventListener('dragstart', this.setDragging);
document.removeEventListener('dragend', this.unsetDragging); document.removeEventListener('dragend', this.unsetDragging);
document.removeEventListener('drop', this.unsetDragging); document.removeEventListener('drop', this.unsetDragging);

View File

@ -20,30 +20,42 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
define(['./components/flexibleLayout.vue', 'vue'], function (FlexibleLayoutComponent, Vue) { import mount from 'utils/mount';
function FlexibleLayoutViewProvider(openmct) { import FlexibleLayoutComponent from './components/flexibleLayout.vue';
return {
key: 'flexible-layout', const FLEXIBLE_LAYOUT_KEY = 'flexible-layout';
name: 'FlexibleLayout', export default class FlexibleLayoutViewProvider {
cssClass: 'icon-layout-view', constructor(openmct) {
canView: function (domainObject) { this.openmct = openmct;
return domainObject.type === 'flexible-layout'; this.key = FLEXIBLE_LAYOUT_KEY;
}, this.name = 'Flexible Layout';
canEdit: function (domainObject) { this.cssClass = 'icon-layout-view';
return domainObject.type === 'flexible-layout'; this.destroy = null;
}, }
view: function (domainObject, objectPath) {
let component; canView(domainObject) {
return domainObject.type === FLEXIBLE_LAYOUT_KEY;
}
canEdit(domainObject) {
return domainObject.type === FLEXIBLE_LAYOUT_KEY;
}
view(domainObject, objectPath) {
let openmct = this.openmct;
let _destroy = null;
let component = null;
return { return {
show: function (element, isEditing) { show: function (element, isEditing) {
component = new Vue({ const { vNode, destroy } = mount(
{
el: element, el: element,
components: { components: {
FlexibleLayoutComponent: FlexibleLayoutComponent.default FlexibleLayoutComponent
}, },
provide: { provide: {
openmct, openmct: openmct,
objectPath, objectPath,
layoutObject: domainObject layoutObject: domainObject
}, },
@ -54,7 +66,14 @@ define(['./components/flexibleLayout.vue', 'vue'], function (FlexibleLayoutCompo
}, },
template: template:
'<flexible-layout-component ref="flexibleLayout" :isEditing="isEditing"></flexible-layout-component>' '<flexible-layout-component ref="flexibleLayout" :isEditing="isEditing"></flexible-layout-component>'
}); },
{
app: openmct.app,
element
}
);
component = vNode.componentInstance;
_destroy = destroy;
}, },
getSelectionContext: function () { getSelectionContext: function () {
return { return {
@ -69,16 +88,14 @@ define(['./components/flexibleLayout.vue', 'vue'], function (FlexibleLayoutCompo
component.isEditing = isEditing; component.isEditing = isEditing;
}, },
destroy: function (element) { destroy: function (element) {
component.$destroy(); if (_destroy) {
component = undefined; _destroy();
component = null;
}
} }
}; };
}, }
priority: function () { priority() {
return 1; return 1;
} }
}; }
}
return FlexibleLayoutViewProvider;
});

View File

@ -27,7 +27,7 @@ define(['./flexibleLayoutViewProvider', './utils/container', './toolbarProvider'
) { ) {
return function plugin() { return function plugin() {
return function install(openmct) { return function install(openmct) {
openmct.objectViews.addProvider(new FlexibleLayoutViewProvider(openmct)); openmct.objectViews.addProvider(new FlexibleLayoutViewProvider.default(openmct));
openmct.types.addType('flexible-layout', { openmct.types.addType('flexible-layout', {
name: 'Flexible Layout', name: 'Flexible Layout',

View File

@ -19,50 +19,51 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
import GridViewComponent from './components/GridView.vue';
import { ALLOWED_FOLDER_TYPES } from './constants.js';
import mount from 'utils/mount';
define(['./components/GridView.vue', './constants.js', 'vue'], function ( export default class FolderGridView {
GridViewComponent, constructor(openmct) {
constants, this.openmct = openmct;
Vue this.key = 'grid';
) { this.name = 'Grid View';
function FolderGridView(openmct) { this.cssClass = 'icon-thumbs-strip';
const ALLOWED_FOLDER_TYPES = constants.ALLOWED_FOLDER_TYPES; }
canView(domainObject) {
return {
key: 'grid',
name: 'Grid View',
cssClass: 'icon-thumbs-strip',
canView: function (domainObject) {
return ALLOWED_FOLDER_TYPES.includes(domainObject.type); return ALLOWED_FOLDER_TYPES.includes(domainObject.type);
}, }
view: function (domainObject) {
let component;
view(domainObject) {
return { return {
show: function (element) { show: (element) => {
component = new Vue({ const { destroy } = mount(
el: element, {
components: { components: {
gridViewComponent: GridViewComponent.default GridViewComponent
}, },
provide: { provide: {
openmct, openmct: this.openmct,
domainObject domainObject
}, },
template: '<grid-view-component></grid-view-component>' template: '<GridViewComponent></GridViewComponent>'
});
}, },
destroy: function (element) { {
component.$destroy(); app: this.openmct.app,
component = undefined; element
} }
}; );
this._destroy = destroy;
}, },
priority: function () { destroy: () => {
return 1; if (this._destroy) {
this._destroy();
}
} }
}; };
} }
return FolderGridView; priority() {
}); return 1;
}
}

View File

@ -19,52 +19,54 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
import mount from 'utils/mount';
import ListViewComponent from './components/ListView.vue';
import Moment from 'moment';
import { ALLOWED_FOLDER_TYPES } from './constants.js';
define(['./components/ListView.vue', './constants.js', 'vue', 'moment'], function ( export default class FolderListView {
ListViewComponent, constructor(openmct) {
constants, this.openmct = openmct;
Vue, this.key = 'list-view';
Moment this.name = 'List View';
) { this.cssClass = 'icon-list-view';
function FolderListView(openmct) { }
const ALLOWED_FOLDER_TYPES = constants.ALLOWED_FOLDER_TYPES;
return { canView(domainObject) {
key: 'list-view',
name: 'List View',
cssClass: 'icon-list-view',
canView: function (domainObject) {
return ALLOWED_FOLDER_TYPES.includes(domainObject.type); return ALLOWED_FOLDER_TYPES.includes(domainObject.type);
}, }
view: function (domainObject) {
let component;
view(domainObject) {
return { return {
show: function (element) { show: (element) => {
component = new Vue({ const { destroy } = mount(
{
el: element, el: element,
components: { components: {
listViewComponent: ListViewComponent.default ListViewComponent
}, },
provide: { provide: {
openmct, openmct: this.openmct,
domainObject, domainObject,
Moment Moment
}, },
template: '<list-view-component></list-view-component>' template: '<ListViewComponent></ListViewComponent>'
});
}, },
destroy: function (element) { {
component.$destroy(); app: this.openmct.app,
component = undefined; element
}
);
this._destroy = destroy;
},
destroy: () => {
if (this._destroy) {
this._destroy();
}
} }
}; };
}, }
priority: function () { priority() {
return 1; return 1;
} }
}; }
}
return FolderListView;
});

View File

@ -21,6 +21,7 @@
--> -->
<template> <template>
<a <a
ref="root"
class="l-grid-view__item c-grid-item js-folder-child" class="l-grid-view__item c-grid-item js-folder-child"
:class="[ :class="[
{ {
@ -30,7 +31,7 @@
}, },
statusClass statusClass
]" ]"
@click="navigate" @click="navigate($event)"
> >
<div <div
class="c-grid-item__type-icon" class="c-grid-item__type-icon"
@ -69,7 +70,7 @@ export default {
} }
}, },
methods: { methods: {
navigate() { navigate(_event) {
this.openmct.router.navigate(this.objectLink); this.openmct.router.navigate(this.objectLink);
} }
} }

View File

@ -21,6 +21,7 @@
--> -->
<template> <template>
<tr <tr
ref="root"
class="c-list-item js-folder-child" class="c-list-item js-folder-child"
:class="{ :class="{
'is-alias': item.isAlias === true 'is-alias': item.isAlias === true

View File

@ -23,11 +23,10 @@ export default {
this.composition.on('remove', this.remove); this.composition.on('remove', this.remove);
this.composition.load(); this.composition.load();
}, },
destroyed() { beforeUnmount() {
if (!this.composition) { if (!this.composition) {
return; return;
} }
this.composition.off('add', this.add); this.composition.off('add', this.add);
this.composition.off('remove', this.remove); this.composition.off('remove', this.remove);
}, },

View File

@ -27,7 +27,7 @@ export default {
this.status = this.openmct.status.get(identifier); this.status = this.openmct.status.get(identifier);
this.removeStatusListener = this.openmct.status.observe(identifier, this.setStatus); this.removeStatusListener = this.openmct.status.observe(identifier, this.setStatus);
}, },
destroyed() { unmounted() {
this.removeStatusListener(); this.removeStatusListener();
} }
}; };

Some files were not shown because too many files have changed in this diff Show More