mirror of
https://github.com/nasa/openmct.git
synced 2025-05-22 10:13:58 +00:00
fix: DisplayLayout and FlexibleLayout toolbar actions only apply to selected layout (#7184)
* refactor: convert to ES6 function * fix: include `keyString` in event name - This negates the need for complicated logic in determining which objectView the action was intended for * fix: handle the case of currentView being null * fix: add keyString to flexibleLayout toolbar events * fix: properly unregister listeners * fix: remove unused imports * fix: revert parameter reorder * refactor: replace usage of `arguments` with `...args` * fix: add a11y to display layout + toolbar * test: add first cut of layout toolbar suite * test: cleanup a bit and add Image test * test: add stubs * fix: remove unused variable * refactor(DisplayLayoutToolbar): convert to ES6 class * test: generate localStorage data for display layout tests * fix: clarify "Add" button label * test: cleanup and don't parameterize tests * test: fix path for recycled_local_storage.json * fix: path to local storage file * docs: add documentation for utilizing localStorage in e2e * fix: path to recycled_local_storage.json * docs: add note hyperlink
This commit is contained in:
parent
bdff210a9c
commit
02f1013770
@ -193,7 +193,7 @@ Current list of test tags:
|
|||||||
|`@ipad` | Test case or test suite is compatible with Playwright's iPad support and Open MCT's read-only mobile view (i.e. no create button).|
|
|`@ipad` | Test case or test suite is compatible with Playwright's iPad support and Open MCT's read-only mobile view (i.e. no create button).|
|
||||||
|`@gds` | Denotes a GDS Test Case used in the VIPER Mission.|
|
|`@gds` | Denotes a GDS Test Case used in the VIPER Mission.|
|
||||||
|`@addInit` | Initializes the browser with an injected and artificial state. Useful for loading non-default plugins. Likely will not work outside of `npm start`.|
|
|`@addInit` | Initializes the browser with an injected and artificial state. Useful for loading non-default plugins. Likely will not work outside of `npm start`.|
|
||||||
|`@localStorage` | Captures or generates session storage to manipulate browser state. Useful for excluding in tests which require a persistent backend (i.e. CouchDB).|
|
|`@localStorage` | Captures or generates session storage to manipulate browser state. Useful for excluding in tests which require a persistent backend (i.e. CouchDB). See [note](#utilizing-localstorage)|
|
||||||
|`@snapshot` | Uses Playwright's snapshot functionality to record a copy of the DOM for direct comparison. Must be run inside of the playwright container.|
|
|`@snapshot` | Uses Playwright's snapshot functionality to record a copy of the DOM for direct comparison. Must be run inside of the playwright container.|
|
||||||
|`@unstable` | A new test or test which is known to be flaky.|
|
|`@unstable` | A new test or test which is known to be flaky.|
|
||||||
|`@2p` | Indicates that multiple users are involved, or multiple tabs/pages are used. Useful for testing multi-user interactivity.|
|
|`@2p` | Indicates that multiple users are involved, or multiple tabs/pages are used. Useful for testing multi-user interactivity.|
|
||||||
@ -352,6 +352,28 @@ By adhering to this principle, we can create tests that are both robust and refl
|
|||||||
1. Avoid repeated setup to test a single assertion. Write longer tests with multiple soft assertions.
|
1. Avoid repeated setup to test a single assertion. Write longer tests with multiple soft assertions.
|
||||||
This ensures that your changes will be picked up with large refactors.
|
This ensures that your changes will be picked up with large refactors.
|
||||||
|
|
||||||
|
##### Utilizing LocalStorage
|
||||||
|
1. In order to save test runtime in the case of tests that require a decent amount of initial setup (such as in the case of testing complex displays), you may use [Playwright's `storageState` feature](https://playwright.dev/docs/api/class-browsercontext#browser-context-storage-state) to generate and load localStorage states.
|
||||||
|
1. To generate a localStorage state to be used in a test:
|
||||||
|
- Add an e2e test to our generateLocalStorageData suite which sets the initial state (creating/configuring objects, etc.), saving it in the `test-data` folder:
|
||||||
|
```js
|
||||||
|
// Save localStorage for future test execution
|
||||||
|
await context.storageState({
|
||||||
|
path: path.join(__dirname, '../../../e2e/test-data/display_layout_with_child_layouts.json')
|
||||||
|
});
|
||||||
|
```
|
||||||
|
- Load the state from file at the beginning of the desired test suite (within the `test.describe()`). (NOTE: the storage state will be used for each test in the suite, so you may need to create a new suite):
|
||||||
|
```js
|
||||||
|
const LOCALSTORAGE_PATH = path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'../../../../test-data/display_layout_with_child_layouts.json'
|
||||||
|
);
|
||||||
|
test.use({
|
||||||
|
storageState: path.resolve(__dirname, LOCALSTORAGE_PATH)
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### How to write a great test
|
### How to write a great test
|
||||||
|
|
||||||
- Avoid using css locators to find elements to the page. Use modern web accessible locators like `getByRole`
|
- Avoid using css locators to find elements to the page. Use modern web accessible locators like `getByRole`
|
||||||
|
22
e2e/test-data/display_layout_with_child_layouts.json
Normal file
22
e2e/test-data/display_layout_with_child_layouts.json
Normal file
File diff suppressed because one or more lines are too long
@ -55,6 +55,42 @@ test.describe('Generate Visual Test Data @localStorage @generatedata', () => {
|
|||||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Generate display layout with 2 child display layouts', async ({ page, context }) => {
|
||||||
|
// Create Display Layout
|
||||||
|
const parent = await createDomainObjectWithDefaults(page, {
|
||||||
|
type: 'Display Layout',
|
||||||
|
name: 'Parent Display Layout'
|
||||||
|
});
|
||||||
|
const child1 = await createDomainObjectWithDefaults(page, {
|
||||||
|
type: 'Display Layout',
|
||||||
|
name: 'Child Layout 1',
|
||||||
|
parent: parent.uuid
|
||||||
|
});
|
||||||
|
const child2 = await createDomainObjectWithDefaults(page, {
|
||||||
|
type: 'Display Layout',
|
||||||
|
name: 'Child Layout 2',
|
||||||
|
parent: parent.uuid
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.goto(parent.url);
|
||||||
|
await page.getByLabel('Edit').click();
|
||||||
|
await page.getByLabel(`${child2.name} Layout Grid`).hover();
|
||||||
|
await page.getByLabel('Move Sub-object Frame').nth(1).click();
|
||||||
|
await page.getByLabel('X:').fill('30');
|
||||||
|
|
||||||
|
await page.getByLabel(`${child1.name} Layout Grid`).hover();
|
||||||
|
await page.getByLabel('Move Sub-object Frame').first().click();
|
||||||
|
await page.getByLabel('Y:').fill('30');
|
||||||
|
|
||||||
|
await page.getByLabel('Save').click();
|
||||||
|
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
|
|
||||||
|
//Save localStorage for future test execution
|
||||||
|
await context.storageState({
|
||||||
|
path: path.join(__dirname, '../../../e2e/test-data/display_layout_with_child_layouts.json')
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// TODO: Visual test for the generated object here
|
// TODO: Visual test for the generated object here
|
||||||
// - Move to using appActions to create the overlay plot
|
// - Move to using appActions to create the overlay plot
|
||||||
// and embedded standard telemetry object
|
// and embedded standard telemetry object
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
* 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.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
/* global __dirname */
|
||||||
/*
|
/*
|
||||||
This test suite is dedicated to tests which verify the basic operations surrounding conditionSets. Note: this
|
This test suite is dedicated to tests which verify the basic operations surrounding conditionSets. Note: this
|
||||||
suite is sharing state between tests which is considered an anti-pattern. Implementing in this way to
|
suite is sharing state between tests which is considered an anti-pattern. Implementing in this way to
|
||||||
@ -31,6 +31,7 @@ const {
|
|||||||
createDomainObjectWithDefaults,
|
createDomainObjectWithDefaults,
|
||||||
createExampleTelemetryObject
|
createExampleTelemetryObject
|
||||||
} = require('../../../../appActions');
|
} = require('../../../../appActions');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
let conditionSetUrl;
|
let conditionSetUrl;
|
||||||
let getConditionSetIdentifierFromUrl;
|
let getConditionSetIdentifierFromUrl;
|
||||||
@ -48,7 +49,9 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
|
|||||||
await Promise.all([page.waitForNavigation(), page.click('button:has-text("OK")')]);
|
await Promise.all([page.waitForNavigation(), page.click('button:has-text("OK")')]);
|
||||||
|
|
||||||
//Save localStorage for future test execution
|
//Save localStorage for future test execution
|
||||||
await context.storageState({ path: './e2e/test-data/recycled_local_storage.json' });
|
await context.storageState({
|
||||||
|
path: path.resolve(__dirname, '../../../../test-data/recycled_local_storage.json')
|
||||||
|
});
|
||||||
|
|
||||||
//Set object identifier from url
|
//Set object identifier from url
|
||||||
conditionSetUrl = page.url();
|
conditionSetUrl = page.url();
|
||||||
@ -59,7 +62,9 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
//Load localStorage for subsequent tests
|
//Load localStorage for subsequent tests
|
||||||
test.use({ storageState: './e2e/test-data/recycled_local_storage.json' });
|
test.use({
|
||||||
|
storageState: path.resolve(__dirname, '../../../../test-data/recycled_local_storage.json')
|
||||||
|
});
|
||||||
|
|
||||||
//Begin suite of tests again localStorage
|
//Begin suite of tests again localStorage
|
||||||
test('Condition set object properties persist in main view and inspector @localStorage', async ({
|
test('Condition set object properties persist in main view and inspector @localStorage', async ({
|
||||||
|
@ -19,8 +19,9 @@
|
|||||||
* 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.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
/* global __dirname */
|
||||||
const { test, expect } = require('../../../../pluginFixtures');
|
const { test, expect } = require('../../../../pluginFixtures');
|
||||||
|
const path = require('path');
|
||||||
const {
|
const {
|
||||||
createDomainObjectWithDefaults,
|
createDomainObjectWithDefaults,
|
||||||
setStartOffset,
|
setStartOffset,
|
||||||
@ -29,6 +30,88 @@ const {
|
|||||||
setIndependentTimeConductorBounds
|
setIndependentTimeConductorBounds
|
||||||
} = require('../../../../appActions');
|
} = require('../../../../appActions');
|
||||||
|
|
||||||
|
const LOCALSTORAGE_PATH = path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'../../../../test-data/display_layout_with_child_layouts.json'
|
||||||
|
);
|
||||||
|
const TINY_IMAGE_BASE64 =
|
||||||
|
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII';
|
||||||
|
|
||||||
|
test.describe('Display Layout Toolbar Actions', () => {
|
||||||
|
const PARENT_DISPLAY_LAYOUT_NAME = 'Parent Display Layout';
|
||||||
|
const CHILD_DISPLAY_LAYOUT_NAME1 = 'Child Layout 1';
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
|
await setRealTimeMode(page);
|
||||||
|
await page
|
||||||
|
.locator('a')
|
||||||
|
.filter({ hasText: 'Parent Display Layout Display Layout' })
|
||||||
|
.first()
|
||||||
|
.click();
|
||||||
|
await page.getByLabel('Edit').click();
|
||||||
|
});
|
||||||
|
test.use({
|
||||||
|
storageState: path.resolve(__dirname, LOCALSTORAGE_PATH)
|
||||||
|
});
|
||||||
|
|
||||||
|
test('can add/remove Text element to a single layout', async ({ page }) => {
|
||||||
|
const layoutObject = 'Text';
|
||||||
|
await test.step(`Add and remove ${layoutObject} from the parent's layout`, async () => {
|
||||||
|
await addAndRemoveDrawingObjectAndAssert(page, layoutObject, PARENT_DISPLAY_LAYOUT_NAME);
|
||||||
|
});
|
||||||
|
await test.step(`Add and remove ${layoutObject} from the child's layout`, async () => {
|
||||||
|
await addAndRemoveDrawingObjectAndAssert(page, layoutObject, CHILD_DISPLAY_LAYOUT_NAME1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
test('can add/remove Image to a single layout', async ({ page }) => {
|
||||||
|
const layoutObject = 'Image';
|
||||||
|
await test.step("Add and remove image element from the parent's layout", async () => {
|
||||||
|
expect(await page.getByLabel(`Move ${layoutObject} Frame`).count()).toBe(0);
|
||||||
|
await addLayoutObject(page, PARENT_DISPLAY_LAYOUT_NAME, layoutObject);
|
||||||
|
expect(await page.getByLabel(`Move ${layoutObject} Frame`).count()).toBe(1);
|
||||||
|
await removeLayoutObject(page, layoutObject);
|
||||||
|
expect(await page.getByLabel(`Move ${layoutObject} Frame`).count()).toBe(0);
|
||||||
|
});
|
||||||
|
await test.step("Add and remove image from the child's layout", async () => {
|
||||||
|
await addLayoutObject(page, CHILD_DISPLAY_LAYOUT_NAME1, layoutObject);
|
||||||
|
expect(await page.getByLabel(`Move ${layoutObject} Frame`).count()).toBe(1);
|
||||||
|
await removeLayoutObject(page, layoutObject);
|
||||||
|
expect(await page.getByLabel(`Move ${layoutObject} Frame`).count()).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
test(`can add/remove Box to a single layout`, async ({ page }) => {
|
||||||
|
const layoutObject = 'Box';
|
||||||
|
await test.step(`Add and remove ${layoutObject} from the parent's layout`, async () => {
|
||||||
|
await addAndRemoveDrawingObjectAndAssert(page, layoutObject, PARENT_DISPLAY_LAYOUT_NAME);
|
||||||
|
});
|
||||||
|
await test.step(`Add and remove ${layoutObject} from the child's layout`, async () => {
|
||||||
|
await addAndRemoveDrawingObjectAndAssert(page, layoutObject, CHILD_DISPLAY_LAYOUT_NAME1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
test(`can add/remove Line to a single layout`, async ({ page }) => {
|
||||||
|
const layoutObject = 'Line';
|
||||||
|
await test.step(`Add and remove ${layoutObject} from the parent's layout`, async () => {
|
||||||
|
await addAndRemoveDrawingObjectAndAssert(page, layoutObject, PARENT_DISPLAY_LAYOUT_NAME);
|
||||||
|
});
|
||||||
|
await test.step(`Add and remove ${layoutObject} from the child's layout`, async () => {
|
||||||
|
await addAndRemoveDrawingObjectAndAssert(page, layoutObject, CHILD_DISPLAY_LAYOUT_NAME1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
test(`can add/remove Ellipse to a single layout`, async ({ page }) => {
|
||||||
|
const layoutObject = 'Ellipse';
|
||||||
|
await test.step(`Add and remove ${layoutObject} from the parent's layout`, async () => {
|
||||||
|
await addAndRemoveDrawingObjectAndAssert(page, layoutObject, PARENT_DISPLAY_LAYOUT_NAME);
|
||||||
|
});
|
||||||
|
await test.step(`Add and remove ${layoutObject} from the child's layout`, async () => {
|
||||||
|
await addAndRemoveDrawingObjectAndAssert(page, layoutObject, CHILD_DISPLAY_LAYOUT_NAME1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
test.fixme('Can switch view types of a single SWG in a layout', async ({ page }) => {});
|
||||||
|
test.fixme('Can merge multiple plots in a layout', async ({ page }) => {});
|
||||||
|
test.fixme('Can adjust stack order of a single object in a layout', async ({ page }) => {});
|
||||||
|
test.fixme('Can duplicate a single object in a layout', async ({ page }) => {});
|
||||||
|
});
|
||||||
|
|
||||||
test.describe('Display Layout', () => {
|
test.describe('Display Layout', () => {
|
||||||
/** @type {import('../../../../appActions').CreatedObjectInfo} */
|
/** @type {import('../../../../appActions').CreatedObjectInfo} */
|
||||||
let sineWaveObject;
|
let sineWaveObject;
|
||||||
@ -41,6 +124,7 @@ test.describe('Display Layout', () => {
|
|||||||
type: 'Sine Wave Generator'
|
type: 'Sine Wave Generator'
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('alpha-numeric widget telemetry value exactly matches latest telemetry value received in real time', async ({
|
test('alpha-numeric widget telemetry value exactly matches latest telemetry value received in real time', async ({
|
||||||
page
|
page
|
||||||
}) => {
|
}) => {
|
||||||
@ -339,6 +423,59 @@ test.describe('Display Layout', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function addAndRemoveDrawingObjectAndAssert(page, layoutObject, DISPLAY_LAYOUT_NAME) {
|
||||||
|
expect(await page.getByLabel(layoutObject, { exact: true }).count()).toBe(0);
|
||||||
|
await addLayoutObject(page, DISPLAY_LAYOUT_NAME, layoutObject);
|
||||||
|
expect(
|
||||||
|
await page
|
||||||
|
.getByLabel(layoutObject, {
|
||||||
|
exact: true
|
||||||
|
})
|
||||||
|
.count()
|
||||||
|
).toBe(1);
|
||||||
|
await removeLayoutObject(page, layoutObject);
|
||||||
|
expect(await page.getByLabel(layoutObject, { exact: true }).count()).toBe(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the first matching layout object from the layout
|
||||||
|
* @param {import('@playwright/test').Page} page
|
||||||
|
* @param {'Box' | 'Ellipse' | 'Line' | 'Text' | 'Image'} layoutObject
|
||||||
|
*/
|
||||||
|
async function removeLayoutObject(page, layoutObject) {
|
||||||
|
await page
|
||||||
|
.getByLabel(`Move ${layoutObject} Frame`, { exact: true })
|
||||||
|
.or(page.getByLabel(layoutObject, { exact: true }))
|
||||||
|
.first()
|
||||||
|
// eslint-disable-next-line playwright/no-force-option
|
||||||
|
.click({ force: true });
|
||||||
|
await page.getByTitle('Delete the selected object').click();
|
||||||
|
await page.getByRole('button', { name: 'OK' }).click();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a layout object to the specified layout
|
||||||
|
* @param {import('@playwright/test').Page} page
|
||||||
|
* @param {string} layoutName
|
||||||
|
* @param {'Box' | 'Ellipse' | 'Line' | 'Text' | 'Image'} layoutObject
|
||||||
|
*/
|
||||||
|
async function addLayoutObject(page, layoutName, layoutObject) {
|
||||||
|
await page.getByLabel(`${layoutName} Layout Grid`).click();
|
||||||
|
await page.getByText('Add Drawing Object').click();
|
||||||
|
await page
|
||||||
|
.getByRole('menuitem', {
|
||||||
|
name: layoutObject
|
||||||
|
})
|
||||||
|
.click();
|
||||||
|
if (layoutObject === 'Text') {
|
||||||
|
await page.getByRole('textbox', { name: 'Text' }).fill('Hello, Universe!');
|
||||||
|
await page.getByText('OK').click();
|
||||||
|
} else if (layoutObject === 'Image') {
|
||||||
|
await page.getByLabel('Image URL').fill(TINY_IMAGE_BASE64);
|
||||||
|
await page.getByText('OK').click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Util for subscribing to a telemetry object by object identifier
|
* Util for subscribing to a telemetry object by object identifier
|
||||||
* Limitations: Currently only works to return telemetry once to the node scope
|
* Limitations: Currently only works to return telemetry once to the node scope
|
||||||
|
@ -20,31 +20,21 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(['lodash'], function (_) {
|
import _ from 'lodash';
|
||||||
function DisplayLayoutToolbar(openmct) {
|
|
||||||
return {
|
|
||||||
name: 'Display Layout Toolbar',
|
|
||||||
key: 'layout',
|
|
||||||
description: 'A toolbar for objects inside a display layout.',
|
|
||||||
forSelection: function (selection) {
|
|
||||||
if (!selection || selection.length === 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let selectionPath = selection[0];
|
const CONTEXT_ACTION = 'contextAction';
|
||||||
let selectedObject = selectionPath[0];
|
const CONTEXT_ACTIONS = Object.freeze({
|
||||||
let selectedParent = selectionPath[1];
|
ADD_ELEMENT: 'addElement',
|
||||||
|
REMOVE_ITEM: 'removeItem',
|
||||||
|
DUPLICATE_ITEM: 'duplicateItem',
|
||||||
|
ORDER_ITEM: 'orderItem',
|
||||||
|
SWITCH_VIEW_TYPE: 'switchViewType',
|
||||||
|
MERGE_MULTIPLE_TELEMETRY_VIEWS: 'mergeMultipleTelemetryViews',
|
||||||
|
MERGE_MULTIPLE_OVERLAY_PLOTS: 'mergeMultipleOverlayPlots',
|
||||||
|
TOGGLE_GRID: 'toggleGrid'
|
||||||
|
});
|
||||||
|
|
||||||
// Apply the layout toolbar if the selected object is inside a layout, or the main layout is selected.
|
const DIALOG_FORM = {
|
||||||
return (
|
|
||||||
(selectedParent &&
|
|
||||||
selectedParent.context.item &&
|
|
||||||
selectedParent.context.item.type === 'layout') ||
|
|
||||||
(selectedObject.context.item && selectedObject.context.item.type === 'layout')
|
|
||||||
);
|
|
||||||
},
|
|
||||||
toolbar: function (selectedObjects) {
|
|
||||||
const DIALOG_FORM = {
|
|
||||||
text: {
|
text: {
|
||||||
title: 'Text Element Properties',
|
title: 'Text Element Properties',
|
||||||
sections: [
|
sections: [
|
||||||
@ -77,8 +67,8 @@ define(['lodash'], function (_) {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const VIEW_TYPES = {
|
const VIEW_TYPES = {
|
||||||
'telemetry-view': {
|
'telemetry-view': {
|
||||||
value: 'telemetry-view',
|
value: 'telemetry-view',
|
||||||
name: 'Alphanumeric',
|
name: 'Alphanumeric',
|
||||||
@ -99,8 +89,8 @@ define(['lodash'], function (_) {
|
|||||||
name: 'Table',
|
name: 'Table',
|
||||||
class: 'icon-tabular-scrolling'
|
class: 'icon-tabular-scrolling'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const APPLICABLE_VIEWS = {
|
const APPLICABLE_VIEWS = {
|
||||||
'telemetry-view': [
|
'telemetry-view': [
|
||||||
VIEW_TYPES['telemetry.plot.overlay'],
|
VIEW_TYPES['telemetry.plot.overlay'],
|
||||||
VIEW_TYPES['telemetry.plot.stacked'],
|
VIEW_TYPES['telemetry.plot.stacked'],
|
||||||
@ -127,13 +117,41 @@ define(['lodash'], function (_) {
|
|||||||
VIEW_TYPES.table
|
VIEW_TYPES.table
|
||||||
],
|
],
|
||||||
'telemetry.plot.overlay-multi': [VIEW_TYPES['telemetry.plot.stacked']]
|
'telemetry.plot.overlay-multi': [VIEW_TYPES['telemetry.plot.stacked']]
|
||||||
};
|
};
|
||||||
|
|
||||||
function getPath(selectionPath) {
|
export default class DisplayLayoutToolbar {
|
||||||
|
#openmct;
|
||||||
|
|
||||||
|
constructor(openmct) {
|
||||||
|
this.#openmct = openmct;
|
||||||
|
this.name = 'Display Layout Toolbar';
|
||||||
|
this.key = 'layout';
|
||||||
|
this.description = 'A toolbar for objects inside a display layout.';
|
||||||
|
}
|
||||||
|
|
||||||
|
forSelection(selection) {
|
||||||
|
if (!selection || selection.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let selectionPath = selection[0];
|
||||||
|
let selectedObject = selectionPath[0];
|
||||||
|
let selectedParent = selectionPath[1];
|
||||||
|
|
||||||
|
// Apply the layout toolbar if the selected object is inside a layout, or the main layout is selected.
|
||||||
|
return (
|
||||||
|
(selectedParent &&
|
||||||
|
selectedParent.context.item &&
|
||||||
|
selectedParent.context.item.type === 'layout') ||
|
||||||
|
(selectedObject.context.item && selectedObject.context.item.type === 'layout')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#getPath(selectionPath) {
|
||||||
return `configuration.items[${selectionPath[0].context.index}]`;
|
return `configuration.items[${selectionPath[0].context.index}]`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAllOfType(selection, specificType) {
|
#getAllOfType(selection, specificType) {
|
||||||
return selection.filter((selectionPath) => {
|
return selection.filter((selectionPath) => {
|
||||||
let type = selectionPath[0].context.layoutItem.type;
|
let type = selectionPath[0].context.layoutItem.type;
|
||||||
|
|
||||||
@ -141,7 +159,7 @@ define(['lodash'], function (_) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAllTypes(selection) {
|
#getAllTypes(selection) {
|
||||||
return selection.filter((selectionPath) => {
|
return selection.filter((selectionPath) => {
|
||||||
let type = selectionPath[0].context.layoutItem.type;
|
let type = selectionPath[0].context.layoutItem.type;
|
||||||
|
|
||||||
@ -157,25 +175,32 @@ define(['lodash'], function (_) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAddButton(selection, selectionPath) {
|
#getAddButton(selection, selectionPath) {
|
||||||
if (selection.length === 1) {
|
if (selection.length === 1) {
|
||||||
selectionPath = selectionPath || selection[0];
|
selectionPath = selectionPath || selection[0];
|
||||||
|
const domainObject = selectionPath[0].context.item;
|
||||||
|
const keyString = this.#openmct.objects.makeKeyString(domainObject.identifier);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
control: 'menu',
|
control: 'menu',
|
||||||
domainObject: selectionPath[0].context.item,
|
domainObject,
|
||||||
method: function (option) {
|
method: (option) => {
|
||||||
let name = option.name.toLowerCase();
|
let name = option.name.toLowerCase();
|
||||||
let form = DIALOG_FORM[name];
|
let form = DIALOG_FORM[name];
|
||||||
if (form) {
|
if (form) {
|
||||||
showForm(form, name, selectionPath);
|
this.#showForm(form, name, selection);
|
||||||
} else {
|
} else {
|
||||||
openmct.objectViews.emit('contextAction', 'addElement', name);
|
this.#openmct.objectViews.emit(
|
||||||
|
`${CONTEXT_ACTION}:${keyString}`,
|
||||||
|
CONTEXT_ACTIONS.ADD_ELEMENT,
|
||||||
|
name,
|
||||||
|
selection
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
key: 'add',
|
key: 'add',
|
||||||
icon: 'icon-plus',
|
icon: 'icon-plus',
|
||||||
label: 'Add',
|
label: 'Add Drawing Object',
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
name: 'Box',
|
name: 'Box',
|
||||||
@ -202,15 +227,15 @@ define(['lodash'], function (_) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getToggleFrameButton(selectedParent, selection) {
|
#getToggleFrameButton(selectedParent, selection) {
|
||||||
return {
|
return {
|
||||||
control: 'toggle-button',
|
control: 'toggle-button',
|
||||||
domainObject: selectedParent,
|
domainObject: selectedParent,
|
||||||
applicableSelectedItems: selection.filter(
|
applicableSelectedItems: selection.filter(
|
||||||
(selectionPath) => selectionPath[0].context.layoutItem.type === 'subobject-view'
|
(selectionPath) => selectionPath[0].context.layoutItem.type === 'subobject-view'
|
||||||
),
|
),
|
||||||
property: function (selectionPath) {
|
property: (selectionPath) => {
|
||||||
return getPath(selectionPath) + '.hasFrame';
|
return this.#getPath(selectionPath) + '.hasFrame';
|
||||||
},
|
},
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
@ -229,25 +254,28 @@ define(['lodash'], function (_) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRemoveButton(selectedParent, selectionPath, selection) {
|
#getRemoveButton(selectedParent, selectionPath, selection) {
|
||||||
|
const domainObject = selectedParent;
|
||||||
|
const keyString = this.#openmct.objects.makeKeyString(domainObject.identifier);
|
||||||
return {
|
return {
|
||||||
control: 'button',
|
control: 'button',
|
||||||
domainObject: selectedParent,
|
domainObject,
|
||||||
icon: 'icon-trash',
|
icon: 'icon-trash',
|
||||||
title: 'Delete the selected object',
|
title: 'Delete the selected object',
|
||||||
method: function () {
|
method: () => {
|
||||||
let prompt = openmct.overlays.dialog({
|
let prompt = this.#openmct.overlays.dialog({
|
||||||
iconClass: 'alert',
|
iconClass: 'alert',
|
||||||
message: `Warning! This action will remove this item from the Display Layout. Do you want to continue?`,
|
message: `Warning! This action will remove this item from the Display Layout. Do you want to continue?`,
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
label: 'OK',
|
label: 'OK',
|
||||||
emphasis: 'true',
|
emphasis: 'true',
|
||||||
callback: function () {
|
callback: () => {
|
||||||
openmct.objectViews.emit(
|
this.#openmct.objectViews.emit(
|
||||||
'contextAction',
|
`${CONTEXT_ACTION}:${keyString}`,
|
||||||
'removeItem',
|
CONTEXT_ACTIONS.REMOVE_ITEM,
|
||||||
getAllTypes(selection)
|
this.#getAllTypes(selection),
|
||||||
|
selection
|
||||||
);
|
);
|
||||||
prompt.dismiss();
|
prompt.dismiss();
|
||||||
}
|
}
|
||||||
@ -264,10 +292,12 @@ define(['lodash'], function (_) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStackOrder(selectedParent, selectionPath) {
|
#getStackOrder(selectedParent, selectionPath, selectedObjects) {
|
||||||
|
const domainObject = selectedParent;
|
||||||
|
const keyString = this.#openmct.objects.makeKeyString(domainObject.identifier);
|
||||||
return {
|
return {
|
||||||
control: 'menu',
|
control: 'menu',
|
||||||
domainObject: selectedParent,
|
domainObject,
|
||||||
icon: 'icon-layers',
|
icon: 'icon-layers',
|
||||||
title: 'Move the selected object above or below other objects',
|
title: 'Move the selected object above or below other objects',
|
||||||
options: [
|
options: [
|
||||||
@ -292,26 +322,26 @@ define(['lodash'], function (_) {
|
|||||||
class: 'icon-arrow-double-down'
|
class: 'icon-arrow-double-down'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
method: function (option) {
|
method: (option) => {
|
||||||
openmct.objectViews.emit(
|
this.#openmct.objectViews.emit(
|
||||||
'contextAction',
|
`${CONTEXT_ACTION}:${keyString}`,
|
||||||
'orderItem',
|
CONTEXT_ACTIONS.ORDER_ITEM,
|
||||||
option.value,
|
option.value,
|
||||||
getAllTypes(selectedObjects)
|
this.#getAllTypes(selectedObjects)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getXInput(selectedParent, selection) {
|
#getXInput(selectedParent, selection) {
|
||||||
if (selection.length === 1) {
|
if (selection.length === 1) {
|
||||||
return {
|
return {
|
||||||
control: 'input',
|
control: 'input',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
domainObject: selectedParent,
|
domainObject: selectedParent,
|
||||||
applicableSelectedItems: getAllTypes(selection),
|
applicableSelectedItems: this.#getAllTypes(selection),
|
||||||
property: function (selectionPath) {
|
property: (selectionPath) => {
|
||||||
return getPath(selectionPath) + '.x';
|
return this.#getPath(selectionPath) + '.x';
|
||||||
},
|
},
|
||||||
label: 'X:',
|
label: 'X:',
|
||||||
title: 'X position'
|
title: 'X position'
|
||||||
@ -319,15 +349,15 @@ define(['lodash'], function (_) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getYInput(selectedParent, selection) {
|
#getYInput(selectedParent, selection) {
|
||||||
if (selection.length === 1) {
|
if (selection.length === 1) {
|
||||||
return {
|
return {
|
||||||
control: 'input',
|
control: 'input',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
domainObject: selectedParent,
|
domainObject: selectedParent,
|
||||||
applicableSelectedItems: getAllTypes(selection),
|
applicableSelectedItems: this.#getAllTypes(selection),
|
||||||
property: function (selectionPath) {
|
property: (selectionPath) => {
|
||||||
return getPath(selectionPath) + '.y';
|
return this.#getPath(selectionPath) + '.y';
|
||||||
},
|
},
|
||||||
label: 'Y:',
|
label: 'Y:',
|
||||||
title: 'Y position'
|
title: 'Y position'
|
||||||
@ -335,15 +365,15 @@ define(['lodash'], function (_) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getWidthInput(selectedParent, selection) {
|
#getWidthInput(selectedParent, selection) {
|
||||||
if (selection.length === 1) {
|
if (selection.length === 1) {
|
||||||
return {
|
return {
|
||||||
control: 'input',
|
control: 'input',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
domainObject: selectedParent,
|
domainObject: selectedParent,
|
||||||
applicableSelectedItems: getAllTypes(selection),
|
applicableSelectedItems: this.#getAllTypes(selection),
|
||||||
property: function (selectionPath) {
|
property: (selectionPath) => {
|
||||||
return getPath(selectionPath) + '.width';
|
return this.#getPath(selectionPath) + '.width';
|
||||||
},
|
},
|
||||||
label: 'W:',
|
label: 'W:',
|
||||||
title: 'Resize object width'
|
title: 'Resize object width'
|
||||||
@ -351,15 +381,15 @@ define(['lodash'], function (_) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getHeightInput(selectedParent, selection) {
|
#getHeightInput(selectedParent, selection) {
|
||||||
if (selection.length === 1) {
|
if (selection.length === 1) {
|
||||||
return {
|
return {
|
||||||
control: 'input',
|
control: 'input',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
domainObject: selectedParent,
|
domainObject: selectedParent,
|
||||||
applicableSelectedItems: getAllTypes(selection),
|
applicableSelectedItems: this.#getAllTypes(selection),
|
||||||
property: function (selectionPath) {
|
property: (selectionPath) => {
|
||||||
return getPath(selectionPath) + '.height';
|
return this.#getPath(selectionPath) + '.height';
|
||||||
},
|
},
|
||||||
label: 'H:',
|
label: 'H:',
|
||||||
title: 'Resize object height'
|
title: 'Resize object height'
|
||||||
@ -367,7 +397,7 @@ define(['lodash'], function (_) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getX2Input(selectedParent, selection) {
|
#getX2Input(selectedParent, selection) {
|
||||||
if (selection.length === 1) {
|
if (selection.length === 1) {
|
||||||
return {
|
return {
|
||||||
control: 'input',
|
control: 'input',
|
||||||
@ -376,8 +406,8 @@ define(['lodash'], function (_) {
|
|||||||
applicableSelectedItems: selection.filter((selectionPath) => {
|
applicableSelectedItems: selection.filter((selectionPath) => {
|
||||||
return selectionPath[0].context.layoutItem.type === 'line-view';
|
return selectionPath[0].context.layoutItem.type === 'line-view';
|
||||||
}),
|
}),
|
||||||
property: function (selectionPath) {
|
property: (selectionPath) => {
|
||||||
return getPath(selectionPath) + '.x2';
|
return this.#getPath(selectionPath) + '.x2';
|
||||||
},
|
},
|
||||||
label: 'X2:',
|
label: 'X2:',
|
||||||
title: 'X2 position'
|
title: 'X2 position'
|
||||||
@ -385,7 +415,7 @@ define(['lodash'], function (_) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getY2Input(selectedParent, selection) {
|
#getY2Input(selectedParent, selection) {
|
||||||
if (selection.length === 1) {
|
if (selection.length === 1) {
|
||||||
return {
|
return {
|
||||||
control: 'input',
|
control: 'input',
|
||||||
@ -394,8 +424,8 @@ define(['lodash'], function (_) {
|
|||||||
applicableSelectedItems: selection.filter((selectionPath) => {
|
applicableSelectedItems: selection.filter((selectionPath) => {
|
||||||
return selectionPath[0].context.layoutItem.type === 'line-view';
|
return selectionPath[0].context.layoutItem.type === 'line-view';
|
||||||
}),
|
}),
|
||||||
property: function (selectionPath) {
|
property: (selectionPath) => {
|
||||||
return getPath(selectionPath) + '.y2';
|
return this.#getPath(selectionPath) + '.y2';
|
||||||
},
|
},
|
||||||
label: 'Y2:',
|
label: 'Y2:',
|
||||||
title: 'Y2 position'
|
title: 'Y2 position'
|
||||||
@ -403,15 +433,15 @@ define(['lodash'], function (_) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTextButton(selectedParent, selection) {
|
#getTextButton(selectedParent, selection) {
|
||||||
return {
|
return {
|
||||||
control: 'button',
|
control: 'button',
|
||||||
domainObject: selectedParent,
|
domainObject: selectedParent,
|
||||||
applicableSelectedItems: selection.filter((selectionPath) => {
|
applicableSelectedItems: selection.filter((selectionPath) => {
|
||||||
return selectionPath[0].context.layoutItem.type === 'text-view';
|
return selectionPath[0].context.layoutItem.type === 'text-view';
|
||||||
}),
|
}),
|
||||||
property: function (selectionPath) {
|
property: (selectionPath) => {
|
||||||
return getPath(selectionPath);
|
return this.#getPath(selectionPath);
|
||||||
},
|
},
|
||||||
icon: 'icon-pencil',
|
icon: 'icon-pencil',
|
||||||
title: 'Edit text properties',
|
title: 'Edit text properties',
|
||||||
@ -420,7 +450,7 @@ define(['lodash'], function (_) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTelemetryValueMenu(selectionPath, selection) {
|
#getTelemetryValueMenu(selectionPath, selection) {
|
||||||
if (selection.length === 1) {
|
if (selection.length === 1) {
|
||||||
return {
|
return {
|
||||||
control: 'select-menu',
|
control: 'select-menu',
|
||||||
@ -428,11 +458,11 @@ define(['lodash'], function (_) {
|
|||||||
applicableSelectedItems: selection.filter((path) => {
|
applicableSelectedItems: selection.filter((path) => {
|
||||||
return path[0].context.layoutItem.type === 'telemetry-view';
|
return path[0].context.layoutItem.type === 'telemetry-view';
|
||||||
}),
|
}),
|
||||||
property: function (path) {
|
property: (path) => {
|
||||||
return getPath(path) + '.value';
|
return this.#getPath(path) + '.value';
|
||||||
},
|
},
|
||||||
title: 'Set value',
|
title: 'Set value',
|
||||||
options: openmct.telemetry
|
options: this.#openmct.telemetry
|
||||||
.getMetadata(selectionPath[0].context.item)
|
.getMetadata(selectionPath[0].context.item)
|
||||||
.values()
|
.values()
|
||||||
.map((value) => {
|
.map((value) => {
|
||||||
@ -445,7 +475,7 @@ define(['lodash'], function (_) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDisplayModeMenu(selectedParent, selection) {
|
#getDisplayModeMenu(selectedParent, selection) {
|
||||||
if (selection.length === 1) {
|
if (selection.length === 1) {
|
||||||
return {
|
return {
|
||||||
control: 'select-menu',
|
control: 'select-menu',
|
||||||
@ -453,8 +483,8 @@ define(['lodash'], function (_) {
|
|||||||
applicableSelectedItems: selection.filter((selectionPath) => {
|
applicableSelectedItems: selection.filter((selectionPath) => {
|
||||||
return selectionPath[0].context.layoutItem.type === 'telemetry-view';
|
return selectionPath[0].context.layoutItem.type === 'telemetry-view';
|
||||||
}),
|
}),
|
||||||
property: function (selectionPath) {
|
property: (selectionPath) => {
|
||||||
return getPath(selectionPath) + '.displayMode';
|
return this.#getPath(selectionPath) + '.displayMode';
|
||||||
},
|
},
|
||||||
title: 'Set display mode',
|
title: 'Set display mode',
|
||||||
options: [
|
options: [
|
||||||
@ -475,19 +505,25 @@ define(['lodash'], function (_) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDuplicateButton(selectedParent, selectionPath, selection) {
|
#getDuplicateButton(selectedParent, selectionPath, selection) {
|
||||||
|
const domainObject = selectedParent;
|
||||||
|
const keyString = this.#openmct.objects.makeKeyString(domainObject.identifier);
|
||||||
return {
|
return {
|
||||||
control: 'button',
|
control: 'button',
|
||||||
domainObject: selectedParent,
|
domainObject,
|
||||||
icon: 'icon-duplicate',
|
icon: 'icon-duplicate',
|
||||||
title: 'Duplicate the selected object',
|
title: 'Duplicate the selected object',
|
||||||
method: function () {
|
method: () => {
|
||||||
openmct.objectViews.emit('contextAction', 'duplicateItem', selection);
|
this.#openmct.objectViews.emit(
|
||||||
|
`${CONTEXT_ACTION}:${keyString}`,
|
||||||
|
CONTEXT_ACTIONS.DUPLICATE_ITEM,
|
||||||
|
selection
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPropertyFromPath(object, path) {
|
#getPropertyFromPath(object, path) {
|
||||||
let splitPath = path.split('.');
|
let splitPath = path.split('.');
|
||||||
let property = Object.assign({}, object);
|
let property = Object.assign({}, object);
|
||||||
|
|
||||||
@ -498,13 +534,13 @@ define(['lodash'], function (_) {
|
|||||||
return property;
|
return property;
|
||||||
}
|
}
|
||||||
|
|
||||||
function areAllViews(type, path, selection) {
|
#areAllViews(type, path, selection) {
|
||||||
let allTelemetry = true;
|
let allTelemetry = true;
|
||||||
|
|
||||||
selection.forEach((selectedItem) => {
|
selection.forEach((selectedItem) => {
|
||||||
let selectedItemContext = selectedItem[0].context;
|
let selectedItemContext = selectedItem[0].context;
|
||||||
|
|
||||||
if (getPropertyFromPath(selectedItemContext, path) !== type) {
|
if (this.#getPropertyFromPath(selectedItemContext, path) !== type) {
|
||||||
allTelemetry = false;
|
allTelemetry = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -512,9 +548,9 @@ define(['lodash'], function (_) {
|
|||||||
return allTelemetry;
|
return allTelemetry;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getToggleUnitsButton(selectedParent, selection) {
|
#getToggleUnitsButton(selectedParent, selection) {
|
||||||
let applicableItems = getAllOfType(selection, 'telemetry-view');
|
let applicableItems = this.#getAllOfType(selection, 'telemetry-view');
|
||||||
applicableItems = unitsOnly(applicableItems);
|
applicableItems = this.#unitsOnly(applicableItems);
|
||||||
if (!applicableItems.length) {
|
if (!applicableItems.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -523,8 +559,8 @@ define(['lodash'], function (_) {
|
|||||||
control: 'toggle-button',
|
control: 'toggle-button',
|
||||||
domainObject: selectedParent,
|
domainObject: selectedParent,
|
||||||
applicableSelectedItems: applicableItems,
|
applicableSelectedItems: applicableItems,
|
||||||
property: function (selectionPath) {
|
property: (selectionPath) => {
|
||||||
return getPath(selectionPath) + '.showUnits';
|
return this.#getPath(selectionPath) + '.showUnits';
|
||||||
},
|
},
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
@ -543,10 +579,10 @@ define(['lodash'], function (_) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function unitsOnly(items) {
|
#unitsOnly(items) {
|
||||||
let results = items.filter((item) => {
|
let results = items.filter((item) => {
|
||||||
let currentItem = item[0];
|
let currentItem = item[0];
|
||||||
let metadata = openmct.telemetry.getMetadata(currentItem.context.item);
|
let metadata = this.#openmct.telemetry.getMetadata(currentItem.context.item);
|
||||||
if (!metadata) {
|
if (!metadata) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -559,10 +595,8 @@ define(['lodash'], function (_) {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getViewSwitcherMenu(selectedParent, selectionPath, selection) {
|
#getViewSwitcherMenu(selectedParent, selectionPath, selection) {
|
||||||
if (selection.length === 1) {
|
if (selection.length === 1) {
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
let displayLayoutContext = selectionPath[1].context;
|
|
||||||
let selectedItemContext = selectionPath[0].context;
|
let selectedItemContext = selectionPath[0].context;
|
||||||
let selectedItemType = selectedItemContext.item.type;
|
let selectedItemType = selectedItemContext.item.type;
|
||||||
|
|
||||||
@ -573,17 +607,19 @@ define(['lodash'], function (_) {
|
|||||||
let viewOptions = APPLICABLE_VIEWS[selectedItemType];
|
let viewOptions = APPLICABLE_VIEWS[selectedItemType];
|
||||||
|
|
||||||
if (viewOptions) {
|
if (viewOptions) {
|
||||||
|
const domainObject = selectedParent;
|
||||||
|
const keyString = this.#openmct.objects.makeKeyString(domainObject.identifier);
|
||||||
return {
|
return {
|
||||||
control: 'menu',
|
control: 'menu',
|
||||||
domainObject: selectedParent,
|
domainObject,
|
||||||
icon: 'icon-object',
|
icon: 'icon-object',
|
||||||
title: 'Switch the way this telemetry is displayed',
|
title: 'Switch the way this telemetry is displayed',
|
||||||
label: 'View type',
|
label: 'View type',
|
||||||
options: viewOptions,
|
options: viewOptions,
|
||||||
method: function (option) {
|
method: (option) => {
|
||||||
openmct.objectViews.emit(
|
this.#openmct.objectViews.emit(
|
||||||
'contextAction',
|
`${CONTEXT_ACTION}:${keyString}`,
|
||||||
'switchViewType',
|
CONTEXT_ACTIONS.SWITCH_VIEW_TYPE,
|
||||||
selectedItemContext,
|
selectedItemContext,
|
||||||
option.value,
|
option.value,
|
||||||
selection
|
selection
|
||||||
@ -592,34 +628,36 @@ define(['lodash'], function (_) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else if (selection.length > 1) {
|
} else if (selection.length > 1) {
|
||||||
if (areAllViews('telemetry-view', 'layoutItem.type', selection)) {
|
const domainObject = selectedParent;
|
||||||
|
const keyString = this.#openmct.objects.makeKeyString(domainObject.identifier);
|
||||||
|
if (this.#areAllViews('telemetry-view', 'layoutItem.type', selection)) {
|
||||||
return {
|
return {
|
||||||
control: 'menu',
|
control: 'menu',
|
||||||
domainObject: selectedParent,
|
domainObject,
|
||||||
icon: 'icon-object',
|
icon: 'icon-object',
|
||||||
title: 'Merge into a telemetry table or plot',
|
title: 'Merge into a telemetry table or plot',
|
||||||
label: 'View type',
|
label: 'View type',
|
||||||
options: APPLICABLE_VIEWS['telemetry-view-multi'],
|
options: APPLICABLE_VIEWS['telemetry-view-multi'],
|
||||||
method: function (option) {
|
method: (option) => {
|
||||||
openmct.objectViews.emit(
|
this.#openmct.objectViews.emit(
|
||||||
'contextAction',
|
`${CONTEXT_ACTION}:${keyString}`,
|
||||||
'mergeMultipleTelemetryViews',
|
CONTEXT_ACTIONS.MERGE_MULTIPLE_TELEMETRY_VIEWS,
|
||||||
selection,
|
selection,
|
||||||
option.value
|
option.value
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else if (areAllViews('telemetry.plot.overlay', 'item.type', selection)) {
|
} else if (this.#areAllViews('telemetry.plot.overlay', 'item.type', selection)) {
|
||||||
return {
|
return {
|
||||||
control: 'menu',
|
control: 'menu',
|
||||||
domainObject: selectedParent,
|
domainObject,
|
||||||
icon: 'icon-object',
|
icon: 'icon-object',
|
||||||
title: 'Merge into a stacked plot',
|
title: 'Merge into a stacked plot',
|
||||||
options: APPLICABLE_VIEWS['telemetry.plot.overlay-multi'],
|
options: APPLICABLE_VIEWS['telemetry.plot.overlay-multi'],
|
||||||
method: function (option) {
|
method: (option) => {
|
||||||
openmct.objectViews.emit(
|
this.#openmct.objectViews.emit(
|
||||||
'contextAction',
|
`${CONTEXT_ACTION}:${keyString}`,
|
||||||
'mergeMultipleOverlayPlots',
|
CONTEXT_ACTIONS.MERGE_MULTIPLE_OVERLAY_PLOTS,
|
||||||
selection,
|
selection,
|
||||||
option.value
|
option.value
|
||||||
);
|
);
|
||||||
@ -629,7 +667,7 @@ define(['lodash'], function (_) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getToggleGridButton(selection, selectionPath) {
|
#getToggleGridButton(selection, selectionPath) {
|
||||||
const ICON_GRID_SHOW = 'icon-grid-on';
|
const ICON_GRID_SHOW = 'icon-grid-on';
|
||||||
const ICON_GRID_HIDE = 'icon-grid-off';
|
const ICON_GRID_HIDE = 'icon-grid-off';
|
||||||
|
|
||||||
@ -641,12 +679,18 @@ define(['lodash'], function (_) {
|
|||||||
displayLayoutContext = selectionPath[1].context;
|
displayLayoutContext = selectionPath[1].context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const domainObject = displayLayoutContext.item;
|
||||||
|
const keyString = this.#openmct.objects.makeKeyString(domainObject.identifier);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
control: 'button',
|
control: 'button',
|
||||||
domainObject: displayLayoutContext.item,
|
domainObject,
|
||||||
icon: ICON_GRID_SHOW,
|
icon: ICON_GRID_SHOW,
|
||||||
method: function () {
|
method: () => {
|
||||||
openmct.objectViews.emit('contextAction', 'toggleGrid');
|
this.#openmct.objectViews.emit(
|
||||||
|
`${CONTEXT_ACTION}:${keyString}`,
|
||||||
|
CONTEXT_ACTIONS.TOGGLE_GRID
|
||||||
|
);
|
||||||
|
|
||||||
this.icon = this.icon === ICON_GRID_SHOW ? ICON_GRID_HIDE : ICON_GRID_SHOW;
|
this.icon = this.icon === ICON_GRID_SHOW ? ICON_GRID_HIDE : ICON_GRID_SHOW;
|
||||||
},
|
},
|
||||||
@ -654,30 +698,37 @@ define(['lodash'], function (_) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSeparator() {
|
#getSeparator() {
|
||||||
return {
|
return {
|
||||||
control: 'separator'
|
control: 'separator'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function isMainLayoutSelected(selectionPath) {
|
#isMainLayoutSelected(selectionPath) {
|
||||||
let selectedObject = selectionPath[0].context.item;
|
let selectedObject = selectionPath[0].context.item;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
selectedObject &&
|
selectedObject && selectedObject.type === 'layout' && !selectionPath[0].context.layoutItem
|
||||||
selectedObject.type === 'layout' &&
|
|
||||||
!selectionPath[0].context.layoutItem
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showForm(formStructure, name, selectionPath) {
|
#showForm(formStructure, name, selection) {
|
||||||
openmct.forms.showForm(formStructure).then((changes) => {
|
const domainObject = selection[0][0].context.item;
|
||||||
openmct.objectViews.emit('contextAction', 'addElement', name, changes);
|
const keyString = this.#openmct.objects.makeKeyString(domainObject.identifier);
|
||||||
|
this.#openmct.forms.showForm(formStructure).then((changes) => {
|
||||||
|
this.#openmct.objectViews.emit(
|
||||||
|
`${CONTEXT_ACTION}:${keyString}`,
|
||||||
|
CONTEXT_ACTIONS.ADD_ELEMENT,
|
||||||
|
name,
|
||||||
|
changes,
|
||||||
|
selection
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMainLayoutSelected(selectedObjects[0])) {
|
toolbar(selectedObjects) {
|
||||||
return [getToggleGridButton(selectedObjects), getAddButton(selectedObjects)];
|
if (this.#isMainLayoutSelected(selectedObjects[0])) {
|
||||||
|
return [this.#getToggleGridButton(selectedObjects), this.#getAddButton(selectedObjects)];
|
||||||
}
|
}
|
||||||
|
|
||||||
let toolbar = {
|
let toolbar = {
|
||||||
@ -705,48 +756,47 @@ define(['lodash'], function (_) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (layoutItem.type === 'subobject-view') {
|
if (layoutItem.type === 'subobject-view') {
|
||||||
if (
|
if (toolbar['add-menu'].length === 0 && selectionPath[0].context.item.type === 'layout') {
|
||||||
toolbar['add-menu'].length === 0 &&
|
toolbar['add-menu'] = [this.#getAddButton(selectedObjects, selectionPath)];
|
||||||
selectionPath[0].context.item.type === 'layout'
|
|
||||||
) {
|
|
||||||
toolbar['add-menu'] = [getAddButton(selectedObjects, selectionPath)];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolbar['toggle-frame'].length === 0) {
|
if (toolbar['toggle-frame'].length === 0) {
|
||||||
toolbar['toggle-frame'] = [getToggleFrameButton(selectedParent, selectedObjects)];
|
toolbar['toggle-frame'] = [this.#getToggleFrameButton(selectedParent, selectedObjects)];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolbar.position.length === 0) {
|
if (toolbar.position.length === 0) {
|
||||||
toolbar.position = [
|
toolbar.position = [
|
||||||
getStackOrder(selectedParent, selectionPath),
|
this.#getStackOrder(selectedParent, selectionPath, selectedObjects),
|
||||||
getSeparator(),
|
this.#getSeparator(),
|
||||||
getXInput(selectedParent, selectedObjects),
|
this.#getXInput(selectedParent, selectedObjects),
|
||||||
getYInput(selectedParent, selectedObjects),
|
this.#getYInput(selectedParent, selectedObjects),
|
||||||
getHeightInput(selectedParent, selectedObjects),
|
this.#getHeightInput(selectedParent, selectedObjects),
|
||||||
getWidthInput(selectedParent, selectedObjects)
|
this.#getWidthInput(selectedParent, selectedObjects)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolbar.remove.length === 0) {
|
if (toolbar.remove.length === 0) {
|
||||||
toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)];
|
toolbar.remove = [this.#getRemoveButton(selectedParent, selectionPath, selectedObjects)];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolbar.viewSwitcher.length === 0) {
|
if (toolbar.viewSwitcher.length === 0) {
|
||||||
toolbar.viewSwitcher = [
|
toolbar.viewSwitcher = [
|
||||||
getViewSwitcherMenu(selectedParent, selectionPath, selectedObjects)
|
this.#getViewSwitcherMenu(selectedParent, selectionPath, selectedObjects)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
} else if (layoutItem.type === 'telemetry-view') {
|
} else if (layoutItem.type === 'telemetry-view') {
|
||||||
if (toolbar['display-mode'].length === 0) {
|
if (toolbar['display-mode'].length === 0) {
|
||||||
toolbar['display-mode'] = [getDisplayModeMenu(selectedParent, selectedObjects)];
|
toolbar['display-mode'] = [this.#getDisplayModeMenu(selectedParent, selectedObjects)];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolbar['telemetry-value'].length === 0) {
|
if (toolbar['telemetry-value'].length === 0) {
|
||||||
toolbar['telemetry-value'] = [getTelemetryValueMenu(selectionPath, selectedObjects)];
|
toolbar['telemetry-value'] = [
|
||||||
|
this.#getTelemetryValueMenu(selectionPath, selectedObjects)
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolbar['unit-toggle'].length === 0) {
|
if (toolbar['unit-toggle'].length === 0) {
|
||||||
let toggleUnitsButton = getToggleUnitsButton(selectedParent, selectedObjects);
|
let toggleUnitsButton = this.#getToggleUnitsButton(selectedParent, selectedObjects);
|
||||||
if (toggleUnitsButton) {
|
if (toggleUnitsButton) {
|
||||||
toolbar['unit-toggle'] = [toggleUnitsButton];
|
toolbar['unit-toggle'] = [toggleUnitsButton];
|
||||||
}
|
}
|
||||||
@ -754,98 +804,98 @@ define(['lodash'], function (_) {
|
|||||||
|
|
||||||
if (toolbar.position.length === 0) {
|
if (toolbar.position.length === 0) {
|
||||||
toolbar.position = [
|
toolbar.position = [
|
||||||
getStackOrder(selectedParent, selectionPath),
|
this.#getStackOrder(selectedParent, selectionPath, selectedObjects),
|
||||||
getSeparator(),
|
this.#getSeparator(),
|
||||||
getXInput(selectedParent, selectedObjects),
|
this.#getXInput(selectedParent, selectedObjects),
|
||||||
getYInput(selectedParent, selectedObjects),
|
this.#getYInput(selectedParent, selectedObjects),
|
||||||
getHeightInput(selectedParent, selectedObjects),
|
this.#getHeightInput(selectedParent, selectedObjects),
|
||||||
getWidthInput(selectedParent, selectedObjects)
|
this.#getWidthInput(selectedParent, selectedObjects)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolbar.remove.length === 0) {
|
if (toolbar.remove.length === 0) {
|
||||||
toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)];
|
toolbar.remove = [this.#getRemoveButton(selectedParent, selectionPath, selectedObjects)];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolbar.viewSwitcher.length === 0) {
|
if (toolbar.viewSwitcher.length === 0) {
|
||||||
toolbar.viewSwitcher = [
|
toolbar.viewSwitcher = [
|
||||||
getViewSwitcherMenu(selectedParent, selectionPath, selectedObjects)
|
this.#getViewSwitcherMenu(selectedParent, selectionPath, selectedObjects)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
} else if (layoutItem.type === 'text-view') {
|
} else if (layoutItem.type === 'text-view') {
|
||||||
if (toolbar.position.length === 0) {
|
if (toolbar.position.length === 0) {
|
||||||
toolbar.position = [
|
toolbar.position = [
|
||||||
getStackOrder(selectedParent, selectionPath),
|
this.#getStackOrder(selectedParent, selectionPath, selectedObjects),
|
||||||
getSeparator(),
|
this.#getSeparator(),
|
||||||
getXInput(selectedParent, selectedObjects),
|
this.#getXInput(selectedParent, selectedObjects),
|
||||||
getYInput(selectedParent, selectedObjects),
|
this.#getYInput(selectedParent, selectedObjects),
|
||||||
getHeightInput(selectedParent, selectedObjects),
|
this.#getHeightInput(selectedParent, selectedObjects),
|
||||||
getWidthInput(selectedParent, selectedObjects)
|
this.#getWidthInput(selectedParent, selectedObjects)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolbar.text.length === 0) {
|
if (toolbar.text.length === 0) {
|
||||||
toolbar.text = [getTextButton(selectedParent, selectedObjects)];
|
toolbar.text = [this.#getTextButton(selectedParent, selectedObjects)];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolbar.remove.length === 0) {
|
if (toolbar.remove.length === 0) {
|
||||||
toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)];
|
toolbar.remove = [this.#getRemoveButton(selectedParent, selectionPath, selectedObjects)];
|
||||||
}
|
}
|
||||||
} else if (layoutItem.type === 'box-view' || layoutItem.type === 'ellipse-view') {
|
} else if (layoutItem.type === 'box-view' || layoutItem.type === 'ellipse-view') {
|
||||||
if (toolbar.position.length === 0) {
|
if (toolbar.position.length === 0) {
|
||||||
toolbar.position = [
|
toolbar.position = [
|
||||||
getStackOrder(selectedParent, selectionPath),
|
this.#getStackOrder(selectedParent, selectionPath, selectedObjects),
|
||||||
getSeparator(),
|
this.#getSeparator(),
|
||||||
getXInput(selectedParent, selectedObjects),
|
this.#getXInput(selectedParent, selectedObjects),
|
||||||
getYInput(selectedParent, selectedObjects),
|
this.#getYInput(selectedParent, selectedObjects),
|
||||||
getHeightInput(selectedParent, selectedObjects),
|
this.#getHeightInput(selectedParent, selectedObjects),
|
||||||
getWidthInput(selectedParent, selectedObjects)
|
this.#getWidthInput(selectedParent, selectedObjects)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolbar.remove.length === 0) {
|
if (toolbar.remove.length === 0) {
|
||||||
toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)];
|
toolbar.remove = [this.#getRemoveButton(selectedParent, selectionPath, selectedObjects)];
|
||||||
}
|
}
|
||||||
} else if (layoutItem.type === 'image-view') {
|
} else if (layoutItem.type === 'image-view') {
|
||||||
if (toolbar.position.length === 0) {
|
if (toolbar.position.length === 0) {
|
||||||
toolbar.position = [
|
toolbar.position = [
|
||||||
getStackOrder(selectedParent, selectionPath),
|
this.#getStackOrder(selectedParent, selectionPath, selectedObjects),
|
||||||
getSeparator(),
|
this.#getSeparator(),
|
||||||
getXInput(selectedParent, selectedObjects),
|
this.#getXInput(selectedParent, selectedObjects),
|
||||||
getYInput(selectedParent, selectedObjects),
|
this.#getYInput(selectedParent, selectedObjects),
|
||||||
getHeightInput(selectedParent, selectedObjects),
|
this.#getHeightInput(selectedParent, selectedObjects),
|
||||||
getWidthInput(selectedParent, selectedObjects)
|
this.#getWidthInput(selectedParent, selectedObjects)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolbar.remove.length === 0) {
|
if (toolbar.remove.length === 0) {
|
||||||
toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)];
|
toolbar.remove = [this.#getRemoveButton(selectedParent, selectionPath, selectedObjects)];
|
||||||
}
|
}
|
||||||
} else if (layoutItem.type === 'line-view') {
|
} else if (layoutItem.type === 'line-view') {
|
||||||
if (toolbar.position.length === 0) {
|
if (toolbar.position.length === 0) {
|
||||||
toolbar.position = [
|
toolbar.position = [
|
||||||
getStackOrder(selectedParent, selectionPath),
|
this.#getStackOrder(selectedParent, selectionPath),
|
||||||
getSeparator(),
|
this.#getSeparator(),
|
||||||
getXInput(selectedParent, selectedObjects),
|
this.#getXInput(selectedParent, selectedObjects),
|
||||||
getYInput(selectedParent, selectedObjects),
|
this.#getYInput(selectedParent, selectedObjects),
|
||||||
getX2Input(selectedParent, selectedObjects),
|
this.#getX2Input(selectedParent, selectedObjects),
|
||||||
getY2Input(selectedParent, selectedObjects)
|
this.#getY2Input(selectedParent, selectedObjects)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolbar.remove.length === 0) {
|
if (toolbar.remove.length === 0) {
|
||||||
toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)];
|
toolbar.remove = [this.#getRemoveButton(selectedParent, selectionPath, selectedObjects)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolbar.duplicate.length === 0) {
|
if (toolbar.duplicate.length === 0) {
|
||||||
toolbar.duplicate = [
|
toolbar.duplicate = [
|
||||||
getDuplicateButton(selectedParent, selectionPath, selectedObjects)
|
this.#getDuplicateButton(selectedParent, selectionPath, selectedObjects)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolbar['toggle-grid'].length === 0) {
|
if (toolbar['toggle-grid'].length === 0) {
|
||||||
toolbar['toggle-grid'] = [getToggleGridButton(selectedObjects, selectionPath)];
|
toolbar['toggle-grid'] = [this.#getToggleGridButton(selectedObjects, selectionPath)];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -859,7 +909,7 @@ define(['lodash'], function (_) {
|
|||||||
accumulator.push(group);
|
accumulator.push(group);
|
||||||
|
|
||||||
if (index < toolbarArray.length - 1) {
|
if (index < toolbarArray.length - 1) {
|
||||||
accumulator.push(getSeparator());
|
accumulator.push(this.#getSeparator());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -867,8 +917,4 @@ define(['lodash'], function (_) {
|
|||||||
}, [])
|
}, [])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return DisplayLayoutToolbar;
|
|
||||||
});
|
|
||||||
|
@ -33,6 +33,9 @@
|
|||||||
class="c-box-view u-style-receiver js-style-receiver"
|
class="c-box-view u-style-receiver js-style-receiver"
|
||||||
:class="[styleClass]"
|
:class="[styleClass]"
|
||||||
:style="style"
|
:style="style"
|
||||||
|
role="application"
|
||||||
|
aria-roledescription="draggable box"
|
||||||
|
aria-label="Box"
|
||||||
></div>
|
></div>
|
||||||
</template>
|
</template>
|
||||||
</layout-frame>
|
</layout-frame>
|
||||||
|
@ -36,6 +36,8 @@
|
|||||||
:grid-size="gridSize"
|
:grid-size="gridSize"
|
||||||
:show-grid="showGrid"
|
:show-grid="showGrid"
|
||||||
:grid-dimensions="gridDimensions"
|
:grid-dimensions="gridDimensions"
|
||||||
|
:aria-label="`${domainObject.name} Layout Grid`"
|
||||||
|
:aria-hidden="showGrid ? 'false' : 'true'"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-if="shouldDisplayLayoutDimensions"
|
v-if="shouldDisplayLayoutDimensions"
|
||||||
|
@ -20,7 +20,14 @@
|
|||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<div class="l-layout__grid-holder" :class="{ 'c-grid': showGrid }">
|
<div
|
||||||
|
class="l-layout__grid-holder"
|
||||||
|
:class="{ 'c-grid': showGrid }"
|
||||||
|
role="grid"
|
||||||
|
:aria-label="'Layout Grid'"
|
||||||
|
:aria-hidden="showGrid ? 'false' : 'true'"
|
||||||
|
:aria-live="showGrid ? 'polite' : 'off'"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
v-if="gridSize[0] >= 3"
|
v-if="gridSize[0] >= 3"
|
||||||
class="c-grid__x l-grid l-grid-x"
|
class="c-grid__x l-grid l-grid-x"
|
||||||
|
@ -33,6 +33,9 @@
|
|||||||
class="c-ellipse-view u-style-receiver js-style-receiver"
|
class="c-ellipse-view u-style-receiver js-style-receiver"
|
||||||
:class="[styleClass]"
|
:class="[styleClass]"
|
||||||
:style="style"
|
:style="style"
|
||||||
|
role="application"
|
||||||
|
aria-roledescription="draggable ellipse"
|
||||||
|
aria-label="Ellipse"
|
||||||
></div>
|
></div>
|
||||||
</template>
|
</template>
|
||||||
</layout-frame>
|
</layout-frame>
|
||||||
|
@ -30,13 +30,18 @@
|
|||||||
:style="style"
|
:style="style"
|
||||||
>
|
>
|
||||||
<slot name="content"></slot>
|
<slot name="content"></slot>
|
||||||
<div class="c-frame__move-bar" @mousedown.left="startMove($event)"></div>
|
<div
|
||||||
|
class="c-frame__move-bar"
|
||||||
|
:aria-label="`Move ${typeName} Frame`"
|
||||||
|
@mousedown.left="startMove($event)"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
import DRAWING_OBJECT_TYPES from '../DrawingObjectTypes';
|
||||||
import LayoutDrag from './../LayoutDrag';
|
import LayoutDrag from './../LayoutDrag';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -58,6 +63,13 @@ export default {
|
|||||||
},
|
},
|
||||||
emits: ['move', 'end-move'],
|
emits: ['move', 'end-move'],
|
||||||
computed: {
|
computed: {
|
||||||
|
typeName() {
|
||||||
|
const { type } = this.item;
|
||||||
|
if (DRAWING_OBJECT_TYPES[type]) {
|
||||||
|
return DRAWING_OBJECT_TYPES[type].name;
|
||||||
|
}
|
||||||
|
return 'Sub-object';
|
||||||
|
},
|
||||||
size() {
|
size() {
|
||||||
let { width, height } = this.item;
|
let { width, height } = this.item;
|
||||||
|
|
||||||
|
@ -21,7 +21,14 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<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"
|
||||||
|
aria-role="application"
|
||||||
|
aria-roledescription="draggable line"
|
||||||
|
aria-label="Line"
|
||||||
|
>
|
||||||
<svg width="100%" height="100%">
|
<svg width="100%" height="100%">
|
||||||
<line
|
<line
|
||||||
v-bind="linePosition"
|
v-bind="linePosition"
|
||||||
|
@ -35,6 +35,9 @@
|
|||||||
:data-font="item.font"
|
:data-font="item.font"
|
||||||
:class="[styleClass]"
|
:class="[styleClass]"
|
||||||
:style="style"
|
:style="style"
|
||||||
|
role="application"
|
||||||
|
aria-roledescription="draggable text"
|
||||||
|
aria-label="Text"
|
||||||
>
|
>
|
||||||
<div class="c-text-view__text">{{ item.text }}</div>
|
<div class="c-text-view__text">{{ item.text }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -85,10 +85,9 @@ class DisplayLayoutView {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
contextAction() {
|
contextAction(action, ...rest) {
|
||||||
const action = arguments[0];
|
if (this?.component.$refs.displayLayout[action]) {
|
||||||
if (this.component && this.component.$refs.displayLayout[action]) {
|
this.component.$refs.displayLayout[action](...rest);
|
||||||
this.component.$refs.displayLayout[action](...Array.from(arguments).splice(1));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,10 +81,9 @@ export default class FlexibleLayoutViewProvider {
|
|||||||
type: 'flexible-layout'
|
type: 'flexible-layout'
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
contextAction() {
|
contextAction(action, ...args) {
|
||||||
const action = arguments[0];
|
if (component?.$refs?.flexibleLayout?.[action]) {
|
||||||
if (component && component.$refs.flexibleLayout[action]) {
|
component.$refs.flexibleLayout[action](...args);
|
||||||
component.$refs.flexibleLayout[action](...Array.from(arguments).splice(1));
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onEditModeChange(isEditing) {
|
onEditModeChange(isEditing) {
|
||||||
|
@ -84,7 +84,8 @@ function ToolbarProvider(openmct) {
|
|||||||
let containerIndex = containers.indexOf(container);
|
let containerIndex = containers.indexOf(container);
|
||||||
let frame = container && container.frames.filter((f) => f.id === frameId)[0];
|
let frame = container && container.frames.filter((f) => f.id === frameId)[0];
|
||||||
let frameIndex = container && container.frames.indexOf(frame);
|
let frameIndex = container && container.frames.indexOf(frame);
|
||||||
|
const primaryKeyString = openmct.objects.makeKeyString(primary.context.item.identifier);
|
||||||
|
const tertiaryKeyString = openmct.objects.makeKeyString(tertiary.context.item.identifier);
|
||||||
deleteFrame = {
|
deleteFrame = {
|
||||||
control: 'button',
|
control: 'button',
|
||||||
domainObject: primary.context.item,
|
domainObject: primary.context.item,
|
||||||
@ -98,7 +99,7 @@ function ToolbarProvider(openmct) {
|
|||||||
emphasis: 'true',
|
emphasis: 'true',
|
||||||
callback: function () {
|
callback: function () {
|
||||||
openmct.objectViews.emit(
|
openmct.objectViews.emit(
|
||||||
'contextAction',
|
`contextAction:${primaryKeyString}`,
|
||||||
'deleteFrame',
|
'deleteFrame',
|
||||||
primary.context.frameId
|
primary.context.frameId
|
||||||
);
|
);
|
||||||
@ -135,11 +136,12 @@ function ToolbarProvider(openmct) {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
addContainer = {
|
addContainer = {
|
||||||
control: 'button',
|
control: 'button',
|
||||||
domainObject: tertiary.context.item,
|
domainObject: tertiary.context.item,
|
||||||
method: function () {
|
method: function (...args) {
|
||||||
openmct.objectViews.emit('contextAction', 'addContainer', ...arguments);
|
openmct.objectViews.emit(`contextAction:${tertiaryKeyString}`, 'addContainer', ...args);
|
||||||
},
|
},
|
||||||
key: 'add',
|
key: 'add',
|
||||||
icon: 'icon-plus-in-rect',
|
icon: 'icon-plus-in-rect',
|
||||||
@ -185,11 +187,14 @@ function ToolbarProvider(openmct) {
|
|||||||
title: 'Remove Container'
|
title: 'Remove Container'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const domainObject = secondary.context.item;
|
||||||
|
const keyString = openmct.objects.makeKeyString(domainObject.identifier);
|
||||||
|
|
||||||
addContainer = {
|
addContainer = {
|
||||||
control: 'button',
|
control: 'button',
|
||||||
domainObject: secondary.context.item,
|
domainObject,
|
||||||
method: function () {
|
method: function (...args) {
|
||||||
openmct.objectViews.emit('contextAction', 'addContainer', ...arguments);
|
openmct.objectViews.emit(`contextAction:${keyString}`, 'addContainer', ...args);
|
||||||
},
|
},
|
||||||
key: 'add',
|
key: 'add',
|
||||||
icon: 'icon-plus-in-rect',
|
icon: 'icon-plus-in-rect',
|
||||||
@ -200,11 +205,14 @@ function ToolbarProvider(openmct) {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const domainObject = primary.context.item;
|
||||||
|
const keyString = openmct.objects.makeKeyString(domainObject.identifier);
|
||||||
|
|
||||||
addContainer = {
|
addContainer = {
|
||||||
control: 'button',
|
control: 'button',
|
||||||
domainObject: primary.context.item,
|
domainObject,
|
||||||
method: function () {
|
method: function (...args) {
|
||||||
openmct.objectViews.emit('contextAction', 'addContainer', ...arguments);
|
openmct.objectViews.emit(`contextAction:${keyString}`, 'addContainer', ...args);
|
||||||
},
|
},
|
||||||
key: 'add',
|
key: 'add',
|
||||||
icon: 'icon-plus-in-rect',
|
icon: 'icon-plus-in-rect',
|
||||||
|
@ -34,9 +34,6 @@ import { STYLE_CONSTANTS } from '@/plugins/condition/utils/constants';
|
|||||||
import stalenessMixin from '@/ui/mixins/staleness-mixin';
|
import stalenessMixin from '@/ui/mixins/staleness-mixin';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
|
||||||
// IndependentTimeConductor
|
|
||||||
},
|
|
||||||
mixins: [stalenessMixin],
|
mixins: [stalenessMixin],
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
props: {
|
props: {
|
||||||
@ -181,7 +178,9 @@ export default {
|
|||||||
this.triggerUnsubscribeFromStaleness(this.domainObject);
|
this.triggerUnsubscribeFromStaleness(this.domainObject);
|
||||||
|
|
||||||
this.openmct.objectViews.off('clearData', this.clearData);
|
this.openmct.objectViews.off('clearData', this.clearData);
|
||||||
this.openmct.objectViews.off('contextAction', this.performContextAction);
|
if (this.contextActionEvent) {
|
||||||
|
this.openmct.objectViews.off(this.contextActionEvent, this.performContextAction);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
getStyleReceiver() {
|
getStyleReceiver() {
|
||||||
let styleReceiver;
|
let styleReceiver;
|
||||||
@ -301,8 +300,11 @@ export default {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.contextActionEvent = `contextAction:${this.openmct.objects.makeKeyString(
|
||||||
|
this.domainObject.identifier
|
||||||
|
)}`;
|
||||||
this.openmct.objectViews.on('clearData', this.clearData);
|
this.openmct.objectViews.on('clearData', this.clearData);
|
||||||
this.openmct.objectViews.on('contextAction', this.performContextAction);
|
this.openmct.objectViews.on(this.contextActionEvent, this.performContextAction);
|
||||||
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.updateStyle(this.styleRuleManager?.currentStyle);
|
this.updateStyle(this.styleRuleManager?.currentStyle);
|
||||||
@ -473,9 +475,9 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
performContextAction() {
|
performContextAction(...args) {
|
||||||
if (this.currentView.contextAction) {
|
if (this?.currentView?.contextAction) {
|
||||||
this.currentView.contextAction(...arguments);
|
this.currentView.contextAction(...args);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isEditingAllowed() {
|
isEditingAllowed() {
|
||||||
|
@ -31,15 +31,19 @@
|
|||||||
{{ options.label }}
|
{{ options.label }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="open" class="c-menu">
|
<div v-if="open" class="c-menu" role="menu">
|
||||||
<ul>
|
<ul>
|
||||||
<li
|
<li
|
||||||
v-for="(option, index) in options.options"
|
v-for="(option, index) in options.options"
|
||||||
:key="index"
|
:key="index"
|
||||||
:class="option.class"
|
:class="option.class"
|
||||||
|
role="menuitem"
|
||||||
|
:aria-labelledby="`${option.name}-menuitem-label`"
|
||||||
@click="onClick(option)"
|
@click="onClick(option)"
|
||||||
>
|
>
|
||||||
|
<span :id="`${option.name}-menuitem-label`">
|
||||||
{{ option.name }}
|
{{ option.name }}
|
||||||
|
</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user