Compare commits
2 Commits
dep-securi
...
fix-lad-co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
637cf7a12f | ||
|
|
5a2afece06 |
20
.github/workflows/dependency-review.yml
vendored
20
.github/workflows/dependency-review.yml
vendored
@@ -1,20 +0,0 @@
|
||||
# Dependency Review Action
|
||||
#
|
||||
# This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging.
|
||||
#
|
||||
# Source repository: https://github.com/actions/dependency-review-action
|
||||
# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement
|
||||
name: 'Dependency Review'
|
||||
on: [pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
dependency-review:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 'Checkout Repository'
|
||||
uses: actions/checkout@v3
|
||||
- name: 'Dependency Review'
|
||||
uses: actions/dependency-review-action@v2
|
||||
@@ -20,11 +20,11 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
const { test } = require('../../pluginFixtures');
|
||||
const { setBoundsToSpanAllActivities } = require('../../helper/planningUtils');
|
||||
const { createDomainObjectWithDefaults, createPlanFromJSON } = require('../../appActions');
|
||||
const { test } = require('../../../pluginFixtures');
|
||||
const { setBoundsToSpanAllActivities } = require('../../../helper/planningUtils');
|
||||
const { createDomainObjectWithDefaults, createPlanFromJSON } = require('../../../appActions');
|
||||
const percySnapshot = require('@percy/playwright');
|
||||
const examplePlanLarge = require('../../test-data/examplePlans/ExamplePlan_Large.json');
|
||||
const examplePlanLarge = require('../../../test-data/ExamplePlan_Large.json');
|
||||
|
||||
test.describe('Visual - Planning', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
@@ -52,9 +52,10 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
|
||||
|
||||
//Set object identifier from url
|
||||
conditionSetUrl = page.url();
|
||||
console.log('conditionSetUrl ' + conditionSetUrl);
|
||||
|
||||
getConditionSetIdentifierFromUrl = conditionSetUrl.split('/').pop().split('?')[0];
|
||||
console.debug(`getConditionSetIdentifierFromUrl: ${getConditionSetIdentifierFromUrl}`);
|
||||
console.debug('getConditionSetIdentifierFromUrl ' + getConditionSetIdentifierFromUrl);
|
||||
await page.close();
|
||||
});
|
||||
|
||||
@@ -245,81 +246,4 @@ test.describe('Basic Condition Set Use', () => {
|
||||
await expect(page.getByRole('menuitem', { name: /Plot/ })).toBeVisible();
|
||||
await expect(page.getByRole('menuitem', { name: /Telemetry Table/ })).toBeVisible();
|
||||
});
|
||||
test('ConditionSet should output blank instead of the default value', async ({ page }) => {
|
||||
//Navigate to baseURL
|
||||
await page.goto('./', { waitUntil: 'networkidle' });
|
||||
|
||||
//Click the Create button
|
||||
await page.click('button:has-text("Create")');
|
||||
|
||||
// Click the object specified by 'type'
|
||||
await page.click(`li[role='menuitem']:text("Sine Wave Generator")`);
|
||||
await page.getByRole('spinbutton', { name: 'Loading Delay (ms)' }).fill('8000');
|
||||
const nameInput = page.locator('form[name="mctForm"] .first input[type="text"]');
|
||||
await nameInput.fill("Delayed Sine Wave Generator");
|
||||
|
||||
// Click OK button and wait for Navigate event
|
||||
await Promise.all([
|
||||
page.waitForLoadState(),
|
||||
page.click('[aria-label="Save"]'),
|
||||
// Wait for Save Banner to appear
|
||||
page.waitForSelector('.c-message-banner__message')
|
||||
]);
|
||||
|
||||
// Create a new condition set
|
||||
await createDomainObjectWithDefaults(page, {
|
||||
type: 'Condition Set',
|
||||
name: "Test Blank Output of Condition Set"
|
||||
});
|
||||
// Change the object to edit mode
|
||||
await page.locator('[title="Edit"]').click();
|
||||
|
||||
// Click Add Condition button twice
|
||||
await page.locator('#addCondition').click();
|
||||
await page.locator('#addCondition').click();
|
||||
await page.locator('#conditionCollection').getByRole('textbox').nth(0).fill('First Condition');
|
||||
await page.locator('#conditionCollection').getByRole('textbox').nth(1).fill('Second Condition');
|
||||
|
||||
// Expand the 'My Items' folder in the left tree
|
||||
await page.locator('.c-tree__item__view-control.c-disclosure-triangle').first().click();
|
||||
// Add the Sine Wave Generator to the Condition Set and save changes
|
||||
const treePane = page.getByRole('tree', {
|
||||
name: 'Main Tree'
|
||||
});
|
||||
const sineWaveGeneratorTreeItem = treePane.getByRole('treeitem', { name: "Delayed Sine Wave Generator"});
|
||||
const conditionCollection = await page.locator('#conditionCollection');
|
||||
|
||||
await sineWaveGeneratorTreeItem.dragTo(conditionCollection);
|
||||
|
||||
const firstCriterionTelemetry = await page.locator('[aria-label="Criterion Telemetry Selection"] >> nth=0');
|
||||
firstCriterionTelemetry.selectOption({ label: 'Delayed Sine Wave Generator' });
|
||||
|
||||
const secondCriterionTelemetry = await page.locator('[aria-label="Criterion Telemetry Selection"] >> nth=1');
|
||||
secondCriterionTelemetry.selectOption({ label: 'Delayed Sine Wave Generator' });
|
||||
|
||||
const firstCriterionMetadata = await page.locator('[aria-label="Criterion Metadata Selection"] >> nth=0');
|
||||
firstCriterionMetadata.selectOption({ label: 'Sine' });
|
||||
|
||||
const secondCriterionMetadata = await page.locator('[aria-label="Criterion Metadata Selection"] >> nth=1');
|
||||
secondCriterionMetadata.selectOption({ label: 'Sine' });
|
||||
|
||||
const firstCriterionComparison = await page.locator('[aria-label="Criterion Comparison Selection"] >> nth=0');
|
||||
firstCriterionComparison.selectOption({ label: 'is greater than or equal to' });
|
||||
|
||||
const secondCriterionComparison = await page.locator('[aria-label="Criterion Comparison Selection"] >> nth=1');
|
||||
secondCriterionComparison.selectOption({ label: 'is less than' });
|
||||
|
||||
const firstCriterionInput = await page.locator('[aria-label="Criterion Input"] >> nth=0');
|
||||
await firstCriterionInput.fill("0");
|
||||
|
||||
const secondCriterionInput = await page.locator('[aria-label="Criterion Input"] >> nth=1');
|
||||
await secondCriterionInput.fill("0");
|
||||
|
||||
const saveButtonLocator = page.locator('button[title="Save"]');
|
||||
await saveButtonLocator.click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
|
||||
const outputValue = await page.locator('[aria-label="Current Output Value"]');
|
||||
await expect(outputValue).toHaveText('---');
|
||||
});
|
||||
});
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 16 KiB |
@@ -268,9 +268,6 @@ async function getCanvasPixelsWithData(page) {
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
async function assertLimitLinesExistAndAreVisible(page) {
|
||||
// Wait for plot series data to load
|
||||
await expect(page.locator('.js-series-data-loaded')).toBeVisible();
|
||||
// Wait for limit lines to be created
|
||||
await page.waitForSelector('.js-limit-area', { state: 'attached' });
|
||||
const limitLineCount = await page.locator('.c-plot-limit-line').count();
|
||||
// There should be 10 limit lines created by default
|
||||
|
||||
@@ -115,9 +115,6 @@ test.describe('Stacked Plot', () => {
|
||||
await expect(stackedPlotItem2).toHaveAttribute('aria-label', `Stacked Plot Item ${swgC.name}`);
|
||||
await expect(stackedPlotItem3).toHaveAttribute('aria-label', `Stacked Plot Item ${swgA.name}`);
|
||||
|
||||
// collapse inspector
|
||||
await page.locator('.l-shell__pane-inspector .l-pane__collapse-button').click();
|
||||
|
||||
// Save (exit edit mode)
|
||||
await page.locator('button[title="Save"]').click();
|
||||
await page.locator('li[title="Save and Finish Editing"]').click();
|
||||
|
||||
@@ -28,14 +28,6 @@ const { test, expect } = require('../../../../pluginFixtures');
|
||||
const { createDomainObjectWithDefaults, setRealTimeMode, setFixedTimeMode } = require('../../../../appActions');
|
||||
|
||||
test.describe('Plot Tagging', () => {
|
||||
/**
|
||||
* Given a canvas and a set of points, tags the points on the canvas.
|
||||
* @param {import('@playwright/test').Page} page
|
||||
* @param {HTMLCanvasElement} canvas a telemetry item with a plot
|
||||
* @param {Number} xEnd a telemetry item with a plot
|
||||
* @param {Number} yEnd a telemetry item with a plot
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async function createTags({page, canvas, xEnd, yEnd}) {
|
||||
await canvas.hover({trial: true});
|
||||
|
||||
@@ -72,20 +64,12 @@ test.describe('Plot Tagging', () => {
|
||||
await page.getByText('Science').click();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a telemetry item (e.g., a Sine Wave Generator) with a plot, tests that the plot can be tagged.
|
||||
* @param {import('@playwright/test').Page} page
|
||||
* @param {import('../../../../appActions').CreatedObjectInfo} telemetryItem a telemetry item with a plot
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async function testTelemetryItem(page, telemetryItem) {
|
||||
async function testTelemetryItem(page, canvas, telemetryItem) {
|
||||
// Check that telemetry item also received the tag
|
||||
await page.goto(telemetryItem.url);
|
||||
|
||||
await expect(page.getByText('No tags to display for this item')).toBeVisible();
|
||||
|
||||
const canvas = page.locator('canvas').nth(1);
|
||||
|
||||
//Wait for canvas to stablize.
|
||||
await canvas.hover({trial: true});
|
||||
|
||||
@@ -101,32 +85,20 @@ test.describe('Plot Tagging', () => {
|
||||
await expect(page.getByText('Driving')).toBeHidden();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a page, tests that tags are searchable, deletable, and persist across reloads.
|
||||
* @param {import('@playwright/test').Page} page
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async function basicTagsTests(page) {
|
||||
// Search for Driving
|
||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
|
||||
|
||||
// Clicking elsewhere should cause annotation selection to be cleared
|
||||
await expect(page.getByText('No tags to display for this item')).toBeVisible();
|
||||
|
||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('driv');
|
||||
// click on the search result
|
||||
await page.getByRole('searchbox', { name: 'OpenMCT Search' }).getByText(/Sine Wave/).first().click();
|
||||
|
||||
// Delete Driving
|
||||
await page.hover('[aria-label="Tag"]:has-text("Driving")');
|
||||
await page.locator('[aria-label="Remove tag Driving"]').click();
|
||||
|
||||
async function basicTagsTests(page, canvas) {
|
||||
// Search for Science
|
||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
|
||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('sc');
|
||||
await expect(page.locator('[aria-label="Search Result"]').nth(0)).toContainText("Science");
|
||||
await expect(page.locator('[aria-label="Search Result"]').nth(0)).not.toContainText("Drilling");
|
||||
|
||||
// Delete Driving
|
||||
await page.hover('[aria-label="Tag"]:has-text("Driving")');
|
||||
await page.locator('[aria-label="Remove tag Driving"]').click();
|
||||
|
||||
await expect(page.locator('[aria-label="Tags Inspector"]')).toContainText("Science");
|
||||
await expect(page.locator('[aria-label="Tags Inspector"]')).not.toContainText("Driving");
|
||||
|
||||
// Search for Driving
|
||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
|
||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('driv');
|
||||
@@ -137,13 +109,12 @@ test.describe('Plot Tagging', () => {
|
||||
page.reload(),
|
||||
page.waitForLoadState('networkidle')
|
||||
]);
|
||||
// wait for plots to load
|
||||
await expect(page.locator('.js-series-data-loaded')).toBeVisible();
|
||||
// wait for plot progress bar to disappear
|
||||
await page.locator('.l-view-section.c-progress-bar').waitFor({ state: 'detached' });
|
||||
|
||||
await page.getByText('Annotations').click();
|
||||
await expect(page.getByText('No tags to display for this item')).toBeVisible();
|
||||
|
||||
const canvas = page.locator('canvas').nth(1);
|
||||
// click on the tagged plot point
|
||||
await canvas.click({
|
||||
position: {
|
||||
@@ -200,23 +171,8 @@ test.describe('Plot Tagging', () => {
|
||||
// changing to fixed time mode rebuilds canvas?
|
||||
canvas = page.locator('canvas').nth(1);
|
||||
|
||||
await basicTagsTests(page);
|
||||
await testTelemetryItem(page, alphaSineWave);
|
||||
|
||||
// set to real time mode
|
||||
await setRealTimeMode(page);
|
||||
|
||||
// Search for Science
|
||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
|
||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('sc');
|
||||
// click on the search result
|
||||
await page.getByRole('searchbox', { name: 'OpenMCT Search' }).getByText('Alpha Sine Wave').first().click();
|
||||
// wait for plots to load
|
||||
await expect(page.locator('.js-series-data-loaded')).toBeVisible();
|
||||
// expect plot to be paused
|
||||
await expect(page.locator('[title="Resume displaying real-time data"]')).toBeVisible();
|
||||
|
||||
await setFixedTimeMode(page);
|
||||
await basicTagsTests(page, canvas);
|
||||
await testTelemetryItem(page, canvas, alphaSineWave);
|
||||
});
|
||||
|
||||
test('Tags work with Plot View of telemetry items', async ({ page }) => {
|
||||
@@ -231,7 +187,7 @@ test.describe('Plot Tagging', () => {
|
||||
xEnd: 700,
|
||||
yEnd: 480
|
||||
});
|
||||
await basicTagsTests(page);
|
||||
await basicTagsTests(page, canvas);
|
||||
});
|
||||
|
||||
test('Tags work with Stacked Plots', async ({ page }) => {
|
||||
@@ -261,7 +217,7 @@ test.describe('Plot Tagging', () => {
|
||||
xEnd: 700,
|
||||
yEnd: 215
|
||||
});
|
||||
await basicTagsTests(page);
|
||||
await testTelemetryItem(page, alphaSineWave);
|
||||
await basicTagsTests(page, canvas);
|
||||
await testTelemetryItem(page, canvas, alphaSineWave);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -191,7 +191,7 @@ test.describe('Recent Objects', () => {
|
||||
expect(await clockBreadcrumbs.count()).toBe(2);
|
||||
expect(await clockBreadcrumbs.nth(0).innerText()).not.toEqual(await clockBreadcrumbs.nth(1).innerText());
|
||||
});
|
||||
test("Enforces a limit of 20 recent objects and clears the recent objects", async ({ page }) => {
|
||||
test("Enforces a limit of 20 recent objects", async ({ page }) => {
|
||||
// Creating 21 objects takes a while, so increase the timeout
|
||||
test.slow();
|
||||
|
||||
@@ -242,15 +242,6 @@ test.describe('Recent Objects', () => {
|
||||
|
||||
// Assert that the Clock treeitem is no longer highlighted
|
||||
await expect(lastClockTreeItem.locator('.c-tree__item')).not.toHaveClass(/is-targeted-item/);
|
||||
|
||||
// Click the aria-label="Clear Recently Viewed" button
|
||||
await page.getByRole('button', { name: 'Clear Recently Viewed' }).click();
|
||||
|
||||
// Click on the "OK" button in the confirmation dialog
|
||||
await page.getByRole('button', { name: 'OK' }).click();
|
||||
|
||||
// Assert that the list is empty
|
||||
expect(await recentObjectsList.locator('.c-recentobjects-listitem').count()).toBe(0);
|
||||
});
|
||||
|
||||
function assertInitialRecentObjectsListState() {
|
||||
|
||||
@@ -63,7 +63,7 @@ test.describe('Grand Search', () => {
|
||||
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toContainText(`Clock A ${myItemsFolderName} Red Folder Blue Folder`);
|
||||
|
||||
// Click [aria-label="OpenMCT Search"] a >> nth=0
|
||||
await page.locator('[aria-label="Search Result"] >> nth=0').click();
|
||||
await page.locator('[aria-label="OpenMCT Search"] a').first().click();
|
||||
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toBeHidden();
|
||||
|
||||
// Fill [aria-label="OpenMCT Search"] input[type="search"]
|
||||
|
||||
@@ -50,6 +50,7 @@ test.describe('Main Tree', () => {
|
||||
|
||||
await expandTreePaneItemByName(page, folder.name);
|
||||
await assertTreeItemIsVisible(page, clock.name);
|
||||
|
||||
});
|
||||
|
||||
test('Creating a child object on one tab and expanding its parent on the other shows the correct composition @2p', async ({ page, openmctConfig }) => {
|
||||
|
||||
@@ -138,7 +138,6 @@ test.describe('Performance tests', () => {
|
||||
await page.evaluate(() => window.performance.mark("notebook-search-processed"));
|
||||
|
||||
//Clear Search
|
||||
await page.locator('.c-search.c-notebook__search .c-search__input').hover();
|
||||
await page.locator('.c-search.c-notebook__search .c-search__clear-input').click();
|
||||
await page.evaluate(() => window.performance.mark("notebook-search-processed"));
|
||||
|
||||
|
||||
16
package.json
16
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "openmct",
|
||||
"version": "2.2.1-SNAPSHOT",
|
||||
"version": "2.2.0-SNAPSHOT",
|
||||
"description": "The Open MCT core platform",
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "7.18.9",
|
||||
@@ -23,7 +23,7 @@
|
||||
"eslint": "8.36.0",
|
||||
"eslint-plugin-compat": "4.1.1",
|
||||
"eslint-plugin-playwright": "0.12.0",
|
||||
"eslint-plugin-vue": "9.10.0",
|
||||
"eslint-plugin-vue": "9.9.0",
|
||||
"eslint-plugin-you-dont-need-lodash-underscore": "6.12.0",
|
||||
"eventemitter3": "1.2.0",
|
||||
"file-saver": "2.0.5",
|
||||
@@ -44,20 +44,20 @@
|
||||
"kdbush": "^3.0.0",
|
||||
"location-bar": "3.0.1",
|
||||
"lodash": "4.17.21",
|
||||
"mini-css-extract-plugin": "2.7.5",
|
||||
"mini-css-extract-plugin": "2.7.2",
|
||||
"moment": "2.29.4",
|
||||
"moment-duration-format": "2.3.2",
|
||||
"moment-timezone": "0.5.41",
|
||||
"nyc": "15.1.0",
|
||||
"painterro": "1.2.78",
|
||||
"playwright-core": "1.29.0",
|
||||
"plotly.js-basic-dist": "2.20.0",
|
||||
"plotly.js-gl2d-dist": "2.20.0",
|
||||
"plotly.js-basic-dist": "2.17.0",
|
||||
"plotly.js-gl2d-dist": "2.17.1",
|
||||
"printj": "1.3.1",
|
||||
"resolve-url-loader": "5.0.0",
|
||||
"sanitize-html": "2.10.0",
|
||||
"sass": "1.59.3",
|
||||
"sass-loader": "13.2.1",
|
||||
"sass": "1.57.1",
|
||||
"sass-loader": "13.2.0",
|
||||
"sinon": "15.0.1",
|
||||
"style-loader": "^3.3.1",
|
||||
"typescript": "4.9.5",
|
||||
@@ -66,7 +66,7 @@
|
||||
"vue-eslint-parser": "9.1.0",
|
||||
"vue-loader": "15.9.8",
|
||||
"vue-template-compiler": "2.6.14",
|
||||
"webpack": "5.76.3",
|
||||
"webpack": "5.74.0",
|
||||
"webpack-cli": "5.0.0",
|
||||
"webpack-dev-server": "4.11.1",
|
||||
"webpack-merge": "5.8.0"
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
</div>
|
||||
<div
|
||||
v-if="!hideOptions && filteredOptions.length > 0"
|
||||
class="c-menu c-input--autocomplete__options js-autocomplete-options"
|
||||
class="c-menu c-input--autocomplete__options"
|
||||
aria-label="Autocomplete Options"
|
||||
@blur="hideOptions = true"
|
||||
>
|
||||
|
||||
@@ -415,10 +415,7 @@ export default class NotificationAPI extends EventEmitter {
|
||||
for (; i < this.notifications.length; i++) {
|
||||
notification = this.notifications[i];
|
||||
|
||||
const isNotificationMinimized = notification.model.minimized
|
||||
|| notification?.model?.options?.minimized;
|
||||
|
||||
if (!isNotificationMinimized
|
||||
if (!notification.model.minimized
|
||||
&& notification !== this.activeNotification) {
|
||||
return notification;
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
}
|
||||
|
||||
.c-object-label__name {
|
||||
color: $objectLabelNameColorFg;
|
||||
filter: $objectLabelNameFilter;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ import DefaultMetadataProvider from './DefaultMetadataProvider';
|
||||
import objectUtils from 'objectUtils';
|
||||
|
||||
export default class TelemetryAPI {
|
||||
#isGreedyLAD;
|
||||
|
||||
constructor(openmct) {
|
||||
this.openmct = openmct;
|
||||
@@ -45,8 +44,8 @@ export default class TelemetryAPI {
|
||||
this.requestProviders = [];
|
||||
this.subscriptionProviders = [];
|
||||
this.valueFormatterCache = new WeakMap();
|
||||
|
||||
this.requestInterceptorRegistry = new TelemetryRequestInterceptorRegistry();
|
||||
this.#isGreedyLAD = true;
|
||||
}
|
||||
|
||||
abortAllRequests() {
|
||||
@@ -227,31 +226,6 @@ export default class TelemetryAPI {
|
||||
return modifiedRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set greedy LAD. For stategy "latest" telemetry in
|
||||
* realtime mode the start bound will be ignored if true and
|
||||
* there is no new data to replace the existing data.
|
||||
* defaults to true
|
||||
*
|
||||
* To turn off greedy LAD:
|
||||
* openmct.telemetry.greedyLAD(false);
|
||||
*
|
||||
* @method greedyLAD
|
||||
* @returns {boolean} if greedyLAD is active or not
|
||||
* @memberof module:openmct.TelemetryAPI#
|
||||
*/
|
||||
greedyLAD(isGreedy) {
|
||||
if (arguments.length > 0) {
|
||||
if (isGreedy !== true && isGreedy !== false) {
|
||||
throw new Error('Error setting greedyLAD. Greedy LAD only accepts true or false values');
|
||||
}
|
||||
|
||||
this.#isGreedyLAD = isGreedy;
|
||||
}
|
||||
|
||||
return this.#isGreedyLAD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request telemetry collection for a domain object.
|
||||
* The `options` argument allows you to specify filters
|
||||
|
||||
@@ -30,8 +30,8 @@ export default class TelemetryCollection extends EventEmitter {
|
||||
/**
|
||||
* Creates a Telemetry Collection
|
||||
*
|
||||
* @param {OpenMCT} openmct - Open MCT
|
||||
* @param {module:openmct.DomainObject} domainObject - Domain Object to use for telemetry collection
|
||||
* @param {object} openmct - Openm MCT
|
||||
* @param {object} domainObject - Domain Object to user for telemetry collection
|
||||
* @param {object} options - Any options passed in for request/subscribe
|
||||
*/
|
||||
constructor(openmct, domainObject, options) {
|
||||
@@ -50,7 +50,6 @@ export default class TelemetryCollection extends EventEmitter {
|
||||
this.lastBounds = undefined;
|
||||
this.requestAbort = undefined;
|
||||
this.isStrategyLatest = this.options.strategy === 'latest';
|
||||
this.dataOutsideTimeBounds = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,6 +129,9 @@ export default class TelemetryCollection extends EventEmitter {
|
||||
this.emit('requestStarted');
|
||||
const modifiedOptions = await this.openmct.telemetry.applyRequestInterceptors(this.domainObject, options);
|
||||
historicalData = await historicalProvider.request(this.domainObject, modifiedOptions);
|
||||
if (!historicalData || !historicalData.length) {
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.name !== 'AbortError') {
|
||||
console.error('Error requesting telemetry data...');
|
||||
@@ -140,10 +142,6 @@ export default class TelemetryCollection extends EventEmitter {
|
||||
this.emit('requestEnded');
|
||||
this.requestAbort = undefined;
|
||||
|
||||
if (!historicalData || !historicalData.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._processNewTelemetry(historicalData);
|
||||
|
||||
}
|
||||
@@ -186,7 +184,6 @@ export default class TelemetryCollection extends EventEmitter {
|
||||
let afterEndOfBounds;
|
||||
let added = [];
|
||||
let addedIndices = [];
|
||||
let hasDataBeforeStartBound = false;
|
||||
|
||||
// loop through, sort and dedupe
|
||||
for (let datum of data) {
|
||||
@@ -194,7 +191,7 @@ export default class TelemetryCollection extends EventEmitter {
|
||||
beforeStartOfBounds = parsedValue < this.lastBounds.start;
|
||||
afterEndOfBounds = parsedValue > this.lastBounds.end;
|
||||
|
||||
if (!afterEndOfBounds && (!beforeStartOfBounds || (this.isStrategyLatest && this.openmct.telemetry.greedyLAD()))) {
|
||||
if (!afterEndOfBounds && !beforeStartOfBounds) {
|
||||
let isDuplicate = false;
|
||||
let startIndex = this._sortedIndex(datum);
|
||||
let endIndex = undefined;
|
||||
@@ -221,10 +218,6 @@ export default class TelemetryCollection extends EventEmitter {
|
||||
this.boundedTelemetry.splice(index, 0, datum);
|
||||
addedIndices.push(index);
|
||||
added.push(datum);
|
||||
|
||||
if (!hasDataBeforeStartBound && beforeStartOfBounds) {
|
||||
hasDataBeforeStartBound = true;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (afterEndOfBounds) {
|
||||
@@ -233,18 +226,12 @@ export default class TelemetryCollection extends EventEmitter {
|
||||
}
|
||||
|
||||
if (added.length) {
|
||||
// if latest strategy is requested, we need to check if the value is the latest unemitted value
|
||||
// if latest strategy is requested, we need to check if the value is the latest unmitted value
|
||||
if (this.isStrategyLatest) {
|
||||
this.boundedTelemetry = [this.boundedTelemetry[this.boundedTelemetry.length - 1]];
|
||||
|
||||
// if true, then this value has yet to be emitted
|
||||
if (this.boundedTelemetry[0] !== latestBoundedDatum) {
|
||||
if (hasDataBeforeStartBound) {
|
||||
this._handleDataOutsideBounds();
|
||||
} else {
|
||||
this._handleDataInsideBounds();
|
||||
}
|
||||
|
||||
this.emit('add', this.boundedTelemetry);
|
||||
}
|
||||
} else {
|
||||
@@ -307,17 +294,6 @@ export default class TelemetryCollection extends EventEmitter {
|
||||
let added = [];
|
||||
let testDatum = {};
|
||||
|
||||
if (endChanged) {
|
||||
testDatum[this.timeKey] = bounds.end;
|
||||
// Calculate the new index of the last item in bounds
|
||||
endIndex = _.sortedLastIndexBy(
|
||||
this.futureBuffer,
|
||||
testDatum,
|
||||
datum => this.parseTime(datum)
|
||||
);
|
||||
added = this.futureBuffer.splice(0, endIndex);
|
||||
}
|
||||
|
||||
if (startChanged) {
|
||||
testDatum[this.timeKey] = bounds.start;
|
||||
|
||||
@@ -331,21 +307,22 @@ export default class TelemetryCollection extends EventEmitter {
|
||||
);
|
||||
discarded = this.boundedTelemetry.splice(0, startIndex);
|
||||
} else if (this.parseTime(testDatum) > this.parseTime(this.boundedTelemetry[0])) {
|
||||
// if greedyLAD is active and there is no new data to replace, don't discard
|
||||
const isGreedyLAD = this.openmct.telemetry.greedyLAD();
|
||||
const shouldRemove = (!isGreedyLAD || (isGreedyLAD && added.length > 0));
|
||||
|
||||
if (shouldRemove) {
|
||||
discarded = this.boundedTelemetry;
|
||||
this.boundedTelemetry = [];
|
||||
// since it IS strategy latest, we can assume there will be at least 1 datum
|
||||
// unless no data was returned in the first request, we need to account for that
|
||||
} else if (this.boundedTelemetry.length === 1) {
|
||||
this._handleDataOutsideBounds();
|
||||
}
|
||||
discarded = this.boundedTelemetry;
|
||||
this.boundedTelemetry = [];
|
||||
}
|
||||
}
|
||||
|
||||
if (endChanged) {
|
||||
testDatum[this.timeKey] = bounds.end;
|
||||
// Calculate the new index of the last item in bounds
|
||||
endIndex = _.sortedLastIndexBy(
|
||||
this.futureBuffer,
|
||||
testDatum,
|
||||
datum => this.parseTime(datum)
|
||||
);
|
||||
added = this.futureBuffer.splice(0, endIndex);
|
||||
}
|
||||
|
||||
if (discarded.length > 0) {
|
||||
this.emit('remove', discarded);
|
||||
}
|
||||
@@ -354,8 +331,6 @@ export default class TelemetryCollection extends EventEmitter {
|
||||
if (!this.isStrategyLatest) {
|
||||
this.boundedTelemetry = [...this.boundedTelemetry, ...added];
|
||||
} else {
|
||||
this._handleDataInsideBounds();
|
||||
|
||||
added = [added[added.length - 1]];
|
||||
this.boundedTelemetry = added;
|
||||
}
|
||||
@@ -370,20 +345,6 @@ export default class TelemetryCollection extends EventEmitter {
|
||||
|
||||
}
|
||||
|
||||
_handleDataInsideBounds() {
|
||||
if (this.dataOutsideTimeBounds) {
|
||||
this.dataOutsideTimeBounds = false;
|
||||
this.emit('dataInsideTimeBounds');
|
||||
}
|
||||
}
|
||||
|
||||
_handleDataOutsideBounds() {
|
||||
if (!this.dataOutsideTimeBounds) {
|
||||
this.dataOutsideTimeBounds = true;
|
||||
this.emit('dataOutsideTimeBounds');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* whenever the time system is updated need to update related values in
|
||||
* the Telemetry Collection and reset the telemetry collection
|
||||
|
||||
@@ -121,9 +121,8 @@ define([
|
||||
}
|
||||
|
||||
TelemetryValueFormatter.prototype.parse = function (datum) {
|
||||
const isDatumArray = Array.isArray(datum);
|
||||
if (_.isObject(datum)) {
|
||||
const objectDatum = isDatumArray ? datum : datum[this.valueMetadata.source];
|
||||
const objectDatum = datum[this.valueMetadata.source];
|
||||
if (Array.isArray(objectDatum)) {
|
||||
return objectDatum.map((item) => {
|
||||
return this.formatter.parse(item);
|
||||
@@ -137,9 +136,8 @@ define([
|
||||
};
|
||||
|
||||
TelemetryValueFormatter.prototype.format = function (datum) {
|
||||
const isDatumArray = Array.isArray(datum);
|
||||
if (_.isObject(datum)) {
|
||||
const objectDatum = isDatumArray ? datum : datum[this.valueMetadata.source];
|
||||
const objectDatum = datum[this.valueMetadata.source];
|
||||
if (Array.isArray(objectDatum)) {
|
||||
return objectDatum.map((item) => {
|
||||
return this.formatter.format(item);
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
const expandColumns = {
|
||||
name: 'Expand Columns',
|
||||
key: 'lad-expand-columns',
|
||||
key: 'expand-columns',
|
||||
description: "Increase column widths to fit currently available data.",
|
||||
cssClass: 'icon-arrows-right-left labeled',
|
||||
invoke: (objectPath, view) => {
|
||||
@@ -34,7 +34,7 @@ const expandColumns = {
|
||||
|
||||
const autosizeColumns = {
|
||||
name: 'Autosize Columns',
|
||||
key: 'lad-autosize-columns',
|
||||
key: 'autosize-columns',
|
||||
description: "Automatically size columns to fit the table into the available space.",
|
||||
cssClass: 'icon-expand labeled',
|
||||
invoke: (objectPath, view) => {
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from 'vue';
|
||||
|
||||
import LadRow from './LADRow.vue';
|
||||
import StalenessUtils from '@/utils/staleness';
|
||||
|
||||
@@ -115,23 +115,7 @@ export default {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
configuration: {
|
||||
handler(newVal) {
|
||||
if (this.viewActionsCollection) {
|
||||
if (newVal.isFixedLayout) {
|
||||
this.viewActionsCollection.show(['lad-expand-columns']);
|
||||
this.viewActionsCollection.hide(['lad-autosize-columns']);
|
||||
} else {
|
||||
this.viewActionsCollection.show(['lad-autosize-columns']);
|
||||
this.viewActionsCollection.hide(['lad-expand-columns']);
|
||||
}
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
mounted() {
|
||||
this.ladTableConfiguration.on('change', this.handleConfigurationChange);
|
||||
this.composition = this.openmct.composition.get(this.domainObject);
|
||||
this.composition.on('add', this.addItem);
|
||||
@@ -139,9 +123,6 @@ export default {
|
||||
this.composition.on('reorder', this.reorder);
|
||||
this.composition.load();
|
||||
this.stalenessSubscription = {};
|
||||
await Vue.nextTick();
|
||||
this.viewActionsCollection = this.openmct.actions.getActionsCollection(this.objectPath, this.currentView);
|
||||
this.initializeViewActions();
|
||||
},
|
||||
destroyed() {
|
||||
this.ladTableConfiguration.off('change', this.handleConfigurationChange);
|
||||
@@ -227,16 +208,6 @@ export default {
|
||||
},
|
||||
toggleFixedLayout() {
|
||||
this.configuration.isFixedLayout = !this.configuration.isFixedLayout;
|
||||
},
|
||||
initializeViewActions() {
|
||||
if (this.configuration.isFixedLayout) {
|
||||
this.viewActionsCollection.show(['lad-expand-columns']);
|
||||
this.viewActionsCollection.hide(['lad-autosize-columns']);
|
||||
|
||||
} else {
|
||||
this.viewActionsCollection.hide(['lad-expand-columns']);
|
||||
this.viewActionsCollection.show(['lad-autosize-columns']);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -93,11 +93,6 @@ export default class ConditionManager extends EventEmitter {
|
||||
);
|
||||
this.updateConditionResults({id: id});
|
||||
this.updateCurrentCondition(latestTimestamp);
|
||||
|
||||
if (Object.keys(this.telemetryObjects).length === 0) {
|
||||
// no telemetry objects
|
||||
this.emit('noTelemetryObjects');
|
||||
}
|
||||
}
|
||||
|
||||
initialize() {
|
||||
@@ -107,11 +102,6 @@ export default class ConditionManager extends EventEmitter {
|
||||
this.initCondition(conditionConfiguration, index);
|
||||
});
|
||||
}
|
||||
|
||||
if (Object.keys(this.telemetryObjects).length === 0) {
|
||||
// no telemetry objects
|
||||
this.emit('noTelemetryObjects');
|
||||
}
|
||||
}
|
||||
|
||||
updateConditionTelemetryObjects() {
|
||||
|
||||
@@ -35,9 +35,7 @@ export default class StyleRuleManager extends EventEmitter {
|
||||
}
|
||||
|
||||
if (styleConfiguration) {
|
||||
// We don't set the selectedConditionId here because we want condition set computation to happen before we apply any selected style
|
||||
const styleConfigurationWithNoSelection = Object.assign(styleConfiguration, {selectedConditionId: ''});
|
||||
this.initialize(styleConfigurationWithNoSelection);
|
||||
this.initialize(styleConfiguration);
|
||||
if (styleConfiguration.conditionSetIdentifier) {
|
||||
this.openmct.time.on("bounds", this.refreshData);
|
||||
this.subscribeToConditionSet();
|
||||
@@ -59,18 +57,14 @@ export default class StyleRuleManager extends EventEmitter {
|
||||
this.applySelectedConditionStyle();
|
||||
}
|
||||
} else if (this.conditionSetIdentifier) {
|
||||
//reset the selected style and let the condition set output determine what it should be
|
||||
this.selectedConditionId = undefined;
|
||||
this.currentStyle = undefined;
|
||||
this.updateDomainObjectStyle();
|
||||
this.subscribeToConditionSet();
|
||||
}
|
||||
}
|
||||
|
||||
initialize(styleConfiguration) {
|
||||
this.conditionSetIdentifier = styleConfiguration.conditionSetIdentifier;
|
||||
this.selectedConditionId = styleConfiguration.selectedConditionId;
|
||||
this.staticStyle = styleConfiguration.staticStyle;
|
||||
this.selectedConditionId = styleConfiguration.selectedConditionId;
|
||||
this.defaultConditionId = styleConfiguration.defaultConditionId;
|
||||
this.updateConditionStylesMap(styleConfiguration.styles || []);
|
||||
}
|
||||
|
||||
@@ -132,7 +132,6 @@
|
||||
<span class="c-cdef__controls">
|
||||
<select
|
||||
v-model="condition.configuration.trigger"
|
||||
aria-label="Condition Trigger"
|
||||
@change="persist"
|
||||
>
|
||||
<option
|
||||
|
||||
@@ -114,11 +114,15 @@ export default {
|
||||
telemetryObjs: [],
|
||||
moveIndex: undefined,
|
||||
isDragging: false,
|
||||
defaultOutput: undefined,
|
||||
dragCounter: 0,
|
||||
currentConditionId: ''
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
defaultOutput(newOutput, oldOutput) {
|
||||
this.$emit('updateDefaultOutput', newOutput);
|
||||
},
|
||||
testData: {
|
||||
handler() {
|
||||
this.updateTestData();
|
||||
@@ -154,7 +158,7 @@ export default {
|
||||
this.observeForChanges();
|
||||
this.conditionManager = new ConditionManager(this.domainObject, this.openmct);
|
||||
this.conditionManager.on('conditionSetResultUpdated', this.handleConditionSetResultUpdated);
|
||||
this.conditionManager.on('noTelemetryObjects', this.emitNoTelemetryObjectEvent);
|
||||
this.updateDefaultCondition();
|
||||
this.stalenessSubscription = {};
|
||||
},
|
||||
methods: {
|
||||
@@ -162,16 +166,18 @@ export default {
|
||||
this.currentConditionId = data.conditionId;
|
||||
this.$emit('conditionSetResultUpdated', data);
|
||||
},
|
||||
emitNoTelemetryObjectEvent(data) {
|
||||
this.currentConditionId = '';
|
||||
this.$emit('noTelemetryObjects');
|
||||
},
|
||||
observeForChanges() {
|
||||
this.stopObservingForChanges = this.openmct.objects.observe(this.domainObject, 'configuration.conditionCollection', (newConditionCollection) => {
|
||||
//this forces children to re-render
|
||||
this.conditionCollection = newConditionCollection.map(condition => condition);
|
||||
this.updateDefaultCondition();
|
||||
});
|
||||
},
|
||||
updateDefaultCondition() {
|
||||
const defaultCondition = this.domainObject.configuration.conditionCollection
|
||||
.find(conditionConfiguration => conditionConfiguration.isDefault);
|
||||
this.defaultOutput = defaultCondition.configuration.output;
|
||||
},
|
||||
setMoveIndex(index) {
|
||||
this.moveIndex = index;
|
||||
this.isDragging = true;
|
||||
|
||||
@@ -28,15 +28,12 @@
|
||||
<section class="c-cs__current-output c-section">
|
||||
<div class="c-cs__content c-cs__current-output-value">
|
||||
<span class="c-cs__current-output-value__label">Current Output</span>
|
||||
<span
|
||||
class="c-cs__current-output-value__value"
|
||||
aria-label="Current Output Value"
|
||||
>
|
||||
<span class="c-cs__current-output-value__value">
|
||||
<template v-if="currentConditionOutput">
|
||||
{{ currentConditionOutput }}
|
||||
</template>
|
||||
<template v-else>
|
||||
---
|
||||
{{ defaultConditionOutput }}
|
||||
</template>
|
||||
</span>
|
||||
</div>
|
||||
@@ -54,7 +51,7 @@
|
||||
:is-editing="isEditing"
|
||||
:test-data="testData"
|
||||
@conditionSetResultUpdated="updateCurrentOutput"
|
||||
@noTelemetryObjects="updateCurrentOutput('---')"
|
||||
@updateDefaultOutput="updateDefaultOutput"
|
||||
@telemetryUpdated="updateTelemetry"
|
||||
@telemetryStaleness="handleStaleness"
|
||||
/>
|
||||
@@ -78,6 +75,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
currentConditionOutput: '',
|
||||
defaultConditionOutput: '',
|
||||
telemetryObjs: [],
|
||||
testData: {},
|
||||
staleObjects: []
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
<select
|
||||
ref="telemetrySelect"
|
||||
v-model="criterion.telemetry"
|
||||
aria-label="Criterion Telemetry Selection"
|
||||
@change="updateMetadataOptions"
|
||||
>
|
||||
<option value="">- Select Telemetry -</option>
|
||||
@@ -51,7 +50,6 @@
|
||||
<select
|
||||
ref="metadataSelect"
|
||||
v-model="criterion.metadata"
|
||||
aria-label="Criterion Metadata Selection"
|
||||
@change="updateOperations"
|
||||
>
|
||||
<option value="">- Select Field -</option>
|
||||
@@ -71,7 +69,6 @@
|
||||
>
|
||||
<select
|
||||
v-model="criterion.operation"
|
||||
aria-label="Criterion Comparison Selection"
|
||||
@change="updateInputVisibilityAndValues"
|
||||
>
|
||||
<option value="">- Select Comparison -</option>
|
||||
@@ -92,7 +89,6 @@
|
||||
<input
|
||||
v-model="criterion.input[inputIndex]"
|
||||
class="c-cdef__control__input"
|
||||
aria-label="Criterion Input"
|
||||
:type="setInputType"
|
||||
@change="persist"
|
||||
>
|
||||
@@ -107,7 +103,6 @@
|
||||
>
|
||||
<select
|
||||
v-model="criterion.input[0]"
|
||||
aria-label="Criterion Else Selection"
|
||||
@change="persist"
|
||||
>
|
||||
<option
|
||||
|
||||
@@ -307,8 +307,6 @@ export default {
|
||||
delete this.stopProvidingTelemetry;
|
||||
}
|
||||
} else {
|
||||
//reset the selectedConditionID so that the condition set computation can drive it.
|
||||
this.applySelectedConditionStyle('');
|
||||
this.subscribeToConditionSet();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -80,9 +80,11 @@
|
||||
.is-editing & {
|
||||
cursor: pointer;
|
||||
pointer-events: initial;
|
||||
transition: $transOut;
|
||||
|
||||
&:hover {
|
||||
background: rgba($colorBodyFg, 0.1);
|
||||
transition: $transIn;
|
||||
}
|
||||
|
||||
&.is-current {
|
||||
|
||||
@@ -72,7 +72,7 @@ export default {
|
||||
this.isEditing = isEditing;
|
||||
},
|
||||
formatTelemetry(event) {
|
||||
const newFormat = event.currentTarget.value;
|
||||
let newFormat = event.currentTarget.value;
|
||||
this.openmct.selection.get().forEach(selectionPath => {
|
||||
selectionPath[0].context.updateTelemetryFormat(newFormat);
|
||||
});
|
||||
|
||||
@@ -193,7 +193,7 @@ export default {
|
||||
},
|
||||
telemetryValue() {
|
||||
if (!this.datum) {
|
||||
return '---';
|
||||
return;
|
||||
}
|
||||
|
||||
return this.formatter && this.formatter.format(this.datum);
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
*[s-selected-parent] {
|
||||
> .l-layout {
|
||||
// When main shell layout is the parent
|
||||
@include displayMarquee(deeppink); // TEMP
|
||||
@include displayMarquee(deeppink);
|
||||
}
|
||||
> * > * > * {
|
||||
// When a sub-layout is the parent
|
||||
|
||||
@@ -45,15 +45,18 @@
|
||||
|
||||
// Has-complex-content objects
|
||||
.c-so-view.has-complex-content {
|
||||
@include transition($prop: transform, $dur: $transOutTime, $delay: $moveBarOutDelay);
|
||||
transition: $transOut;
|
||||
transition-delay: $moveBarOutDelay;
|
||||
|
||||
> .c-so-view__local-controls {
|
||||
@include transition($prop: transform, $dur: 250ms, $delay: $moveBarOutDelay);
|
||||
transition: transform 250ms ease-in-out;
|
||||
transition-delay: $moveBarOutDelay;
|
||||
}
|
||||
|
||||
+ .c-frame__move-bar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.l-layout {
|
||||
@@ -62,11 +65,13 @@
|
||||
> .l-layout__frame {
|
||||
> .c-so-view.has-complex-content {
|
||||
> .c-so-view__local-controls {
|
||||
@include transition($prop: transform, $dur: $transOutTime, $delay: $moveBarOutDelay);
|
||||
transition: transform $transOutTime ease-in-out;
|
||||
transition-delay: $moveBarOutDelay;
|
||||
}
|
||||
|
||||
+ .c-frame__move-bar {
|
||||
@include transition($prop: height, $delay: $moveBarOutDelay);
|
||||
transition: $transOut;
|
||||
transition-delay: $moveBarOutDelay;
|
||||
@include userSelectNone();
|
||||
background: $editFrameMovebarColorBg;
|
||||
box-shadow: rgba(black, 0.3) 0 2px;
|
||||
@@ -98,17 +103,18 @@
|
||||
|
||||
&:hover {
|
||||
> .c-so-view.has-complex-content {
|
||||
transition: $transInTransform;
|
||||
transition: $transIn;
|
||||
transition-delay: 0s;
|
||||
|
||||
> .c-so-view__local-controls {
|
||||
transform: translateY($editFrameMovebarH);
|
||||
@include transition(height, $transOutTime);
|
||||
transition: transform $transInTime ease-in-out;
|
||||
transition-delay: 0s;
|
||||
}
|
||||
|
||||
+ .c-frame__move-bar {
|
||||
@include transition(height);
|
||||
transition: $transIn;
|
||||
transition-delay: 0s;
|
||||
height: $editFrameMovebarH;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,10 +244,11 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 0 0 ($margin * 2) + $size;
|
||||
transition: $transOut;
|
||||
|
||||
&:before {
|
||||
// The visible resize line
|
||||
background-color: $editUIColor;
|
||||
background: $editUIColor;
|
||||
content: '';
|
||||
display: block;
|
||||
flex: 1 1 auto;
|
||||
@@ -269,9 +270,10 @@
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transition: $transOut;
|
||||
&:before {
|
||||
// The visible resize line
|
||||
background-color: $editUIColorHov;
|
||||
background: $editUIColorHov;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
.c-grid-item {
|
||||
// Mobile-first
|
||||
@include button($bg: $colorItemBg, $fg: $colorItemFg);
|
||||
@include cControlHov();
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
padding: $interiorMarginLg;
|
||||
@@ -143,10 +142,15 @@
|
||||
body.desktop & {
|
||||
$transOutMs: 300ms;
|
||||
flex-flow: column nowrap;
|
||||
transition: $transOutMs ease-in-out;
|
||||
|
||||
&:hover {
|
||||
filter: $filterItemHoverFg;
|
||||
transition: $transIn;
|
||||
|
||||
.c-grid-item__type-icon {
|
||||
transform: scale(1.1);
|
||||
transition: $transInBounce;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,6 +171,8 @@
|
||||
font-size: floor(math.div($gridItemDesk, 3));
|
||||
margin: $interiorMargin 22.5% $interiorMargin * 3 22.5%;
|
||||
order: 2;
|
||||
transform-origin: center;
|
||||
transition: all $transOutMs ease-in-out;
|
||||
}
|
||||
|
||||
&__details {
|
||||
|
||||
@@ -322,7 +322,7 @@ export default {
|
||||
rgba(125,125,125,.2) 8px
|
||||
)`
|
||||
) : ''}`,
|
||||
transform: `scale(${this.zoomFactor}) translate(${this.imageTranslateX / 2}px, ${this.imageTranslateY / 2}px)`,
|
||||
transform: `scale(${this.zoomFactor}) translate(${this.imageTranslateX}px, ${this.imageTranslateY}px)`,
|
||||
transition: `${!this.pan && this.animateZoom ? 'transform 250ms ease-in' : 'initial'}`,
|
||||
width: `${this.sizedImageWidth}px`,
|
||||
height: `${this.sizedImageHeight}px`
|
||||
@@ -709,7 +709,7 @@ export default {
|
||||
getVisibleLayerStyles(layer) {
|
||||
return {
|
||||
backgroundImage: `url(${layer.source})`,
|
||||
transform: `scale(${this.zoomFactor}) translate(${this.imageTranslateX / 2}px, ${this.imageTranslateY / 2}px)`,
|
||||
transform: `scale(${this.zoomFactor}) translate(${this.imageTranslateX}px, ${this.imageTranslateY}px)`,
|
||||
transition: `${!this.pan && this.animateZoom ? 'transform 250ms ease-in' : 'initial'}`
|
||||
};
|
||||
},
|
||||
|
||||
@@ -195,7 +195,7 @@
|
||||
margin-bottom: 1px;
|
||||
padding-bottom: $interiorMarginSm;
|
||||
&.animate-scroll {
|
||||
scroll-behavior: smooth;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,7 +320,7 @@
|
||||
flex-direction: row;
|
||||
position: absolute;
|
||||
left: $interiorMargin; top: $interiorMargin;
|
||||
z-index: 10;
|
||||
z-index: 70;
|
||||
background: $colorLocalControlOvrBg;
|
||||
border-radius: $basicCr;
|
||||
align-items: center;
|
||||
@@ -495,6 +495,7 @@
|
||||
&:hover {
|
||||
z-index: 2;
|
||||
|
||||
filter: brightness(1) contrast(1) !important;
|
||||
[class*='__image-handle'] {
|
||||
background-color: $colorBodyFg;
|
||||
}
|
||||
@@ -518,4 +519,9 @@
|
||||
display: block;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
&:hover div.c-imagery-tsv__image-wrapper {
|
||||
// TODO CH: convert to theme constants
|
||||
filter: brightness(0.5) contrast(0.7);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,17 +23,11 @@
|
||||
import Annotations from './AnnotationsInspectorView.vue';
|
||||
import Vue from 'vue';
|
||||
|
||||
export default function AnnotationsViewProvider(openmct) {
|
||||
export default function ElementsViewProvider(openmct) {
|
||||
return {
|
||||
key: 'annotationsView',
|
||||
name: 'Annotations',
|
||||
canView: function (selection) {
|
||||
const availableTags = openmct.annotation.getAvailableTags();
|
||||
|
||||
if (availableTags.length < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return selection.length;
|
||||
},
|
||||
view: function (selection) {
|
||||
|
||||
@@ -177,9 +177,10 @@ export default {
|
||||
if (this.$refs.TagEditor) {
|
||||
const clickedInsideTagEditor = this.$refs.TagEditor.contains(event.target);
|
||||
if (!clickedInsideTagEditor) {
|
||||
// Remove last tag when user clicks outside of TagSelection
|
||||
this.addedTags.pop();
|
||||
// Hide TagSelection and show "Add Tag" button
|
||||
this.userAddingTag = false;
|
||||
this.tagsChanged();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -13,24 +13,6 @@
|
||||
|
||||
/******************************* TAGS */
|
||||
.c-tag {
|
||||
/* merge conflict in 5247
|
||||
border-radius: 10px; //TODO: convert to theme constant
|
||||
display: inline-flex;
|
||||
padding: 1px 10px; //TODO: convert to theme constant
|
||||
|
||||
> * + * {
|
||||
margin-left: $interiorMargin;
|
||||
}
|
||||
|
||||
&__remove-btn {
|
||||
color: inherit !important;
|
||||
display: none;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
padding: 1px !important;
|
||||
@include transition(opacity);
|
||||
width: 0;
|
||||
*/
|
||||
border-radius: $tagBorderRadius;
|
||||
display: inline-flex;
|
||||
overflow: hidden;
|
||||
@@ -46,15 +28,15 @@
|
||||
transition: $transIn;
|
||||
width: 0;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* SEARCH RESULTS */
|
||||
&.--is-not-search-match {
|
||||
opacity: 0.5;
|
||||
}
|
||||
/* SEARCH RESULTS */
|
||||
&.--is-not-search-match {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.c-tag-holder {
|
||||
@@ -69,31 +51,6 @@
|
||||
|
||||
/******************************* TAGS IN INSPECTOR / TAG SELECTION & APPLICATION */
|
||||
.c-tag-applier {
|
||||
/* merge conflict in fix-repaint-5247
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
|
||||
> * + * {
|
||||
margin-left: $interiorMargin;
|
||||
}
|
||||
|
||||
&__add-btn {
|
||||
&:before {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
}
|
||||
|
||||
.c-tag {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding-right: 3px !important;
|
||||
|
||||
&__remove-btn {
|
||||
display: block;
|
||||
}
|
||||
*/
|
||||
$tagApplierPadding: 3px 6px;
|
||||
@include tagHolder;
|
||||
grid-column: 1 / 3;
|
||||
@@ -124,6 +81,7 @@
|
||||
min-height: auto !important;
|
||||
padding: $tagApplierPadding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.c-tag-btn__label {
|
||||
@@ -132,21 +90,6 @@
|
||||
|
||||
/******************************* HOVERS */
|
||||
.has-tag-applier {
|
||||
/* merge conflict in fix-repaint-5247
|
||||
$p: opacity, width;
|
||||
// Apply this class to all components that should trigger tag removal btn on hover
|
||||
.c-tag__remove-btn {
|
||||
@include transition($prop: $p, $dur: $transOutTime);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.c-tag__remove-btn {
|
||||
width: 1.1em;
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
// Apply this class to all components that should trigger tag removal btn on hover
|
||||
&:hover {
|
||||
.c-tag {
|
||||
@@ -177,4 +120,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,13 +26,12 @@
|
||||
flex-wrap: wrap;
|
||||
|
||||
&__item {
|
||||
$m: 1px;
|
||||
cursor: pointer;
|
||||
margin: 0 $m $m 0;
|
||||
margin: 0 $interiorMarginSm $interiorMarginSm 0;
|
||||
|
||||
.c-object-label {
|
||||
border-radius: $smallCr;
|
||||
padding: 2px 3px;
|
||||
padding: 0;
|
||||
transition: $transOut;
|
||||
|
||||
&__type-icon {
|
||||
width: auto;
|
||||
@@ -40,8 +39,9 @@
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
@include hover() {
|
||||
background: $colorItemTreeHoverBg;
|
||||
&:hover {
|
||||
transition: $transIn;
|
||||
filter: $filterHov;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ export default class MoveAction {
|
||||
}
|
||||
|
||||
navigateTo(objectPath) {
|
||||
const urlPath = objectPath.reverse()
|
||||
let urlPath = objectPath.reverse()
|
||||
.map(object => this.openmct.objects.makeKeyString(object.identifier))
|
||||
.join("/");
|
||||
|
||||
@@ -53,8 +53,8 @@ export default class MoveAction {
|
||||
}
|
||||
|
||||
addToNewParent(child, newParent) {
|
||||
const newParentKeyString = this.openmct.objects.makeKeyString(newParent.identifier);
|
||||
const compositionCollection = this.openmct.composition.get(newParent);
|
||||
let newParentKeyString = this.openmct.objects.makeKeyString(newParent.identifier);
|
||||
let compositionCollection = this.openmct.composition.get(newParent);
|
||||
|
||||
this.openmct.objects.mutate(child, 'location', newParentKeyString);
|
||||
compositionCollection.add(child);
|
||||
@@ -63,7 +63,11 @@ export default class MoveAction {
|
||||
async onSave(changes) {
|
||||
this.startTransaction();
|
||||
|
||||
const inNavigationPath = this.inNavigationPath(this.object);
|
||||
let inNavigationPath = this.inNavigationPath(this.object);
|
||||
if (inNavigationPath && this.openmct.editor.isEditing()) {
|
||||
this.openmct.editor.save();
|
||||
}
|
||||
|
||||
const parentDomainObjectpath = changes.location || [this.parent];
|
||||
const parent = parentDomainObjectpath[0];
|
||||
|
||||
@@ -87,15 +91,12 @@ export default class MoveAction {
|
||||
}
|
||||
|
||||
let newObjectPath;
|
||||
|
||||
if (parentDomainObjectpath) {
|
||||
newObjectPath = parentDomainObjectpath && [this.object].concat(parentDomainObjectpath);
|
||||
} else {
|
||||
const root = await this.openmct.objects.getRoot();
|
||||
const rootCompositionCollection = this.openmct.composition.get(root);
|
||||
const rootComposition = await rootCompositionCollection.load();
|
||||
const rootChildCount = rootComposition.length;
|
||||
newObjectPath = await this.openmct.objects.getOriginalPath(this.object.identifier);
|
||||
let root = await this.openmct.objects.getRoot();
|
||||
let rootChildCount = root.composition.length;
|
||||
|
||||
// if not multiple root children, remove root from path
|
||||
if (rootChildCount < 2) {
|
||||
@@ -107,7 +108,8 @@ export default class MoveAction {
|
||||
}
|
||||
|
||||
removeFromOldParent(child) {
|
||||
const compositionCollection = this.openmct.composition.get(this.oldParent);
|
||||
let compositionCollection = this.openmct.composition.get(this.oldParent);
|
||||
|
||||
compositionCollection.remove(child);
|
||||
}
|
||||
|
||||
@@ -164,9 +166,9 @@ export default class MoveAction {
|
||||
return false;
|
||||
}
|
||||
|
||||
const objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier);
|
||||
const parentCandidateComposition = parentCandidate.composition;
|
||||
let objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier);
|
||||
|
||||
const parentCandidateComposition = parentCandidate.composition;
|
||||
if (parentCandidateComposition && parentCandidateComposition.indexOf(objectKeystring) !== -1) {
|
||||
return false;
|
||||
}
|
||||
@@ -176,18 +178,20 @@ export default class MoveAction {
|
||||
}
|
||||
|
||||
appliesTo(objectPath) {
|
||||
const parent = objectPath[1];
|
||||
const parentType = parent && this.openmct.types.get(parent.type);
|
||||
const child = objectPath[0];
|
||||
const childType = child && this.openmct.types.get(child.type);
|
||||
const isPersistable = this.openmct.objects.isPersistable(child.identifier);
|
||||
let parent = objectPath[1];
|
||||
let parentType = parent && this.openmct.types.get(parent.type);
|
||||
let child = objectPath[0];
|
||||
let childType = child && this.openmct.types.get(child.type);
|
||||
let isPersistable = this.openmct.objects.isPersistable(child.identifier);
|
||||
|
||||
if (parent?.locked || !isPersistable) {
|
||||
if (child.locked || (parent && parent.locked) || !isPersistable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return parentType?.definition.creatable
|
||||
&& childType?.definition.creatable
|
||||
return parentType
|
||||
&& parentType.definition.creatable
|
||||
&& childType
|
||||
&& childType.definition.creatable
|
||||
&& Array.isArray(parent.composition);
|
||||
}
|
||||
|
||||
|
||||
@@ -44,8 +44,7 @@ describe("The Move Action plugin", () => {
|
||||
identifier: {
|
||||
namespace: "",
|
||||
key: "child-folder-object"
|
||||
},
|
||||
location: "parent-folder-object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}).folder;
|
||||
@@ -91,31 +90,6 @@ describe("The Move Action plugin", () => {
|
||||
expect(moveAction).toBeDefined();
|
||||
});
|
||||
|
||||
describe("when determining the object is applicable", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
spyOn(moveAction, 'appliesTo').and.callThrough();
|
||||
});
|
||||
|
||||
it("should be true when the parent is creatable and has composition", () => {
|
||||
let applies = moveAction.appliesTo([childObject, parentObject]);
|
||||
expect(applies).toBe(true);
|
||||
});
|
||||
|
||||
it("should be true when the child is locked and not an alias", () => {
|
||||
childObject.locked = true;
|
||||
let applies = moveAction.appliesTo([childObject, parentObject]);
|
||||
expect(applies).toBe(true);
|
||||
});
|
||||
|
||||
it("should still be true when the child is locked and is an alias", () => {
|
||||
childObject.locked = true;
|
||||
childObject.location = 'another-parent-folder-object';
|
||||
let applies = moveAction.appliesTo([childObject, parentObject]);
|
||||
expect(applies).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when moving an object to a new parent and removing from the old parent", () => {
|
||||
let unObserve;
|
||||
beforeEach((done) => {
|
||||
|
||||
@@ -236,7 +236,7 @@ export default {
|
||||
sidebarCoversEntries: false,
|
||||
filteredAndSortedEntries: [],
|
||||
notebookAnnotations: {},
|
||||
selectedEntryId: undefined,
|
||||
selectedEntryId: '',
|
||||
activeTransaction: false,
|
||||
savingTransaction: false
|
||||
};
|
||||
@@ -381,10 +381,8 @@ export default {
|
||||
});
|
||||
},
|
||||
updateSelection(selection) {
|
||||
const keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||
|
||||
if (selection?.[0]?.[0]?.context?.targetDetails?.[keyString]?.entryId === undefined) {
|
||||
this.selectedEntryId = undefined;
|
||||
if (selection?.[0]?.[0]?.context?.targetDetails?.entryId === undefined) {
|
||||
this.selectedEntryId = '';
|
||||
}
|
||||
},
|
||||
async loadAnnotations() {
|
||||
@@ -524,8 +522,6 @@ export default {
|
||||
this.openmct.notifications.alert('Warning: unable to delete entry');
|
||||
console.error(`unable to delete entry ${entryId} from section ${this.selectedSection}, page ${this.selectedPage}`);
|
||||
|
||||
this.cancelTransaction();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -538,15 +534,10 @@ export default {
|
||||
emphasis: true,
|
||||
callback: () => {
|
||||
const entries = getNotebookEntries(this.domainObject, this.selectedSection, this.selectedPage);
|
||||
if (entries) {
|
||||
entries.splice(entryPos, 1);
|
||||
this.updateEntries(entries);
|
||||
this.filterAndSortEntries();
|
||||
this.removeAnnotations(entryId);
|
||||
} else {
|
||||
this.cancelTransaction();
|
||||
}
|
||||
|
||||
entries.splice(entryPos, 1);
|
||||
this.updateEntries(entries);
|
||||
this.filterAndSortEntries();
|
||||
this.removeAnnotations(entryId);
|
||||
dialog.dismiss();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
class="c-ne__remove c-icon-button c-icon-button--major icon-trash"
|
||||
title="Delete this entry"
|
||||
tabindex="-1"
|
||||
@click.stop.prevent="deleteEntry"
|
||||
@click="deleteEntry"
|
||||
>
|
||||
</button>
|
||||
</span>
|
||||
@@ -466,10 +466,7 @@ export default {
|
||||
if (!this.isSelectedEntry) {
|
||||
$event.preventDefault();
|
||||
// blur the previous focused entry if clicking on non selected entry input
|
||||
const focusedElementId = document.activeElement?.id;
|
||||
if (focusedElementId !== this.entry.id) {
|
||||
document.activeElement.blur();
|
||||
}
|
||||
document.activeElement.blur();
|
||||
}
|
||||
},
|
||||
editingEntry() {
|
||||
|
||||
@@ -76,6 +76,13 @@
|
||||
}
|
||||
|
||||
.c-list__item {
|
||||
@include hover() {
|
||||
[class*="__menu-indicator"] {
|
||||
opacity: 0.7;
|
||||
transition: $transIn;
|
||||
}
|
||||
}
|
||||
|
||||
> * + * {
|
||||
margin-left: $interiorMargin;
|
||||
}
|
||||
@@ -85,10 +92,10 @@
|
||||
}
|
||||
|
||||
&__menu-indicator {
|
||||
// Not sure this is being used
|
||||
flex: 0 0 auto;
|
||||
font-size: 0.8em;
|
||||
opacity: 0;
|
||||
transition: $transOut;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,9 +22,7 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="loaded"
|
||||
ref="plot"
|
||||
class="gl-plot"
|
||||
:class="{ 'js-series-data-loaded' : seriesDataLoaded }"
|
||||
>
|
||||
<slot></slot>
|
||||
<div class="plot-wrapper-axis-and-display-area flex-elem grows">
|
||||
@@ -349,9 +347,6 @@ export default {
|
||||
const parentLeftTickWidth = this.parentYTickWidth.leftTickWidth;
|
||||
|
||||
return parentLeftTickWidth || leftTickWidth;
|
||||
},
|
||||
seriesDataLoaded() {
|
||||
return ((this.pending === 0) && this.loaded);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -405,7 +400,7 @@ export default {
|
||||
this.removeStatusListener = this.openmct.status.observe(this.domainObject.identifier, this.updateStatus);
|
||||
|
||||
this.openmct.objectViews.on('clearData', this.clearData);
|
||||
this.$on('loadingComplete', this.loadAnnotations);
|
||||
this.$on('loadingUpdated', this.loadAnnotations);
|
||||
this.openmct.selection.on('change', this.updateSelection);
|
||||
this.setTimeContext();
|
||||
|
||||
@@ -417,11 +412,10 @@ export default {
|
||||
this.openmct.selection.off('change', this.updateSelection);
|
||||
document.removeEventListener('keydown', this.handleKeyDown);
|
||||
document.removeEventListener('keyup', this.handleKeyUp);
|
||||
document.body.removeEventListener('click', this.cancelSelection);
|
||||
this.destroy();
|
||||
},
|
||||
methods: {
|
||||
async updateSelection(selection) {
|
||||
updateSelection(selection) {
|
||||
const selectionContext = selection?.[0]?.[0]?.context?.item;
|
||||
// on clicking on a search result we highlight the annotation and zoom - we know it's an annotation result when isAnnotationSearchResult === true
|
||||
// We shouldn't zoom when we're selecting existing annotations to view them or creating new annotations.
|
||||
@@ -440,7 +434,15 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.waitForAxesToLoad();
|
||||
const currentXaxis = this.config.xAxis.get('displayRange');
|
||||
const currentYaxis = this.config.yAxis.get('displayRange');
|
||||
|
||||
// when there is no plot data, the ranges can be undefined
|
||||
// in which case we should not perform selection
|
||||
if (!currentXaxis || !currentYaxis) {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedAnnotations = selection?.[0]?.[0]?.context?.annotations;
|
||||
//This section is only for the annotations search results entry to displaying annotations
|
||||
if (isAnnotationSearchResult) {
|
||||
@@ -450,39 +452,10 @@ export default {
|
||||
//This section is common to all entry points for annotation display
|
||||
this.prepareExistingAnnotationSelection(selectedAnnotations);
|
||||
},
|
||||
cancelSelection(event) {
|
||||
if (this.$refs?.plot) {
|
||||
const clickedInsidePlot = this.$refs.plot.contains(event.target);
|
||||
const clickedInsideInspector = event.target.closest('.js-inspector') !== null;
|
||||
const clickedOption = event.target.closest('.js-autocomplete-options') !== null;
|
||||
if (!clickedInsidePlot && !clickedInsideInspector && !clickedOption) {
|
||||
this.rectangles = [];
|
||||
this.annotationSelections = [];
|
||||
this.selectPlot();
|
||||
document.body.removeEventListener('click', this.cancelSelection);
|
||||
}
|
||||
}
|
||||
},
|
||||
waitForAxesToLoad() {
|
||||
return new Promise(resolve => {
|
||||
// When there is no plot data, the ranges can be undefined
|
||||
// in which case we should not perform selection.
|
||||
const currentXaxis = this.config.xAxis.get('displayRange');
|
||||
const currentYaxis = this.config.yAxis.get('displayRange');
|
||||
if (!currentXaxis || !currentYaxis) {
|
||||
this.$once('loadingComplete', () => {
|
||||
resolve();
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
},
|
||||
showAnnotationsFromSearchResults(selectedAnnotations) {
|
||||
//Start section
|
||||
|
||||
if (selectedAnnotations?.length) {
|
||||
// pause the plot if we haven't already so we can actually display
|
||||
// the annotations
|
||||
this.freeze();
|
||||
// just use first annotation
|
||||
const boundingBoxes = Object.values(selectedAnnotations[0].targets);
|
||||
let minX = Number.MAX_SAFE_INTEGER;
|
||||
@@ -699,9 +672,6 @@ export default {
|
||||
stopLoading() {
|
||||
this.pending -= 1;
|
||||
this.updateLoading();
|
||||
if (this.pending === 0) {
|
||||
this.$emit('loadingComplete');
|
||||
}
|
||||
},
|
||||
|
||||
updateLoading() {
|
||||
@@ -1295,8 +1265,6 @@ export default {
|
||||
}
|
||||
|
||||
this.openmct.selection.select(selection, true);
|
||||
|
||||
document.body.addEventListener('click', this.cancelSelection);
|
||||
},
|
||||
selectNewPlotAnnotations(boundingBoxPerYAxis, pointsInBox, event) {
|
||||
let targetDomainObjects = {};
|
||||
@@ -1719,9 +1687,6 @@ export default {
|
||||
},
|
||||
|
||||
resumeRealtimeData() {
|
||||
// remove annotation selections
|
||||
this.rectangles = [];
|
||||
|
||||
this.clearPanZoomHistory();
|
||||
this.userViewportChangeEnd();
|
||||
},
|
||||
|
||||
@@ -603,20 +603,19 @@ export default {
|
||||
const mainYAxisId = this.config.yAxis.get('id');
|
||||
//There has to be at least one yAxis
|
||||
const yAxisIds = [mainYAxisId].concat(this.config.additionalYAxes.map(yAxis => yAxis.get('id')));
|
||||
|
||||
// Repeat drawing for all yAxes
|
||||
yAxisIds.filter(this.canDraw).forEach((id, yAxisIndex) => {
|
||||
this.updateViewport(id);
|
||||
this.drawSeries(id);
|
||||
if (yAxisIndex === 0) {
|
||||
yAxisIds.forEach((id) => {
|
||||
if (this.canDraw(id)) {
|
||||
this.updateViewport(id);
|
||||
this.drawSeries(id);
|
||||
this.drawRectangles(id);
|
||||
}
|
||||
this.drawHighlights(id);
|
||||
|
||||
this.drawHighlights(id);
|
||||
// only draw these in fixed time mode or plot is paused
|
||||
if (this.annotationViewingAndEditingAllowed) {
|
||||
this.drawAnnotatedPoints(id);
|
||||
this.drawAnnotationSelections(id);
|
||||
// only draw these in fixed time mode or plot is paused
|
||||
if (this.annotationViewingAndEditingAllowed) {
|
||||
this.drawAnnotatedPoints(id);
|
||||
this.drawAnnotationSelections(id);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2022, United States Government
|
||||
* Open MCT, Copyright (c) 2014-2023, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
@@ -20,8 +20,6 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
const SPECIAL_MESSAGE_TYPES = ['layout', 'flexible-layout'];
|
||||
|
||||
export default class RemoveAction {
|
||||
#transaction;
|
||||
|
||||
@@ -41,37 +39,28 @@ export default class RemoveAction {
|
||||
}
|
||||
|
||||
async invoke(objectPath) {
|
||||
const child = objectPath[0];
|
||||
const parent = objectPath[1];
|
||||
let object = objectPath[0];
|
||||
let parent = objectPath[1];
|
||||
|
||||
try {
|
||||
await this.showConfirmDialog(child, parent);
|
||||
await this.showConfirmDialog(object);
|
||||
} catch (error) {
|
||||
return; // form canceled, exit invoke
|
||||
}
|
||||
|
||||
await this.removeFromComposition(parent, child, objectPath);
|
||||
await this.removeFromComposition(parent, object);
|
||||
|
||||
if (this.inNavigationPath(child)) {
|
||||
if (this.inNavigationPath(object)) {
|
||||
this.navigateTo(objectPath.slice(1));
|
||||
}
|
||||
}
|
||||
|
||||
showConfirmDialog(child, parent) {
|
||||
let message = 'Warning! This action will remove this object. Are you sure you want to continue?';
|
||||
|
||||
if (SPECIAL_MESSAGE_TYPES.includes(parent.type)) {
|
||||
const type = this.openmct.types.get(parent.type);
|
||||
const typeName = type.definition.name;
|
||||
|
||||
message = `Warning! This action will remove this item from the ${typeName}. Are you sure you want to continue?`;
|
||||
}
|
||||
|
||||
showConfirmDialog(object) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const dialog = this.openmct.overlays.dialog({
|
||||
title: `Remove ${child.name}`,
|
||||
let dialog = this.openmct.overlays.dialog({
|
||||
title: `Remove ${object.name}`,
|
||||
iconClass: 'alert',
|
||||
message,
|
||||
message: 'Warning! This action will remove this object. Are you sure you want to continue?',
|
||||
buttons: [
|
||||
{
|
||||
label: 'OK',
|
||||
@@ -105,13 +94,13 @@ export default class RemoveAction {
|
||||
this.openmct.router.navigate('#/browse/' + urlPath);
|
||||
}
|
||||
|
||||
async removeFromComposition(parent, child, objectPath) {
|
||||
async removeFromComposition(parent, child) {
|
||||
this.startTransaction();
|
||||
|
||||
const composition = this.openmct.composition.get(parent);
|
||||
composition.remove(child);
|
||||
|
||||
if (!this.openmct.objects.isObjectPathToALink(child, objectPath)) {
|
||||
if (!this.isAlias(child, parent)) {
|
||||
this.openmct.objects.mutate(child, 'location', null);
|
||||
}
|
||||
|
||||
@@ -122,6 +111,18 @@ export default class RemoveAction {
|
||||
await this.saveTransaction();
|
||||
}
|
||||
|
||||
isAlias(child, parent) {
|
||||
if (parent === undefined) {
|
||||
// then it's a root item, not an alias
|
||||
return false;
|
||||
}
|
||||
|
||||
const parentKeyString = this.openmct.objects.makeKeyString(parent.identifier);
|
||||
const childLocation = child.location;
|
||||
|
||||
return childLocation !== parentKeyString;
|
||||
}
|
||||
|
||||
appliesTo(objectPath) {
|
||||
const parent = objectPath[1];
|
||||
const parentType = parent && this.openmct.types.get(parent.type);
|
||||
@@ -129,9 +130,9 @@ export default class RemoveAction {
|
||||
const locked = child.locked ? child.locked : parent && parent.locked;
|
||||
const isEditing = this.openmct.editor.isEditing();
|
||||
const isPersistable = this.openmct.objects.isPersistable(child.identifier);
|
||||
const isLink = this.openmct.objects.isObjectPathToALink(child, objectPath);
|
||||
const isAlias = this.isAlias(child, parent);
|
||||
|
||||
if (!isLink && (locked || !isPersistable)) {
|
||||
if (locked || (!isPersistable && !isAlias)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -141,8 +142,9 @@ export default class RemoveAction {
|
||||
}
|
||||
}
|
||||
|
||||
return parentType?.definition.creatable
|
||||
&& Array.isArray(parent?.composition);
|
||||
return parentType
|
||||
&& parentType.definition.creatable
|
||||
&& Array.isArray(parent.composition);
|
||||
}
|
||||
|
||||
startTransaction() {
|
||||
|
||||
@@ -52,10 +52,6 @@ describe("The Remove Action plugin", () => {
|
||||
objectKeyStrings: ['folder'],
|
||||
overwrite: {
|
||||
folder: {
|
||||
identifier: {
|
||||
namespace: "",
|
||||
key: "parent-folder-object"
|
||||
},
|
||||
name: "Parent Folder",
|
||||
composition: [childObject.identifier]
|
||||
}
|
||||
@@ -120,18 +116,10 @@ describe("The Remove Action plugin", () => {
|
||||
expect(applies).toBe(true);
|
||||
});
|
||||
|
||||
it("should be false when the child is locked and not an alias", () => {
|
||||
it("should be false when the child is locked", () => {
|
||||
childObject.locked = true;
|
||||
childObject.location = 'parent-folder-object';
|
||||
let applies = removeAction.appliesTo([childObject, parentObject]);
|
||||
expect(applies).toBe(false);
|
||||
});
|
||||
|
||||
it("should be true when the child is locked and IS an alias", () => {
|
||||
childObject.locked = true;
|
||||
childObject.location = 'other-folder-object';
|
||||
let applies = removeAction.appliesTo([childObject, parentObject]);
|
||||
expect(applies).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -49,10 +49,12 @@
|
||||
background-size: 3px 30%;
|
||||
background-color: $colorBodyBgSubtle;
|
||||
box-shadow: inset rgba(black, 0.4) 0 1px 1px;
|
||||
transition: $transOut;
|
||||
|
||||
svg text {
|
||||
fill: $colorBodyFg;
|
||||
stroke: $colorBodyBgSubtle;
|
||||
transition: $transOut;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
&:hover,
|
||||
&:active {
|
||||
cursor: col-resize;
|
||||
filter: $timeConductorAxisHoverFilter;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -268,6 +269,7 @@
|
||||
grid-column-gap: 3px;
|
||||
grid-row-gap: 4px;
|
||||
align-items: start;
|
||||
filter: $filterMenu;
|
||||
box-shadow: $shdwMenu;
|
||||
padding: $interiorMargin;
|
||||
position: absolute;
|
||||
|
||||
@@ -72,7 +72,7 @@ $colorHeadBg: #262626;
|
||||
$colorHeadFg: $colorBodyFg;
|
||||
$colorKey: #0099cc;
|
||||
$colorKeyFg: #fff;
|
||||
$colorKeyHov: lighten($colorKey, 10%);
|
||||
$colorKeyHov: #26d8ff;
|
||||
$colorKeyFilter: invert(36%) sepia(76%) saturate(2514%) hue-rotate(170deg) brightness(99%) contrast(101%);
|
||||
$colorKeyFilterHov: invert(63%) sepia(88%) saturate(3029%) hue-rotate(154deg) brightness(101%) contrast(100%);
|
||||
$colorKeySelectedBg: $colorKey;
|
||||
@@ -86,7 +86,7 @@ $colorSelectedFg: pullForward($colorBodyFg, 20%);
|
||||
|
||||
// Object labels
|
||||
$objectLabelTypeIconOpacity: 0.7;
|
||||
$objectLabelNameColorFg: lighten($colorBodyFg, 10%);
|
||||
$objectLabelNameFilter: brightness(1.3);
|
||||
|
||||
// Layout
|
||||
$shellMainPad: 4px 0;
|
||||
@@ -135,7 +135,7 @@ $colorPausedFg: #333;
|
||||
|
||||
// Base variations
|
||||
$colorBodyBgSubtle: pullForward($colorBodyBg, 5%);
|
||||
$colorBodyBgSubtleHov: pullForward($colorBodyBg, 10%);
|
||||
$colorBodyBgSubtleHov: pushBack($colorKey, 50%);
|
||||
$colorKeySubtle: pushBack($colorKey, 10%);
|
||||
|
||||
// Time Colors
|
||||
@@ -202,7 +202,6 @@ $colorTabsHolderBg: rgba(black, 0.2);
|
||||
// Buttons and Controls
|
||||
$colorBtnBg: pullForward($colorBodyBg, 10%);
|
||||
$colorBtnBgHov: pullForward($colorBtnBg, 10%);
|
||||
$shdwBtnHov: inset rgba(white, 10%) 0 0 0 100px;
|
||||
$colorBtnFg: pullForward($colorBodyFg, 10%);
|
||||
$colorBtnReverseFg: pullForward($colorBtnFg, 10%);
|
||||
$colorBtnReverseBg: pullForward($colorBtnBg, 10%);
|
||||
@@ -334,12 +333,6 @@ $colorLimitCyanBg: #4BA6B3;
|
||||
$colorLimitCyanFg: #D3FAFF;
|
||||
$colorLimitCyanIc: #6BEDFF;
|
||||
|
||||
// Events
|
||||
$colorEventPurpleFg: #6433ff;
|
||||
$colorEventRedFg: #cc0000;
|
||||
$colorEventOrangeFg: orange;
|
||||
$colorEventYellowFg: #ffcc00;
|
||||
|
||||
// Bubble colors
|
||||
$colorInfoBubbleBg: #dddddd;
|
||||
$colorInfoBubbleFg: #666;
|
||||
@@ -471,8 +464,6 @@ $transInTime: 50ms;
|
||||
$transOutTime: 250ms;
|
||||
$transIn: all $transInTime ease-in-out;
|
||||
$transOut: all $transOutTime ease-in-out;
|
||||
$transInTransform: transform $transInTime ease-in-out;
|
||||
$transOutTransform: transform $transOutTime ease-in-out;
|
||||
$transInBounce: all 200ms cubic-bezier(.47,.01,.25,1.5);
|
||||
$transInBounceBig: all 300ms cubic-bezier(.2,1.6,.6,3);
|
||||
|
||||
|
||||
@@ -337,12 +337,6 @@ $colorLimitCyanBg: #4BA6B3;
|
||||
$colorLimitCyanFg: #D3FAFF;
|
||||
$colorLimitCyanIc: #6BEDFF;
|
||||
|
||||
// Events
|
||||
$colorEventPurpleFg: #6433ff;
|
||||
$colorEventRedFg: #cc0000;
|
||||
$colorEventOrangeFg: orange;
|
||||
$colorEventYellowFg: #ffcc00;
|
||||
|
||||
// Bubble colors
|
||||
$colorInfoBubbleBg: #dddddd;
|
||||
$colorInfoBubbleFg: #666;
|
||||
@@ -474,8 +468,6 @@ $transInTime: 50ms;
|
||||
$transOutTime: 250ms;
|
||||
$transIn: all $transInTime ease-in-out;
|
||||
$transOut: all $transOutTime ease-in-out;
|
||||
$transInTransform: transform $transInTime ease-in-out;
|
||||
$transOutTransform: transform $transOutTime ease-in-out;
|
||||
$transInBounce: all 200ms cubic-bezier(.47,.01,.25,1.5);
|
||||
$transInBounceBig: all 300ms cubic-bezier(.2,1.6,.6,3);
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ $mobileOverlayMargin: 20px;
|
||||
$mobileMenuIconD: 25px;
|
||||
$phoneItemH: floor(math.div($gridItemMobile, 4));
|
||||
$tabletItemH: floor(math.div($gridItemMobile, 3));
|
||||
$shellTimeConductorMobileH: 90px;
|
||||
|
||||
/************************** MOBILE TREE MENU DIMENSIONS */
|
||||
$mobileTreeItemH: 35px;
|
||||
|
||||
@@ -86,7 +86,7 @@ $colorSelectedFg: pullForward($colorBodyFg, 10%);
|
||||
|
||||
// Object labels
|
||||
$objectLabelTypeIconOpacity: 0.5;
|
||||
$objectLabelNameColorFg: darken($colorBodyFg, 10%);
|
||||
$objectLabelNameFilter: brightness(0.9);
|
||||
|
||||
// Layout
|
||||
$shellMainPad: 4px 0;
|
||||
@@ -135,7 +135,7 @@ $colorPausedFg: #fff;
|
||||
|
||||
// Base variations
|
||||
$colorBodyBgSubtle: pullForward($colorBodyBg, 5%);
|
||||
$colorBodyBgSubtleHov: pullForward($colorBodyBg, 10%);
|
||||
$colorBodyBgSubtleHov: pushBack($colorKey, 50%);
|
||||
$colorKeySubtle: pushBack($colorKey, 20%);
|
||||
|
||||
// Time Colors
|
||||
@@ -202,7 +202,6 @@ $colorTabsHolderBg: rgba($colorBodyFg, 0.2);
|
||||
// Buttons and Controls
|
||||
$colorBtnBg: #aaa;
|
||||
$colorBtnBgHov: pullForward($colorBtnBg, 10%);
|
||||
$shdwBtnHov: inset rgba(white, 10%) 0 0 0 20px;
|
||||
$colorBtnFg: #fff;
|
||||
$colorBtnReverseFg: $colorBodyBg;
|
||||
$colorBtnReverseBg: $colorBodyFg;
|
||||
@@ -334,12 +333,6 @@ $colorLimitCyanBg: #4BA6B3;
|
||||
$colorLimitCyanFg: #D3FAFF;
|
||||
$colorLimitCyanIc: #1795c0;
|
||||
|
||||
// Events
|
||||
$colorEventPurpleFg: #6433ff;
|
||||
$colorEventRedFg: #cc0000;
|
||||
$colorEventOrangeFg: orange;
|
||||
$colorEventYellowFg: #ffcc00;
|
||||
|
||||
// Bubble colors
|
||||
$colorInfoBubbleBg: $colorMenuBg;
|
||||
$colorInfoBubbleFg: #666;
|
||||
@@ -471,8 +464,6 @@ $transInTime: 50ms;
|
||||
$transOutTime: 250ms;
|
||||
$transIn: all $transInTime ease-in-out;
|
||||
$transOut: all $transOutTime ease-in-out;
|
||||
$transInTransform: transform $transInTime ease-in-out;
|
||||
$transOutTransform: transform $transOutTime ease-in-out;
|
||||
$transInBounce: all 200ms cubic-bezier(.47,.01,.25,1.5);
|
||||
$transInBounceBig: all 300ms cubic-bezier(.2,1.6,.6,3);
|
||||
|
||||
|
||||
@@ -47,9 +47,6 @@ $overlayOuterMarginDialog: (5%, 20%);
|
||||
$overlayInnerMargin: 25px;
|
||||
$mainViewPad: 0px;
|
||||
$treeNavArrowD: 20px;
|
||||
$shellMainBrowseBarH: 22px;
|
||||
$shellTimeConductorH: 55px;
|
||||
$shellToolBarH: 29px;
|
||||
/*************** Items */
|
||||
$itemPadLR: 5px;
|
||||
$gridItemDesk: 175px;
|
||||
|
||||
@@ -565,7 +565,7 @@ select {
|
||||
}
|
||||
|
||||
@include hover() {
|
||||
box-shadow: $shdwBtnHov;
|
||||
filter: $filterHov;
|
||||
}
|
||||
|
||||
&.is-current {
|
||||
@@ -725,6 +725,11 @@ select {
|
||||
width: $d;
|
||||
height: $d;
|
||||
text-align: center;
|
||||
transition: $transOut;
|
||||
|
||||
&:hover {
|
||||
transition: $transIn;
|
||||
}
|
||||
|
||||
&.is-selected {
|
||||
border-width: 1px;
|
||||
@@ -1079,7 +1084,7 @@ input[type="range"] {
|
||||
// Controls that are in close proximity to an element they effect
|
||||
&--show-on-hover {
|
||||
// Hidden by default; requires a hover 1 - 3 levels above to display
|
||||
@include transition(opacity, $transOutTime);
|
||||
transition: $transOut;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -1091,7 +1096,7 @@ input[type="range"] {
|
||||
> * > * > .c-local-controls--show-on-hover,
|
||||
> * > * > * > .c-local-controls--show-on-hover
|
||||
{
|
||||
@include transition(opacity);
|
||||
transition: $transIn;
|
||||
opacity: 1;
|
||||
pointer-events: inherit;
|
||||
|
||||
@@ -1104,7 +1109,6 @@ input[type="range"] {
|
||||
.c-drop-hint {
|
||||
// Used in Tabs View, Flexible Grid Layouts
|
||||
@include abs();
|
||||
@include transition($prop: background-color, $dur: $transOutTime);
|
||||
background-color: $colorDropHintBg;
|
||||
color: $colorDropHintFg;
|
||||
border-radius: $basicCr;
|
||||
@@ -1113,6 +1117,7 @@ input[type="range"] {
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transition: $transOut;
|
||||
z-index: 50;
|
||||
|
||||
&:not(.c-drop-hint--always-show) {
|
||||
@@ -1137,11 +1142,13 @@ input[type="range"] {
|
||||
.is-dragging &,
|
||||
&.is-dragging {
|
||||
pointer-events: inherit;
|
||||
transition: $transIn;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.is-mouse-over &,
|
||||
&.is-mouse-over {
|
||||
transition: $transIn;
|
||||
background-color: $colorDropHintBgHov;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
margin-right: 0;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
@include transition($prop: width, $dur: $transOutTime);
|
||||
transition: $transOut;
|
||||
width: 0;
|
||||
|
||||
.c-icon-button:before { font-size: 1em; }
|
||||
@@ -78,7 +78,7 @@
|
||||
|
||||
&:hover {
|
||||
.c-timer__controls {
|
||||
@include transition($prop: width, $dur: $transOutTime); // On purpose: want this to take a bit longer
|
||||
transition: $transOut; // On purpose: want this to take a bit longer
|
||||
margin-right: $interiorMargin;
|
||||
width: $ctrlW * 2;
|
||||
}
|
||||
@@ -168,6 +168,7 @@
|
||||
.widget-rules-wrapper,
|
||||
.widget-rule-content,
|
||||
.w-widget-test-data-content {
|
||||
transition: $transIn;
|
||||
min-height: 0;
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
@@ -429,6 +430,7 @@
|
||||
|
||||
&:hover {
|
||||
.l-autoflow-header .s-button.change-column-width {
|
||||
transition: $transIn;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@@ -442,7 +444,7 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
.s-button.change-column-width {
|
||||
@include transition($prop: opacity, $dur: $transOutTime);
|
||||
transition: $transOut;
|
||||
opacity: 0;
|
||||
}
|
||||
.l-filter {
|
||||
@@ -735,7 +737,7 @@ body.desktop {
|
||||
&:hover {
|
||||
&:after {
|
||||
background-color: $colorSplitterHover !important;
|
||||
@include transition($prop: background-color, $dur: 150ms);
|
||||
transiiton: background-color, 150ms;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,13 +96,6 @@
|
||||
animation-timing-function: ease-in-out;
|
||||
}
|
||||
|
||||
@mixin transition($prop: all, $dur: $transInTime, $timing: ease-in-out, $delay: 0ms) {
|
||||
transition-property: $prop;
|
||||
transition-duration: $dur;
|
||||
transition-timing-function: $timing;
|
||||
transition-delay: $delay;
|
||||
}
|
||||
|
||||
/************************** VISUALS */
|
||||
@mixin ancillaryIcon($d, $c) {
|
||||
// Used for small icons used in combination with larger icons,
|
||||
@@ -470,12 +463,22 @@
|
||||
}
|
||||
|
||||
@mixin button($bg: $colorBtnBg, $fg: $colorBtnFg, $radius: $controlCr, $shdw: none) {
|
||||
// Is this being used? Remove if not.
|
||||
background: $bg;
|
||||
color: $fg;
|
||||
border-radius: $radius;
|
||||
box-shadow: $shdw;
|
||||
}
|
||||
|
||||
@mixin buttonBehavior() {
|
||||
// Assign transition timings
|
||||
transition: $transOut;
|
||||
|
||||
@include hover() {
|
||||
transition: $transIn;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin cControl() {
|
||||
$fs: 1em;
|
||||
@include userSelectNone();
|
||||
@@ -512,18 +515,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
@mixin cControlHov($styleConst: $shdwBtnHov) {
|
||||
transition: box-shadow $transOutTime;
|
||||
|
||||
@include hover() {
|
||||
transition: box-shadow $transInTime;
|
||||
box-shadow: $styleConst !important;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin cButton() {
|
||||
@include cControl();
|
||||
@include cControlHov();
|
||||
@include themedButton();
|
||||
border-radius: $controlCr;
|
||||
color: $colorBtnFg;
|
||||
@@ -535,6 +528,10 @@
|
||||
margin-left: $interiorMarginSm;
|
||||
}
|
||||
|
||||
@include hover() {
|
||||
filter: $filterHov;
|
||||
}
|
||||
|
||||
&[class*="--major"],
|
||||
&[class*='is-active']{
|
||||
background: $colorBtnMajorBg;
|
||||
@@ -549,11 +546,11 @@
|
||||
|
||||
@mixin cClickIcon() {
|
||||
@include cControl();
|
||||
@include cControlHov();
|
||||
color: $colorBodyFg;
|
||||
cursor: pointer;
|
||||
padding: 4px; // Bigger hit area
|
||||
opacity: 0.7;
|
||||
transition: $transOut;
|
||||
transform-origin: center;
|
||||
|
||||
&[class*="--major"] {
|
||||
@@ -563,6 +560,7 @@
|
||||
|
||||
@include hover() {
|
||||
transform: scale(1.1);
|
||||
transition: $transIn;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@@ -586,14 +584,21 @@
|
||||
// Padding is included to facilitate a bigger hit area
|
||||
// Make the icon bigger relative to its container
|
||||
@include cControl();
|
||||
@include cControlHov();
|
||||
@include cClickIconButtonLayout();
|
||||
background: none;
|
||||
color: $colorClickIconButton;
|
||||
box-shadow: none;
|
||||
cursor: pointer;
|
||||
transition: $transOut;
|
||||
border-radius: $controlCr;
|
||||
|
||||
@include hover() {
|
||||
transition: $transIn;
|
||||
background: $colorClickIconButtonBgHov;
|
||||
//color: $colorClickIconButtonFgHov;
|
||||
filter: $filterHov;
|
||||
}
|
||||
|
||||
&[class*="--major"] {
|
||||
color: $colorBtnMajorBg !important;
|
||||
}
|
||||
@@ -649,6 +654,10 @@
|
||||
border-color: $colorFg;
|
||||
}
|
||||
|
||||
@include hover {
|
||||
filter: $filterHov;
|
||||
}
|
||||
|
||||
&--up, &--prev {
|
||||
&:before {
|
||||
transform: translate(-30%, -50%) rotate(135deg);
|
||||
|
||||
@@ -192,12 +192,3 @@ tr {
|
||||
@include isStatus($glyph: $glyph-icon-alert-rect, $color: $colorWarningLo);
|
||||
}
|
||||
}
|
||||
|
||||
.is-event {
|
||||
&--purple { color: $colorEventPurpleFg !important; }
|
||||
&--red { color: $colorEventRedFg !important; }
|
||||
&--orange { color: $colorEventOrangeFg !important; }
|
||||
&--yellow { color: $colorEventYellowFg !important; }
|
||||
&--no-style { color: inherit; }
|
||||
}
|
||||
|
||||
|
||||
@@ -208,8 +208,6 @@ div.c-table {
|
||||
}
|
||||
th, td {
|
||||
width: 33%; // Needed to prevent size jumping as values dynamically update
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
td {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
.c-list-view {
|
||||
tbody tr {
|
||||
background: $colorListItemBg;
|
||||
transition: $transOut;
|
||||
}
|
||||
|
||||
td {
|
||||
@@ -21,6 +22,8 @@
|
||||
|
||||
&:hover {
|
||||
background: $colorListItemBgHov;
|
||||
filter: $filterHov;
|
||||
transition: $transIn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
}
|
||||
|
||||
&__name {
|
||||
color: $objectLabelNameColorFg;
|
||||
filter: $objectLabelNameFilter;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,7 @@
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 0; right: 0; bottom: auto; left: 0;
|
||||
z-index: 10;
|
||||
z-index: 2;
|
||||
|
||||
.c-object-label {
|
||||
visibility: hidden;
|
||||
@@ -99,8 +99,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.c-so-view--flexible-layout,
|
||||
&.c-so-view--layout {
|
||||
// For sub-layouts with hidden frames, completely hide the header to avoid overlapping buttons
|
||||
> .c-so-view__header {
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
|
||||
&__name {
|
||||
@include ellipsize();
|
||||
color: $objectLabelNameColorFg;
|
||||
display: inline;
|
||||
padding: 1px 0;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@mixin visibleRegexButton {
|
||||
opacity: 1;
|
||||
padding: 1px 3px;
|
||||
min-width: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
.c-search {
|
||||
@@ -31,7 +31,7 @@
|
||||
overflow: hidden;
|
||||
padding: 1px 0;
|
||||
transform-origin: left;
|
||||
@include transition($prop: min-width, $dur: $transOutTime);
|
||||
transition: $transOut;
|
||||
width: 0;
|
||||
|
||||
&.is-active {
|
||||
@@ -54,19 +54,8 @@
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
&:before {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
input[type='text'],
|
||||
input[type='search'] {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
@include hover {
|
||||
.c-search__clear-input {
|
||||
display: block;
|
||||
}
|
||||
.c-search__clear-input {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,9 +66,10 @@
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
@include hover {
|
||||
&:hover {
|
||||
.c-search__use-regex {
|
||||
@include visibleRegexButton();
|
||||
transition: $transIn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div class="c-inspector js-inspector">
|
||||
<div class="c-inspector">
|
||||
<object-name />
|
||||
<InspectorTabs
|
||||
:selection="selection"
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
}
|
||||
|
||||
&__selected {
|
||||
.c-object-label__name {
|
||||
filter: $objectLabelNameFilter;
|
||||
}
|
||||
|
||||
.c-object-label__type-icon {
|
||||
opacity: $objectLabelTypeIconOpacity;
|
||||
}
|
||||
|
||||
@@ -93,18 +93,9 @@
|
||||
:persist-position="true"
|
||||
>
|
||||
<RecentObjectsList
|
||||
ref="recentObjectsList"
|
||||
class="l-shell__tree"
|
||||
@openAndScrollTo="openAndScrollTo($event)"
|
||||
/>
|
||||
<button
|
||||
slot="controls"
|
||||
class="c-icon-button icon-clear-data"
|
||||
aria-label="Clear Recently Viewed"
|
||||
title="Clear Recently Viewed"
|
||||
@click="handleClearRecentObjects"
|
||||
>
|
||||
</button>
|
||||
</pane>
|
||||
</multipane>
|
||||
</pane>
|
||||
@@ -288,9 +279,6 @@ export default {
|
||||
handleTreeReset() {
|
||||
this.triggerReset = !this.triggerReset;
|
||||
},
|
||||
handleClearRecentObjects() {
|
||||
this.$refs.recentObjectsList.clearRecentObjects();
|
||||
},
|
||||
onStartResizing() {
|
||||
this.isResizing = true;
|
||||
},
|
||||
|
||||
@@ -191,33 +191,6 @@ export default {
|
||||
shouldTrackCompositionFor(domainObject, navigationPath) {
|
||||
return this.compositionCollections[navigationPath] === undefined
|
||||
&& this.openmct.composition.supportsComposition(domainObject);
|
||||
},
|
||||
/**
|
||||
* Clears the Recent Objects list in localStorage and in the component.
|
||||
* Before clearing, prompts the user to confirm the action with a dialog.
|
||||
*/
|
||||
clearRecentObjects() {
|
||||
const dialog = this.openmct.overlays.dialog({
|
||||
title: 'Clear Recently Viewed Objects',
|
||||
iconClass: 'alert',
|
||||
message: 'This action will clear the Recently Viewed Objects list. Are you sure you want to continue?',
|
||||
buttons: [
|
||||
{
|
||||
label: 'OK',
|
||||
callback: () => {
|
||||
localStorage.removeItem(LOCAL_STORAGE_KEY__RECENT_OBJECTS);
|
||||
this.recents = [];
|
||||
dialog.dismiss();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Cancel',
|
||||
callback: () => {
|
||||
dialog.dismiss();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
z-index: 70;
|
||||
|
||||
[class*="__icon"] {
|
||||
filter: $colorKeyFilter;
|
||||
filter: $colorKeyFilter;
|
||||
}
|
||||
|
||||
[class*="__item-description"] {
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
min-height: 0;
|
||||
max-height: 15%;
|
||||
overflow: hidden;
|
||||
@include transition(min-height);
|
||||
transition: $transIn;
|
||||
|
||||
&.is-expanded {
|
||||
min-height: 100px;
|
||||
@@ -65,7 +65,8 @@
|
||||
}
|
||||
|
||||
&__pane-tree,
|
||||
&__pane-inspector {
|
||||
&__pane-inspector,
|
||||
&__pane-main {
|
||||
.l-pane__contents {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
@@ -73,7 +74,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__pane-tree {
|
||||
&__pane-tree,
|
||||
&__pane-main {
|
||||
.l-pane__contents {
|
||||
> * {
|
||||
flex: 0 0 auto;
|
||||
@@ -87,37 +89,6 @@
|
||||
|
||||
&__pane-main {
|
||||
.l-pane__header { display: none; }
|
||||
|
||||
.l-pane__contents {
|
||||
.l-shell__main-.l-shell__main-view-browse-bar {
|
||||
position: relative;
|
||||
}
|
||||
// Using `position: absolute` due to flex having repaint issues with the Time Conductor, #5247
|
||||
.l-shell__time-conductor,
|
||||
.l-shell__main-container {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: auto;
|
||||
width: auto;
|
||||
|
||||
}
|
||||
|
||||
.l-shell__main-container {
|
||||
top: $shellMainBrowseBarH + $interiorMarginLg;
|
||||
bottom: $shellTimeConductorH + $interiorMargin;
|
||||
}
|
||||
|
||||
@include phonePortrait() {
|
||||
.l-shell__main-container {
|
||||
bottom: $shellTimeConductorMobileH + $interiorMargin;
|
||||
}
|
||||
}
|
||||
|
||||
.l-shell__time-conductor {
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body.mobile & {
|
||||
@@ -336,7 +307,6 @@
|
||||
height: $p + 24px; // Need to standardize the height
|
||||
justify-content: space-between;
|
||||
padding: $p;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&__resizing {
|
||||
@@ -362,7 +332,6 @@
|
||||
box-shadow: $colorBodyBg 0 0 0 1px, $editUIAreaShdw;
|
||||
margin-left: $m;
|
||||
margin-right: $m;
|
||||
top: $shellToolBarH + $shellMainBrowseBarH + $interiorMarginLg !important;
|
||||
|
||||
&[s-selected] {
|
||||
// Provide a clearer selection context articulation for the main edit area
|
||||
@@ -439,6 +408,10 @@
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
|
||||
.c-object-label__name {
|
||||
filter: $objectLabelNameFilter;
|
||||
}
|
||||
|
||||
.c-object-label__type-icon {
|
||||
opacity: $objectLabelTypeIconOpacity;
|
||||
}
|
||||
@@ -460,7 +433,7 @@
|
||||
|
||||
/************************** DRAWER */
|
||||
.c-drawer {
|
||||
/* Sliding overlay or push element to contain things
|
||||
/* New sliding overlay or push element to contain things
|
||||
* Designed for mobile and compact desktop scenarios
|
||||
* Variations:
|
||||
* --overlays: position absolute, overlays neighboring elements
|
||||
@@ -469,8 +442,7 @@
|
||||
* &.is-expanded: applied when expanded.
|
||||
*/
|
||||
|
||||
$transProps: width, min-width, height, min-height;
|
||||
|
||||
transition: $transOut;
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
@@ -483,12 +455,11 @@
|
||||
}
|
||||
|
||||
&.c-drawer--align-left {
|
||||
@include transition($prop: $transProps, $dur: $transOutTime);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&.c-drawer--align-top {
|
||||
@include transition($prop: $transProps, $dur: $transOutTime);
|
||||
// Need anything here?
|
||||
}
|
||||
|
||||
&.c-drawer--overlays {
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
|
||||
@include hover {
|
||||
background: $colorItemTreeHoverBg;
|
||||
//filter: $filterHov; // FILTER REMOVAL, CONVERT TO THEME CONSTANT
|
||||
filter: $filterHov;
|
||||
}
|
||||
|
||||
&.is-navigated-object,
|
||||
@@ -192,6 +192,14 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__item__label {
|
||||
@include desktop {
|
||||
&:hover {
|
||||
filter: $filterHov;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.is-editing .is-navigated-object {
|
||||
|
||||
@@ -355,18 +355,17 @@ export default {
|
||||
this.abortItemLoad(path);
|
||||
}
|
||||
|
||||
const pathIndex = this.openTreeItems.indexOf(path);
|
||||
let pathIndex = this.openTreeItems.indexOf(path);
|
||||
|
||||
if (pathIndex === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.treeItems = this.treeItems.filter((item) => {
|
||||
const otherPath = item.navigationPath;
|
||||
if (otherPath !== path
|
||||
&& this.isTreeItemAChildOf(otherPath, path)) {
|
||||
this.destroyObserverByPath(otherPath);
|
||||
this.destroyMutableByPath(otherPath);
|
||||
this.treeItems = this.treeItems.filter((checkItem) => {
|
||||
if (checkItem.navigationPath !== path
|
||||
&& checkItem.navigationPath.includes(path)) {
|
||||
this.destroyObserverByPath(checkItem.navigationPath);
|
||||
this.destroyMutableByPath(checkItem.navigationPath);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -451,14 +450,13 @@ export default {
|
||||
|
||||
}, Promise.resolve()).then(() => {
|
||||
if (this.isSelectorTree) {
|
||||
let item = this.getTreeItemByPath(navigationPath);
|
||||
// If item is missing due to error in object creation,
|
||||
// walk up the navigationPath until we find an item
|
||||
while (!item && navigationPath !== '') {
|
||||
let item = this.getTreeItemByPath(navigationPath);
|
||||
while (!item) {
|
||||
const startIndex = 0;
|
||||
const endIndex = navigationPath.lastIndexOf('/');
|
||||
navigationPath = navigationPath.substring(startIndex, endIndex);
|
||||
|
||||
item = this.getTreeItemByPath(navigationPath);
|
||||
}
|
||||
|
||||
@@ -961,24 +959,6 @@ export default {
|
||||
isTreeItemPathOpen(path) {
|
||||
return this.openTreeItems.includes(path);
|
||||
},
|
||||
isTreeItemAChildOf(childNavigationPath, parentNavigationPath) {
|
||||
const childPathKeys = childNavigationPath.split('/');
|
||||
const parentPathKeys = parentNavigationPath.split('/');
|
||||
|
||||
// If child path is shorter than or same length as
|
||||
// the parent path, then it's not a child.
|
||||
if (childPathKeys.length <= parentPathKeys.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < parentPathKeys.length; i++) {
|
||||
if (childPathKeys[i] !== parentPathKeys[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
getElementStyleValue(el, style) {
|
||||
if (!el) {
|
||||
return;
|
||||
|
||||
@@ -107,10 +107,10 @@
|
||||
/************************************************ DESKTOP STYLES */
|
||||
body.desktop & {
|
||||
&__handle {
|
||||
background-color: $colorSplitterBg;
|
||||
background: $colorSplitterBg;
|
||||
display: block;
|
||||
position: absolute;
|
||||
@include transition(background-color, $transOutTime);
|
||||
transition: $transOut;
|
||||
|
||||
&:before {
|
||||
// Extended hit area
|
||||
@@ -121,8 +121,8 @@
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: $colorSplitterHover;
|
||||
@include transition(background-color);
|
||||
background: $colorSplitterHover;
|
||||
transition: $transIn;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,14 +142,14 @@
|
||||
|
||||
[class*="expand-button"] {
|
||||
display: none; // Hidden by default
|
||||
background-color: $splitterCollapsedBtnColorBg;
|
||||
background: $splitterCollapsedBtnColorBg;
|
||||
color: $splitterCollapsedBtnColorFg;
|
||||
font-size: 0.9em;
|
||||
|
||||
&:hover {
|
||||
background-color: $splitterCollapsedBtnColorBgHov;
|
||||
background: $splitterCollapsedBtnColorBgHov;
|
||||
color: inherit;
|
||||
@include transition(background-color);
|
||||
transition: $transIn;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@
|
||||
|
||||
.l-pane {
|
||||
&__handle {
|
||||
background-color: $colorSplitterHover;
|
||||
background: $colorSplitterHover;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,11 +129,9 @@ export default {
|
||||
mounted() {
|
||||
this.previewAction = new PreviewAction(this.openmct);
|
||||
this.previewAction.on('isVisible', this.togglePreviewState);
|
||||
this.clickedPlotAnnotation = this.clickedPlotAnnotation.bind(this);
|
||||
},
|
||||
destroyed() {
|
||||
this.previewAction.off('isVisible', this.togglePreviewState);
|
||||
this.openmct.selection.off('change', this.clickedPlotAnnotation);
|
||||
},
|
||||
methods: {
|
||||
clickedResult(event) {
|
||||
@@ -143,19 +141,15 @@ export default {
|
||||
this.preview(objectPath);
|
||||
} else {
|
||||
const resultUrl = identifierToString(this.openmct, objectPath);
|
||||
if (!this.openmct.router.isNavigatedObject(objectPath)) {
|
||||
// if we're not on the correct page, navigate to the object,
|
||||
// then wait for the selection event to fire before issuing a new selection
|
||||
if (this.result.annotationType === this.openmct.annotation.ANNOTATION_TYPES.PLOT_SPATIAL) {
|
||||
this.openmct.selection.on('change', this.clickedPlotAnnotation);
|
||||
}
|
||||
|
||||
this.openmct.router.navigate(resultUrl);
|
||||
} else {
|
||||
// if this is the navigated object, then we are already on the correct page
|
||||
// and just need to issue the selection event
|
||||
this.openmct.router.navigate(resultUrl);
|
||||
}
|
||||
|
||||
if (this.result.annotationType === this.openmct.annotation.ANNOTATION_TYPES.PLOT_SPATIAL) {
|
||||
//wait a beat for the navigation
|
||||
setTimeout(() => {
|
||||
this.clickedPlotAnnotation();
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
},
|
||||
preview(objectPath) {
|
||||
@@ -164,8 +158,6 @@ export default {
|
||||
}
|
||||
},
|
||||
clickedPlotAnnotation() {
|
||||
this.openmct.selection.off('change', this.clickedPlotAnnotation);
|
||||
|
||||
const targetDetails = {};
|
||||
const targetDomainObjects = {};
|
||||
Object.entries(this.result.targets).forEach(([key, value]) => {
|
||||
|
||||
@@ -43,8 +43,8 @@
|
||||
.s-button,
|
||||
.c-button {
|
||||
// Make <a> in label look like buttons
|
||||
@include transition(background-color);
|
||||
background-color: transparent;
|
||||
transition: $transIn;
|
||||
background: transparent;
|
||||
border: 1px solid rgba($colorIndicatorMenuFg, 0.5);
|
||||
border-radius: $controlCr;
|
||||
box-sizing: border-box;
|
||||
@@ -53,8 +53,8 @@
|
||||
height: auto;
|
||||
line-height: normal;
|
||||
padding: 0 2px;
|
||||
@include hover {
|
||||
background-color: rgba($colorIndicatorMenuFg, 0.1);
|
||||
&:hover {
|
||||
background: rgba($colorIndicatorMenuFg, 0.1);
|
||||
border-color: rgba($colorIndicatorMenuFg, 0.75);
|
||||
color: $colorIndicatorMenuFgHov;
|
||||
}
|
||||
|
||||
@@ -49,19 +49,9 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
highlightedText() {
|
||||
const highlight = this.highlight;
|
||||
let regex = new RegExp(`(?<!<[^>]*)(${this.highlight})`, 'gi');
|
||||
|
||||
const normalCharsRegex = /^[^A-Za-z0-9]+$/g;
|
||||
|
||||
const newHighLight = normalCharsRegex.test(highlight)
|
||||
? `\\${highlight}`
|
||||
: highlight;
|
||||
|
||||
const highlightRegex = new RegExp(`(?<!<[^>]*)(${newHighLight})`, 'gi');
|
||||
|
||||
const replacement = `<span class="${this.highlightClass}">${highlight}</span>`;
|
||||
|
||||
return this.text.replace(highlightRegex, replacement);
|
||||
return this.text.replace(regex, `<span class="${this.highlightClass}">${this.highlight}</span>`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user