mirror of
https://github.com/nasa/openmct.git
synced 2025-06-25 18:50:11 +00:00
Compare commits
12 Commits
remove-dep
...
fix-exampl
Author | SHA1 | Date | |
---|---|---|---|
b59ed09274 | |||
810d580b18 | |||
977792fae8 | |||
a69e300f1c | |||
17bc6cb722 | |||
b3d3465734 | |||
fb0d74e87f | |||
a961d7e3bf | |||
5a06b51c5a | |||
ef8b353d01 | |||
6c5b925454 | |||
e91aba2e37 |
@ -5,7 +5,7 @@ orbs:
|
||||
executors:
|
||||
pw-focal-development:
|
||||
docker:
|
||||
- image: mcr.microsoft.com/playwright:v1.42.1-focal
|
||||
- image: mcr.microsoft.com/playwright:v1.44.0-focal
|
||||
environment:
|
||||
NODE_ENV: development # Needed to ensure 'dist' folder created and devDependencies installed
|
||||
PERCY_POSTINSTALL_BROWSER: 'true' # Needed to store the percy browser in cache deps
|
||||
@ -159,7 +159,7 @@ jobs:
|
||||
steps:
|
||||
- build_and_install:
|
||||
node-version: lts/hydrogen
|
||||
- run: npx playwright@1.42.1 install #Necessary for bare ubuntu machine
|
||||
- run: npx playwright@1.44.0 install #Necessary for bare ubuntu machine
|
||||
- run: |
|
||||
export $(cat src/plugins/persistence/couch/.env.ci | xargs)
|
||||
docker-compose -f src/plugins/persistence/couch/couchdb-compose.yaml up --detach
|
||||
|
@ -497,7 +497,8 @@
|
||||
"checksnapshots",
|
||||
"specced",
|
||||
"composables",
|
||||
"countup"
|
||||
"countup",
|
||||
"darkmatter"
|
||||
],
|
||||
"dictionaries": ["npm", "softwareTerms", "node", "html", "css", "bash", "en_US", "en-gb", "misc"],
|
||||
"ignorePaths": [
|
||||
|
2
.github/workflows/e2e-couchdb.yml
vendored
2
.github/workflows/e2e-couchdb.yml
vendored
@ -37,7 +37,7 @@ jobs:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- run: npx playwright@1.42.1 install
|
||||
- run: npx playwright@1.44.0 install
|
||||
|
||||
- name: Start CouchDB Docker Container and Init with Setup Scripts
|
||||
run: |
|
||||
|
2
.github/workflows/e2e-flakefinder.yml
vendored
2
.github/workflows/e2e-flakefinder.yml
vendored
@ -30,7 +30,7 @@ jobs:
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
|
||||
- run: npx playwright@1.42.1 install
|
||||
- run: npx playwright@1.44.0 install
|
||||
- run: npm ci --no-audit --progress=false
|
||||
|
||||
- name: Run E2E Tests (Repeated 10 Times)
|
||||
|
2
.github/workflows/e2e-perf.yml
vendored
2
.github/workflows/e2e-perf.yml
vendored
@ -28,7 +28,7 @@ jobs:
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
|
||||
- run: npx playwright@1.42.1 install
|
||||
- run: npx playwright@1.44.0 install
|
||||
- run: npm ci --no-audit --progress=false
|
||||
- run: npm run test:perf:localhost
|
||||
- run: npm run test:perf:contract
|
||||
|
2
.github/workflows/e2e-pr.yml
vendored
2
.github/workflows/e2e-pr.yml
vendored
@ -33,7 +33,7 @@ jobs:
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
|
||||
- run: npx playwright@1.42.1 install
|
||||
- run: npx playwright@1.44.0 install
|
||||
- run: npx playwright install chrome-beta
|
||||
- run: npm ci --no-audit --progress=false
|
||||
- run: npm run test:e2e:full -- --max-failures=40
|
||||
|
@ -19,7 +19,7 @@ import { merge } from 'webpack-merge';
|
||||
let gitRevision = 'error-retrieving-revision';
|
||||
let gitBranch = 'error-retrieving-branch';
|
||||
|
||||
const packageDefinition = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url)));
|
||||
const { version } = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url)));
|
||||
|
||||
try {
|
||||
gitRevision = execSync('git rev-parse HEAD').toString().trim();
|
||||
@ -49,7 +49,8 @@ const config = {
|
||||
couchDBChangesFeed: './src/plugins/persistence/couch/CouchChangesFeed.js',
|
||||
inMemorySearchWorker: './src/api/objects/InMemorySearchWorker.js',
|
||||
espressoTheme: './src/plugins/themes/espresso-theme.scss',
|
||||
snowTheme: './src/plugins/themes/snow-theme.scss'
|
||||
snowTheme: './src/plugins/themes/snow-theme.scss',
|
||||
darkmatterTheme: './src/plugins/themes/darkmatter-theme.scss'
|
||||
},
|
||||
output: {
|
||||
globalObject: 'this',
|
||||
@ -84,7 +85,7 @@ const config = {
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
__OPENMCT_VERSION__: `'${packageDefinition.version}'`,
|
||||
__OPENMCT_VERSION__: `'${version}'`,
|
||||
__OPENMCT_BUILD_DATE__: `'${new Date()}'`,
|
||||
__OPENMCT_REVISION__: `'${gitRevision}'`,
|
||||
__OPENMCT_BUILD_BRANCH__: `'${gitBranch}'`,
|
||||
|
@ -275,6 +275,17 @@ async function navigateToObjectWithFixedTimeBounds(page, url, start, end) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates directly to a given object url, in real-time mode.
|
||||
* @param {import('@playwright/test').Page} page
|
||||
* @param {string} url The url to the domainObject
|
||||
*/
|
||||
async function navigateToObjectWithRealTime(page, url, start = '1800000', end = '30000') {
|
||||
await page.goto(
|
||||
`${url}?tc.mode=local&tc.startDelta=${start}&tc.endDelta=${end}&tc.timeSystem=utc`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the given `domainObject`'s context menu from the object tree.
|
||||
* Expands the path to the object and scrolls to it if necessary.
|
||||
@ -656,6 +667,7 @@ export {
|
||||
getFocusedObjectUuid,
|
||||
getHashUrlToDomainObject,
|
||||
navigateToObjectWithFixedTimeBounds,
|
||||
navigateToObjectWithRealTime,
|
||||
openObjectTreeContextMenu,
|
||||
renameObjectFromContextMenu,
|
||||
setEndOffset,
|
||||
|
47
e2e/helper/hotkeys/clipboard.js
Normal file
47
e2e/helper/hotkeys/clipboard.js
Normal file
@ -0,0 +1,47 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2024, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
const isMac = process.platform === 'darwin';
|
||||
const modifier = isMac ? 'Meta' : 'Control';
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
async function selectAll(page) {
|
||||
await page.keyboard.press(`${modifier}+KeyA`);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
async function copy(page) {
|
||||
await page.keyboard.press(`${modifier}+KeyC`);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
async function paste(page) {
|
||||
await page.keyboard.press(`${modifier}+KeyV`);
|
||||
}
|
||||
|
||||
export { copy, paste, selectAll };
|
23
e2e/helper/hotkeys/hotkeys.js
Normal file
23
e2e/helper/hotkeys/hotkeys.js
Normal file
@ -0,0 +1,23 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2024, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
export * from './clipboard.js';
|
@ -28,16 +28,28 @@ import { fileURLToPath } from 'url';
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
* @param {string} text
|
||||
*/
|
||||
async function enterTextEntry(page, text) {
|
||||
// Click the 'Add Notebook Entry' area
|
||||
await page.locator(NOTEBOOK_DROP_AREA).click();
|
||||
|
||||
// enter text
|
||||
await page.getByLabel('Notebook Entry Input').last().fill(text);
|
||||
await addNotebookEntry(page);
|
||||
await enterTextInLastEntry(page, text);
|
||||
await commitEntry(page);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
async function addNotebookEntry(page) {
|
||||
await page.locator(NOTEBOOK_DROP_AREA).click();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
async function enterTextInLastEntry(page, text) {
|
||||
await page.getByLabel('Notebook Entry Input').last().fill(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
@ -140,10 +152,13 @@ async function createNotebookEntryAndTags(page, iterations = 1) {
|
||||
}
|
||||
|
||||
export {
|
||||
addNotebookEntry,
|
||||
commitEntry,
|
||||
createNotebookAndEntry,
|
||||
createNotebookEntryAndTags,
|
||||
dragAndDropEmbed,
|
||||
enterTextEntry,
|
||||
enterTextInLastEntry,
|
||||
lockPage,
|
||||
startAndAddRestrictedNotebookObject
|
||||
};
|
||||
|
@ -18,7 +18,7 @@
|
||||
"@types/sinonjs__fake-timers": "8.1.5",
|
||||
"@percy/cli": "1.27.4",
|
||||
"@percy/playwright": "1.0.4",
|
||||
"@playwright/test": "1.42.1",
|
||||
"@playwright/test": "1.44.0",
|
||||
"@axe-core/playwright": "4.8.5",
|
||||
"sinon": "17.0.0"
|
||||
},
|
||||
|
@ -36,6 +36,13 @@ const config = {
|
||||
browserName: 'chromium',
|
||||
theme: 'snow'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'darkmatter-theme', //Runs the same visual tests but with darkmatter-theme
|
||||
use: {
|
||||
browserName: 'chromium',
|
||||
theme: 'darkmatter-theme'
|
||||
}
|
||||
}
|
||||
],
|
||||
reporter: [
|
||||
|
@ -371,7 +371,7 @@ test.describe('Basic Condition Set Use', () => {
|
||||
|
||||
// Validate that the condition set is evaluating and outputting
|
||||
// the correct value when the underlying telemetry subscription is active.
|
||||
let outputValue = page.locator('[aria-label="Current Output Value"]');
|
||||
let outputValue = page.getByLabel('Current Output Value');
|
||||
await expect(outputValue).toHaveText('false');
|
||||
|
||||
await page.goto(exampleTelemetry.url);
|
||||
@ -462,7 +462,7 @@ test.describe('Basic Condition Set Use', () => {
|
||||
|
||||
// Validate that the condition set is evaluating and outputting
|
||||
// the correct value when the underlying telemetry subscription is active.
|
||||
let outputValue = page.locator('[aria-label="Current Output Value"]');
|
||||
let outputValue = page.getByLabel('Current Output Value');
|
||||
await expect(outputValue).toHaveText('false');
|
||||
|
||||
await page.goto(exampleTelemetry.url);
|
||||
@ -475,3 +475,81 @@ test.describe('Basic Condition Set Use', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Condition Set Composition', () => {
|
||||
let conditionSet;
|
||||
let exampleTelemetry;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
|
||||
// Create Condition Set
|
||||
conditionSet = await createDomainObjectWithDefaults(page, {
|
||||
type: 'Condition Set'
|
||||
});
|
||||
|
||||
// Create Telemetry Object as child to Condition Set
|
||||
exampleTelemetry = await createExampleTelemetryObject(page, conditionSet.uuid);
|
||||
|
||||
// Edit Condition Set
|
||||
await page.goto(conditionSet.url);
|
||||
await page.getByRole('button', { name: 'Edit Object' }).click();
|
||||
|
||||
// Add Condition to Condition Set
|
||||
await page.getByRole('button', { name: 'Add Condition' }).click();
|
||||
|
||||
// Enter Condition Output
|
||||
await page.getByLabel('Condition Name Input').first().fill('Negative');
|
||||
await page.getByLabel('Condition Output Type').first().selectOption({ value: 'string' });
|
||||
await page.getByLabel('Condition Output String').first().fill('Negative');
|
||||
|
||||
// Condition Trigger default is okay so no change needed to form
|
||||
|
||||
// Enter Condition Criterion
|
||||
await page.getByLabel('Criterion Telemetry Selection').first().selectOption({ value: 'all' });
|
||||
await page.getByLabel('Criterion Metadata Selection').first().selectOption({ value: 'sin' });
|
||||
await page
|
||||
.locator('select[aria-label="Criterion Comparison Selection"]')
|
||||
.first()
|
||||
.selectOption({ value: 'lessThan' });
|
||||
await page.getByLabel('Criterion Input').first().fill('0');
|
||||
|
||||
// Save the Condition Set
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
});
|
||||
|
||||
test('You can remove telemetry from a condition set with existing conditions', async ({
|
||||
page
|
||||
}) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/7710'
|
||||
});
|
||||
|
||||
await page.getByLabel('Expand My Items folder').click();
|
||||
await page.getByLabel(`Expand ${conditionSet.name} conditionSet`).click();
|
||||
|
||||
await page
|
||||
.getByLabel(`Navigate to ${exampleTelemetry.name}`, { exact: false })
|
||||
.click({ button: 'right' });
|
||||
|
||||
await page
|
||||
.getByLabel(`${exampleTelemetry.name} Context Menu`)
|
||||
.getByRole('menuitem', { name: 'Remove' })
|
||||
.click();
|
||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
||||
|
||||
await page
|
||||
.getByLabel(`Navigate to ${conditionSet.name} conditionSet Object`, { exact: true })
|
||||
.click();
|
||||
await page.getByRole('button', { name: 'Edit Object' }).click();
|
||||
await page.getByRole('tab', { name: 'Elements' }).click();
|
||||
expect(
|
||||
await page
|
||||
.getByRole('tabpanel', { name: 'Inspector Views' })
|
||||
.getByRole('listitem', { name: exampleTelemetry.name })
|
||||
.count()
|
||||
).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
@ -78,8 +78,8 @@ test.describe('Flexible Layout', () => {
|
||||
// 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 and Clock to the Flexible Layout
|
||||
await sineWaveGeneratorTreeItem.dragTo(page.locator('.c-fl__container.is-empty').first());
|
||||
await clockTreeItem.dragTo(page.locator('.c-fl__container.is-empty'));
|
||||
await sineWaveGeneratorTreeItem.dragTo(page.locator('.c-fl-container.is-empty').first());
|
||||
await clockTreeItem.dragTo(page.locator('.c-fl-container.is-empty'));
|
||||
// Check that panes can be dragged while Flexible Layout is in Edit mode
|
||||
let dragWrapper = page
|
||||
.locator('.c-fl-container__frames-holder .c-fl-frame__drag-wrapper')
|
||||
@ -105,8 +105,8 @@ test.describe('Flexible Layout', () => {
|
||||
// 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 and Clock to the Flexible Layout
|
||||
await sineWaveGeneratorTreeItem.dragTo(page.locator('.c-fl__container.is-empty').first());
|
||||
await clockTreeItem.dragTo(page.locator('.c-fl__container.is-empty'));
|
||||
await sineWaveGeneratorTreeItem.dragTo(page.locator('.c-fl-container.is-empty').first());
|
||||
await clockTreeItem.dragTo(page.locator('.c-fl-container.is-empty'));
|
||||
|
||||
// Click on the first frame to select it
|
||||
await page.locator('.c-fl-container__frame').first().click();
|
||||
@ -122,7 +122,7 @@ test.describe('Flexible Layout', () => {
|
||||
expect(await page.locator('.c-fl--rows').count()).toEqual(0);
|
||||
|
||||
// Change the layout to rows orientation
|
||||
await page.getByTitle('Columns layout').click();
|
||||
await page.getByTitle('Switch to rows layout').click();
|
||||
|
||||
// Assert the layout is in rows orientation
|
||||
expect(await page.locator('.c-fl--rows').count()).toBeGreaterThan(0);
|
||||
@ -171,7 +171,7 @@ test.describe('Flexible Layout', () => {
|
||||
// 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 Flexible Layout and save changes
|
||||
await sineWaveGeneratorTreeItem.dragTo(page.locator('.c-fl__container.is-empty').first());
|
||||
await sineWaveGeneratorTreeItem.dragTo(page.locator('.c-fl-container.is-empty').first());
|
||||
await page.locator('button[title="Save"]').click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
|
||||
@ -202,7 +202,7 @@ test.describe('Flexible Layout', () => {
|
||||
// Expand the 'My Items' folder in the left tree
|
||||
await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click();
|
||||
// Add the Sine Wave Generator to the Flexible Layout and save changes
|
||||
await sineWaveGeneratorTreeItem.dragTo(page.locator('.c-fl__container.is-empty').first());
|
||||
await sineWaveGeneratorTreeItem.dragTo(page.locator('.c-fl-container.is-empty').first());
|
||||
await page.locator('button[title="Save"]').click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
|
||||
@ -242,7 +242,7 @@ test.describe('Flexible Layout', () => {
|
||||
name: new RegExp(exampleImageryObject.name)
|
||||
});
|
||||
// Add the Sine Wave Generator to the Flexible Layout and save changes
|
||||
await exampleImageryTreeItem.dragTo(page.locator('.c-fl__container.is-empty').first());
|
||||
await exampleImageryTreeItem.dragTo(page.locator('.c-fl-container.is-empty').first());
|
||||
|
||||
await page.locator('button[title="Save"]').click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
@ -309,9 +309,9 @@ test.describe('Flexible Layout Toolbar Actions @localStorage', () => {
|
||||
await page.getByRole('columnheader', { name: 'Container Handle 1' }).click();
|
||||
const flexRows = page.getByLabel('Flexible Layout Row');
|
||||
expect(await flexRows.count()).toEqual(0);
|
||||
await page.getByTitle('Columns layout').click();
|
||||
await page.getByTitle('Switch to rows layout').click();
|
||||
expect(await flexRows.count()).toEqual(1);
|
||||
await page.getByTitle('Rows layout').click();
|
||||
await page.getByTitle('Switch to columns layout').click();
|
||||
expect(await flexRows.count()).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
@ -27,6 +27,7 @@ This test suite is dedicated to tests which verify the basic operations surround
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
import { createDomainObjectWithDefaults } from '../../../../appActions.js';
|
||||
import { copy, paste, selectAll } from '../../../../helper/hotkeys/hotkeys.js';
|
||||
import * as nbUtils from '../../../../helper/notebookUtils.js';
|
||||
import { expect, streamToString, test } from '../../../../pluginFixtures.js';
|
||||
|
||||
@ -546,4 +547,53 @@ test.describe('Notebook entry tests', () => {
|
||||
);
|
||||
await expect(secondLineOfBlockquoteText).toBeVisible();
|
||||
});
|
||||
|
||||
/**
|
||||
* Paste into notebook entry tests
|
||||
*/
|
||||
test('Can paste text into a notebook entry', async ({ page }) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/7686'
|
||||
});
|
||||
const TEST_TEXT = 'This is a test';
|
||||
const iterations = 20;
|
||||
const EXPECTED_TEXT = TEST_TEXT.repeat(iterations);
|
||||
|
||||
await page.goto(notebookObject.url);
|
||||
|
||||
await nbUtils.addNotebookEntry(page);
|
||||
await nbUtils.enterTextInLastEntry(page, TEST_TEXT);
|
||||
await selectAll(page);
|
||||
await copy(page);
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
await paste(page);
|
||||
}
|
||||
await nbUtils.commitEntry(page);
|
||||
|
||||
await expect(page.locator(`text="${EXPECTED_TEXT}"`)).toBeVisible();
|
||||
});
|
||||
|
||||
test('Prevents pasting text into selected notebook entry if not editing', async ({ page }) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/7686'
|
||||
});
|
||||
const TEST_TEXT = 'This is a test';
|
||||
|
||||
await page.goto(notebookObject.url);
|
||||
|
||||
await nbUtils.addNotebookEntry(page);
|
||||
await nbUtils.enterTextInLastEntry(page, TEST_TEXT);
|
||||
await selectAll(page);
|
||||
await copy(page);
|
||||
await paste(page);
|
||||
await nbUtils.commitEntry(page);
|
||||
|
||||
// This should not paste text into the entry
|
||||
await paste(page);
|
||||
|
||||
await expect(await page.locator(`text="${TEST_TEXT.repeat(1)}"`).count()).toEqual(1);
|
||||
await expect(await page.locator(`text="${TEST_TEXT.repeat(2)}"`).count()).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
@ -22,8 +22,8 @@
|
||||
|
||||
import {
|
||||
createDomainObjectWithDefaults,
|
||||
setTimeConductorBounds,
|
||||
setTimeConductorMode
|
||||
navigateToObjectWithRealTime,
|
||||
setTimeConductorBounds
|
||||
} from '../../../../appActions.js';
|
||||
import { expect, test } from '../../../../pluginFixtures.js';
|
||||
|
||||
@ -39,12 +39,52 @@ test.describe('Telemetry Table', () => {
|
||||
type: 'Sine Wave Generator',
|
||||
parent: table.uuid
|
||||
});
|
||||
await page.goto(table.url);
|
||||
await setTimeConductorMode(page, false);
|
||||
await navigateToObjectWithRealTime(page, table.url);
|
||||
const rows = page.getByLabel('table content').getByLabel('Table Row');
|
||||
await expect(rows).toHaveCount(50);
|
||||
});
|
||||
|
||||
test('on load, auto scrolls to top for descending, and to bottom for ascending', async ({
|
||||
page
|
||||
}) => {
|
||||
const sineWaveGenerator = await createDomainObjectWithDefaults(page, {
|
||||
type: 'Sine Wave Generator',
|
||||
parent: table.uuid
|
||||
});
|
||||
|
||||
// verify in telemetry table object view
|
||||
await navigateToObjectWithRealTime(page, table.url);
|
||||
|
||||
expect(await getScrollPosition(page)).toBe(0);
|
||||
|
||||
// verify in telemetry table view
|
||||
await page.goto(sineWaveGenerator.url);
|
||||
await page.getByLabel('Open the View Switcher Menu').click();
|
||||
await page.getByText('Telemetry Table', { exact: true }).click();
|
||||
|
||||
expect(await getScrollPosition(page)).toBe(0);
|
||||
|
||||
// navigate back to table
|
||||
await page.goto(table.url);
|
||||
|
||||
// go into edit mode
|
||||
await page.getByLabel('Edit Object').click();
|
||||
|
||||
// change sort direction
|
||||
await page.locator('thead div').filter({ hasText: 'Time' }).click();
|
||||
|
||||
// save view
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
|
||||
// navigate away and back
|
||||
await page.goto(sineWaveGenerator.url);
|
||||
await page.goto(table.url);
|
||||
|
||||
// verify scroll position
|
||||
expect(await getScrollPosition(page, false)).toBeLessThan(1);
|
||||
});
|
||||
|
||||
test('unpauses and filters data when paused by button and user changes bounds', async ({
|
||||
page
|
||||
}) => {
|
||||
@ -183,3 +223,42 @@ test.describe('Telemetry Table', () => {
|
||||
await page.click('button[title="Pause"]');
|
||||
});
|
||||
});
|
||||
|
||||
async function getScrollPosition(page, top = true) {
|
||||
const tableBody = page.locator('.c-table__body-w');
|
||||
|
||||
// Wait for the scrollbar to appear
|
||||
await tableBody.evaluate((node) => {
|
||||
return new Promise((resolve) => {
|
||||
function checkScroll() {
|
||||
if (node.scrollHeight > node.clientHeight) {
|
||||
resolve();
|
||||
} else {
|
||||
setTimeout(checkScroll, 100);
|
||||
}
|
||||
}
|
||||
checkScroll();
|
||||
});
|
||||
});
|
||||
|
||||
// make sure there are rows
|
||||
const rows = page.getByLabel('table content').getByLabel('Table Row');
|
||||
await rows.first().waitFor();
|
||||
|
||||
// Using this to allow for rows to come and go, so we can truly test the scroll position
|
||||
// eslint-disable-next-line playwright/no-wait-for-timeout
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
const { scrollTop, clientHeight, scrollHeight } = await tableBody.evaluate((node) => ({
|
||||
scrollTop: node.scrollTop,
|
||||
clientHeight: node.clientHeight,
|
||||
scrollHeight: node.scrollHeight
|
||||
}));
|
||||
|
||||
// eslint-disable-next-line playwright/no-conditional-in-test
|
||||
if (top) {
|
||||
return scrollTop;
|
||||
} else {
|
||||
return Math.abs(scrollHeight - (scrollTop + clientHeight));
|
||||
}
|
||||
}
|
||||
|
@ -265,8 +265,8 @@ test.describe('Verify tooltips', () => {
|
||||
name: 'Test Flexible Layout'
|
||||
});
|
||||
|
||||
await page.dragAndDrop(`text=${sineWaveObject1.name}`, '.c-fl__container >> nth=0');
|
||||
await page.dragAndDrop(`text=${sineWaveObject3.name}`, '.c-fl__container >> nth=1');
|
||||
await page.dragAndDrop(`text=${sineWaveObject1.name}`, '.c-fl-container >> nth=0');
|
||||
await page.dragAndDrop(`text=${sineWaveObject3.name}`, '.c-fl-container >> nth=1');
|
||||
|
||||
await page.locator('button[title="Save"]').click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
|
@ -141,7 +141,7 @@ export default class ExampleUserProvider extends EventEmitter {
|
||||
}
|
||||
|
||||
getPossibleRoles() {
|
||||
return this.user.getRoles();
|
||||
return this.loginPromise.then(() => this.user.getRoles());
|
||||
}
|
||||
|
||||
getPossibleMissionActions() {
|
||||
|
@ -24,12 +24,11 @@ import ExampleUserProvider from './ExampleUserProvider.js';
|
||||
const AUTO_LOGIN_USER = 'mct-user';
|
||||
const STATUS_ROLES = ['flight', 'driver'];
|
||||
|
||||
export default function ExampleUserPlugin(
|
||||
{ autoLoginUser, statusRoles } = {
|
||||
autoLoginUser: AUTO_LOGIN_USER,
|
||||
statusRoles: STATUS_ROLES
|
||||
}
|
||||
) {
|
||||
export default function ExampleUserPlugin(options = {}) {
|
||||
const {
|
||||
autoLoginUser = AUTO_LOGIN_USER,
|
||||
statusRoles = STATUS_ROLES
|
||||
} = options;
|
||||
return function install(openmct) {
|
||||
const userProvider = new ExampleUserProvider(openmct, {
|
||||
statusRoles
|
||||
|
227
package-lock.json
generated
227
package-lock.json
generated
@ -84,7 +84,7 @@
|
||||
"tiny-emitter": "2.1.0",
|
||||
"typescript": "5.3.3",
|
||||
"uuid": "9.0.1",
|
||||
"vue": "3.4.19",
|
||||
"vue": "3.4.24",
|
||||
"vue-eslint-parser": "9.4.2",
|
||||
"vue-loader": "16.8.3",
|
||||
"webpack": "5.90.3",
|
||||
@ -104,7 +104,7 @@
|
||||
"@axe-core/playwright": "4.8.5",
|
||||
"@percy/cli": "1.27.4",
|
||||
"@percy/playwright": "1.0.4",
|
||||
"@playwright/test": "1.42.1",
|
||||
"@playwright/test": "1.44.0",
|
||||
"@types/sinonjs__fake-timers": "8.1.5",
|
||||
"sinon": "17.0.0"
|
||||
}
|
||||
@ -398,9 +398,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz",
|
||||
"integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==",
|
||||
"version": "7.24.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz",
|
||||
"integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
@ -1559,12 +1559,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.42.1",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.42.1.tgz",
|
||||
"integrity": "sha512-Gq9rmS54mjBL/7/MvBaNOBwbfnh7beHvS6oS4srqXFcQHpQCV1+c8JXWE8VLPyRDhgS3H8x8A7hztqI9VnwrAQ==",
|
||||
"version": "1.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.0.tgz",
|
||||
"integrity": "sha512-rNX5lbNidamSUorBhB4XZ9SQTjAqfe5M+p37Z8ic0jPFBMo5iCtQz1kRWkEMg+rYOKSlVycpQmpqjSFq7LXOfg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"playwright": "1.42.1"
|
||||
"playwright": "1.44.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
@ -1996,103 +1996,103 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/reactivity": {
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.19.tgz",
|
||||
"integrity": "sha512-+VcwrQvLZgEclGZRHx4O2XhyEEcKaBi50WbxdVItEezUf4fqRh838Ix6amWTdX0CNb/b6t3Gkz3eOebfcSt+UA==",
|
||||
"version": "3.4.24",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.24.tgz",
|
||||
"integrity": "sha512-nup3fSYg4i4LtNvu9slF/HF/0dkMQYfepUdORBcMSsankzRPzE7ypAFurpwyRBfU1i7Dn1kcwpYsE1wETSh91g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.4.19"
|
||||
"@vue/shared": "3.4.24"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/reactivity/node_modules/@vue/shared": {
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
|
||||
"integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==",
|
||||
"version": "3.4.24",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.24.tgz",
|
||||
"integrity": "sha512-BW4tajrJBM9AGAknnyEw5tO2xTmnqgup0VTnDAMcxYmqOX0RG0b9aSUGAbEKolD91tdwpA6oCwbltoJoNzpItw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vue/runtime-core": {
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.19.tgz",
|
||||
"integrity": "sha512-/Z3tFwOrerJB/oyutmJGoYbuoadphDcJAd5jOuJE86THNZji9pYjZroQ2NFsZkTxOq0GJbb+s2kxTYToDiyZzw==",
|
||||
"version": "3.4.24",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.24.tgz",
|
||||
"integrity": "sha512-c7iMfj6cJMeAG3s5yOn9Rc5D9e2/wIuaozmGf/ICGCY3KV5H7mbTVdvEkd4ZshTq7RUZqj2k7LMJWVx+EBiY1g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/reactivity": "3.4.19",
|
||||
"@vue/shared": "3.4.19"
|
||||
"@vue/reactivity": "3.4.24",
|
||||
"@vue/shared": "3.4.24"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-core/node_modules/@vue/shared": {
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
|
||||
"integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==",
|
||||
"version": "3.4.24",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.24.tgz",
|
||||
"integrity": "sha512-BW4tajrJBM9AGAknnyEw5tO2xTmnqgup0VTnDAMcxYmqOX0RG0b9aSUGAbEKolD91tdwpA6oCwbltoJoNzpItw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vue/runtime-dom": {
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.19.tgz",
|
||||
"integrity": "sha512-IyZzIDqfNCF0OyZOauL+F4yzjMPN2rPd8nhqPP2N1lBn3kYqJpPHHru+83Rkvo2lHz5mW+rEeIMEF9qY3PB94g==",
|
||||
"version": "3.4.24",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.24.tgz",
|
||||
"integrity": "sha512-uXKzuh/Emfad2Y7Qm0ABsLZZV6H3mAJ5ZVqmAOlrNQRf+T5mxpPGZBfec1hkP41t6h6FwF6RSGCs/gd8WbuySQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/runtime-core": "3.4.19",
|
||||
"@vue/shared": "3.4.19",
|
||||
"@vue/runtime-core": "3.4.24",
|
||||
"@vue/shared": "3.4.24",
|
||||
"csstype": "^3.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-dom/node_modules/@vue/shared": {
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
|
||||
"integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==",
|
||||
"version": "3.4.24",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.24.tgz",
|
||||
"integrity": "sha512-BW4tajrJBM9AGAknnyEw5tO2xTmnqgup0VTnDAMcxYmqOX0RG0b9aSUGAbEKolD91tdwpA6oCwbltoJoNzpItw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vue/server-renderer": {
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.19.tgz",
|
||||
"integrity": "sha512-eAj2p0c429RZyyhtMRnttjcSToch+kTWxFPHlzGMkR28ZbF1PDlTcmGmlDxccBuqNd9iOQ7xPRPAGgPVj+YpQw==",
|
||||
"version": "3.4.24",
|
||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.24.tgz",
|
||||
"integrity": "sha512-H+DLK4sQF6sRgzKyofmlEVBIV/9KrQU6HIV7nt6yIwSGGKvSwlV8pqJlebUKLpbXaNHugdSfAbP6YmXF69lxow==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-ssr": "3.4.19",
|
||||
"@vue/shared": "3.4.19"
|
||||
"@vue/compiler-ssr": "3.4.24",
|
||||
"@vue/shared": "3.4.24"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "3.4.19"
|
||||
"vue": "3.4.24"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/server-renderer/node_modules/@vue/compiler-core": {
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.19.tgz",
|
||||
"integrity": "sha512-gj81785z0JNzRcU0Mq98E56e4ltO1yf8k5PQ+tV/7YHnbZkrM0fyFyuttnN8ngJZjbpofWE/m4qjKBiLl8Ju4w==",
|
||||
"version": "3.4.24",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.24.tgz",
|
||||
"integrity": "sha512-vbW/tgbwJYj62N/Ww99x0zhFTkZDTcGh3uwJEuadZ/nF9/xuFMC4693P9r+3sxGXISABpDKvffY5ApH9pmdd1A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.23.9",
|
||||
"@vue/shared": "3.4.19",
|
||||
"@babel/parser": "^7.24.4",
|
||||
"@vue/shared": "3.4.24",
|
||||
"entities": "^4.5.0",
|
||||
"estree-walker": "^2.0.2",
|
||||
"source-map-js": "^1.0.2"
|
||||
"source-map-js": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/server-renderer/node_modules/@vue/compiler-dom": {
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.19.tgz",
|
||||
"integrity": "sha512-vm6+cogWrshjqEHTzIDCp72DKtea8Ry/QVpQRYoyTIg9k7QZDX6D8+HGURjtmatfgM8xgCFtJJaOlCaRYRK3QA==",
|
||||
"version": "3.4.24",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.24.tgz",
|
||||
"integrity": "sha512-4XgABML/4cNndVsQndG6BbGN7+EoisDwi3oXNovqL/4jdNhwvP8/rfRMTb6FxkxIxUUtg6AI1/qZvwfSjxJiWA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-core": "3.4.19",
|
||||
"@vue/shared": "3.4.19"
|
||||
"@vue/compiler-core": "3.4.24",
|
||||
"@vue/shared": "3.4.24"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/server-renderer/node_modules/@vue/compiler-ssr": {
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.19.tgz",
|
||||
"integrity": "sha512-P0PLKC4+u4OMJ8sinba/5Z/iDT84uMRRlrWzadgLA69opCpI1gG4N55qDSC+dedwq2fJtzmGald05LWR5TFfLw==",
|
||||
"version": "3.4.24",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.24.tgz",
|
||||
"integrity": "sha512-ZsAtr4fhaUFnVcDqwW3bYCSDwq+9Gk69q2r/7dAHDrOMw41kylaMgOP4zRnn6GIEJkQznKgrMOGPMFnLB52RbQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.4.19",
|
||||
"@vue/shared": "3.4.19"
|
||||
"@vue/compiler-dom": "3.4.24",
|
||||
"@vue/shared": "3.4.24"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/server-renderer/node_modules/@vue/shared": {
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
|
||||
"integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==",
|
||||
"version": "3.4.24",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.24.tgz",
|
||||
"integrity": "sha512-BW4tajrJBM9AGAknnyEw5tO2xTmnqgup0VTnDAMcxYmqOX0RG0b9aSUGAbEKolD91tdwpA6oCwbltoJoNzpItw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vue/shared": {
|
||||
@ -7778,15 +7778,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.8",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz",
|
||||
"integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==",
|
||||
"version": "0.30.10",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz",
|
||||
"integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.4.15"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/make-dir": {
|
||||
@ -8836,12 +8833,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.42.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.42.1.tgz",
|
||||
"integrity": "sha512-PgwB03s2DZBcNRoW+1w9E+VkLBxweib6KTXM0M3tkiT4jVxKSi6PmVJ591J+0u10LUrgxB7dLRbiJqO5s2QPMg==",
|
||||
"version": "1.44.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.0.tgz",
|
||||
"integrity": "sha512-F9b3GUCLQ3Nffrfb6dunPOkE5Mh68tR7zN32L4jCk4FjQamgesGay7/dAAe1WaMEGV04DkdJfcJzjoCKygUaRQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"playwright-core": "1.42.1"
|
||||
"playwright-core": "1.44.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
@ -8854,9 +8851,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.42.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.42.1.tgz",
|
||||
"integrity": "sha512-mxz6zclokgrke9p1vtdy/COWBH+eOZgYUVVU34C73M+4j4HLlQJHtfcqiqqxpP0o8HhMkflvfbquLX5dg6wlfA==",
|
||||
"version": "1.44.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.0.tgz",
|
||||
"integrity": "sha512-ZTbkNpFfYcGWohvTTl+xewITm7EOuqIqex0c7dNZ+aXsbrLj0qI8XlGKfPpipjm0Wny/4Lt4CJsWJk1stVS5qQ==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
@ -8901,9 +8898,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.35",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",
|
||||
"integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==",
|
||||
"version": "8.4.38",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
|
||||
"integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@ -8922,7 +8919,7 @@
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.7",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
"source-map-js": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
@ -10366,9 +10363,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
|
||||
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@ -11241,16 +11238,16 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/vue": {
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.19.tgz",
|
||||
"integrity": "sha512-W/7Fc9KUkajFU8dBeDluM4sRGc/aa4YJnOYck8dkjgZoXtVsn3OeTGni66FV1l3+nvPA7VBFYtPioaGKUmEADw==",
|
||||
"version": "3.4.24",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.24.tgz",
|
||||
"integrity": "sha512-NPdx7dLGyHmKHGRRU5bMRYVE+rechR+KDU5R2tSTNG36PuMwbfAJ+amEvOAw7BPfZp5sQulNELSLm5YUkau+Sg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.4.19",
|
||||
"@vue/compiler-sfc": "3.4.19",
|
||||
"@vue/runtime-dom": "3.4.19",
|
||||
"@vue/server-renderer": "3.4.19",
|
||||
"@vue/shared": "3.4.19"
|
||||
"@vue/compiler-dom": "3.4.24",
|
||||
"@vue/compiler-sfc": "3.4.24",
|
||||
"@vue/runtime-dom": "3.4.24",
|
||||
"@vue/server-renderer": "3.4.24",
|
||||
"@vue/shared": "3.4.24"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "*"
|
||||
@ -11440,59 +11437,59 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vue/node_modules/@vue/compiler-core": {
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.19.tgz",
|
||||
"integrity": "sha512-gj81785z0JNzRcU0Mq98E56e4ltO1yf8k5PQ+tV/7YHnbZkrM0fyFyuttnN8ngJZjbpofWE/m4qjKBiLl8Ju4w==",
|
||||
"version": "3.4.24",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.24.tgz",
|
||||
"integrity": "sha512-vbW/tgbwJYj62N/Ww99x0zhFTkZDTcGh3uwJEuadZ/nF9/xuFMC4693P9r+3sxGXISABpDKvffY5ApH9pmdd1A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.23.9",
|
||||
"@vue/shared": "3.4.19",
|
||||
"@babel/parser": "^7.24.4",
|
||||
"@vue/shared": "3.4.24",
|
||||
"entities": "^4.5.0",
|
||||
"estree-walker": "^2.0.2",
|
||||
"source-map-js": "^1.0.2"
|
||||
"source-map-js": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue/node_modules/@vue/compiler-dom": {
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.19.tgz",
|
||||
"integrity": "sha512-vm6+cogWrshjqEHTzIDCp72DKtea8Ry/QVpQRYoyTIg9k7QZDX6D8+HGURjtmatfgM8xgCFtJJaOlCaRYRK3QA==",
|
||||
"version": "3.4.24",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.24.tgz",
|
||||
"integrity": "sha512-4XgABML/4cNndVsQndG6BbGN7+EoisDwi3oXNovqL/4jdNhwvP8/rfRMTb6FxkxIxUUtg6AI1/qZvwfSjxJiWA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-core": "3.4.19",
|
||||
"@vue/shared": "3.4.19"
|
||||
"@vue/compiler-core": "3.4.24",
|
||||
"@vue/shared": "3.4.24"
|
||||
}
|
||||
},
|
||||
"node_modules/vue/node_modules/@vue/compiler-sfc": {
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.19.tgz",
|
||||
"integrity": "sha512-LQ3U4SN0DlvV0xhr1lUsgLCYlwQfUfetyPxkKYu7dkfvx7g3ojrGAkw0AERLOKYXuAGnqFsEuytkdcComei3Yg==",
|
||||
"version": "3.4.24",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.24.tgz",
|
||||
"integrity": "sha512-nRAlJUK02FTWfA2nuvNBAqsDZuERGFgxZ8sGH62XgFSvMxO2URblzulExsmj4gFZ8e+VAyDooU9oAoXfEDNxTA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.23.9",
|
||||
"@vue/compiler-core": "3.4.19",
|
||||
"@vue/compiler-dom": "3.4.19",
|
||||
"@vue/compiler-ssr": "3.4.19",
|
||||
"@vue/shared": "3.4.19",
|
||||
"@babel/parser": "^7.24.4",
|
||||
"@vue/compiler-core": "3.4.24",
|
||||
"@vue/compiler-dom": "3.4.24",
|
||||
"@vue/compiler-ssr": "3.4.24",
|
||||
"@vue/shared": "3.4.24",
|
||||
"estree-walker": "^2.0.2",
|
||||
"magic-string": "^0.30.6",
|
||||
"postcss": "^8.4.33",
|
||||
"source-map-js": "^1.0.2"
|
||||
"magic-string": "^0.30.10",
|
||||
"postcss": "^8.4.38",
|
||||
"source-map-js": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue/node_modules/@vue/compiler-ssr": {
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.19.tgz",
|
||||
"integrity": "sha512-P0PLKC4+u4OMJ8sinba/5Z/iDT84uMRRlrWzadgLA69opCpI1gG4N55qDSC+dedwq2fJtzmGald05LWR5TFfLw==",
|
||||
"version": "3.4.24",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.24.tgz",
|
||||
"integrity": "sha512-ZsAtr4fhaUFnVcDqwW3bYCSDwq+9Gk69q2r/7dAHDrOMw41kylaMgOP4zRnn6GIEJkQznKgrMOGPMFnLB52RbQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.4.19",
|
||||
"@vue/shared": "3.4.19"
|
||||
"@vue/compiler-dom": "3.4.24",
|
||||
"@vue/shared": "3.4.24"
|
||||
}
|
||||
},
|
||||
"node_modules/vue/node_modules/@vue/shared": {
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
|
||||
"integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==",
|
||||
"version": "3.4.24",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.24.tgz",
|
||||
"integrity": "sha512-BW4tajrJBM9AGAknnyEw5tO2xTmnqgup0VTnDAMcxYmqOX0RG0b9aSUGAbEKolD91tdwpA6oCwbltoJoNzpItw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/watchpack": {
|
||||
|
@ -87,7 +87,7 @@
|
||||
"tiny-emitter": "2.1.0",
|
||||
"typescript": "5.3.3",
|
||||
"uuid": "9.0.1",
|
||||
"vue": "3.4.19",
|
||||
"vue": "3.4.24",
|
||||
"vue-eslint-parser": "9.4.2",
|
||||
"vue-loader": "16.8.3",
|
||||
"webpack": "5.90.3",
|
||||
|
@ -430,7 +430,7 @@ class IndependentTimeContext extends TimeContext {
|
||||
this.followTimeContext();
|
||||
|
||||
// Emit bounds so that views that are changing context get the upstream bounds
|
||||
this.emit('bounds', this.bounds());
|
||||
this.emit('bounds', this.getBounds());
|
||||
this.emit(TIME_CONTEXT_EVENTS.boundsChanged, this.getBounds());
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ describe('The Time API', function () {
|
||||
expect(api.timeOfInterest()).toBe(toi);
|
||||
});
|
||||
|
||||
it('Allows setting of valid bounds', function () {
|
||||
it('[Legacy TimeAPI]: Allows setting of valid bounds', function () {
|
||||
bounds = {
|
||||
start: 0,
|
||||
end: 1
|
||||
@ -67,7 +67,17 @@ describe('The Time API', function () {
|
||||
expect(api.bounds()).toEqual(bounds);
|
||||
});
|
||||
|
||||
it('Disallows setting of invalid bounds', function () {
|
||||
it('Allows setting of valid bounds', function () {
|
||||
bounds = {
|
||||
start: 0,
|
||||
end: 1
|
||||
};
|
||||
expect(api.getBounds()).not.toBe(bounds);
|
||||
expect(api.setBounds.bind(api, bounds)).not.toThrow();
|
||||
expect(api.getBounds()).toEqual(bounds);
|
||||
});
|
||||
|
||||
it('[Legacy TimeAPI]: Disallows setting of invalid bounds', function () {
|
||||
bounds = {
|
||||
start: 1,
|
||||
end: 0
|
||||
@ -82,7 +92,22 @@ describe('The Time API', function () {
|
||||
expect(api.bounds()).not.toEqual(bounds);
|
||||
});
|
||||
|
||||
it('Allows setting of previously registered time system with bounds', function () {
|
||||
it('Disallows setting of invalid bounds', function () {
|
||||
bounds = {
|
||||
start: 1,
|
||||
end: 0
|
||||
};
|
||||
expect(api.getBounds()).not.toEqual(bounds);
|
||||
expect(api.setBounds.bind(api, bounds)).toThrow();
|
||||
expect(api.getBounds()).not.toEqual(bounds);
|
||||
|
||||
bounds = { start: 1 };
|
||||
expect(api.getBounds()).not.toEqual(bounds);
|
||||
expect(api.setBounds.bind(api, bounds)).toThrow();
|
||||
expect(api.getBounds()).not.toEqual(bounds);
|
||||
});
|
||||
|
||||
it('[Legacy TimeAPI]: Allows setting of previously registered time system with bounds', function () {
|
||||
api.addTimeSystem(timeSystem);
|
||||
expect(api.timeSystem()).not.toBe(timeSystem);
|
||||
expect(function () {
|
||||
@ -91,7 +116,16 @@ describe('The Time API', function () {
|
||||
expect(api.timeSystem()).toEqual(timeSystem);
|
||||
});
|
||||
|
||||
it('Disallows setting of time system without bounds', function () {
|
||||
it('Allows setting of previously registered time system with bounds', function () {
|
||||
api.addTimeSystem(timeSystem);
|
||||
expect(api.getTimeSystem()).not.toBe(timeSystem);
|
||||
expect(function () {
|
||||
api.setTimeSystem(timeSystem, bounds);
|
||||
}).not.toThrow();
|
||||
expect(api.getTimeSystem()).toEqual(timeSystem);
|
||||
});
|
||||
|
||||
it('[Legacy TimeAPI]: Disallows setting of time system without bounds', function () {
|
||||
api.addTimeSystem(timeSystem);
|
||||
expect(api.timeSystem()).not.toBe(timeSystem);
|
||||
expect(function () {
|
||||
@ -100,6 +134,32 @@ describe('The Time API', function () {
|
||||
expect(api.timeSystem()).not.toBe(timeSystem);
|
||||
});
|
||||
|
||||
it('Allows setting of time system without bounds', function () {
|
||||
api.addTimeSystem(timeSystem);
|
||||
expect(api.getTimeSystem()).not.toBe(timeSystem);
|
||||
expect(function () {
|
||||
api.setTimeSystem(timeSystemKey);
|
||||
}).not.toThrow();
|
||||
expect(api.getTimeSystem()).not.toBe(timeSystem);
|
||||
});
|
||||
|
||||
it('Disallows setting of invalid time system', function () {
|
||||
expect(function () {
|
||||
api.setTimeSystem();
|
||||
}).toThrow();
|
||||
expect(function () {
|
||||
api.setTimeSystem('invalidTimeSystemKey');
|
||||
}).toThrow();
|
||||
expect(function () {
|
||||
api.setTimeSystem({
|
||||
key: 'invalidTimeSystemKey'
|
||||
});
|
||||
}).toThrow();
|
||||
expect(function () {
|
||||
api.setTimeSystem(42);
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
it('allows setting of timesystem without bounds with clock', function () {
|
||||
api.addTimeSystem(timeSystem);
|
||||
api.addClock(clock);
|
||||
@ -114,7 +174,7 @@ describe('The Time API', function () {
|
||||
expect(api.timeSystem()).toEqual(timeSystem);
|
||||
});
|
||||
|
||||
it('Emits an event when time system changes', function () {
|
||||
it('Emits a legacy event when time system changes', function () {
|
||||
api.addTimeSystem(timeSystem);
|
||||
expect(eventListener).not.toHaveBeenCalled();
|
||||
api.on('timeSystem', eventListener);
|
||||
@ -122,6 +182,14 @@ describe('The Time API', function () {
|
||||
expect(eventListener).toHaveBeenCalledWith(timeSystem);
|
||||
});
|
||||
|
||||
it('Emits an event when time system changes', function () {
|
||||
api.addTimeSystem(timeSystem);
|
||||
expect(eventListener).not.toHaveBeenCalled();
|
||||
api.on('timeSystemChanged', eventListener);
|
||||
api.timeSystem(timeSystemKey, bounds);
|
||||
expect(eventListener).toHaveBeenCalledWith(timeSystem);
|
||||
});
|
||||
|
||||
it('Emits an event when time of interest changes', function () {
|
||||
expect(eventListener).not.toHaveBeenCalled();
|
||||
api.on('timeOfInterest', eventListener);
|
||||
@ -129,13 +197,20 @@ describe('The Time API', function () {
|
||||
expect(eventListener).toHaveBeenCalledWith(toi);
|
||||
});
|
||||
|
||||
it('Emits an event when bounds change', function () {
|
||||
it('Emits a legacy event when bounds change', function () {
|
||||
expect(eventListener).not.toHaveBeenCalled();
|
||||
api.on('bounds', eventListener);
|
||||
api.bounds(bounds);
|
||||
expect(eventListener).toHaveBeenCalledWith(bounds, false);
|
||||
});
|
||||
|
||||
it('Emits an event when bounds change', function () {
|
||||
expect(eventListener).not.toHaveBeenCalled();
|
||||
api.on('boundsChanged', eventListener);
|
||||
api.bounds(bounds);
|
||||
expect(eventListener).toHaveBeenCalledWith(bounds, false);
|
||||
});
|
||||
|
||||
it('If bounds are set and TOI lies inside them, do not change TOI', function () {
|
||||
api.timeOfInterest(6);
|
||||
api.bounds({
|
||||
@ -154,13 +229,39 @@ describe('The Time API', function () {
|
||||
expect(api.timeOfInterest()).toBeUndefined();
|
||||
});
|
||||
|
||||
it('Maintains delta during tick', function () {});
|
||||
it('Maintains delta during tick', function () {
|
||||
const initialBounds = { start: 100, end: 200 };
|
||||
api.bounds(initialBounds);
|
||||
const mockTickSource = jasmine.createSpyObj('mockTickSource', ['on', 'off', 'currentValue']);
|
||||
mockTickSource.key = 'mct';
|
||||
mockTickSource.currentValue.and.returnValue(150);
|
||||
api.addClock(mockTickSource);
|
||||
api.clock('mct', { start: 0, end: 100 });
|
||||
|
||||
it('Allows registered time system to be activated', function () {});
|
||||
// Simulate a tick event
|
||||
const tickCallback = mockTickSource.on.calls.mostRecent().args[1];
|
||||
tickCallback(150);
|
||||
|
||||
const newBounds = api.bounds();
|
||||
expect(newBounds.end - newBounds.start).toEqual(initialBounds.end - initialBounds.start);
|
||||
});
|
||||
|
||||
it('Allows registered time system to be activated', function () {
|
||||
api.addClock(clock);
|
||||
api.clock(clockKey, { start: 0, end: 100 });
|
||||
api.addTimeSystem(timeSystem);
|
||||
api.timeSystem(timeSystemKey);
|
||||
expect(api.timeSystem().key).toEqual(timeSystemKey);
|
||||
});
|
||||
|
||||
it('Allows a registered tick source to be activated', function () {
|
||||
const mockTickSource = jasmine.createSpyObj('mockTickSource', ['on', 'off', 'currentValue']);
|
||||
mockTickSource.key = 'mockTickSource';
|
||||
mockTickSource.currentValue.and.returnValue(50);
|
||||
api.addClock(mockTickSource);
|
||||
api.clock(mockTickSource.key, { start: 0, end: 100 });
|
||||
|
||||
expect(mockTickSource.on).toHaveBeenCalledWith('tick', jasmine.any(Function));
|
||||
});
|
||||
|
||||
describe(' when enabling a tick source', function () {
|
||||
@ -184,7 +285,7 @@ describe('The Time API', function () {
|
||||
api.addClock(anotherMockTickSource);
|
||||
});
|
||||
|
||||
it('sets bounds based on current value', function () {
|
||||
it('[Legacy TimeAPI]: sets bounds based on current value', function () {
|
||||
api.clock('mts', mockOffsets);
|
||||
expect(api.bounds()).toEqual({
|
||||
start: 10,
|
||||
@ -192,23 +293,46 @@ describe('The Time API', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('a new tick listener is registered', function () {
|
||||
it('does not set bounds based on current value', function () {
|
||||
api.setClock('mts');
|
||||
expect(api.getBounds()).toEqual({});
|
||||
});
|
||||
|
||||
it('does not set invalid clock', function () {
|
||||
expect(function () {
|
||||
api.setClock();
|
||||
}).toThrow();
|
||||
expect(function () {
|
||||
api.setClock({});
|
||||
}).toThrow();
|
||||
expect(function () {
|
||||
api.setClock('invalidClockKey');
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
it('[Legacy TimeAPI]: a new tick listener is registered', function () {
|
||||
api.clock('mts', mockOffsets);
|
||||
expect(mockTickSource.on).toHaveBeenCalledWith('tick', jasmine.any(Function));
|
||||
});
|
||||
|
||||
it('a new tick listener is registered', function () {
|
||||
api.setClock('mts', mockOffsets);
|
||||
expect(mockTickSource.on).toHaveBeenCalledWith('tick', jasmine.any(Function));
|
||||
});
|
||||
|
||||
it('listener of existing tick source is reregistered', function () {
|
||||
api.clock('mts', mockOffsets);
|
||||
api.clock('amts', mockOffsets);
|
||||
expect(mockTickSource.off).toHaveBeenCalledWith('tick', jasmine.any(Function));
|
||||
});
|
||||
|
||||
xit('Allows the active clock to be set and unset', function () {
|
||||
it('[Legacy TimeAPI]: Allows the active clock to be set and unset', function () {
|
||||
expect(api.clock()).toBeUndefined();
|
||||
api.clock('mts', mockOffsets);
|
||||
expect(api.clock()).toBeDefined();
|
||||
// api.stopClock();
|
||||
// expect(api.clock()).toBeUndefined();
|
||||
// Unset the clock
|
||||
api.stopClock();
|
||||
expect(api.clock()).toBeUndefined();
|
||||
});
|
||||
|
||||
it('Provides a default time context', () => {
|
||||
|
@ -20,7 +20,7 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import EventEmitter from 'EventEmitter';
|
||||
import EventEmitter from 'eventemitter3';
|
||||
|
||||
import { FIXED_MODE_KEY, MODES, REALTIME_MODE_KEY, TIME_CONTEXT_EVENTS } from './constants.js';
|
||||
|
||||
@ -34,7 +34,7 @@ import { FIXED_MODE_KEY, MODES, REALTIME_MODE_KEY, TIME_CONTEXT_EVENTS } from '.
|
||||
|
||||
/**
|
||||
* @typedef {Object} TimeConductorBounds
|
||||
* @property {number } start The start time displayed by the time conductor
|
||||
* @property {number} start The start time displayed by the time conductor
|
||||
* in ms since epoch. Epoch determined by currently active time system
|
||||
* @property {number} end The end time displayed by the time conductor in ms
|
||||
* since epoch.
|
||||
@ -426,8 +426,8 @@ class TimeContext extends EventEmitter {
|
||||
/**
|
||||
* Set the time system of the TimeAPI.
|
||||
* Emits a "timeSystem" event with the new time system.
|
||||
* @param {TimeSystem | string} timeSystemOrKey
|
||||
* @param {TimeConductorBounds} bounds
|
||||
* @param {TimeSystem | string} timeSystemOrKey The time system to set, or its key
|
||||
* @param {TimeConductorBounds} [bounds] Optional bounds to set
|
||||
*/
|
||||
setTimeSystem(timeSystemOrKey, bounds) {
|
||||
if (timeSystemOrKey === undefined) {
|
||||
|
@ -61,7 +61,7 @@ export default class URLTimeSettingsSynchronizer {
|
||||
TIME_EVENTS.forEach((event) => {
|
||||
this.openmct.time.on(event, this.setUrlFromTimeApi);
|
||||
});
|
||||
this.openmct.time.on('bounds', this.updateBounds);
|
||||
this.openmct.time.on('boundsChanged', this.updateBounds);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
@ -73,7 +73,7 @@ export default class URLTimeSettingsSynchronizer {
|
||||
TIME_EVENTS.forEach((event) => {
|
||||
this.openmct.time.off(event, this.setUrlFromTimeApi);
|
||||
});
|
||||
this.openmct.time.off('bounds', this.updateBounds);
|
||||
this.openmct.time.off('boundsChanged', this.updateBounds);
|
||||
}
|
||||
|
||||
updateTimeSettings() {
|
||||
|
@ -115,11 +115,11 @@ export default {
|
||||
this.followTimeContext();
|
||||
},
|
||||
followTimeContext() {
|
||||
this.timeContext.on('bounds', this.refreshData);
|
||||
this.timeContext.on('boundsChanged', this.refreshData);
|
||||
},
|
||||
stopFollowingTimeContext() {
|
||||
if (this.timeContext) {
|
||||
this.timeContext.off('bounds', this.refreshData);
|
||||
this.timeContext.off('boundsChanged', this.refreshData);
|
||||
}
|
||||
},
|
||||
addToComposition(telemetryObject) {
|
||||
|
@ -105,11 +105,11 @@ export default {
|
||||
this.followTimeContext();
|
||||
},
|
||||
followTimeContext() {
|
||||
this.timeContext.on('bounds', this.reloadTelemetryOnBoundsChange);
|
||||
this.timeContext.on('boundsChanged', this.reloadTelemetryOnBoundsChange);
|
||||
},
|
||||
stopFollowingTimeContext() {
|
||||
if (this.timeContext) {
|
||||
this.timeContext.off('bounds', this.reloadTelemetryOnBoundsChange);
|
||||
this.timeContext.off('boundsChanged', this.reloadTelemetryOnBoundsChange);
|
||||
}
|
||||
},
|
||||
addToComposition(telemetryObject) {
|
||||
|
@ -46,14 +46,6 @@ export default class ConditionManager extends EventEmitter {
|
||||
applied: false
|
||||
};
|
||||
this.initialize();
|
||||
|
||||
this.stopObservingForChanges = this.openmct.objects.observe(
|
||||
this.conditionSetDomainObject,
|
||||
'*',
|
||||
(newDomainObject) => {
|
||||
this.conditionSetDomainObject = newDomainObject;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async requestLatestValue(endpoint) {
|
||||
@ -518,10 +510,6 @@ export default class ConditionManager extends EventEmitter {
|
||||
Object.values(this.subscriptions).forEach((unsubscribe) => unsubscribe());
|
||||
delete this.subscriptions;
|
||||
|
||||
if (this.stopObservingForChanges) {
|
||||
this.stopObservingForChanges();
|
||||
}
|
||||
|
||||
this.conditions.forEach((condition) => {
|
||||
condition.destroy();
|
||||
});
|
||||
|
@ -41,7 +41,7 @@ export default class StyleRuleManager extends EventEmitter {
|
||||
});
|
||||
this.initialize(styleConfigurationWithNoSelection);
|
||||
if (styleConfiguration.conditionSetIdentifier) {
|
||||
this.openmct.time.on('bounds', this.refreshData);
|
||||
this.openmct.time.on('boundsChanged', this.refreshData);
|
||||
this.subscribeToConditionSet();
|
||||
} else {
|
||||
this.applyStaticStyle();
|
||||
@ -216,7 +216,7 @@ export default class StyleRuleManager extends EventEmitter {
|
||||
}
|
||||
|
||||
if (!skipEventListeners) {
|
||||
this.openmct.time.off('bounds', this.refreshData);
|
||||
this.openmct.time.off('boundsChanged', this.refreshData);
|
||||
this.openmct.editor.off('isEditing', this.toggleSubscription);
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,11 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<section id="conditionCollection" :class="{ 'is-expanded': expanded }">
|
||||
<section
|
||||
id="conditionCollection"
|
||||
:class="{ 'is-expanded': expanded }"
|
||||
aria-label="Condition Set Condition Collection"
|
||||
>
|
||||
<div class="c-cs__header c-section__header">
|
||||
<span
|
||||
class="c-disclosure-triangle c-tree__item__view-control is-enabled"
|
||||
|
@ -24,6 +24,7 @@
|
||||
<div
|
||||
class="c-condition-h"
|
||||
:class="{ 'is-drag-target': draggingOver }"
|
||||
aria-label="Condition Set Condition"
|
||||
@dragover.prevent
|
||||
@drop.prevent="dropCondition($event, conditionIndex)"
|
||||
@dragenter="dragEnter($event, conditionIndex)"
|
||||
@ -83,6 +84,7 @@
|
||||
<input
|
||||
v-model="condition.configuration.name"
|
||||
class="t-condition-input__name"
|
||||
aria-label="Condition Name Input"
|
||||
type="text"
|
||||
@change="persist"
|
||||
/>
|
||||
@ -91,7 +93,11 @@
|
||||
<span class="c-cdef__label">Output</span>
|
||||
<span class="c-cdef__controls">
|
||||
<span class="c-cdef__control">
|
||||
<select v-model="selectedOutputSelection" @change="setOutputValue">
|
||||
<select
|
||||
v-model="selectedOutputSelection"
|
||||
aria-label="Condition Output Type"
|
||||
@change="setOutputValue"
|
||||
>
|
||||
<option v-for="option in outputOptions" :key="option" :value="option">
|
||||
{{ initCap(option) }}
|
||||
</option>
|
||||
@ -101,6 +107,7 @@
|
||||
<input
|
||||
v-if="selectedOutputSelection === outputOptions[2]"
|
||||
v-model="condition.configuration.output"
|
||||
aria-label="Condition Output String"
|
||||
class="t-condition-name-input"
|
||||
type="text"
|
||||
@change="persist"
|
||||
|
@ -21,7 +21,7 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="c-cs" :class="{ 'is-stale': isStale }">
|
||||
<div class="c-cs" :class="{ 'is-stale': isStale }" aria-label="Condition Set">
|
||||
<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>
|
||||
|
@ -21,7 +21,12 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<section v-show="isEditing" id="test-data" :class="{ 'is-expanded': expanded }">
|
||||
<section
|
||||
v-show="isEditing"
|
||||
id="test-data"
|
||||
:class="{ 'is-expanded': expanded }"
|
||||
aria-label="Condition Set Test Data"
|
||||
>
|
||||
<div class="c-cs__header c-section__header">
|
||||
<span
|
||||
class="c-disclosure-triangle c-tree__item__view-control is-enabled"
|
||||
|
@ -18,6 +18,7 @@
|
||||
}
|
||||
|
||||
&__value {
|
||||
@include telemetryView();
|
||||
@include isLimit();
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@
|
||||
@object-drop-to="moveOrCreateNewFrame"
|
||||
/>
|
||||
|
||||
<div role="row" class="c-fl-container__frames-holder">
|
||||
<div role="row" class="c-fl-container__frames-holder" :class="flexLayoutCssClass">
|
||||
<template v-for="(frame, i) in frames" :key="frame.id">
|
||||
<frame-component
|
||||
class="c-fl-container__frame"
|
||||
@ -118,6 +118,9 @@ export default {
|
||||
},
|
||||
emits: ['new-frame', 'move-frame', 'persist'],
|
||||
computed: {
|
||||
flexLayoutCssClass() {
|
||||
return this.rowsLayout ? '--layout-rows' : '--layout-cols';
|
||||
},
|
||||
frames() {
|
||||
return this.container.frames;
|
||||
},
|
||||
|
@ -30,10 +30,8 @@
|
||||
|
||||
<div
|
||||
class="c-fl__container-holder u-style-receiver js-style-receiver"
|
||||
:class="{
|
||||
'c-fl--rows': rowsLayout === true
|
||||
}"
|
||||
:aria-label="`Flexible Layout ${rowsLayout ? 'Row' : 'Column'}`"
|
||||
:class="flexLayoutCssClass"
|
||||
:aria-label="`Flexible Layout ${rowsLayout ? 'Rows' : 'Columns'}`"
|
||||
>
|
||||
<template v-for="(container, index) in containers" :key="`component-${container.id}`">
|
||||
<drop-hint
|
||||
@ -45,7 +43,6 @@
|
||||
/>
|
||||
|
||||
<container-component
|
||||
class="c-fl__container"
|
||||
:index="index"
|
||||
:container="container"
|
||||
:rows-layout="rowsLayout"
|
||||
@ -148,15 +145,11 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
layoutDirectionStr() {
|
||||
if (this.rowsLayout) {
|
||||
return 'Rows';
|
||||
} else {
|
||||
return 'Columns';
|
||||
}
|
||||
},
|
||||
allContainersAreEmpty() {
|
||||
return this.containers.every((container) => container.frames.length === 0);
|
||||
},
|
||||
flexLayoutCssClass() {
|
||||
return this.rowsLayout ? 'c-fl--rows' : 'c-fl--cols';
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
@ -7,7 +7,6 @@
|
||||
$majorOffset: 35%;
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
@include grippy($c: $editFrameSelectedMovebarColorFg, $dir: $dir);
|
||||
@if $dir == 'x' {
|
||||
top: $minorOffset;
|
||||
@ -35,18 +34,15 @@
|
||||
flex: 1 1 100%; // Must be 100% to work
|
||||
overflow: auto;
|
||||
|
||||
// Columns by default
|
||||
flex-direction: row;
|
||||
> * + * {
|
||||
margin-left: 1px;
|
||||
// Controls layout of c-fl__container(s)
|
||||
&[class*='--cols'] {
|
||||
flex-direction: row;
|
||||
column-gap: 1px;
|
||||
}
|
||||
|
||||
&[class*='--rows'] {
|
||||
flex-direction: column;
|
||||
> * + * {
|
||||
margin-left: 0;
|
||||
margin-top: 1px;
|
||||
}
|
||||
row-gap: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,10 +115,18 @@
|
||||
&__frames-holder {
|
||||
display: flex;
|
||||
flex: 1 1 100%; // Must be 100% to work
|
||||
flex-direction: column; // Default
|
||||
flex-direction: row; // Default
|
||||
align-content: stretch;
|
||||
align-items: stretch;
|
||||
overflow: hidden; // This sucks, but doing in the short-term
|
||||
|
||||
&.--layout-cols {
|
||||
flex-direction: column !important;
|
||||
}
|
||||
|
||||
&.--layout-rows {
|
||||
flex-direction: row !important;
|
||||
}
|
||||
}
|
||||
|
||||
.is-editing & {
|
||||
|
@ -61,13 +61,13 @@ function ToolbarProvider(openmct) {
|
||||
options: [
|
||||
{
|
||||
value: true,
|
||||
icon: 'icon-columns',
|
||||
title: 'Columns layout'
|
||||
icon: 'icon-rows',
|
||||
title: 'Switch to rows layout'
|
||||
},
|
||||
{
|
||||
value: false,
|
||||
icon: 'icon-rows',
|
||||
title: 'Rows layout'
|
||||
icon: 'icon-columns',
|
||||
title: 'Switch to columns layout'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
@ -164,7 +164,7 @@ describe('Gauge plugin', () => {
|
||||
});
|
||||
spyOn(openmct.telemetry, 'getLimits').and.returnValue({ limits: () => Promise.resolve() });
|
||||
spyOn(openmct.telemetry, 'request').and.returnValue(Promise.resolve([randomValue]));
|
||||
spyOn(openmct.time, 'bounds').and.returnValue({
|
||||
spyOn(openmct.time, 'getBounds').and.returnValue({
|
||||
start: 1000,
|
||||
end: 5000
|
||||
});
|
||||
@ -306,7 +306,7 @@ describe('Gauge plugin', () => {
|
||||
});
|
||||
spyOn(openmct.telemetry, 'getLimits').and.returnValue({ limits: () => Promise.resolve() });
|
||||
spyOn(openmct.telemetry, 'request').and.returnValue(Promise.resolve([randomValue]));
|
||||
spyOn(openmct.time, 'bounds').and.returnValue({
|
||||
spyOn(openmct.time, 'getBounds').and.returnValue({
|
||||
start: 1000,
|
||||
end: 5000
|
||||
});
|
||||
@ -448,7 +448,7 @@ describe('Gauge plugin', () => {
|
||||
});
|
||||
spyOn(openmct.telemetry, 'getLimits').and.returnValue({ limits: () => Promise.resolve() });
|
||||
spyOn(openmct.telemetry, 'request').and.returnValue(Promise.resolve([randomValue]));
|
||||
spyOn(openmct.time, 'bounds').and.returnValue({
|
||||
spyOn(openmct.time, 'getBounds').and.returnValue({
|
||||
start: 1000,
|
||||
end: 5000
|
||||
});
|
||||
@ -763,7 +763,7 @@ describe('Gauge plugin', () => {
|
||||
})
|
||||
});
|
||||
spyOn(openmct.telemetry, 'request').and.returnValue(Promise.resolve([randomValue]));
|
||||
spyOn(openmct.time, 'bounds').and.returnValue({
|
||||
spyOn(openmct.time, 'getBounds').and.returnValue({
|
||||
start: 1000,
|
||||
end: 5000
|
||||
});
|
||||
|
@ -545,7 +545,7 @@ export default {
|
||||
|
||||
this.composition.load();
|
||||
|
||||
this.openmct.time.on('bounds', this.refreshData);
|
||||
this.openmct.time.on('boundsChanged', this.refreshData);
|
||||
this.openmct.time.on('timeSystem', this.setTimeSystem);
|
||||
|
||||
this.setupClockChangedEvent((domainObject) => {
|
||||
@ -561,7 +561,7 @@ export default {
|
||||
this.unsubscribe();
|
||||
}
|
||||
|
||||
this.openmct.time.off('bounds', this.refreshData);
|
||||
this.openmct.time.off('boundsChanged', this.refreshData);
|
||||
this.openmct.time.off('timeSystem', this.setTimeSystem);
|
||||
},
|
||||
methods: {
|
||||
|
@ -84,11 +84,12 @@ $meterNeedleBorderRadius: 5px;
|
||||
fill: $colorGaugeValue;
|
||||
}
|
||||
&__needle-value {
|
||||
fill: $colorGaugeValue;
|
||||
fill: $colorGaugeNeedle;
|
||||
|
||||
}
|
||||
&__current-value-text {
|
||||
fill: $colorGaugeTextValue;
|
||||
font-family: $heroFont;
|
||||
font-family: $numericFont;
|
||||
}
|
||||
|
||||
&__units-text,
|
||||
@ -125,7 +126,8 @@ $meterNeedleBorderRadius: 5px;
|
||||
// Filled area
|
||||
position: absolute;
|
||||
background: $colorGaugeValue;
|
||||
z-index: 1;
|
||||
box-shadow: $gaugeMeterValueShadow 0px 2px 10px 1px;
|
||||
//z-index: 3;
|
||||
}
|
||||
|
||||
&__value-needle {
|
||||
@ -135,6 +137,7 @@ $meterNeedleBorderRadius: 5px;
|
||||
content: '';
|
||||
display: block;
|
||||
background: $colorGaugeValue;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,7 +161,7 @@ $meterNeedleBorderRadius: 5px;
|
||||
|
||||
&__current-value-text {
|
||||
fill: $colorGaugeTextValue;
|
||||
font-family: $heroFont;
|
||||
font-family: $numericFont;
|
||||
}
|
||||
|
||||
.c-gauge__curval {
|
||||
|
@ -109,12 +109,12 @@ export default {
|
||||
this.stopFollowingTimeContext();
|
||||
this.timeContext = this.openmct.time.getContextForView(this.objectPath);
|
||||
this.timeContext.on('timeSystem', this.setScaleAndPlotImagery);
|
||||
this.timeContext.on('bounds', this.updateViewBounds);
|
||||
this.timeContext.on('boundsChanged', this.updateViewBounds);
|
||||
},
|
||||
stopFollowingTimeContext() {
|
||||
if (this.timeContext) {
|
||||
this.timeContext.off('timeSystem', this.setScaleAndPlotImagery);
|
||||
this.timeContext.off('bounds', this.updateViewBounds);
|
||||
this.timeContext.off('boundsChanged', this.updateViewBounds);
|
||||
}
|
||||
},
|
||||
expand(imageTimestamp) {
|
||||
|
@ -144,24 +144,132 @@ export default class ImportAsJSONAction {
|
||||
|
||||
return Array.from(new Set([...objectIdentifiers, ...itemObjectReferences]));
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
* @param {Object} tree
|
||||
* @param {string} namespace
|
||||
* @returns {Object}
|
||||
*/
|
||||
_generateNewIdentifiers(tree, newNamespace) {
|
||||
// For each domain object in the file, generate new ID, replace in tree
|
||||
Object.keys(tree.openmct).forEach((domainObjectId) => {
|
||||
const oldId = parseKeyString(domainObjectId);
|
||||
|
||||
/**
|
||||
* Generates a map of old IDs to new IDs for efficient lookup during tree walking.
|
||||
* This function considers cases where original namespaces are blank and updates those IDs as well.
|
||||
*
|
||||
* @param {Object} tree - The object tree containing the old IDs.
|
||||
* @param {string} newNamespace - The namespace for the new IDs.
|
||||
* @returns {Object} A map of old IDs to new IDs.
|
||||
*/
|
||||
_generateIdMap(tree, newNamespace) {
|
||||
const idMap = {};
|
||||
const keys = Object.keys(tree.openmct);
|
||||
|
||||
for (const oldIdKey of keys) {
|
||||
const oldId = parseKeyString(oldIdKey);
|
||||
const newId = {
|
||||
namespace: newNamespace,
|
||||
key: uuid()
|
||||
};
|
||||
tree = this._rewriteId(oldId, newId, tree);
|
||||
}, this);
|
||||
const newIdKeyString = this.openmct.objects.makeKeyString(newId);
|
||||
|
||||
// Update the map with the old and new ID key strings.
|
||||
idMap[oldIdKey] = newIdKeyString;
|
||||
|
||||
// If the old namespace is blank, also map the non-namespaced ID.
|
||||
if (!oldId.namespace) {
|
||||
const nonNamespacedOldIdKey = oldId.key;
|
||||
idMap[nonNamespacedOldIdKey] = newIdKeyString;
|
||||
}
|
||||
}
|
||||
|
||||
return idMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks through the object tree and updates IDs according to the provided ID map.
|
||||
* @param {Object} obj - The current object being visited in the tree.
|
||||
* @param {Object} idMap - A map of old IDs to new IDs for rewriting.
|
||||
* @param {Object} importDialog - Optional progress dialog for import.
|
||||
* @returns {Promise<Object>} The object with updated IDs.
|
||||
*/
|
||||
async _walkAndRewriteIds(obj, idMap, importDialog) {
|
||||
// How many rewrites to do before yielding to the event loop
|
||||
const UI_UPDATE_INTERVAL = 300;
|
||||
// The percentage of the progress dialog to allocate to rewriting IDs
|
||||
const PERCENT_OF_DIALOG = 80;
|
||||
if (obj === null || obj === undefined) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (typeof obj === 'string') {
|
||||
const possibleId = idMap[obj];
|
||||
if (possibleId) {
|
||||
return possibleId;
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.hasOwn(obj, 'key') && Object.hasOwn(obj, 'namespace')) {
|
||||
const oldId = this.openmct.objects.makeKeyString(obj);
|
||||
const possibleId = idMap[oldId];
|
||||
|
||||
if (possibleId) {
|
||||
const newIdParts = possibleId.split(':');
|
||||
if (newIdParts.length >= 2) {
|
||||
// new ID is namespaced, so update both the namespace and key
|
||||
obj.namespace = newIdParts[0];
|
||||
obj.key = newIdParts[1];
|
||||
} else {
|
||||
// old ID was not namespaced, so update the key only
|
||||
obj.namespace = '';
|
||||
obj.key = newIdParts[0];
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
for (let i = 0; i < obj.length; i++) {
|
||||
obj[i] = await this._walkAndRewriteIds(obj[i], idMap); // Process each item in the array
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (typeof obj === 'object') {
|
||||
const newObj = {};
|
||||
|
||||
const keys = Object.keys(obj);
|
||||
let processedCount = 0;
|
||||
for (const key of keys) {
|
||||
const value = obj[key];
|
||||
const possibleId = idMap[key];
|
||||
const newKey = possibleId || key;
|
||||
|
||||
newObj[newKey] = await this._walkAndRewriteIds(value, idMap);
|
||||
|
||||
// Optionally update the importDialog here, after each property has been processed
|
||||
if (importDialog) {
|
||||
processedCount++;
|
||||
if (processedCount % UI_UPDATE_INTERVAL === 0) {
|
||||
// yield to the event loop to allow the UI to update
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
const percentPersisted = Math.ceil(PERCENT_OF_DIALOG * (processedCount / keys.length));
|
||||
const message = `Rewriting ${processedCount} of ${keys.length} imported objects.`;
|
||||
importDialog.updateProgress(percentPersisted, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newObj;
|
||||
}
|
||||
|
||||
// Return the input as-is for types that are not objects, strings, or arrays
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Object} tree
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
async _generateNewIdentifiers(tree, newNamespace, importDialog) {
|
||||
const idMap = this._generateIdMap(tree, newNamespace);
|
||||
tree.rootId = idMap[tree.rootId];
|
||||
tree.openmct = await this._walkAndRewriteIds(tree.openmct, idMap, importDialog);
|
||||
return tree;
|
||||
}
|
||||
/**
|
||||
@ -170,9 +278,16 @@ export default class ImportAsJSONAction {
|
||||
* @param {Object} objTree
|
||||
*/
|
||||
async _importObjectTree(domainObject, objTree) {
|
||||
// make rewriting objects IDs 80% of the progress bar
|
||||
const importDialog = this.openmct.overlays.progressDialog({
|
||||
progressPerc: 0,
|
||||
message: `Importing ${Object.keys(objTree.openmct).length} objects`,
|
||||
iconClass: 'info',
|
||||
title: 'Importing'
|
||||
});
|
||||
const objectsToCreate = [];
|
||||
const namespace = domainObject.identifier.namespace;
|
||||
const tree = this._generateNewIdentifiers(objTree, namespace);
|
||||
const tree = await this._generateNewIdentifiers(objTree, namespace, importDialog);
|
||||
const rootId = tree.rootId;
|
||||
|
||||
const rootObj = tree.openmct[rootId];
|
||||
@ -182,11 +297,24 @@ export default class ImportAsJSONAction {
|
||||
this._deepInstantiate(rootObj, tree.openmct, [], objectsToCreate);
|
||||
|
||||
try {
|
||||
await Promise.all(objectsToCreate.map(this._instantiate, this));
|
||||
let persistedObjects = 0;
|
||||
// make saving objects objects 20% of the progress bar
|
||||
await Promise.all(
|
||||
objectsToCreate.map(async (objectToCreate) => {
|
||||
persistedObjects++;
|
||||
const percentPersisted =
|
||||
Math.ceil(20 * (persistedObjects / objectsToCreate.length)) + 80;
|
||||
const message = `Saving ${persistedObjects} of ${objectsToCreate.length} imported objects.`;
|
||||
importDialog.updateProgress(percentPersisted, message);
|
||||
await this._instantiate(objectToCreate);
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
this.openmct.notifications.error('Error saving objects');
|
||||
|
||||
throw error;
|
||||
} finally {
|
||||
importDialog.dismiss();
|
||||
}
|
||||
|
||||
const compositionCollection = this.openmct.composition.get(domainObject);
|
||||
@ -194,7 +322,8 @@ export default class ImportAsJSONAction {
|
||||
this.openmct.objects.mutate(rootObj, 'location', domainObjectKeyString);
|
||||
compositionCollection.add(rootObj);
|
||||
} else {
|
||||
const dialog = this.openmct.overlays.dialog({
|
||||
importDialog.dismiss();
|
||||
const cannotImportDialog = this.openmct.overlays.dialog({
|
||||
iconClass: 'alert',
|
||||
message: "We're sorry, but you cannot import that object type into this object.",
|
||||
buttons: [
|
||||
@ -202,7 +331,7 @@ export default class ImportAsJSONAction {
|
||||
label: 'Ok',
|
||||
emphasis: true,
|
||||
callback: function () {
|
||||
dialog.dismiss();
|
||||
cannotImportDialog.dismiss();
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -217,43 +346,7 @@ export default class ImportAsJSONAction {
|
||||
_instantiate(model) {
|
||||
return this.openmct.objects.save(model);
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
* @param {Object} oldId
|
||||
* @param {Object} newId
|
||||
* @param {Object} tree
|
||||
* @returns {Object}
|
||||
*/
|
||||
_rewriteId(oldId, newId, tree) {
|
||||
let newIdKeyString = this.openmct.objects.makeKeyString(newId);
|
||||
let oldIdKeyString = this.openmct.objects.makeKeyString(oldId);
|
||||
const newTreeString = JSON.stringify(tree).replace(
|
||||
new RegExp(oldIdKeyString, 'g'),
|
||||
newIdKeyString
|
||||
);
|
||||
const newTree = JSON.parse(newTreeString, (key, value) => {
|
||||
if (
|
||||
value !== undefined &&
|
||||
value !== null &&
|
||||
Object.prototype.hasOwnProperty.call(value, 'key') &&
|
||||
Object.prototype.hasOwnProperty.call(value, 'namespace')
|
||||
) {
|
||||
// first check if key is messed up from regex and contains a colon
|
||||
// if it does, repair it
|
||||
if (value.key.includes(':')) {
|
||||
const splitKey = value.key.split(':');
|
||||
value.key = splitKey[1];
|
||||
value.namespace = splitKey[0];
|
||||
}
|
||||
// now check if we need to replace the id
|
||||
if (value.key === oldId.key && value.namespace === oldId.namespace) {
|
||||
return newId;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
});
|
||||
return newTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Object} domainObject
|
||||
|
@ -111,7 +111,6 @@ describe('The import JSON action', function () {
|
||||
});
|
||||
|
||||
it('protects against prototype pollution', (done) => {
|
||||
spyOn(console, 'warn');
|
||||
spyOn(openmct.forms, 'showForm').and.callFake(returnResponseWithPrototypePollution);
|
||||
|
||||
unObserve = openmct.objects.observe(folderObject, '*', callback);
|
||||
@ -123,8 +122,6 @@ describe('The import JSON action', function () {
|
||||
Object.prototype.hasOwnProperty.call(newObject, '__proto__') ||
|
||||
Object.prototype.hasOwnProperty.call(Object.getPrototypeOf(newObject), 'toString');
|
||||
|
||||
// warning from openmct.objects.get
|
||||
expect(console.warn).not.toHaveBeenCalled();
|
||||
expect(hasPollutedProto).toBeFalse();
|
||||
|
||||
done();
|
||||
@ -192,6 +189,12 @@ describe('The import JSON action', function () {
|
||||
type: 'folder'
|
||||
};
|
||||
spyOn(openmct.objects, 'save').and.callFake((model) => Promise.resolve(model));
|
||||
spyOn(openmct.overlays, 'progressDialog').and.callFake(() => {
|
||||
return {
|
||||
updateProgress: () => {},
|
||||
dismiss: () => {}
|
||||
};
|
||||
});
|
||||
try {
|
||||
await importFromJSONAction.onSave(targetDomainObject, {
|
||||
selectFile: { body: JSON.stringify(incomingObject) }
|
||||
|
@ -61,7 +61,7 @@ describe('The local time', () => {
|
||||
});
|
||||
|
||||
it('can be set to be the main time system', () => {
|
||||
expect(openmct.time.getTimeSystem().key).toBe(LOCAL_SYSTEM_KEY);
|
||||
expect(openmct.time.timeSystem().key).toBe(LOCAL_SYSTEM_KEY);
|
||||
});
|
||||
|
||||
it('uses the local-format time format', () => {
|
||||
|
@ -278,7 +278,7 @@ export default {
|
||||
const bounds = this.openmct.time.getBounds();
|
||||
const isTimeBoundChanged =
|
||||
this.embed.bounds.start !== bounds.start || this.embed.bounds.end !== bounds.end;
|
||||
const isFixedTimespanMode = !this.openmct.time.clock();
|
||||
const isFixedTimespanMode = this.openmct.time.isFixed();
|
||||
|
||||
let message = '';
|
||||
if (isTimeBoundChanged) {
|
||||
|
@ -31,7 +31,7 @@
|
||||
@drop.capture="cancelEditMode"
|
||||
@drop.prevent="dropOnEntry"
|
||||
@click="selectAndEmitEntry($event, entry)"
|
||||
@paste="addImageFromPaste"
|
||||
@paste="handlePaste"
|
||||
>
|
||||
<div class="c-ne__time-and-content">
|
||||
<div class="c-ne__time-and-creator-and-delete">
|
||||
@ -368,6 +368,28 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handlePaste(event) {
|
||||
const clipboardItems = Array.from(
|
||||
(event.clipboardData || event.originalEvent.clipboardData).items
|
||||
);
|
||||
const hasClipboardText = clipboardItems.some(
|
||||
(clipboardItem) => clipboardItem.kind === 'string'
|
||||
);
|
||||
const clipboardImages = clipboardItems.filter(
|
||||
(clipboardItem) => clipboardItem.kind === 'file' && clipboardItem.type.includes('image')
|
||||
);
|
||||
const hasClipboardImages = clipboardImages?.length > 0;
|
||||
|
||||
if (hasClipboardImages) {
|
||||
if (hasClipboardText) {
|
||||
console.warn('Image and text kinds found in paste. Only processing images.');
|
||||
}
|
||||
|
||||
this.addImageFromPaste(clipboardImages, event);
|
||||
} else if (hasClipboardText) {
|
||||
this.addTextFromPaste(event);
|
||||
}
|
||||
},
|
||||
async addNewEmbed(objectPath) {
|
||||
const bounds = this.openmct.time.getBounds();
|
||||
const snapshotMeta = {
|
||||
@ -384,32 +406,34 @@ export default {
|
||||
|
||||
this.manageEmbedLayout();
|
||||
},
|
||||
async addImageFromPaste(event) {
|
||||
const clipboardItems = Array.from(
|
||||
(event.clipboardData || event.originalEvent.clipboardData).items
|
||||
);
|
||||
const hasImage = clipboardItems.some(
|
||||
(clipboardItem) => clipboardItem.type.includes('image') && clipboardItem.kind === 'file'
|
||||
);
|
||||
// If the clipboard contained an image, prevent the paste event from reaching the textarea.
|
||||
if (hasImage) {
|
||||
addTextFromPaste(event) {
|
||||
if (!this.editMode) {
|
||||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
async addImageFromPaste(clipboardImages, event) {
|
||||
event?.preventDefault();
|
||||
let updated = false;
|
||||
|
||||
await Promise.all(
|
||||
Array.from(clipboardItems).map(async (clipboardItem) => {
|
||||
const isImage = clipboardItem.type.includes('image') && clipboardItem.kind === 'file';
|
||||
if (isImage) {
|
||||
const imageFile = clipboardItem.getAsFile();
|
||||
const imageEmbed = await createNewImageEmbed(imageFile, this.openmct, imageFile?.name);
|
||||
if (!this.entry.embeds) {
|
||||
this.entry.embeds = [];
|
||||
}
|
||||
this.entry.embeds.push(imageEmbed);
|
||||
Array.from(clipboardImages).map(async (clipboardImage) => {
|
||||
const imageFile = clipboardImage.getAsFile();
|
||||
const imageEmbed = await createNewImageEmbed(imageFile, this.openmct, imageFile?.name);
|
||||
|
||||
if (!this.entry.embeds) {
|
||||
this.entry.embeds = [];
|
||||
}
|
||||
|
||||
this.entry.embeds.push(imageEmbed);
|
||||
|
||||
updated = true;
|
||||
})
|
||||
);
|
||||
this.manageEmbedLayout();
|
||||
this.timestampAndUpdate();
|
||||
|
||||
if (updated) {
|
||||
this.manageEmbedLayout();
|
||||
this.timestampAndUpdate();
|
||||
}
|
||||
},
|
||||
convertMarkDownToHtml(text = '') {
|
||||
let markDownHtml = this.marked.parse(text, {
|
||||
|
@ -199,7 +199,7 @@ export default {
|
||||
this.updateViewBounds(this.timeContext.getBounds());
|
||||
|
||||
this.timeContext.on('timeSystem', this.setScaleAndGenerateActivities);
|
||||
this.timeContext.on('bounds', this.updateViewBounds);
|
||||
this.timeContext.on('boundsChanged', this.updateViewBounds);
|
||||
},
|
||||
loadComposition() {
|
||||
if (this.composition) {
|
||||
@ -211,7 +211,7 @@ export default {
|
||||
stopFollowingTimeContext() {
|
||||
if (this.timeContext) {
|
||||
this.timeContext.off('timeSystem', this.setScaleAndGenerateActivities);
|
||||
this.timeContext.off('bounds', this.updateViewBounds);
|
||||
this.timeContext.off('boundsChanged', this.updateViewBounds);
|
||||
}
|
||||
},
|
||||
showReplacePlanDialog(domainObject) {
|
||||
|
@ -1860,8 +1860,8 @@ export default {
|
||||
},
|
||||
|
||||
showSynchronizeDialog() {
|
||||
const isLocalClock = this.timeContext.clock();
|
||||
if (isLocalClock !== undefined) {
|
||||
const isFixedTimespanMode = this.timeContext.isFixed();
|
||||
if (!isFixedTimespanMode) {
|
||||
const message = `
|
||||
This action will change the Time Conductor to Fixed Timespan mode with this plot view's current time bounds.
|
||||
Do you want to continue?
|
||||
|
@ -140,7 +140,7 @@ export default class PlotSeries extends Model {
|
||||
//this triggers Model.destroy which in turn triggers destroy methods for other classes.
|
||||
super.destroy();
|
||||
this.stopListening();
|
||||
this.openmct.time.off('bounds', this.updateLimits);
|
||||
this.openmct.time.off('boundsChanged', this.updateLimits);
|
||||
|
||||
if (this.unsubscribe) {
|
||||
this.unsubscribe();
|
||||
@ -171,7 +171,7 @@ export default class PlotSeries extends Model {
|
||||
this.limitEvaluator = this.openmct.telemetry.limitEvaluator(options.domainObject);
|
||||
this.limitDefinition = this.openmct.telemetry.limitDefinition(options.domainObject);
|
||||
this.limits = [];
|
||||
this.openmct.time.on('bounds', this.updateLimits);
|
||||
this.openmct.time.on('boundsChanged', this.updateLimits);
|
||||
this.removeMutationListener = this.openmct.objects.observe(
|
||||
this.domainObject,
|
||||
'name',
|
||||
|
@ -72,6 +72,7 @@ import SummaryWidget from './summaryWidget/plugin.js';
|
||||
import Tabs from './tabs/plugin.js';
|
||||
import TelemetryMean from './telemetryMean/plugin.js';
|
||||
import TelemetryTablePlugin from './telemetryTable/plugin.js';
|
||||
import DarkMatter from './themes/darkmatter.js';
|
||||
import Espresso from './themes/espresso.js';
|
||||
import Snow from './themes/snow.js';
|
||||
import TimeConductorPlugin from './timeConductor/plugin.js';
|
||||
@ -125,7 +126,6 @@ plugins.Plot = PlotPlugin;
|
||||
plugins.BarChart = BarChartPlugin;
|
||||
plugins.ScatterPlot = ScatterPlotPlugin;
|
||||
plugins.TelemetryTable = TelemetryTablePlugin;
|
||||
|
||||
plugins.SummaryWidget = SummaryWidget;
|
||||
plugins.TelemetryMean = TelemetryMean;
|
||||
plugins.URLIndicator = URLIndicatorPlugin;
|
||||
@ -145,6 +145,7 @@ plugins.OpenInNewTabAction = OpenInNewTabAction;
|
||||
plugins.ReloadAction = ReloadAction;
|
||||
plugins.ClearData = ClearData;
|
||||
plugins.WebPage = WebPagePlugin;
|
||||
plugins.DarkmatterTheme = DarkMatter;
|
||||
plugins.Espresso = Espresso;
|
||||
plugins.Snow = Snow;
|
||||
plugins.Condition = ConditionPlugin;
|
||||
|
@ -149,6 +149,8 @@ export default class RemoteClock extends DefaultClock {
|
||||
|
||||
/**
|
||||
* Waits for the clock to have a non-default tick value.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
#waitForReady() {
|
||||
const waitForInitialTick = (resolve) => {
|
||||
|
@ -611,7 +611,7 @@ describe('The Mean Telemetry Provider', function () {
|
||||
}
|
||||
|
||||
function createMockTimeApi() {
|
||||
return jasmine.createSpyObj('timeApi', ['getTimeSystem', 'setTimeSystem']);
|
||||
return jasmine.createSpyObj('timeApi', ['getTimeSystem']);
|
||||
}
|
||||
|
||||
function setTimeSystemTo(timeSystemKey) {
|
||||
|
@ -546,7 +546,7 @@ export default {
|
||||
this.table.tableRows.on('sort', this.throttledUpdateVisibleRows);
|
||||
this.table.tableRows.on('filter', this.throttledUpdateVisibleRows);
|
||||
|
||||
this.openmct.time.on('bounds', this.boundsChanged);
|
||||
this.openmct.time.on('boundsChanged', this.boundsChanged);
|
||||
|
||||
//Default sort
|
||||
this.sortOptions = this.table.tableRows.sortBy();
|
||||
@ -561,6 +561,9 @@ export default {
|
||||
|
||||
this.table.initialize();
|
||||
this.rescaleToContainer();
|
||||
|
||||
// Scroll to the top of the table after loading
|
||||
this.addToAfterLoadActions(this.scroll);
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.table.off('object-added', this.addObject);
|
||||
@ -579,7 +582,7 @@ export default {
|
||||
|
||||
this.table.configuration.off('change', this.updateConfiguration);
|
||||
|
||||
this.openmct.time.off('bounds', this.boundsChanged);
|
||||
this.openmct.time.off('boundsChanged', this.boundsChanged);
|
||||
|
||||
this.table.configuration.destroy();
|
||||
|
||||
|
44
src/plugins/themes/darkmatter-theme.scss
Normal file
44
src/plugins/themes/darkmatter-theme.scss
Normal file
@ -0,0 +1,44 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2024, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
@import '../../styles/vendor/normalize-min';
|
||||
@import '../../styles/constants';
|
||||
@import '../../styles/constants-mobile.scss';
|
||||
|
||||
@import '../../styles/constants-darkmatter';
|
||||
|
||||
@import '../../styles/mixins';
|
||||
@import '../../styles/animations';
|
||||
@import '../../styles/about';
|
||||
@import '../../styles/glyphs';
|
||||
@import '../../styles/global';
|
||||
@import '../../styles/status';
|
||||
@import '../../styles/limits';
|
||||
@import '../../styles/controls';
|
||||
@import '../../styles/forms';
|
||||
@import '../../styles/table';
|
||||
@import '../../styles/legacy';
|
||||
@import '../../styles/legacy-plots';
|
||||
@import '../../styles/plotly';
|
||||
@import '../../styles/legacy-messages';
|
||||
|
||||
@import '../../styles/vue-styles.scss';
|
30
src/plugins/themes/darkmatter.js
Normal file
30
src/plugins/themes/darkmatter.js
Normal file
@ -0,0 +1,30 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2024, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
// Note: This darkmatter theme is in Beta and is not yet ready for prime time. It needs some more tweaking.
|
||||
|
||||
import { installTheme } from './installTheme.js';
|
||||
|
||||
export default function plugin() {
|
||||
return function install(openmct) {
|
||||
installTheme(openmct, 'darkmatter');
|
||||
};
|
||||
}
|
@ -1,3 +1,25 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2024, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
@import '../../styles/vendor/normalize-min';
|
||||
@import '../../styles/constants';
|
||||
@import '../../styles/constants-mobile.scss';
|
||||
|
@ -1,3 +1,25 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2024, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import { installTheme } from './installTheme.js';
|
||||
|
||||
export default function plugin() {
|
||||
|
@ -1,3 +1,25 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2024, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
@import '../../styles/vendor/normalize-min';
|
||||
@import '../../styles/constants';
|
||||
@import '../../styles/constants-mobile.scss';
|
||||
|
@ -1,3 +1,25 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2024, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import { installTheme } from './installTheme.js';
|
||||
|
||||
export default function plugin() {
|
||||
|
@ -198,13 +198,13 @@ export default {
|
||||
this.timeContext = this.openmct.time.getContextForView(this.objectPath);
|
||||
this.getTimeSystems();
|
||||
this.updateViewBounds();
|
||||
this.timeContext.on('bounds', this.updateViewBounds);
|
||||
this.timeContext.on('clock', this.updateViewBounds);
|
||||
this.timeContext.on('boundsChanged', this.updateViewBounds);
|
||||
this.timeContext.on('clockChanged', this.updateViewBounds);
|
||||
},
|
||||
stopFollowingTimeContext() {
|
||||
if (this.timeContext) {
|
||||
this.timeContext.off('bounds', this.updateViewBounds);
|
||||
this.timeContext.off('clock', this.updateViewBounds);
|
||||
this.timeContext.off('boundsChanged', this.updateViewBounds);
|
||||
this.timeContext.off('clockChanged', this.updateViewBounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
592
src/styles/_constants-darkmatter.scss
Normal file
592
src/styles/_constants-darkmatter.scss
Normal file
@ -0,0 +1,592 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2024, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/************************************************** DARKMATTER THEME*/
|
||||
// Fonts
|
||||
@import url('https://fonts.googleapis.com/css2?family=Roboto+Condensed&display=swap'); // Header font, Roboto Condensed. This is an alternative to the DIN Alt font, which is not available on Google Fonts.
|
||||
@import url('https://fonts.googleapis.com/css2?family=Exo:ital,wght@0,100..900;1,100..900&display=swap'); // Body Font, Exo
|
||||
@import url('https://fonts.googleapis.com/css2?family=Chakra+Petch&family=Roboto+Condensed&display=swap');// Temporary numeric font, Chakra Petch (need to import local font instead).
|
||||
$heroFont: 'Teko', sans-serif;
|
||||
$headerFont: 'Cabin Condensed', sans-serif;
|
||||
$bodyFont: 'Exo', sans-serif;
|
||||
$numericFont: 'Chakra Petch', sans-serif;
|
||||
|
||||
@mixin heroFont($size: 1em) {
|
||||
font-family: $heroFont;
|
||||
font-size: $size;
|
||||
}
|
||||
|
||||
@mixin headerFont($size: 1em) {
|
||||
font-family: $headerFont;
|
||||
font-size: $size;
|
||||
}
|
||||
|
||||
@mixin bodyFont($size: 1em) {
|
||||
font-family: $bodyFont;
|
||||
font-size: $size;
|
||||
}
|
||||
|
||||
@mixin numericFont($size: 1em){
|
||||
font-family: $numericFont;
|
||||
font-size: $size;
|
||||
}
|
||||
|
||||
@mixin discreteItem() {
|
||||
background: $colorDiscreteItemBg;
|
||||
border: none;
|
||||
border-radius: $controlCr;
|
||||
|
||||
.c-input-inline:hover {
|
||||
background: $colorBodyBg;
|
||||
}
|
||||
|
||||
&--current-match {
|
||||
background: $colorDiscreteItemCurrentBg;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin discreteItemInnerElem() {
|
||||
border: 1px solid rgba(#fff, 0.1);
|
||||
border-radius: $controlCr;
|
||||
}
|
||||
|
||||
@mixin themedButton($c: $colorBtnBg) {
|
||||
background: radial-gradient(rgba($c, 1) , rgba($c, .7));
|
||||
box-shadow: rgba(black, 0.5) 0 0.5px 2px;
|
||||
}
|
||||
|
||||
@mixin telemetryView(){
|
||||
border: 1px solid $colorBodyFg;
|
||||
border-radius: $controlCr;
|
||||
}
|
||||
|
||||
@mixin browseFrameBorder() { // Used on main object container to add highlighted corners to non-hidden frames.
|
||||
border-image: radial-gradient(circle, #575757, #6c6c6c, #818181, #979797, #aeaeae);
|
||||
border-style: solid;
|
||||
padding: 10px;
|
||||
$browseFrameCornerWidth: 4px;
|
||||
background:
|
||||
linear-gradient(to right, $browseFrameCornerColor, transparent $browseFrameCornerWidth) 0 0,
|
||||
linear-gradient(to right, $browseFrameCornerColor, transparent $browseFrameCornerWidth) 0 100%,
|
||||
linear-gradient(to left, $browseFrameCornerColor, transparent $browseFrameCornerWidth) 100% 0,
|
||||
linear-gradient(to left, $browseFrameCornerColor, transparent $browseFrameCornerWidth) 100% 100%,
|
||||
linear-gradient(to bottom, $browseFrameCornerColor, transparent $browseFrameCornerWidth) 0 0,
|
||||
linear-gradient(to bottom, $browseFrameCornerColor, transparent $browseFrameCornerWidth) 100% 0,
|
||||
linear-gradient(to top, $browseFrameCornerColor, transparent $browseFrameCornerWidth) 0 100%,
|
||||
linear-gradient(to top, $browseFrameCornerColor, transparent $browseFrameCornerWidth) 100% 100%,
|
||||
rgb(0, 0, 0, .4);
|
||||
|
||||
background-repeat: no-repeat;
|
||||
background-size: 35px 35px;
|
||||
border-radius: $interiorMarginLg;
|
||||
box-shadow: 0px 0px 20px 2px rgb(140 140 140 / 20%);
|
||||
}
|
||||
|
||||
|
||||
// Functions
|
||||
@function buttonBg($c: $colorBtnBg) {
|
||||
@return radial-gradient(rgba($colorBodyBg, 1) , rgba($colorBodyBg, .6));
|
||||
}
|
||||
|
||||
@function pullForward($val, $amt) {
|
||||
@return lighten($val, $amt);
|
||||
}
|
||||
|
||||
@function pushBack($val, $amt) {
|
||||
@return darken($val, $amt);
|
||||
}
|
||||
|
||||
/**************************************************** CONSTANTS */
|
||||
$fontBaseSize: 12px;
|
||||
$smallCr: 2px;
|
||||
$controlCr: 3px;
|
||||
$basicCr: 4px;
|
||||
$shdwBtns: rgba(black, 0.2) 0 1px 2px;
|
||||
$shdwBtnsOverlay: rgba(black, 0.5) 0 1px 5px;
|
||||
|
||||
// Base colors
|
||||
$colorBodyBg: #17171B;
|
||||
$colorBodyBgSubtle: pullForward($colorBodyBg, 5%);
|
||||
$colorBodyBgSubtleHov: pullForward($colorBodyBg, 10%);
|
||||
$colorBodyFg: #aaaaaa;
|
||||
$colorBodyFgSubtle: #9c9c9c;
|
||||
$colorBodyFgEm: #fff;
|
||||
$colorGenBg: #222;
|
||||
$colorHeadBg: rgba($colorBodyBg, .5);
|
||||
$colorHeadFg: $colorBodyFg;
|
||||
$colorKey: #1C67E3;
|
||||
$colorKeyBg: #015fca;
|
||||
$colorKeyFg: #fff; // Darker version of colorKey for use in major buttons
|
||||
$colorKeyHov: lighten($colorKey, 10%);
|
||||
$colorKeyBgHov: lighten($colorKeyBg, 10%);
|
||||
$colorKeyFilter: invert(36%) sepia(10%) saturate(2512%) hue-rotate(170deg) brightness(100%) contrast(200%);
|
||||
$colorKeyFilterHov: invert(63%) sepia(88%) saturate(3029%) hue-rotate(154deg) brightness(101%)
|
||||
contrast(100%);
|
||||
$colorKeySelectedBg: $colorKey;
|
||||
$uiColor: #0093ff; // Resize bars, splitter bars, etc.
|
||||
$colorInteriorBorder: rgba($colorBodyFg, 0.2);
|
||||
$colorA: #ccc;
|
||||
$colorAHov: #fff;
|
||||
$filterHov: brightness(1.3) contrast(1.5); // Tree, location items
|
||||
$filterHovSubtle: brightness(1.2) contrast(1.2);
|
||||
$colorSelectedBg: rgba($colorKey, 0.3);
|
||||
$colorSelectedFg: pullForward($colorBodyFg, 20%);
|
||||
|
||||
// Body constants
|
||||
$bodyBg: $colorBodyBg url('../ui/layout/assets/images/darkmatter-bg.png') no-repeat center 85%; // Reference: https://science.nasa.gov/wp-content/uploads/2023/08/s2-1280.jpg?w=4096&format=webp
|
||||
$bodyBgSize: cover;
|
||||
$bodySize: 100vh;
|
||||
|
||||
// Object labels
|
||||
$objectLabelTypeIconOpacity: 0.9;
|
||||
$objectLabelNameColorFg: $colorBodyFgEm;
|
||||
|
||||
// Layout
|
||||
$shellMainPad: 4px 0;
|
||||
$shellPanePad: $interiorMargin, 7px;
|
||||
$drawerBg: lighten($colorBodyBg, 5%);
|
||||
$drawerFg: lighten($colorBodyFg, 5%);
|
||||
$sideBarBg: $drawerBg;
|
||||
$sideBarHeaderBg: rgba($colorBodyFg, 0.1);
|
||||
$sideBarHeaderFg: rgba($colorBodyFg, 0.7);
|
||||
|
||||
// Status colors, mainly used for messaging and item ancillary symbols
|
||||
$colorStatusFg: #888;
|
||||
$colorStatusDefault: #ccc;
|
||||
$colorStatusInfo: #60ba7b;
|
||||
$colorStatusInfoFilter: invert(58%) sepia(44%) saturate(405%) hue-rotate(85deg) brightness(102%) contrast(92%);
|
||||
$colorStatusAlert: #ffb66c;
|
||||
$colorStatusAlertFilter: invert(78%) sepia(26%) saturate(1160%) hue-rotate(324deg) brightness(107%) contrast(101%);
|
||||
$colorStatusError: #da0004;
|
||||
$colorStatusErrorFilter: invert(10%) sepia(96%) saturate(4360%) hue-rotate(351deg) brightness(111%) contrast(115%);
|
||||
$colorStatusBtnBg: #666; // Where is this used?
|
||||
$colorStatusPartialBg: #3f5e8b;
|
||||
$colorStatusCompleteBg: #457638;
|
||||
$colorAlert: #ff8a0d;
|
||||
$colorAlertFg: #fff;
|
||||
$colorError: #ff3c00;
|
||||
$colorErrorFg: #fff;
|
||||
$colorWarningHi: #990000;
|
||||
$colorWarningHiFg: #ff9594;
|
||||
$colorWarningLo: #ff9900;
|
||||
$colorWarningLoFg: #523400;
|
||||
$colorDiagnostic: #a4b442;
|
||||
$colorDiagnosticFg: #39461a;
|
||||
$colorCommand: #3693bd;
|
||||
$colorCommandFg: #fff;
|
||||
$colorInfo: #2294a2;
|
||||
$colorInfoFg: #fff;
|
||||
$colorOk: #33cc33;
|
||||
$colorOkFg: #fff;
|
||||
$colorFilterBg: #44449c;
|
||||
$colorFilterFg: #8984e9;
|
||||
$colorFilter: $colorFilterFg; // Standalone against $colorBodyBg
|
||||
|
||||
// States
|
||||
$colorPausedBg: #ff9900;
|
||||
$colorPausedFg: #333;
|
||||
|
||||
// Time Colors
|
||||
$colorTimeCommonFg: #eee;
|
||||
$colorTimeFixed: #59554c;
|
||||
$colorTimeFixedBg: $colorTimeFixed;
|
||||
$colorTimeFixedFg: #eee;
|
||||
$colorTimeFixedFgSubtle: #b2aa98;
|
||||
$colorTimeFixedHov: pullForward($colorTimeFixed, 5%);
|
||||
$colorTimeFixedSubtle: pushBack($colorTimeFixed, 20%);
|
||||
$colorTimeFixedBtnBg: pullForward($colorTimeFixed, 5%);
|
||||
$colorTimeFixedBtnFg: $colorTimeFixedFgSubtle;
|
||||
$colorTimeFixedBtnBgMajor: #a09375;
|
||||
$colorTimeFixedBtnFgMajor: #fff;
|
||||
$colorTimeRealtime: #445890;
|
||||
$colorTimeRealtimeBg: $colorTimeRealtime;
|
||||
$colorTimeRealtimeFg: #eee;
|
||||
$colorTimeRealtimeFgSubtle: #88b0ff;
|
||||
$colorTimeRealtimeHov: pullForward($colorTimeRealtime, 5%);
|
||||
$colorTimeRealtimeSubtle: pushBack($colorTimeRealtime, 20%);
|
||||
$colorTimeRealtimeBtnBg: pullForward($colorTimeRealtime, 5%);
|
||||
$colorTimeRealtimeBtnFg: $colorTimeRealtimeFgSubtle;
|
||||
$colorTimeRealtimeBtnBgMajor: #588ffa;
|
||||
$colorTimeRealtimeBtnFgMajor: #fff;
|
||||
$colorTOI: $colorBodyFg; // was $timeControllerToiLineColor
|
||||
$colorTOIHov: $colorTimeRealtime; // was $timeControllerToiLineColorHov
|
||||
$timeConductorAxisHoverFilter: brightness(1.2);
|
||||
$timeConductorActiveBg: $colorKey;
|
||||
$timeConductorActivePanBg: #226074;
|
||||
|
||||
// Browse
|
||||
$browseFrameColor: pullForward($colorBodyBg, 10%);
|
||||
$browseFrameBorder: 1px solid rgb(89, 89, 89, .4); // Frames in Disp and Flex Layouts when frame is showing
|
||||
$browseSelectableShdwHov: rgba($colorBodyFg, 0.3) 0 0 3px;
|
||||
$browseSelectedBorder: 1px solid rgba($colorBodyFg, 0.4);
|
||||
$filterItemHoverFg: brightness(1.2) contrast(1.1);
|
||||
$interiorMarginObjectFrameVertical: 10px;
|
||||
$interiorMarginObjectFrameHorizontal: 10px;
|
||||
|
||||
// Missing Items
|
||||
$filterItemMissing: brightness(0.6) grayscale(1);
|
||||
$opacityMissing: 0.5;
|
||||
$borderMissing: 1px dashed $colorAlert !important;
|
||||
$browseFrameCornerColor: $colorKey 4px; //Color used for the corners of frames
|
||||
|
||||
// Edit
|
||||
$editUIColor: $uiColor; // Base color
|
||||
$editUIColorBg: $editUIColor;
|
||||
$editUIColorFg: #fff;
|
||||
$editUIColorHov: pullForward(saturate($uiColor, 10%), 10%); // Hover color when $editUIColor is applied as a base color
|
||||
$editUIBaseColor: #344b8d; // Base color, toolbar bg
|
||||
$editUIBaseColorHov: pullForward($editUIBaseColor, 20%);
|
||||
$editUIBaseColorFg: #ffffff; // Toolbar button icon colors, etc.
|
||||
$editUIAreaBaseColor: pullForward(saturate($editUIBaseColor, 30%), 20%);
|
||||
$editUIAreaShdw: $editUIAreaBaseColor 0 0 0 2px; // Edit area s-selected-parent
|
||||
$editUIAreaShdwSelected: $editUIAreaBaseColor 0 0 0 3px; // Edit area s-selected
|
||||
$editUIGridColorBg: rgba($editUIBaseColor, 0.2); // Background of layout editing area
|
||||
$editUIGridColorFg: rgba(#000, 0.1); // Grid lines in layout editing area
|
||||
$editDimensionsColor: #6a5ea6;
|
||||
$editFrameColor: $browseFrameColor; // Solid or dotted border applied to non-selected frames in a layout; move-bar on frame hover
|
||||
$editFrameBorder: 1px dotted $editFrameColor;
|
||||
$editFrameColorHov: $editUIColor; // Solid border hover on frames; hover should not be applied to selected objects
|
||||
$editFrameBorderHov: 1px solid $editFrameColorHov; // Hover on selectable frames
|
||||
$editFrameColorSelected: #ffefc2; // Border of selected frames while editing
|
||||
$editFrameColorHandleBg: $colorBodyBg; // Resize handle 'offset' color to make handle standout
|
||||
$editFrameColorHandleFg: $editFrameColorSelected; // Resize handle main color
|
||||
$editFrameSelectedShdw: rgba(black, 0.4) 0 1px 5px 1px;
|
||||
$editFrameMovebarColorBg: $editFrameColor; // Movebar bg color
|
||||
$editFrameMovebarColorFg: pullForward($editFrameMovebarColorBg, 20%); // Grippy lines, container size text
|
||||
$editFrameHovMovebarColorBg: pullForward($editFrameMovebarColorBg, 10%); // Hover style
|
||||
$editFrameHovMovebarColorFg: pullForward($editFrameMovebarColorFg, 10%);
|
||||
$editFrameSelectedMovebarColorBg: pullForward($editFrameMovebarColorBg, 15%); // Selected style
|
||||
$editFrameSelectedMovebarColorFg: pullForward($editFrameMovebarColorFg, 15%);
|
||||
$editFrameMovebarH: 10px; // Height of move bar in layout frame
|
||||
$editMarqueeBorder: 1px dashed $editFrameColorSelected;
|
||||
$editFrameSelectedBorder: $editMarqueeBorder; // Selected frame element
|
||||
|
||||
// Icons
|
||||
$colorIconAlias: #4af6f3;
|
||||
$colorIconAliasForKeyFilter: #aaa;
|
||||
|
||||
// Holders
|
||||
$colorTabsHolderBg: rgba(black, 0.2);
|
||||
|
||||
// Buttons and Controls
|
||||
$colorBtnBg: pullForward($colorBodyBg, 20%);
|
||||
$colorBtnBgHov: pullForward($colorBtnBg, 10%);
|
||||
$shdwBtnHov: inset rgba(white, 10%) 0 0 0 100px;
|
||||
$colorBtnFg: pullForward($colorBodyFg, 100%);
|
||||
$colorBtnReverseFg: pullForward($colorBtnFg, 10%);
|
||||
$colorBtnReverseBg: pullForward($colorBtnBg, 10%);
|
||||
$colorBtnFgHov: $colorBtnFg;
|
||||
$colorBtnMajorBg: $colorKey;
|
||||
$colorBtnMajorBgHov: $colorKeyHov;
|
||||
$colorBtnMajorFg: $colorKeyFg;
|
||||
$colorBtnMajorFgHov: pushBack($colorBtnMajorFg, 10%);
|
||||
$colorBtnCautionBg: $colorStatusAlert;
|
||||
$colorBtnCautionBgHov: #f1504e;
|
||||
$colorBtnCautionFg: $colorBtnBg;
|
||||
$colorBtnActiveBg: $colorOk;
|
||||
$colorBtnActiveFg: $colorOkFg;
|
||||
$colorBtnSelectedBg: $colorSelectedBg;
|
||||
$colorBtnSelectedFg: $colorSelectedFg;
|
||||
$colorClickIconButton: $colorKey;
|
||||
$colorClickIconButtonBgHov: rgba($colorKey, 0.3);
|
||||
$colorClickIconButtonFgHov: $colorKeyHov;
|
||||
$colorDropHint: $colorKey;
|
||||
$colorDropHintBg: pushBack($colorDropHint, 10%);
|
||||
$colorDropHintBgHov: $colorDropHint;
|
||||
$colorDropHintFg: pullForward($colorDropHint, 40%);
|
||||
$colorDisclosureCtrl: rgba($colorBodyFg, 0.5);
|
||||
$colorDisclosureCtrlHov: rgba($colorBodyFg, 0.7);
|
||||
$btnStdH: 24px;
|
||||
$colorCursorGuide: rgba(white, 0.6);
|
||||
$shdwCursorGuide: rgba(black, 0.4) 0 0 2px;
|
||||
$colorLocalControlOvrBg: rgba($colorBodyBg, 0.8);
|
||||
$colorSelectBg: $colorBtnBg; // This must be a solid color, not a gradient, due to usage of SVG bg in selects
|
||||
$colorSelectFg: $colorBtnFg;
|
||||
$colorSelectArw: lighten($colorBtnBg, 20%);
|
||||
$shdwSelect: rgba(black, 0.5) 0 0.5px 3px;
|
||||
$controlDisabledOpacity: 0.2;
|
||||
|
||||
// Menus
|
||||
$colorMenuBg: rgba($colorBodyBg, 0.6);
|
||||
$colorMenuFg: $colorBodyFg;
|
||||
$colorMenuIc: $colorKey;
|
||||
$filterMenu: brightness(1.4);
|
||||
$colorMenuHovBg: rgba($colorKey, 0.5);
|
||||
$colorMenuHovFg: $colorBodyFgEm;
|
||||
$colorMenuHovIc: $colorMenuHovFg;
|
||||
$colorMenuElementHilite: pullForward($colorMenuBg, 10%);
|
||||
$shdwMenu: rgba(black, 0.8) 0 2px 10px;
|
||||
$shdwMenuInner: inset 0 0 0 1px rgba(white, 0.2);
|
||||
$shdwMenuText: none;
|
||||
$menuItemPad: $interiorMargin, floor($interiorMargin * 1.25);
|
||||
|
||||
// Palettes and Swatches
|
||||
$paletteItemBorderOuterColorSelected: black;
|
||||
$paletteItemBorderInnerColorSelected: white;
|
||||
$paletteItemBorderInnerColor: rgba($paletteItemBorderOuterColorSelected, 0.3);
|
||||
$mixedSettingBg: (transparent rgba($editUIBaseColorHov, 0.7)); // Used in .c-click-icon--mixed
|
||||
$mixedSettingBgSize: 5px;
|
||||
|
||||
// Forms
|
||||
$colorCheck: $colorKey;
|
||||
$colorFormRequired: $colorKey;
|
||||
$colorFormValid: $colorOk;
|
||||
$colorFormError: #990000;
|
||||
$colorFormInvalid: #ff2200;
|
||||
$colorFormFieldErrorBg: $colorFormError;
|
||||
$colorFormFieldErrorFg: rgba(#fff, 0.6);
|
||||
$colorFormLines: rgba(#000, 0.2);
|
||||
$colorFormSectionHeaderBg: rgba(#000, 0.1);
|
||||
$colorFormSectionHeaderFg: rgba($colorBodyFg, 0.8);
|
||||
$colorInputBg: rgba(rgb(70, 70, 70), 0.3);
|
||||
$colorInputBgHov: rgba(black, 0.1);
|
||||
$colorInputFg: $colorBodyFg;
|
||||
$colorFormText: pushBack($colorBodyFg, 10%);
|
||||
$colorInputIcon: pushBack($colorBodyFg, 25%);
|
||||
$colorFieldHint: pullForward($colorBodyFg, 40%);
|
||||
$shdwInput: inset rgba(black, 0.4) 0 0 1px;
|
||||
$shdwInputHov: inset rgba(black, 0.7) 0 0 2px;
|
||||
$shdwInputFoc: inset rgba(black, 0.8) 0 0.25px 3px;
|
||||
$formTBPad: $interiorMargin;
|
||||
$formLRPad: $interiorMargin;
|
||||
$formInputH: 22px;
|
||||
$formRowCtrlsH: 14px;
|
||||
|
||||
// Inspector
|
||||
$colorInspectorBg: pullForward($colorBodyBg, 5%);
|
||||
$colorInspectorFg: $colorBodyFg;
|
||||
$colorInspectorPropName: $colorBodyFg;
|
||||
$colorInspectorPropVal: pullForward($colorInspectorFg, 15%);
|
||||
$colorInspectorSectionHeaderBg: rgba($colorBodyBg, .75);
|
||||
$colorInspectorSectionHeaderFg: pullForward($colorInspectorBg, 40%);
|
||||
|
||||
// Tabs
|
||||
$colorTabBg: $colorBodyBg;
|
||||
$colorTabFg: $colorBodyFgEm;
|
||||
$colorTabCurrentBg: rgba($colorKey, .71);
|
||||
$colorTabCurrentFg: $colorBodyFgEm;
|
||||
$colorTabsBaseline: $colorBodyBg;
|
||||
|
||||
// Overlay
|
||||
$colorOvrBlocker: rgba(black, 0.8);
|
||||
$overlayCr: $interiorMargin;
|
||||
|
||||
// Indicator colors
|
||||
$colorIndicatorAvailable: $colorKey;
|
||||
$colorIndicatorDisabled: #555555;
|
||||
$colorIndicatorOn: $colorOk;
|
||||
$colorIndicatorOff: #777777;
|
||||
$colorIndicatorBgHov: rgba($colorHeadFg, 0.1);
|
||||
$colorIndicatorMenuBg: $colorHeadBg;
|
||||
$colorIndicatorMenuBgShdw: rgba(white, 0.6) 0 0 6px;
|
||||
$colorIndicatorMenuFg: $colorHeadFg;
|
||||
$colorIndicatorMenuFgHov: pullForward($colorHeadFg, 10%);
|
||||
|
||||
// Staleness
|
||||
$colorTelemStale: cyan;
|
||||
$colorTelemStaleFg: #002a2a;
|
||||
$styleTelemStale: italic;
|
||||
|
||||
// Limits
|
||||
$colorLimitYellowBg: #b18b05;
|
||||
$colorLimitYellowFg: #feeeb5;
|
||||
$colorLimitYellowIc: #fdc707;
|
||||
$colorLimitOrangeBg: #b36b00;
|
||||
$colorLimitOrangeFg: #ffe0b2;
|
||||
$colorLimitOrangeIc: #ff9900;
|
||||
$colorLimitRedBg: #B60109;
|
||||
$colorLimitRedFg: #ffa489;
|
||||
$colorLimitRedIc: #ff4222;
|
||||
$colorLimitPurpleBg: #891bb3;
|
||||
$colorLimitPurpleFg: #edbeff;
|
||||
$colorLimitPurpleIc: #c327ff;
|
||||
$colorLimitCyanBg: #4ba6b3;
|
||||
$colorLimitCyanFg: #d3faff;
|
||||
$colorLimitCyanIc: #6bedff;
|
||||
|
||||
// Events
|
||||
$colorEventPurpleFg: #aB8fff;
|
||||
$colorEventRedFg: #ff9999;
|
||||
$colorEventOrangeFg: #ff8800;
|
||||
$colorEventYellowFg: #ffdb63;
|
||||
$colorEventPurpleBg: #31204a;
|
||||
$colorEventRedBg: #3c1616;
|
||||
$colorEventOrangeBg: #3e2a13;
|
||||
$colorEventYellowBg: #3e3316;
|
||||
|
||||
// Bubble colors
|
||||
$colorInfoBubbleBg: #dddddd;
|
||||
$colorInfoBubbleFg: #666;
|
||||
$colorThumbsBubbleFg: pullForward($colorBodyFg, 10%);
|
||||
$colorThumbsBubbleBg: pullForward($colorBodyBg, 10%);
|
||||
|
||||
// Items
|
||||
$colorItemBg: buttonBg($colorBtnBg);
|
||||
$colorItemBgHov: buttonBg(pullForward($colorBtnBg, 5%));
|
||||
$colorListItemBg: transparent;
|
||||
$colorListItemBgHov: rgba($colorKey, 0.1);
|
||||
$colorItemFg: $colorBtnFg;
|
||||
$colorItemFgDetails: $colorBodyFgSubtle;
|
||||
$shdwItemText: none;
|
||||
|
||||
// Tabular
|
||||
$colorTabBorder: pullForward($colorBodyBg, 10%);
|
||||
$colorTabBodyBg: $colorBodyBg;
|
||||
$colorTabBodyFg: pullForward($colorBodyFg, 20%);
|
||||
$colorTabHeaderBg: #575757;
|
||||
$colorTabHeaderFg: $colorBodyFg;
|
||||
$colorTabHeaderBorder: $colorBodyBg;
|
||||
$colorTabGroupHeaderBg: pullForward($colorBodyBg, 5%);
|
||||
$colorTabGroupHeaderFg: pushBack($colorTabHeaderFg, 10%);
|
||||
$colorSummaryBg: #2c2c2c;
|
||||
$colorSummaryFg: rgba($colorBodyFg, 0.7);
|
||||
$colorSummaryFgEm: $colorBodyFg;
|
||||
|
||||
// Plot
|
||||
$colorPlotBg: rgba(black, 0.1);
|
||||
$colorPlotFg: $colorBodyFg;
|
||||
$colorPlotHash: $colorPlotFg;
|
||||
$opacityPlotHash: 0.2;
|
||||
$stylePlotHash: dashed;
|
||||
$colorPlotAreaBorder: $colorInteriorBorder;
|
||||
$colorPlotLabelFg: pushBack($colorPlotFg, 20%);
|
||||
$legendHoverValueBg: rgba($colorBodyFg, 0.2);
|
||||
$legendTableHeadBg: $colorTabHeaderBg;
|
||||
$colorPlotLimitLineBg: rgba($colorBodyBg, 0.2);
|
||||
|
||||
// Gauges
|
||||
$colorGaugeBase: $colorKeyBg;
|
||||
$colorGaugeBg: rgba($colorGaugeBase, .35); // Gauge radial area background, meter background
|
||||
$colorGaugeValue: $colorKeyBg; // Gauge value graphic (radial sweep, bar) color
|
||||
$colorGaugeTextValue: #fff; // Radial gauge text value
|
||||
$colorGaugeMeterTextValue: #fff; // Meter text value, overlaid on value bar
|
||||
$colorGaugeRange: $colorBodyFg; // Range text color
|
||||
$colorGaugeLimitHigh: rgba($colorLimitRedBg, .5);
|
||||
$colorGaugeLimitLow: $colorGaugeLimitHigh;
|
||||
$colorGaugeNeedle: $colorGaugeBase; // Color of needle in a needle gauge.
|
||||
$transitionTimeGauge: 150ms; // CSS transition time used to smooth needle gauge and meter value transitions
|
||||
$marginGaugeMeterValue: 10%; // Margin between meter value bar and bounds edges
|
||||
$gaugeMeterValueShadow: rgba(255, 255, 255, .5);
|
||||
// TODO: This is some code regarding how we can make Gauges include a border or glow. We may need to revisit this.
|
||||
// padding: 5%;
|
||||
// background: radial-gradient(circle, transparent 0%, transparent 65%, rgba(255, 255, 255,0.4) 64%, rgba(255,255,255,0) 70%)
|
||||
|
||||
// Time Strip and Lists
|
||||
$colorPastBg: #444;
|
||||
$colorPastFg: pushBack($colorBodyFg, 10%);
|
||||
$colorPastFgEm: $colorBodyFg;
|
||||
$colorCurrentBg: #666;
|
||||
$colorCurrentFg: $colorBodyFg;
|
||||
$colorCurrentFgEm: $colorBodyFgEm;
|
||||
$colorCurrentBorder: $colorBodyBg;
|
||||
$colorFutureBg: $colorPastBg;
|
||||
$colorFutureFg: $colorCurrentFg;
|
||||
$colorFutureFgEm: $colorCurrentFgEm;
|
||||
$colorFutureBorder: $colorCurrentBorder;
|
||||
$colorInProgressBg: $colorTimeRealtimeBg;
|
||||
$colorInProgressFg: $colorTimeRealtimeFgSubtle;
|
||||
$colorInProgressFgEm: $colorTimeRealtimeFg;
|
||||
$colorGanttSelectedBorder: rgba(#fff, 0.3);
|
||||
|
||||
// Tree
|
||||
$colorTreeBg: transparent;
|
||||
$colorItemTreeHoverBg: rgba(#fff, 0.1);
|
||||
$colorItemTreeHoverFg: #fff;
|
||||
$colorItemTreeIcon: $colorKey;
|
||||
$colorItemTreeIconHover: $colorItemTreeIcon;
|
||||
$colorItemTreeFg: #ccc;
|
||||
$colorItemTreeSelectedBg: $colorSelectedBg;
|
||||
$colorItemTreeSelectedFg: $colorItemTreeHoverFg;
|
||||
$filterItemTreeSelected: $filterHov;
|
||||
$colorItemTreeSelectedIcon: $colorItemTreeSelectedFg;
|
||||
$colorItemTreeEditingBg: pushBack($editUIColor, 20%);
|
||||
$colorItemTreeEditingFg: $editUIColor;
|
||||
$colorItemTreeEditingIcon: $editUIColor;
|
||||
$colorItemTreeVC: $colorDisclosureCtrl;
|
||||
$colorItemTreeVCHover: $colorDisclosureCtrlHov;
|
||||
$colorItemTreeNewNode: rgba($colorBodyFg, 0.7);
|
||||
$shdwItemTreeIcon: none;
|
||||
|
||||
// Layout frame controls
|
||||
$frameControlsColorFg: white;
|
||||
$frameControlsColorBg: $colorKey;
|
||||
$frameControlsShdw: $shdwMenu;
|
||||
|
||||
// Images
|
||||
$colorThumbHoverBg: $colorItemTreeHoverBg;
|
||||
|
||||
// Scrollbar
|
||||
$scrollbarTrackSize: 7px;
|
||||
$scrollbarTrackShdw: rgba(#000, 0.2) 0 1px 2px;
|
||||
$scrollbarTrackColorBg: rgba(#000, 0.2);
|
||||
$scrollbarThumbColor: pushBack($colorBodyBg, 50%);
|
||||
$scrollbarThumbColorHov: $colorKey;
|
||||
$scrollbarThumbColorMenu: pullForward($colorMenuBg, 10%);
|
||||
$scrollbarThumbColorMenuHov: pullForward($scrollbarThumbColorMenu, 2%);
|
||||
|
||||
// Splitter
|
||||
$splitterHandleD: 2px;
|
||||
$splitterD: $splitterHandleD;
|
||||
$splitterHandleHitMargin: 4px;
|
||||
$colorSplitterBaseBg: $colorBodyBg;
|
||||
$colorSplitterBg: pullForward($colorBodyBg, 10%);
|
||||
$colorSplitterFg: $colorBodyBg;
|
||||
$colorSplitterHover: $uiColor;
|
||||
$colorSplitterActive: $colorKey;
|
||||
$splitterBtnD: (16px, 35px); // height, width
|
||||
$splitterBtnColorBg: $colorBtnBg;
|
||||
$splitterBtnColorFg: #999;
|
||||
$splitterBtnLabelColorFg: #9d9d9d;
|
||||
$splitterCollapsedBtnColorBg: #222;
|
||||
$splitterCollapsedBtnColorFg: #555;
|
||||
$splitterCollapsedBtnColorBgHov: $colorKey;
|
||||
$splitterCollapsedBtnColorFgHov: $colorKeyFg;
|
||||
|
||||
// Mobile
|
||||
$colorMobilePaneLeft: pushBack($colorBodyBg, 2%);
|
||||
$colorMobilePaneLeftTreeItemBg: rgba($colorBodyFg, 0.1);
|
||||
$colorMobilePaneLeftTreeItemFg: $colorItemTreeFg;
|
||||
$colorMobileSelectListTreeItemBg: rgba(#000, 0.05);
|
||||
|
||||
// About Screen
|
||||
$colorAboutLink: #9bb5ff;
|
||||
|
||||
// Loading
|
||||
$colorLoadingFg: #776ba2;
|
||||
$colorLoadingBg: rgba($colorLoadingFg, 0.1);
|
||||
|
||||
// Transitions
|
||||
$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(0.47, 0.01, 0.25, 1.5);
|
||||
$transInBounceBig: all 300ms cubic-bezier(0.2, 1.6, 0.6, 3);
|
||||
|
||||
// Discrete items
|
||||
$createBtnTextTransform: uppercase;
|
||||
$colorDiscreteItemBg: rgba($colorBodyFg, 0.1);
|
||||
$colorDiscreteItemBgHov: rgba($colorBodyFg, 0.2);
|
||||
$colorDiscreteItemCurrentBg: rgba($colorOk, 0.3);
|
||||
$scrollContainer: $colorBodyBg;
|
@ -25,6 +25,7 @@
|
||||
$heroFont: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
$headerFont: $heroFont;
|
||||
$bodyFont: $heroFont;
|
||||
$numericFont: $heroFont;
|
||||
|
||||
@mixin heroFont($size: 1em) {
|
||||
font-family: $heroFont;
|
||||
@ -40,6 +41,7 @@ $bodyFont: $heroFont;
|
||||
font-family: $bodyFont;
|
||||
font-size: $size;
|
||||
}
|
||||
|
||||
@mixin discreteItem() {
|
||||
background: $colorDiscreteItemBg;
|
||||
border: none;
|
||||
@ -64,6 +66,9 @@ $bodyFont: $heroFont;
|
||||
box-shadow: rgba(black, 0.5) 0 0.5px 2px;
|
||||
}
|
||||
|
||||
@mixin telemetryView(){}
|
||||
@mixin browseFrameBorder() {}
|
||||
|
||||
// Functions
|
||||
@function buttonBg($c: $colorBtnBg) {
|
||||
@return linear-gradient(lighten($c, 5%), $c);
|
||||
@ -113,6 +118,11 @@ $filterHovSubtle: brightness(1.2) contrast(1.2);
|
||||
$colorSelectedBg: rgba($colorKey, 0.3);
|
||||
$colorSelectedFg: pullForward($colorBodyFg, 20%);
|
||||
|
||||
// Body constants
|
||||
$bodyBg: $colorBodyBg;
|
||||
$bodyBgSize: cover;
|
||||
$bodySize: 100%;
|
||||
|
||||
// Object labels
|
||||
$objectLabelTypeIconOpacity: 0.8; //JOHN
|
||||
$objectLabelNameColorFg: lighten($colorBodyFg, 10%);
|
||||
@ -196,6 +206,8 @@ $browseFrameBorder: 1px solid $browseFrameColor; // Frames in Disp and Flex Layo
|
||||
$browseSelectableShdwHov: rgba($colorBodyFg, 0.3) 0 0 3px;
|
||||
$browseSelectedBorder: 1px solid rgba($colorBodyFg, 0.4);
|
||||
$filterItemHoverFg: brightness(1.2) contrast(1.1);
|
||||
$interiorMarginObjectFrameVertical: 0px;
|
||||
$interiorMarginObjectFrameHorizontal: 3px;
|
||||
|
||||
// Missing Items
|
||||
$filterItemMissing: brightness(0.6) grayscale(1);
|
||||
@ -435,8 +447,10 @@ $colorGaugeMeterTextValue: $colorGaugeTextValue; // Meter text value, overlaid o
|
||||
$colorGaugeRange: $colorBodyFg; // Range text color
|
||||
$colorGaugeLimitHigh: rgba($colorLimitRedBg, 0.4);
|
||||
$colorGaugeLimitLow: $colorGaugeLimitHigh;
|
||||
$colorGaugeNeedle: rgba(#fff, 0);
|
||||
$transitionTimeGauge: 150ms; // CSS transition time used to smooth needle gauge and meter value transitions
|
||||
$marginGaugeMeterValue: 10%; // Margin between meter value bar and bounds edges
|
||||
$gaugeMeterValueShadow: rgba(255, 255, 255, 0);
|
||||
|
||||
// Time Strip and Lists
|
||||
$colorPastBg: #444;
|
||||
@ -459,8 +473,8 @@ $colorGanttSelectedBorder: rgba(#fff, 0.3);
|
||||
$colorTreeBg: transparent;
|
||||
$colorItemTreeHoverBg: rgba(#fff, 0.1);
|
||||
$colorItemTreeHoverFg: #fff;
|
||||
$colorItemTreeIcon: $colorKey; // Used
|
||||
$colorItemTreeIconHover: $colorItemTreeIcon; // Used
|
||||
$colorItemTreeIcon: $colorKey;
|
||||
$colorItemTreeIconHover: $colorItemTreeIcon;
|
||||
$colorItemTreeFg: #ccc;
|
||||
$colorItemTreeSelectedBg: $colorSelectedBg;
|
||||
$colorItemTreeSelectedFg: $colorItemTreeHoverFg;
|
||||
|
@ -27,6 +27,7 @@
|
||||
$heroFont: 'Teko', sans-serif;
|
||||
$headerFont: 'Michroma', sans-serif;
|
||||
$bodyFont: 'Chakra Petch', sans-serif;
|
||||
$numericFont: $heroFont;
|
||||
|
||||
@mixin heroFont($size: 1em) {
|
||||
font-family: $heroFont;
|
||||
@ -65,6 +66,9 @@ $bodyFont: 'Chakra Petch', sans-serif;
|
||||
box-shadow: rgba(black, 0.5) 0 0.5px 2px;
|
||||
}
|
||||
|
||||
@mixin telemetryView(){}
|
||||
@mixin browseFrameBorder() {}
|
||||
|
||||
/**************************************************** OVERRIDES */
|
||||
.c-frame {
|
||||
&:not(.no-frame) {
|
||||
@ -130,6 +134,11 @@ $filterHovSubtle: brightness(1.2) contrast(1.2);
|
||||
$colorSelectedBg: rgba($colorKey, 0.3);
|
||||
$colorSelectedFg: pullForward($colorBodyFg, 20%);
|
||||
|
||||
// Body constants
|
||||
$bodyBg: $colorBodyBg;
|
||||
$bodyBgSize: cover;
|
||||
$bodySize: 100%;
|
||||
|
||||
// Object labels
|
||||
$objectLabelTypeIconOpacity: 0.7;
|
||||
$objectLabelNameColorFg: lighten($colorBodyFg, 10%);
|
||||
@ -213,6 +222,8 @@ $browseFrameBorder: 1px solid $browseFrameColor; // Frames in Disp and Flex Layo
|
||||
$browseSelectableShdwHov: rgba($colorBodyFg, 0.3) 0 0 3px;
|
||||
$browseSelectedBorder: 1px solid rgba($colorBodyFg, 0.4);
|
||||
$filterItemHoverFg: brightness(1.2) contrast(1.1);
|
||||
$interiorMarginObjectFrameVertical: 0px;
|
||||
$interiorMarginObjectFrameHorizontal: 3px;
|
||||
|
||||
// Missing Items
|
||||
$filterItemMissing: contrast(0.2);
|
||||
@ -452,8 +463,10 @@ $colorGaugeMeterTextValue: $colorGaugeTextValue; // Meter text value, overlaid o
|
||||
$colorGaugeRange: $colorBodyFg; // Range text color
|
||||
$colorGaugeLimitHigh: rgba($colorLimitRedBg, 0.4);
|
||||
$colorGaugeLimitLow: $colorGaugeLimitHigh;
|
||||
$colorGaugeNeedle: rgba(#fff, 0);
|
||||
$transitionTimeGauge: 150ms; // CSS transition time used to smooth needle gauge and meter value transitions
|
||||
$marginGaugeMeterValue: 10%; // Margin between meter value bar and bounds edges
|
||||
$gaugeMeterValueShadow: rgba(255, 255, 255, 0);
|
||||
|
||||
// Time Strip and Lists
|
||||
$colorPastBg: #444;
|
||||
@ -476,8 +489,8 @@ $colorGanttSelectedBorder: rgba(#fff, 0.3);
|
||||
$colorTreeBg: transparent;
|
||||
$colorItemTreeHoverBg: rgba(#fff, 0.03);
|
||||
$colorItemTreeHoverFg: #fff;
|
||||
$colorItemTreeIcon: $colorKey; // Used
|
||||
$colorItemTreeIconHover: $colorItemTreeIcon; // Used
|
||||
$colorItemTreeIcon: $colorKey;
|
||||
$colorItemTreeIconHover: $colorItemTreeIcon;
|
||||
$colorItemTreeFg: $colorA;
|
||||
$colorItemTreeSelectedBg: $colorSelectedBg;
|
||||
$colorItemTreeSelectedFg: $colorItemTreeHoverFg;
|
||||
@ -554,4 +567,4 @@ $createBtnTextTransform: uppercase;
|
||||
$colorDiscreteItemBg: rgba($colorBodyFg, 0.1);
|
||||
$colorDiscreteItemBgHov: rgba($colorBodyFg, 0.2);
|
||||
$colorDiscreteItemCurrentBg: rgba($colorOk, 0.3);
|
||||
$scrollContainer: $colorBodyBg;
|
||||
$scrollContainer: $colorBodyBg;
|
@ -25,6 +25,7 @@
|
||||
$heroFont: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
$headerFont: $heroFont;
|
||||
$bodyFont: $heroFont;
|
||||
$numericFont: $heroFont;
|
||||
|
||||
@mixin heroFont($size: 1em) {
|
||||
font-family: $heroFont;
|
||||
@ -64,6 +65,9 @@ $bodyFont: $heroFont;
|
||||
background: $c;
|
||||
}
|
||||
|
||||
@mixin telemetryView(){}
|
||||
@mixin browseFrameBorder() {}
|
||||
|
||||
// Functions
|
||||
@function buttonBg($c: $colorBtnBg) {
|
||||
@return $c;
|
||||
@ -113,6 +117,11 @@ $filterHovSubtle: hue-rotate(-8deg) brightness(0.5) contrast(1.2);
|
||||
$colorSelectedBg: pushBack($colorKey, 40%);
|
||||
$colorSelectedFg: pullForward($colorBodyFg, 10%);
|
||||
|
||||
// Body constants
|
||||
$bodyBg: $colorBodyBg;
|
||||
$bodyBgSize: cover;
|
||||
$bodySize: 100%;
|
||||
|
||||
// Object labels
|
||||
$objectLabelTypeIconOpacity: 0.5;
|
||||
$objectLabelNameColorFg: darken($colorBodyFg, 10%);
|
||||
@ -196,6 +205,8 @@ $browseFrameBorder: 1px solid $browseFrameColor; // Frames in Disp and Flex Layo
|
||||
$browseSelectableShdwHov: rgba($colorBodyFg, 0.3) 0 0 3px;
|
||||
$browseSelectedBorder: 1px solid rgba($colorBodyFg, 0.4);
|
||||
$filterItemHoverFg: brightness(0.9);
|
||||
$interiorMarginObjectFrameVertical: 0px;
|
||||
$interiorMarginObjectFrameHorizontal: 3px;
|
||||
|
||||
// Missing Items
|
||||
$filterItemMissing: contrast(0.2);
|
||||
@ -435,8 +446,10 @@ $colorGaugeMeterTextValue: $colorGaugeTextValue; // Meter text value, overlaid o
|
||||
$colorGaugeRange: $colorBodyFg; // Range text color
|
||||
$colorGaugeLimitHigh: rgba($colorLimitRedBg, 0.2);
|
||||
$colorGaugeLimitLow: $colorGaugeLimitHigh;
|
||||
$colorGaugeNeedle: rgba(#fff, 0);
|
||||
$transitionTimeGauge: 150ms; // CSS transition time used to smooth needle gauge and meter value transitions
|
||||
$marginGaugeMeterValue: 10%; // Margin between meter value bar and bounds edges
|
||||
$gaugeMeterValueShadow: rgba(255, 255, 255, 0);
|
||||
|
||||
// Time Strip and Lists
|
||||
$colorPastBg: #f0f0f0;
|
||||
@ -459,8 +472,8 @@ $colorGanttSelectedBorder: #fff;
|
||||
$colorTreeBg: transparent;
|
||||
$colorItemTreeHoverBg: rgba(black, 0.07);
|
||||
$colorItemTreeHoverFg: pullForward($colorBodyFg, 10%);
|
||||
$colorItemTreeIcon: $colorKey; // Used
|
||||
$colorItemTreeIconHover: $colorItemTreeIcon; // Used
|
||||
$colorItemTreeIcon: $colorKey;
|
||||
$colorItemTreeIconHover: $colorItemTreeIcon;
|
||||
$colorItemTreeFg: $colorBodyFg;
|
||||
$colorItemTreeSelectedBg: $colorSelectedBg;
|
||||
$colorItemTreeSelectedFg: $colorItemTreeHoverFg;
|
||||
|
@ -30,6 +30,7 @@
|
||||
box-shadow: $shdwMenuInner, $shdwMenu;
|
||||
}
|
||||
background: $colorMenuBg;
|
||||
backdrop-filter: blur($formRowCtrlsH);
|
||||
color: $colorMenuFg;
|
||||
text-shadow: $shdwMenuText;
|
||||
padding: $interiorMarginSm;
|
||||
|
@ -161,7 +161,7 @@ a {
|
||||
|
||||
body,
|
||||
html {
|
||||
height: 100%;
|
||||
height: $bodySize;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@ -173,7 +173,9 @@ body {
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
@include bodyFont($fontBaseSize);
|
||||
background-color: $colorBodyBg;
|
||||
// background-color: $colorBodyBg;
|
||||
background: $bodyBg;
|
||||
background-size: $bodyBgSize;
|
||||
color: $colorBodyFg;
|
||||
}
|
||||
|
||||
|
@ -365,6 +365,7 @@ mct-plot {
|
||||
left: $m;
|
||||
z-index: 9;
|
||||
|
||||
|
||||
&__reset {
|
||||
transition: right 100ms;
|
||||
top: $m;
|
||||
|
@ -925,3 +925,8 @@
|
||||
$c: $colorPausedBg;
|
||||
border: 1px solid $c;
|
||||
}
|
||||
|
||||
// @mixin telemetryView(){
|
||||
// border: 1px solid $colorBodyFg;
|
||||
// border-radius: $controlCr;
|
||||
// }
|
@ -1,6 +1,10 @@
|
||||
.c-so-view {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
// &__container{
|
||||
// display: contents;
|
||||
// }
|
||||
|
||||
/*************************** HEADER */
|
||||
&__header {
|
||||
@ -29,13 +33,18 @@
|
||||
|
||||
&:not(.c-so-view--no-frame) {
|
||||
border: $browseFrameBorder;
|
||||
padding: 0 $interiorMarginSm;
|
||||
|
||||
@include browseFrameBorder;
|
||||
padding: $interiorMarginObjectFrameVertical $interiorMarginObjectFrameHorizontal;
|
||||
|
||||
.is-editing & {
|
||||
background: rgba($colorBodyBg, 0.8);
|
||||
@include browseFrameBorder;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*************************** FRAME CONTROLS */
|
||||
&__frame-controls {
|
||||
display: flex;
|
||||
|
BIN
src/ui/layout/assets/images/darkmatter-bg.png
Normal file
BIN
src/ui/layout/assets/images/darkmatter-bg.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 303 KiB |
@ -30,6 +30,7 @@
|
||||
flex-flow: column nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
|
||||
&__drawer {
|
||||
background: $drawerBg;
|
||||
display: flex;
|
||||
@ -289,7 +290,6 @@
|
||||
flex: 1 1 auto !important;
|
||||
height: 100%; // Chrome 73 overflow bug fix
|
||||
overflow: auto;
|
||||
|
||||
> * + * {
|
||||
margin-top: $interiorMargin;
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
overflow: hidden;
|
||||
|
||||
|
||||
&--horizontal,
|
||||
> .l-pane {
|
||||
|
Reference in New Issue
Block a user