Compare commits

..

11 Commits

Author SHA1 Message Date
9561ac446a Merge remote-tracking branch 'origin' into recent-objects-style
# Conflicts:
#	src/ui/layout/pane.scss
2024-03-28 10:08:36 -07:00
890e0115b3 Better styling applied to Recents pane for mobile.
- Change padding at bottom of the tree to gap in multipane.
2024-03-28 09:58:33 -07:00
bc46ab7393 Better styling applied to Recents pane for mobile.
- Better strategy for height.
- More specific selector via explicit class `l-pane__recently-viewed`.
2024-03-27 14:08:33 -07:00
c8fbdafc1a Fix vertical heights and add comments 2024-03-26 10:20:07 -07:00
7a82e8c0a6 Fix vertical space between object icons and title 2024-03-26 10:12:02 -07:00
4640ab8331 Fix logic such that pane icons are hidden when tree is collapsed 2024-03-25 15:18:20 -07:00
16253a921f Change mobile constatnt to make hit area smaller 2024-03-19 15:05:20 -07:00
1ab48b6f50 Refactor HTML to regroup wrappers for recents. Add touch areas for mobile 2024-03-19 10:55:24 -07:00
48843ba3b0 Bring back pane labels 2024-03-11 16:03:46 -07:00
de46b26029 Fix recent objects style issue 7528 2024-03-11 11:50:39 -07:00
45996f730b Fix recent objects style issue 7528 2024-03-11 11:47:32 -07:00
74 changed files with 752 additions and 2329 deletions

View File

@ -22,3 +22,9 @@
!index.html
!openmct.js
!SECURITY.md
# Add e2e tests to npm package
!/e2e/**/*
# ... except our test-data folder files.
/e2e/test-data/*.json

View File

@ -1,8 +1,8 @@
/*
This is the OpenMCT common webpack file. It is imported by the other three webpack configurations:
- webpack.prod.mjs - the production configuration for OpenMCT (default)
- webpack.dev.mjs - the development configuration for OpenMCT
- webpack.coverage.mjs - imports webpack.dev.js and adds code coverage
- webpack.prod.js - the production configuration for OpenMCT (default)
- webpack.dev.js - the development configuration for OpenMCT
- webpack.coverage.js - imports webpack.dev.js and adds code coverage
There are separate npm scripts to use these configurations, though simply running `npm install`
will use the default production configuration.
*/
@ -15,7 +15,6 @@ import CopyWebpackPlugin from 'copy-webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import { VueLoaderPlugin } from 'vue-loader';
import webpack from 'webpack';
import { merge } from 'webpack-merge';
let gitRevision = 'error-retrieving-revision';
let gitBranch = 'error-retrieving-branch';
@ -55,11 +54,9 @@ const config = {
globalObject: 'this',
filename: '[name].js',
path: path.resolve(projectRootDir, 'dist'),
library: {
name: 'openmct',
type: 'umd',
export: 'default'
},
library: 'openmct',
libraryExport: 'default',
libraryTarget: 'umd',
publicPath: '',
hashFunction: 'xxhash64',
clean: true

View File

@ -1,10 +1,10 @@
/*
This file extends the webpack.dev.mjs config to add babel istanbul coverage.
This file extends the webpack.dev.js config to add babel istanbul coverage.
OpenMCT Continuous Integration servers use this configuration to add code coverage
information to pull requests.
*/
import config from './webpack.dev.mjs';
import config from './webpack.dev.js';
config.devtool = 'source-map';
config.devServer.hot = false;
@ -16,6 +16,7 @@ config.module.rules.push({
loader: 'babel-loader',
options: {
retainLines: true,
// eslint-disable-next-line no-undef
plugins: [
[
'babel-plugin-istanbul',

View File

@ -1,15 +1,14 @@
/*
This configuration should be used for development purposes. It contains full source map, a
devServer (which be invoked using by `npm start`), and a non-minified Vue.js distribution.
If OpenMCT is to be used for a production server, use webpack.prod.mjs instead.
If OpenMCT is to be used for a production server, use webpack.prod.js instead.
*/
import { fileURLToPath } from 'node:url';
import path from 'path';
import webpack from 'webpack';
import { merge } from 'webpack-merge';
import { fileURLToPath } from 'node:url';
import common from './webpack.common.mjs';
import common from './webpack.common.js';
export default merge(common, {
mode: 'development',

View File

@ -6,7 +6,7 @@ It is the default webpack configuration.
import webpack from 'webpack';
import { merge } from 'webpack-merge';
import common from './webpack.common.mjs';
import common from './webpack.common.js';
export default merge(common, {
mode: 'production',

View File

@ -63,7 +63,7 @@ Once the file is generated, it can be published to codecov with
### e2e
The e2e line coverage is a bit more complex than the karma implementation. This is the general sequence of events:
1. Each e2e suite will start webpack with the ```npm run start:coverage``` command with config `webpack.coverage.mjs` and the `babel-plugin-istanbul` plugin to generate code coverage during e2e test execution using our custom [baseFixture](./baseFixtures.js).
1. Each e2e suite will start webpack with the ```npm run start:coverage``` command with config `webpack.coverage.js` and the `babel-plugin-istanbul` plugin to generate code coverage during e2e test execution using our custom [baseFixture](./baseFixtures.js).
1. During testcase execution, each e2e shard will generate its piece of the larger coverage suite. **This coverage file is not merged**. The raw coverage file is stored in a `.nyc_report` directory.
1. [nyc](https://github.com/istanbuljs/nyc) converts this directory into a `lcov` file with the following command `npm run cov:e2e:report`
1. Most of the tests are run in the '@stable' configuration and focus on chrome/ubuntu at a single resolution. This coverage is published to codecov with `npm run cov:e2e:stable:publish`.

View File

@ -1,7 +0,0 @@
*
!appActions.js
!baseFixtures.js
!pluginFixtures.js
!avpFixtures.js
!index.js
!*.md

View File

@ -167,9 +167,9 @@ When an a11y test fails, the result must be interpreted in the html test report
The open source performance tests function in three ways which match their naming and folder structure:
`tests/performance` - The tests at the root of this folder path detect functional changes which are mostly apparent with large performance regressions like [this](https://github.com/nasa/openmct/issues/6879). These tests run against openmct webpack in `production-mode` with the `npm run test:perf:localhost` script.
`tests/performance/contract/` - These tests serve as [contracts](https://martinfowler.com/bliki/ContractTest.html) for the locator logic, functionality, and assumptions will work in our downstream, closed source test suites. These tests run against openmct webpack in `dev-mode` with the `npm run test:perf:contract` script.
`tests/performance/memory/` - These tests execute memory leak detection checks in various ways. This is expected to evolve as we move to the `memlab` project. These tests run against openmct webpack in `production-mode` with the `npm run test:perf:memory` script.
`./e2e/tests/performance` - The tests at the root of this folder path detect functional changes which are mostly apparent with large performance regressions like [this](https://github.com/nasa/openmct/issues/6879). These tests run against openmct webpack in `production-mode` with the `npm run test:perf:localhost` script.
`./e2e/tests/performance/contract/` - These tests serve as [contracts](https://martinfowler.com/bliki/ContractTest.html) for the locator logic, functionality, and assumptions will work in our downstream, closed source test suites. These tests run against openmct webpack in `dev-mode` with the `npm run test:perf:contract` script.
`./e2e/tests/performance/memory/` - These tests execute memory leak detection checks in various ways. This is expected to evolve as we move to the `memlab` project. These tests run against openmct webpack in `production-mode` with the `npm run test:perf:memory` script.
These tests are expected to become blocking and gating with assertions as we extend the capabilities of Playwright.

View File

@ -60,16 +60,14 @@ function waitForAnimations(locator) {
);
}
const istanbulCLIOutput = fileURLToPath(new URL('.nyc_output', import.meta.url));
/**
* This is part of our codecoverage shim.
* @see {@link https://github.com/mxschmitt/playwright-test-coverage Github Example Project}
* @constant {string}
*/
const istanbulCLIOutput = path.join(process.cwd(), '.nyc_output');
const extendedTest = test.extend({
/**
* Path to output raw coverage files. Can be overridden in Playwright config file.
* @see {@link https://github.com/mxschmitt/playwright-test-coverage Github Example Project}
* @constant {string}
*/
coveragePath: [istanbulCLIOutput, { option: true }],
/**
* This allows the test to manipulate the browser clock. This is useful for Visual and Snapshot tests which need
* the Time Indicator Clock to be in a specific state.
@ -150,17 +148,17 @@ const extendedTest = test.extend({
* Extends the base context class to add codecoverage shim.
* @see {@link https://github.com/mxschmitt/playwright-test-coverage Github Project}
*/
context: async ({ context, coveragePath }, use) => {
context: async ({ context }, use) => {
await context.addInitScript(() =>
window.addEventListener('beforeunload', () =>
window.collectIstanbulCoverage(JSON.stringify(window.__coverage__))
)
);
await fs.promises.mkdir(coveragePath, { recursive: true });
await fs.promises.mkdir(istanbulCLIOutput, { recursive: true });
await context.exposeFunction('collectIstanbulCoverage', (coverageJSON) => {
if (coverageJSON) {
fs.writeFileSync(
path.join(coveragePath, `playwright_coverage_${uuid()}.json`),
path.join(istanbulCLIOutput, `playwright_coverage_${uuid()}.json`),
coverageJSON
);
}
@ -168,9 +166,9 @@ const extendedTest = test.extend({
await use(context);
for (const page of context.pages()) {
await page.evaluate(() => {
window.collectIstanbulCoverage(JSON.stringify(window.__coverage__));
});
await page.evaluate(() =>
window.collectIstanbulCoverage(JSON.stringify(window.__coverage__))
);
}
},
/**

View File

@ -1,8 +0,0 @@
// Import everything from the specific fixture files
import * as appActions from './appActions.js';
import * as avpFixtures from './avpFixtures.js';
import * as baseFixtures from './baseFixtures.js';
import * as pluginFixtures from './pluginFixtures.js';
// Export these as named exports
export { appActions, avpFixtures, baseFixtures, pluginFixtures };

1449
e2e/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,27 +0,0 @@
{
"name": "openmct-e2e",
"version": "4.0.0-next",
"description": "The Open MCT e2e framework",
"type": "module",
"module": "index.js",
"exports": {
".": {
"import": "./index.js"
}
},
"scripts": {
"pretest:visual": "npm install",
"test": "npx playwright test",
"test:visual": "percy exec"
},
"devDependencies": {
"@types/sinonjs__fake-timers": "8.1.5",
"@percy/cli": "1.27.4",
"@percy/playwright": "1.0.4",
"@playwright/test": "1.42.1",
"@axe-core/playwright": "4.8.5",
"sinon": "17.0.0"
},
"author": "NASA Ames Research Center",
"license": "Apache-2.0"
}

View File

@ -3,7 +3,6 @@
// eslint-disable-next-line no-unused-vars
import { devices } from '@playwright/test';
import { fileURLToPath } from 'url';
const MAX_FAILURES = 5;
const NUM_WORKERS = 2;
@ -16,7 +15,6 @@ const config = {
timeout: 60 * 1000,
webServer: {
command: 'npm run start:coverage',
cwd: fileURLToPath(new URL('../', import.meta.url)), // Provide cwd for the root of the project
url: 'http://localhost:8080/#',
timeout: 200 * 1000,
reuseExistingServer: true //This was originally disabled to prevent differences in local debugging vs. CI. However, it significantly speeds up local debugging.
@ -29,9 +27,7 @@ const config = {
ignoreHTTPSErrors: true,
screenshot: 'only-on-failure',
trace: 'on-first-retry',
video: 'off',
// @ts-ignore - custom configuration option for nyc codecoverage output path
coveragePath: fileURLToPath(new URL('../.nyc_output', import.meta.url))
video: 'off'
},
projects: [
{

View File

@ -1,6 +1,6 @@
// playwright.config.js
// @ts-check
import { fileURLToPath } from 'url';
/** @type {import('@playwright/test').PlaywrightTestConfig} */
const config = {
retries: 0,
@ -10,7 +10,6 @@ const config = {
timeout: 30 * 1000,
webServer: {
command: 'npm run start:coverage',
cwd: fileURLToPath(new URL('../', import.meta.url)), // Provide cwd for the root of the project
url: 'http://localhost:8080/#',
timeout: 120 * 1000,
reuseExistingServer: true

View File

@ -14,7 +14,6 @@ const config = {
timeout: 30 * 1000,
webServer: {
command: 'npm run start:coverage',
cwd: fileURLToPath(new URL('../', import.meta.url)), // Provide cwd for the root of the project
url: 'http://localhost:8080/#',
timeout: 200 * 1000,
reuseExistingServer: true //This was originally disabled to prevent differences in local debugging vs. CI. However, it significantly speeds up local debugging.
@ -28,9 +27,7 @@ const config = {
ignoreHTTPSErrors: true,
screenshot: 'only-on-failure',
trace: 'on-first-retry',
video: 'off',
// @ts-ignore - custom configuration option for nyc codecoverage output path
coveragePath: fileURLToPath(new URL('../.nyc_output', import.meta.url))
video: 'off'
},
projects: [
{

View File

@ -1,6 +1,6 @@
// playwright.config.js
// @ts-check
import { fileURLToPath } from 'url';
/** @type {import('@playwright/test').PlaywrightTestConfig} */
const config = {
retries: 1, //Only for debugging purposes for trace: 'on-first-retry'
@ -10,7 +10,6 @@ const config = {
workers: 1, //Only run in serial with 1 worker
webServer: {
command: 'npm run start', //need development mode for performance.marks and others
cwd: fileURLToPath(new URL('../', import.meta.url)), // Provide cwd for the root of the project
url: 'http://localhost:8080/#',
timeout: 200 * 1000,
reuseExistingServer: false

View File

@ -1,6 +1,6 @@
// playwright.config.js
// @ts-check
import { fileURLToPath } from 'url';
/** @type {import('@playwright/test').PlaywrightTestConfig} */
const config = {
retries: 0, //Only for debugging purposes for trace: 'on-first-retry'
@ -10,7 +10,6 @@ const config = {
workers: 1, //Only run in serial with 1 worker
webServer: {
command: 'npm run start:prod', //Production mode
cwd: fileURLToPath(new URL('../', import.meta.url)), // Provide cwd for the root of the project
url: 'http://localhost:8080/#',
timeout: 200 * 1000,
reuseExistingServer: false //Must be run with this option to prevent dev mode

View File

@ -1,6 +1,6 @@
// playwright.config.js
// @ts-check
import { fileURLToPath } from 'url';
/** @type {import('@playwright/test').PlaywrightTestConfig<{ theme: string }>} */
const config = {
retries: 0, // Visual tests should never retry due to snapshot comparison errors. Leaving as a shim
@ -10,7 +10,6 @@ const config = {
workers: 1, //Lower stress on Circle CI Agent for Visual tests https://github.com/percy/cli/discussions/1067
webServer: {
command: 'npm run start:coverage',
cwd: fileURLToPath(new URL('../', import.meta.url)), // Provide cwd for the root of the project
url: 'http://localhost:8080/#',
timeout: 200 * 1000,
reuseExistingServer: !process.env.CI

View File

@ -1,5 +1,6 @@
// playwright.config.js
// @ts-check
import { devices } from '@playwright/test';
import { fileURLToPath } from 'url';
@ -10,7 +11,6 @@ const config = {
timeout: 60 * 1000,
webServer: {
command: 'npm run start', //Start in dev mode for hot reloading
cwd: fileURLToPath(new URL('../', import.meta.url)), // Provide cwd for the root of the project
url: 'http://localhost:8080/#',
timeout: 200 * 1000,
reuseExistingServer: true //This was originally disabled to prevent differences in local debugging vs. CI. However, it significantly speeds up local debugging.

View File

@ -31,8 +31,8 @@ import { createDomainObjectWithDefaults } from '../../appActions.js';
import { expect, test } from '../../pluginFixtures.js';
const TEST_FOLDER = 'test folder';
const jsonFilePath = 'test-data/ExampleLayouts.json';
const imageFilePath = 'test-data/rick.jpg';
const jsonFilePath = 'e2e/test-data/ExampleLayouts.json';
const imageFilePath = 'e2e/test-data/rick.jpg';
test.describe('Form Validation Behavior', () => {
test('Required Field indicators appear if title is empty and can be corrected', async ({

View File

@ -21,6 +21,7 @@
*****************************************************************************/
import fs from 'fs';
import { getPreciseDuration } from '../../../../src/utils/duration.js';
import { createDomainObjectWithDefaults, createPlanFromJSON } from '../../../appActions.js';
import {
assertPlanActivities,
@ -131,58 +132,3 @@ test.describe('Gantt Chart', () => {
);
});
});
const ONE_SECOND = 1000;
const ONE_MINUTE = 60 * ONE_SECOND;
const ONE_HOUR = ONE_MINUTE * 60;
const ONE_DAY = ONE_HOUR * 24;
function normalizeAge(num) {
const hundredtized = num * 100;
const isWhole = hundredtized % 100 === 0;
return isWhole ? hundredtized / 100 : num;
}
function padLeadingZeros(num, numOfLeadingZeros) {
return num.toString().padStart(numOfLeadingZeros, '0');
}
function toDoubleDigits(num) {
return padLeadingZeros(num, 2);
}
function toTripleDigits(num) {
return padLeadingZeros(num, 3);
}
function getPreciseDuration(value, { excludeMilliSeconds, useDayFormat } = {}) {
let preciseDuration;
const ms = value || 0;
const duration = [
Math.floor(normalizeAge(ms / ONE_DAY)),
toDoubleDigits(Math.floor(normalizeAge((ms % ONE_DAY) / ONE_HOUR))),
toDoubleDigits(Math.floor(normalizeAge((ms % ONE_HOUR) / ONE_MINUTE))),
toDoubleDigits(Math.floor(normalizeAge((ms % ONE_MINUTE) / ONE_SECOND)))
];
if (!excludeMilliSeconds) {
duration.push(toTripleDigits(Math.floor(normalizeAge(ms % ONE_SECOND))));
}
if (useDayFormat) {
// Format days as XD
const days = duration.shift();
if (days > 0) {
preciseDuration = `${days}D ${duration.join(':')}`;
} else {
preciseDuration = duration.join(':');
}
} else {
const days = toDoubleDigits(duration.shift());
duration.unshift(days);
preciseDuration = duration.join(':');
}
return preciseDuration;
}

View File

@ -34,7 +34,7 @@ TODO:
import { expect, test } from '@playwright/test';
const filePath = 'test-data/PerformanceDisplayLayout.json';
const filePath = 'e2e/test-data/PerformanceDisplayLayout.json';
test.describe('Performance tests', () => {
test.beforeEach(async ({ page, browser }, testInfo) => {

View File

@ -33,7 +33,7 @@ TODO:
import { expect, test } from '@playwright/test';
const notebookFilePath = 'test-data/PerformanceNotebook.json';
const notebookFilePath = 'e2e/test-data/PerformanceNotebook.json';
test.describe('Performance tests', () => {
test.beforeEach(async ({ page, browser }, testInfo) => {

View File

@ -33,7 +33,7 @@ test.describe('Visual - Inspector @ally @clock', () => {
await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' });
});
test.use({
storageState: 'test-data/overlay_plot_with_delay_storage.json',
storageState: './e2e/test-data/overlay_plot_with_delay_storage.json',
clockOptions: {
now: MISSION_TIME,
shouldAdvanceTime: true

View File

@ -35,7 +35,7 @@ test.describe('Visual - Controlled Clock @clock', () => {
await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' });
});
test.use({
storageState: 'test-data/overlay_plot_with_delay_storage.json',
storageState: './e2e/test-data/overlay_plot_with_delay_storage.json',
clockOptions: {
now: MISSION_TIME,
shouldAdvanceTime: false //Don't advance the clock

View File

@ -23,7 +23,7 @@
/*
Collection of Visual Tests set to run in a default context with default Plugins. The tests within this suite
are only meant to run against openmct's app.js started by `npm run start` within the
`playwright-visual.config.js` file.
`./e2e/playwright-visual.config.js` file.
*/
import percySnapshot from '@percy/playwright';

View File

@ -24,13 +24,13 @@
const loadWebpackConfig = async () => {
if (process.env.KARMA_DEBUG) {
return {
config: (await import('./.webpack/webpack.dev.mjs')).default,
config: (await import('./.webpack/webpack.dev.js')).default,
browsers: ['ChromeDebugging'],
singleRun: false
};
} else {
return {
config: (await import('./.webpack/webpack.coverage.mjs')).default,
config: (await import('./.webpack/webpack.coverage.js')).default,
browsers: ['ChromeHeadless'],
singleRun: true
};

42
package-lock.json generated
View File

@ -8,12 +8,13 @@
"name": "openmct",
"version": "4.0.0-next",
"license": "Apache-2.0",
"workspaces": [
"e2e"
],
"devDependencies": {
"@axe-core/playwright": "4.8.5",
"@babel/eslint-parser": "7.23.3",
"@braintree/sanitize-url": "6.0.4",
"@percy/cli": "1.27.4",
"@percy/playwright": "1.0.4",
"@playwright/test": "1.42.1",
"@types/d3-axis": "3.0.6",
"@types/d3-scale": "4.0.8",
"@types/d3-selection": "3.0.10",
@ -21,6 +22,7 @@
"@types/eventemitter3": "1.2.0",
"@types/jasmine": "5.1.2",
"@types/lodash": "4.17.0",
"@types/sinonjs__fake-timers": "8.1.5",
"@vue/compiler-sfc": "3.4.3",
"babel-loader": "9.1.0",
"babel-plugin-istanbul": "6.1.1",
@ -79,6 +81,7 @@
"sanitize-html": "2.12.1",
"sass": "1.71.1",
"sass-loader": "14.1.1",
"sinon": "17.0.0",
"style-loader": "3.3.3",
"terser-webpack-plugin": "5.3.9",
"tiny-emitter": "2.1.0",
@ -96,19 +99,6 @@
"node": ">=18.14.2 <22"
}
},
"e2e": {
"name": "openmct-e2e",
"version": "4.0.0-next",
"license": "Apache-2.0",
"devDependencies": {
"@axe-core/playwright": "4.8.5",
"@percy/cli": "1.27.4",
"@percy/playwright": "1.0.4",
"@playwright/test": "1.42.1",
"@types/sinonjs__fake-timers": "8.1.5",
"sinon": "17.0.0"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
@ -1506,9 +1496,9 @@
}
},
"node_modules/@percy/sdk-utils": {
"version": "1.28.2",
"resolved": "https://registry.npmjs.org/@percy/sdk-utils/-/sdk-utils-1.28.2.tgz",
"integrity": "sha512-cMFz8AjZ2KunN0dVwzA+Wosk4B+6G9dUkh2YPhYvqs0KLcCyYs3s91IzOQmtBOYwAUVja/W/u6XmBHw0jaxg0A==",
"version": "1.28.1",
"resolved": "https://registry.npmjs.org/@percy/sdk-utils/-/sdk-utils-1.28.1.tgz",
"integrity": "sha512-joS3i5wjFYXRSVL/NbUvip+bB7ErgwNjoDcID31l61y/QaSYUVCOxl/Fy4nvePJtHVyE1hpV0O7XO3tkoG908g==",
"dev": true,
"engines": {
"node": ">=14"
@ -1939,6 +1929,16 @@
"@types/node": "*"
}
},
"node_modules/@types/yauzl": {
"version": "2.10.3",
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
"integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
"dev": true,
"optional": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@ungap/structured-clone": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
@ -8549,10 +8549,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/openmct-e2e": {
"resolved": "e2e",
"link": true
},
"node_modules/optionator": {
"version": "0.9.3",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",

View File

@ -2,21 +2,15 @@
"name": "openmct",
"version": "4.0.0-next",
"description": "The Open MCT core platform",
"module": "dist/openmct.js",
"type": "module",
"main": "dist/openmct.js",
"types": "dist/types/index.d.ts",
"exports": {
".": {
"import": "./dist/openmct.js",
"require": "./dist/openmct.js"
}
},
"workspaces": [
"e2e"
],
"devDependencies": {
"@axe-core/playwright": "4.8.5",
"@babel/eslint-parser": "7.23.3",
"@braintree/sanitize-url": "6.0.4",
"@percy/cli": "1.27.4",
"@percy/playwright": "1.0.4",
"@playwright/test": "1.42.1",
"@types/d3-axis": "3.0.6",
"@types/d3-scale": "4.0.8",
"@types/d3-selection": "3.0.10",
@ -24,6 +18,7 @@
"@types/eventemitter3": "1.2.0",
"@types/jasmine": "5.1.2",
"@types/lodash": "4.17.0",
"@types/sinonjs__fake-timers": "8.1.5",
"@vue/compiler-sfc": "3.4.3",
"babel-loader": "9.1.0",
"babel-plugin-istanbul": "6.1.1",
@ -82,6 +77,7 @@
"sanitize-html": "2.12.1",
"sass": "1.71.1",
"sass-loader": "14.1.1",
"sinon": "17.0.0",
"style-loader": "3.3.3",
"terser-webpack-plugin": "5.3.9",
"tiny-emitter": "2.1.0",
@ -96,39 +92,39 @@
"webpack-merge": "5.10.0"
},
"scripts": {
"clean": "rm -rf ./dist ./node_modules ./coverage ./html-test-results ./test-results ./.nyc_output",
"start": "npx webpack serve --config ./.webpack/webpack.dev.mjs",
"start:prod": "npx webpack serve --config ./.webpack/webpack.prod.mjs",
"start:coverage": "npx webpack serve --config ./.webpack/webpack.coverage.mjs",
"clean": "rm -rf ./dist ./node_modules ./coverage ./html-test-results ./test-results ./.nyc_output ",
"start": "npx webpack serve --config ./.webpack/webpack.dev.js",
"start:prod": "npx webpack serve --config ./.webpack/webpack.prod.js",
"start:coverage": "npx webpack serve --config ./.webpack/webpack.coverage.js",
"lint:js": "eslint \"example/**/*.js\" \"src/**/*.js\" \"e2e/**/*.js\" \"openmct.js\" --max-warnings=0",
"lint:vue": "eslint \"src/**/*.vue\"",
"lint:spelling": "cspell \"**/*.{js,md,vue}\" --show-context --gitignore --quiet",
"lint": "run-p \"lint:js -- {1}\" \"lint:vue -- {1}\" \"lint:spelling -- {1}\" --",
"lint:fix": "eslint example src e2e --ext .js,.vue openmct.js --fix",
"build:prod": "webpack --config ./.webpack/webpack.prod.mjs",
"build:dev": "webpack --config ./.webpack/webpack.dev.mjs",
"build:coverage": "webpack --config ./.webpack/webpack.coverage.mjs",
"build:watch": "webpack --config ./.webpack/webpack.dev.mjs --watch",
"build:prod": "webpack --config ./.webpack/webpack.prod.js",
"build:dev": "webpack --config ./.webpack/webpack.dev.js",
"build:coverage": "webpack --config ./.webpack/webpack.coverage.js",
"build:watch": "webpack --config ./.webpack/webpack.dev.js --watch",
"info": "npx envinfo --system --browsers --npmPackages --binaries --languages --markdown",
"test": "karma start karma.conf.cjs",
"test:debug": "KARMA_DEBUG=true karma start karma.conf.cjs",
"test:e2e": "npm test --workspace e2e",
"test:e2e:a11y": "npm test --workspace e2e -- --config=playwright-visual-a11y.config.js --project=chrome --grep @a11y",
"test:e2e:mobile": "npm test --workspace e2e -- --config=playwright-mobile.config.js",
"test:e2e:couchdb": "npm test --workspace e2e -- --config=playwright-ci.config.js --project=chrome --grep @couchdb --workers=1",
"test:e2e:stable": "npm test --workspace e2e -- --config=playwright-ci.config.js --project=chrome --grep-invert \"@unstable|@couchdb|@generatedata\"",
"test:e2e:unstable": "npm test --workspace e2e -- --config=playwright-ci.config.js --project=chrome --grep @unstable",
"test:e2e:local": "npm test --workspace e2e -- --config=playwright-local.config.js --project=chrome",
"test:e2e:generatedata": "npm test --workspace e2e -- --config=playwright-ci.config.js --project=chrome --grep @generatedata",
"test:e2e:checksnapshots": "npm test --workspace e2e -- --config=playwright-ci.config.js --project=chrome --grep @snapshot --retries=0",
"test:e2e:updatesnapshots": "npm test --workspace e2e -- --config=playwright-ci.config.js --project=chrome --grep @snapshot --update-snapshots",
"test:e2e:visual:ci": "npm run test:visual --workspace e2e -- --config .percy.ci.yml --partial -- npx playwright test --config=playwright-visual-a11y.config.js --project=chrome --grep-invert @unstable",
"test:e2e:visual:full": "npm run test:visual --workspace e2e -- --config .percy.nightly.yml -- npx playwright test --config=playwright-visual-a11y.config.js --grep-invert @unstable",
"test:e2e:full": "npm test --workspace e2e -- --config=playwright-ci.config.js --grep-invert @couchdb",
"test:e2e:watch": "npm test --workspace e2e -- --ui --config=playwright-watch.config.js",
"test:perf:contract": "npm test --workspace e2e -- --config=playwright-performance-dev.config.js",
"test:perf:localhost": "npm test --workspace e2e -- --config=playwright-performance-prod.config.js --project=chrome",
"test:perf:memory": "npm test --workspace e2e -- --config=playwright-performance-prod.config.js --project=chrome-memory",
"test:e2e": "npx playwright test",
"test:e2e:a11y": "npx playwright test --config=e2e/playwright-visual-a11y.config.js --project=chrome --grep @a11y",
"test:e2e:mobile": "npx playwright test --config=e2e/playwright-mobile.config.js",
"test:e2e:couchdb": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep @couchdb --workers=1",
"test:e2e:stable": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep-invert \"@unstable|@couchdb|@generatedata\"",
"test:e2e:unstable": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep @unstable",
"test:e2e:local": "npx playwright test --config=e2e/playwright-local.config.js --project=chrome",
"test:e2e:generatedata": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep @generatedata",
"test:e2e:checksnapshots": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep @snapshot --retries=0",
"test:e2e:updatesnapshots": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep @snapshot --update-snapshots",
"test:e2e:visual:ci": "percy exec --config ./e2e/.percy.ci.yml --partial -- npx playwright test --config=e2e/playwright-visual-a11y.config.js --project=chrome --grep-invert @unstable",
"test:e2e:visual:full": "percy exec --config ./e2e/.percy.nightly.yml -- npx playwright test --config=e2e/playwright-visual-a11y.config.js --grep-invert @unstable",
"test:e2e:full": "npx playwright test --config=e2e/playwright-ci.config.js --grep-invert @couchdb",
"test:e2e:watch": "npx playwright test --ui --config=e2e/playwright-watch.config.js",
"test:perf:contract": "npx playwright test --config=e2e/playwright-performance-dev.config.js",
"test:perf:localhost": "npx playwright test --config=e2e/playwright-performance-prod.config.js --project=chrome",
"test:perf:memory": "npx playwright test --config=e2e/playwright-performance-prod.config.js --project=chrome-memory",
"update-about-dialog-copyright": "perl -pi -e 's/20\\d\\d\\-202\\d/2014\\-2023/gm' ./src/ui/layout/AboutDialog.vue",
"update-copyright-date": "npm run update-about-dialog-copyright && grep -lr --null --include=*.{js,scss,vue,ts,sh,html,md,frag} 'Copyright (c) 20' . | xargs -r0 perl -pi -e 's/Copyright\\s\\(c\\)\\s20\\d\\d\\-20\\d\\d/Copyright \\(c\\)\\ 2014\\-2024/gm'",
"cov:e2e:report": "nyc report --reporter=lcovonly --report-dir=./coverage/e2e",

View File

@ -22,10 +22,6 @@
import TimeContext from './TimeContext.js';
/**
* @typedef {import('./TimeAPI').TimeConductorBounds} TimeConductorBounds
*/
/**
* The GlobalContext handles getting and setting time of the openmct application in general.
* Views will use this context unless they specify an alternate/independent time context
@ -42,10 +38,12 @@ class GlobalTimeContext extends TimeContext {
* Get or set the start and end time of the time conductor. Basic validation
* of bounds is performed.
*
* @param {TimeConductorBounds} newBounds
* @param {module:openmct.TimeAPI~TimeConductorBounds} newBounds
* @throws {Error} Validation error
* @returns {TimeConductorBounds}
* @override
* @fires module:openmct.TimeAPI~bounds
* @returns {module:openmct.TimeAPI~TimeConductorBounds}
* @memberof module:openmct.TimeAPI#
* @method bounds
*/
bounds(newBounds) {
if (arguments.length > 0) {
@ -63,9 +61,9 @@ class GlobalTimeContext extends TimeContext {
/**
* Update bounds based on provided time and current offsets
* @private
* @param {number} timestamp A time from which bounds will be calculated
* using current offsets.
* @override
*/
tick(timestamp) {
super.tick.call(this, ...arguments);
@ -83,8 +81,11 @@ class GlobalTimeContext extends TimeContext {
* be manipulated by the user from the time conductor or from other views.
* The time of interest can effectively be unset by assigning a value of
* 'undefined'.
* @fires module:openmct.TimeAPI~timeOfInterest
* @param newTOI
* @returns {number} the current time of interest
* @memberof module:openmct.TimeAPI#
* @method timeOfInterest
*/
timeOfInterest(newTOI) {
if (arguments.length > 0) {
@ -92,7 +93,8 @@ class GlobalTimeContext extends TimeContext {
/**
* The Time of Interest has moved.
* @event timeOfInterest
* @property {number} timeOfInterest time of interest
* @memberof module:openmct.TimeAPI~
* @property {number} Current time of interest
*/
this.emit('timeOfInterest', this.toi);
}

View File

@ -23,36 +23,19 @@
import { MODES, REALTIME_MODE_KEY, TIME_CONTEXT_EVENTS } from './constants.js';
import TimeContext from './TimeContext.js';
/**
* @typedef {import('./TimeAPI.js').default} TimeAPI
* @typedef {import('./GlobalTimeContext.js').default} GlobalTimeContext
* @typedef {import('./TimeAPI.js').TimeSystem} TimeSystem
* @typedef {import('./TimeContext.js').Mode} Mode
* @typedef {import('./TimeContext.js').TimeConductorBounds} TimeConductorBounds
* @typedef {import('./TimeAPI.js').ClockOffsets} ClockOffsets
*/
/**
* The IndependentTimeContext handles getting and setting time of the openmct application in general.
* Views will use the GlobalTimeContext unless they specify an alternate/independent time context here.
*/
class IndependentTimeContext extends TimeContext {
/**
* @param {import('openmct').OpenMCT} openmct - The Open MCT application instance.
* @param {TimeAPI & GlobalTimeContext} globalTimeContext - The global time context.
* @param {import('openmct').ObjectPath} objectPath - The path of objects.
*/
constructor(openmct, globalTimeContext, objectPath) {
super();
/** @type {any} */
this.openmct = openmct;
/** @type {Function[]} */
this.unlisteners = [];
/** @type {TimeAPI & GlobalTimeContext | undefined} */
this.globalTimeContext = globalTimeContext;
/** @type {TimeAPI & GlobalTimeContext | undefined} */
// We always start with the global time context.
// This upstream context will be undefined when an independent time context is added later.
this.upstreamTimeContext = this.globalTimeContext;
/** @type {Array<any>} */
this.objectPath = objectPath;
this.refreshContext = this.refreshContext.bind(this);
this.resetContext = this.resetContext.bind(this);
@ -64,10 +47,6 @@ class IndependentTimeContext extends TimeContext {
this.globalTimeContext.on('removeOwnContext', this.removeIndependentContext);
}
/**
* @deprecated
* @override
*/
bounds() {
if (this.upstreamTimeContext) {
return this.upstreamTimeContext.bounds(...arguments);
@ -76,9 +55,6 @@ class IndependentTimeContext extends TimeContext {
}
}
/**
* @override
*/
getBounds() {
if (this.upstreamTimeContext) {
return this.upstreamTimeContext.getBounds();
@ -87,9 +63,6 @@ class IndependentTimeContext extends TimeContext {
}
}
/**
* @override
*/
setBounds() {
if (this.upstreamTimeContext) {
return this.upstreamTimeContext.setBounds(...arguments);
@ -98,9 +71,6 @@ class IndependentTimeContext extends TimeContext {
}
}
/**
* @override
*/
tick() {
if (this.upstreamTimeContext) {
return this.upstreamTimeContext.tick(...arguments);
@ -109,9 +79,6 @@ class IndependentTimeContext extends TimeContext {
}
}
/**
* @override
*/
clockOffsets() {
if (this.upstreamTimeContext) {
return this.upstreamTimeContext.clockOffsets(...arguments);
@ -120,9 +87,6 @@ class IndependentTimeContext extends TimeContext {
}
}
/**
* @override
*/
getClockOffsets() {
if (this.upstreamTimeContext) {
return this.upstreamTimeContext.getClockOffsets();
@ -131,9 +95,6 @@ class IndependentTimeContext extends TimeContext {
}
}
/**
* @override
*/
setClockOffsets() {
if (this.upstreamTimeContext) {
return this.upstreamTimeContext.setClockOffsets(...arguments);
@ -142,24 +103,12 @@ class IndependentTimeContext extends TimeContext {
}
}
/**
*
* @param {number} newTOI
* @returns {number}
*/
timeOfInterest(newTOI) {
return this.globalTimeContext.timeOfInterest(...arguments);
}
/**
*
* @param {TimeSystem | string} timeSystemOrKey
* @param {TimeConductorBounds} bounds
* @returns {TimeSystem}
* @override
*/
timeSystem(timeSystemOrKey, bounds) {
return this.globalTimeContext.setTimeSystem(...arguments);
return this.globalTimeContext.timeSystem(...arguments);
}
/**
@ -167,7 +116,6 @@ class IndependentTimeContext extends TimeContext {
* @returns {TimeSystem} The currently applied time system
* @memberof module:openmct.TimeAPI#
* @method getTimeSystem
* @override
*/
getTimeSystem() {
return this.globalTimeContext.getTimeSystem();
@ -298,7 +246,6 @@ class IndependentTimeContext extends TimeContext {
/**
* Get the current mode.
* @return {Mode} the current mode;
* @override
*/
getMode() {
if (this.upstreamTimeContext) {
@ -312,8 +259,9 @@ class IndependentTimeContext extends TimeContext {
* Set the mode to either fixed or realtime.
*
* @param {Mode} mode The mode to activate
* @param {TimeConductorBounds | ClockOffsets} offsetsOrBounds A time window of a fixed width
* @return {Mode | undefined} the currently active mode;
* @param {TimeBounds | ClockOffsets} offsetsOrBounds A time window of a fixed width
* @fires module:openmct.TimeAPI~clock
* @return {Mode} the currently active mode;
*/
setMode(mode, offsetsOrBounds) {
if (!mode) {
@ -351,10 +299,6 @@ class IndependentTimeContext extends TimeContext {
return this.mode;
}
/**
* @returns {boolean}
* @override
*/
isRealTime() {
if (this.upstreamTimeContext) {
return this.upstreamTimeContext.isRealTime(...arguments);
@ -363,10 +307,6 @@ class IndependentTimeContext extends TimeContext {
}
}
/**
* @returns {number}
* @override
*/
now() {
if (this.upstreamTimeContext) {
return this.upstreamTimeContext.now(...arguments);
@ -403,9 +343,6 @@ class IndependentTimeContext extends TimeContext {
this.unlisteners = [];
}
/**
* Reset the time context to the global time context
*/
resetContext() {
if (this.upstreamTimeContext) {
this.stopFollowingTimeContext();
@ -415,7 +352,6 @@ class IndependentTimeContext extends TimeContext {
/**
* Refresh the time context, following any upstream time contexts as necessary
* @param {string} [viewKey] The key of the view to refresh
*/
refreshContext(viewKey) {
const key = this.openmct.objects.makeKeyString(this.objectPath[0].identifier);
@ -434,17 +370,10 @@ class IndependentTimeContext extends TimeContext {
this.emit(TIME_CONTEXT_EVENTS.boundsChanged, this.getBounds());
}
/**
* @returns {boolean} True if this time context has an independent context, false otherwise
*/
hasOwnContext() {
return this.upstreamTimeContext === undefined;
}
/**
* Get the upstream time context of this time context
* @returns {TimeAPI & GlobalTimeContext | undefined} The upstream time context
*/
getUpstreamContext() {
// If a view has an independent context, don't return an upstream context
// Be aware that when a new independent time context is created, we assign the global context as default

View File

@ -25,41 +25,6 @@ import IndependentTimeContext from '@/api/time/IndependentTimeContext';
import GlobalTimeContext from './GlobalTimeContext.js';
/**
* @typedef {import('./TimeContext.js').default} TimeContext
*/
/**
* @typedef {import('./TimeContext.js').TimeConductorBounds} TimeConductorBounds
*/
/**
* @typedef {import('./TimeContext.js').ClockOffsets} ClockOffsets
*/
/**
* A TimeSystem provides meaning to the values returned by the TimeAPI. Open
* MCT supports multiple different types of time values, although all are
* intrinsically represented by numbers, the meaning of those numbers can
* differ depending on context.
*
* A default time system is provided by Open MCT in the form of the {@link UTCTimeSystem},
* which represents integer values as ms in the Unix epoch. An example of
* another time system might be "sols" for a Martian mission. TimeSystems do
* not address the issue of converting between time systems.
*
* @typedef {Object} TimeSystem
* @property {string} key A unique identifier
* @property {string} name A human-readable descriptor
* @property {string} [cssClass] Specify a css class defining an icon for
* this time system. This will be visible next to the time system in the
* menu in the Time Conductor
* @property {string} timeFormat The key of a format to use when displaying
* discrete timestamps from this time system
* @property {string} [durationFormat] The key of a format to use when
* displaying a duration or relative span of time in this time system.
*/
/**
* The public API for setting and querying the temporal state of the
* application. The concept of time is integral to Open MCT, and at least
@ -76,8 +41,8 @@ import GlobalTimeContext from './GlobalTimeContext.js';
* fired when properties of the time conductor change, which are documented
* below.
*
* @class
* @extends {GlobalTimeContext}
* @interface
* @memberof module:openmct
*/
class TimeAPI extends GlobalTimeContext {
constructor(openmct) {
@ -86,9 +51,33 @@ class TimeAPI extends GlobalTimeContext {
this.independentContexts = new Map();
}
/**
* A TimeSystem provides meaning to the values returned by the TimeAPI. Open
* MCT supports multiple different types of time values, although all are
* intrinsically represented by numbers, the meaning of those numbers can
* differ depending on context.
*
* A default time system is provided by Open MCT in the form of the {@link UTCTimeSystem},
* which represents integer values as ms in the Unix epoch. An example of
* another time system might be "sols" for a Martian mission. TimeSystems do
* not address the issue of converting between time systems.
*
* @typedef {Object} TimeSystem
* @property {string} key A unique identifier
* @property {string} name A human-readable descriptor
* @property {string} [cssClass] Specify a css class defining an icon for
* this time system. This will be visible next to the time system in the
* menu in the Time Conductor
* @property {string} timeFormat The key of a format to use when displaying
* discrete timestamps from this time system
* @property {string} [durationFormat] The key of a format to use when
* displaying a duration or relative span of time in this time system.
*/
/**
* Register a new time system. Once registered it can activated using
* {@link TimeAPI.timeSystem}, and can be referenced via its key in [Time Conductor configuration](@link https://github.com/nasa/openmct/blob/master/API.md#time-conductor).
* @memberof module:openmct.TimeAPI#
* @param {TimeSystem} timeSystem A time system object.
*/
addTimeSystem(timeSystem) {
@ -120,6 +109,7 @@ class TimeAPI extends GlobalTimeContext {
/**
* Register a new Clock.
* @memberof module:openmct.TimeAPI#
* @param {Clock} clock
*/
addClock(clock) {
@ -127,7 +117,9 @@ class TimeAPI extends GlobalTimeContext {
}
/**
* @memberof module:openmct.TimeAPI#
* @returns {Clock[]}
* @memberof module:openmct.TimeAPI#
*/
getAllClocks() {
return Array.from(this.clocks.values());
@ -136,9 +128,11 @@ class TimeAPI extends GlobalTimeContext {
/**
* Get or set an independent time context which follows the TimeAPI timeSystem,
* but with different offsets for a given domain object
* @param {string} key The identifier key of the domain object these offsets are set for
* @param {ClockOffsets | TimeConductorBounds} value This maintains a sliding time window of a fixed width that automatically updates
* @param {key | string} key The identifier key of the domain object these offsets are set for
* @param {ClockOffsets | TimeBounds} value This maintains a sliding time window of a fixed width that automatically updates
* @param {key | string} clockKey the real time clock key currently in use
* @memberof module:openmct.TimeAPI#
* @method addIndependentTimeContext
*/
addIndependentContext(key, value, clockKey) {
let timeContext = this.getIndependentContext(key);
@ -165,8 +159,9 @@ class TimeAPI extends GlobalTimeContext {
/**
* Get the independent time context which follows the TimeAPI timeSystem,
* but with different offsets.
* @param {string} key The identifier key of the domain object these offsets
* @returns {IndependentTimeContext} The independent time context
* @param {key | string} key The identifier key of the domain object these offsets
* @memberof module:openmct.TimeAPI#
* @method getIndependentTimeContext
*/
getIndependentContext(key) {
return this.independentContexts.get(key);
@ -175,8 +170,9 @@ class TimeAPI extends GlobalTimeContext {
/**
* Get the a timeContext for a view based on it's objectPath. If there is any object in the objectPath with an independent time context, it will be returned.
* Otherwise, the global time context will be returned.
* @param {Array} objectPath The view's objectPath
* @returns {TimeContext | GlobalTimeContext} The time context
* @param { Array } objectPath The view's objectPath
* @memberof module:openmct.TimeAPI#
* @method getContextForView
*/
getContextForView(objectPath) {
if (!objectPath || !Array.isArray(objectPath)) {

View File

@ -24,85 +24,22 @@ import EventEmitter from 'EventEmitter';
import { FIXED_MODE_KEY, MODES, REALTIME_MODE_KEY, TIME_CONTEXT_EVENTS } from './constants.js';
/**
* @typedef {import('../../utils/clock/DefaultClock.js').default} Clock
*/
/**
* @typedef {import('./TimeAPI.js').TimeSystem} TimeSystem
*/
/**
* @typedef {Object} TimeConductorBounds
* @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.
*/
/**
* Clock offsets are used to calculate temporal bounds when the system is
* ticking on a clock source.
*
* @typedef {Object} ClockOffsets
* @property {number} start A time span relative to the current value of the
* ticking clock, from which start bounds will be calculated. This value must
* be < 0. When a clock is active, bounds will be calculated automatically
* based on the value provided by the clock, and the defined clock offsets.
* @property {number} end A time span relative to the current value of the
* ticking clock, from which end bounds will be calculated. This value must
* be >= 0.
*/
/**
* @typedef {Object} ValidationResult
* @property {boolean} valid Result of the validation - true or false.
* @property {string} message An error message if valid is false.
*/
/**
* @typedef {'fixed' | 'realtime'} Mode The time conductor mode.
*/
/**
* @class TimeContext
* @extends EventEmitter
*/
class TimeContext extends EventEmitter {
constructor() {
super();
/**
* The time systems available to the TimeAPI.
* @type {Map<string, TimeSystem>}
*/
//The Time System
this.timeSystems = new Map();
/**
* The currently applied time system.
* @type {TimeSystem | undefined}
*/
this.system = undefined;
/**
* The clocks available to the TimeAPI.
* @type {Map<string, import('../../utils/clock/DefaultClock.js').default>}
*/
this.clocks = new Map();
/**
* The current bounds of the time conductor.
* @type {TimeConductorBounds}
*/
this.boundsVal = {
start: undefined,
end: undefined
};
/**
* The currently active clock.
* @type {Clock | undefined}
*/
this.activeClock = undefined;
this.offsets = undefined;
this.mode = undefined;
@ -114,9 +51,11 @@ class TimeContext extends EventEmitter {
/**
* Get or set the time system of the TimeAPI.
* @param {TimeSystem | string} timeSystemOrKey
* @param {TimeConductorBounds} bounds
* @param {module:openmct.TimeAPI~TimeConductorBounds} bounds
* @fires module:openmct.TimeAPI~timeSystem
* @returns {TimeSystem} The currently applied time system
* @deprecated This method is deprecated. Use "getTimeSystem" and "setTimeSystem" instead.
* @memberof module:openmct.TimeAPI#
* @method timeSystem
*/
timeSystem(timeSystemOrKey, bounds) {
this.#warnMethodDeprecated('"timeSystem"', '"getTimeSystem" and "setTimeSystem"');
@ -162,8 +101,11 @@ class TimeContext extends EventEmitter {
* The time system used by the time
* conductor has changed. A change in Time System will always be
* followed by a bounds event specifying new query bounds.
* @type {TimeSystem}
*/
*
* @event module:openmct.TimeAPI~timeSystem
* @property {TimeSystem} The value of the currently applied
* Time System
* */
const system = this.#copy(this.system);
this.emit('timeSystem', system);
this.emit(TIME_CONTEXT_EVENTS.timeSystemChanged, system);
@ -176,11 +118,21 @@ class TimeContext extends EventEmitter {
return this.system;
}
/**
* Clock offsets are used to calculate temporal bounds when the system is
* ticking on a clock source.
*
* @typedef {Object} ValidationResult
* @property {boolean} valid Result of the validation - true or false.
* @property {string} message An error message if valid is false.
*/
/**
* Validate the given bounds. This can be used for pre-validation of bounds,
* for example by views validating user inputs.
* @param {TimeConductorBounds} bounds The start and end time of the conductor.
* @param {TimeBounds} bounds The start and end time of the conductor.
* @returns {ValidationResult} A validation error, or true if valid
* @memberof module:openmct.TimeAPI#
* @method validateBounds
*/
validateBounds(bounds) {
if (
@ -210,10 +162,12 @@ class TimeContext extends EventEmitter {
* Get or set the start and end time of the time conductor. Basic validation
* of bounds is performed.
*
* @param {TimeConductorBounds} [newBounds] The new bounds to set. If not provided, current bounds will be returned.
* @param {module:openmct.TimeAPI~TimeConductorBounds} newBounds
* @throws {Error} Validation error
* @returns {TimeConductorBounds} The current bounds of the time conductor.
* @deprecated This method is deprecated. Use "getBounds" and "setBounds" instead.
* @fires module:openmct.TimeAPI~bounds
* @returns {module:openmct.TimeAPI~TimeConductorBounds}
* @memberof module:openmct.TimeAPI#
* @method bounds
*/
bounds(newBounds) {
this.#warnMethodDeprecated('"bounds"', '"getBounds" and "setBounds"');
@ -229,6 +183,7 @@ class TimeContext extends EventEmitter {
/**
* The start time, end time, or both have been updated.
* @event bounds
* @memberof module:openmct.TimeAPI~
* @property {TimeConductorBounds} bounds The newly updated bounds
* @property {boolean} [tick] `true` if the bounds update was due to
* a "tick" event (ie. was an automatic update), false otherwise.
@ -245,7 +200,9 @@ class TimeContext extends EventEmitter {
* Validate the given offsets. This can be used for pre-validation of
* offsets, for example by views validating user inputs.
* @param {ClockOffsets} offsets The start and end offsets from a 'now' value.
* @returns {ValidationResult} A validation error, and true/false if valid or not
* @returns { ValidationResult } A validation error, and true/false if valid or not
* @memberof module:openmct.TimeAPI#
* @method validateOffsets
*/
validateOffsets(offsets) {
if (
@ -271,13 +228,34 @@ class TimeContext extends EventEmitter {
};
}
/**
* @typedef {Object} TimeBounds
* @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.
* @memberof module:openmct.TimeAPI~
*/
/**
* Clock offsets are used to calculate temporal bounds when the system is
* ticking on a clock source.
*
* @typedef {Object} ClockOffsets
* @property {number} start A time span relative to the current value of the
* ticking clock, from which start bounds will be calculated. This value must
* be < 0. When a clock is active, bounds will be calculated automatically
* based on the value provided by the clock, and the defined clock offsets.
* @property {number} end A time span relative to the current value of the
* ticking clock, from which end bounds will be calculated. This value must
* be >= 0.
*/
/**
* Get or set the currently applied clock offsets. If no parameter is provided,
* the current value will be returned. If provided, the new value will be
* used as the new clock offsets.
* @param {ClockOffsets} [offsets] The new clock offsets to set. If not provided, current offsets will be returned.
* @returns {ClockOffsets} The current clock offsets.
* @deprecated This method is deprecated. Use "getClockOffsets" and "setClockOffsets" instead.
* @param {ClockOffsets} offsets
* @returns {ClockOffsets}
*/
clockOffsets(offsets) {
this.#warnMethodDeprecated('"clockOffsets"', '"getClockOffsets" and "setClockOffsets"');
@ -315,7 +293,6 @@ class TimeContext extends EventEmitter {
* Stop following the currently active clock. This will
* revert all views to showing a static time frame defined by the current
* bounds.
* @deprecated This method is deprecated.
*/
stopClock() {
this.#warnMethodDeprecated('"stopClock"');
@ -327,14 +304,12 @@ class TimeContext extends EventEmitter {
* Set the active clock. Tick source will be immediately subscribed to
* and ticking will begin. Offsets from 'now' must also be provided.
*
* @param {string|Clock} keyOrClock The clock to activate, or its key
* @param {Clock || string} keyOrClock The clock to activate, or its key
* @param {ClockOffsets} offsets on each tick these will be used to calculate
* the start and end bounds. This maintains a sliding time window of a fixed
* width that automatically updates.
* (Legacy) Emits a "clock" event with the new clock.
* Emits a "clockChanged" event with the new clock.
* @return {Clock|undefined} the currently active clock; undefined if in fixed mode
* @deprecated This method is deprecated. Use "getClock" and "setClock" instead.
* @fires module:openmct.TimeAPI~clock
* @return {Clock} the currently active clock;
*/
clock(keyOrClock, offsets) {
this.#warnMethodDeprecated('"clock"', '"getClock" and "setClock"');
@ -364,6 +339,7 @@ class TimeContext extends EventEmitter {
/**
* The active clock has changed.
* @event clock
* @memberof module:openmct.TimeAPI~
* @property {Clock} clock The newly activated clock, or undefined
* if the system is no longer following a clock source
*/
@ -385,7 +361,7 @@ class TimeContext extends EventEmitter {
}
/**
* Update bounds based on provided time and current offsets.
* Update bounds based on provided time and current offsets
* @param {number} timestamp A time from which bounds will be calculated
* using current offsets.
*/
@ -409,6 +385,8 @@ class TimeContext extends EventEmitter {
/**
* Get the timestamp of the current clock
* @returns {number} current timestamp of current clock regardless of mode
* @memberof module:openmct.TimeAPI#
* @method now
*/
now() {
@ -418,6 +396,8 @@ class TimeContext extends EventEmitter {
/**
* Get the time system of the TimeAPI.
* @returns {TimeSystem} The currently applied time system
* @memberof module:openmct.TimeAPI#
* @method getTimeSystem
*/
getTimeSystem() {
return this.system;
@ -425,9 +405,12 @@ 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 {module:openmct.TimeAPI~TimeConductorBounds} bounds
* @fires module:openmct.TimeAPI~timeSystem
* @returns {TimeSystem} The currently applied time system
* @memberof module:openmct.TimeAPI#
* @method setTimeSystem
*/
setTimeSystem(timeSystemOrKey, bounds) {
if (timeSystemOrKey === undefined) {
@ -458,6 +441,7 @@ class TimeContext extends EventEmitter {
* conductor has changed. A change in Time System will always be
* followed by a bounds event specifying new query bounds.
*
* @event module:openmct.TimeAPI~timeSystem
* @property {TimeSystem} The value of the currently applied
* Time System
* */
@ -472,7 +456,9 @@ class TimeContext extends EventEmitter {
/**
* Get the start and end time of the time conductor. Basic validation
* of bounds is performed.
* @returns {TimeConductorBounds} The current bounds of the time conductor.
* @returns {module:openmct.TimeAPI~TimeConductorBounds}
* @memberof module:openmct.TimeAPI#
* @method bounds
*/
getBounds() {
//Return a copy to prevent direct mutation of time conductor bounds.
@ -483,8 +469,12 @@ class TimeContext extends EventEmitter {
* Set the start and end time of the time conductor. Basic validation
* of bounds is performed.
*
* @param {TimeConductorBounds} newBounds The new bounds to set.
* @throws {Error} Validation error if bounds are invalid
* @param {module:openmct.TimeAPI~TimeConductorBounds} newBounds
* @throws {Error} Validation error
* @fires module:openmct.TimeAPI~bounds
* @returns {module:openmct.TimeAPI~TimeConductorBounds}
* @memberof module:openmct.TimeAPI#
* @method bounds
*/
setBounds(newBounds) {
const validationResult = this.validateBounds(newBounds);
@ -497,6 +487,7 @@ class TimeContext extends EventEmitter {
/**
* The start time, end time, or both have been updated.
* @event bounds
* @memberof module:openmct.TimeAPI~
* @property {TimeConductorBounds} bounds The newly updated bounds
* @property {boolean} [tick] `true` if the bounds update was due to
* a "tick" event (i.e. was an automatic update), false otherwise.
@ -507,7 +498,7 @@ class TimeContext extends EventEmitter {
/**
* Get the active clock.
* @return {Clock|undefined} the currently active clock; undefined if in fixed mode.
* @return {Clock} the currently active clock;
*/
getClock() {
return this.activeClock;
@ -518,7 +509,9 @@ class TimeContext extends EventEmitter {
* and the currently ticking will begin.
* Offsets from 'now', if provided, will be used to set realtime mode offsets
*
* @param {string|Clock} keyOrClock The clock to activate, or its key
* @param {Clock || string} keyOrClock The clock to activate, or its key
* @fires module:openmct.TimeAPI~clock
* @return {Clock} the currently active clock;
*/
setClock(keyOrClock) {
let clock;
@ -547,7 +540,7 @@ class TimeContext extends EventEmitter {
* The active clock has changed.
* @event clock
* @memberof module:openmct.TimeAPI~
* @property {TimeContext} clock The newly activated clock, or undefined
* @property {Clock} clock The newly activated clock, or undefined
* if the system is no longer following a clock source
*/
this.emit(TIME_CONTEXT_EVENTS.clockChanged, this.activeClock);
@ -556,7 +549,7 @@ class TimeContext extends EventEmitter {
/**
* Get the current mode.
* @return {Mode} the current mode
* @return {Mode} the current mode;
*/
getMode() {
return this.mode;
@ -566,9 +559,9 @@ class TimeContext extends EventEmitter {
* Set the mode to either fixed or realtime.
*
* @param {Mode} mode The mode to activate
* @param {TimeConductorBounds|ClockOffsets} offsetsOrBounds A time window of a fixed width
* @param {TimeBounds | ClockOffsets} offsetsOrBounds A time window of a fixed width
* @fires module:openmct.TimeAPI~clock
* @return {Mode | undefined} the currently active mode
* @return {Mode} the currently active mode;
*/
setMode(mode, offsetsOrBounds) {
if (!mode) {
@ -584,6 +577,7 @@ class TimeContext extends EventEmitter {
/**
* The active mode has changed.
* @event modeChanged
* @memberof module:openmct.TimeAPI~
* @property {Mode} mode The newly activated mode
*/
this.emit(TIME_CONTEXT_EVENTS.modeChanged, this.#copy(this.mode));
@ -616,15 +610,18 @@ class TimeContext extends EventEmitter {
/**
* Get the currently applied clock offsets.
* @returns {ClockOffsets} The current clock offsets.
* @returns {ClockOffsets}
*/
getClockOffsets() {
return this.offsets;
}
/**
* Set the currently applied clock offsets.
* @param {ClockOffsets} offsets The new clock offsets to set.
* Set the currently applied clock offsets. If no parameter is provided,
* the current value will be returned. If provided, the new value will be
* used as the new clock offsets.
* @param {ClockOffsets} offsets
* @returns {ClockOffsets}
*/
setClockOffsets(offsets) {
const validationResult = this.validateOffsets(offsets);
@ -645,20 +642,13 @@ class TimeContext extends EventEmitter {
/**
* Event that is triggered when clock offsets change.
* @event clockOffsets
* @memberof module:openmct.TimeAPI~
* @property {ClockOffsets} clockOffsets The newly activated clock
* offsets.
*/
this.emit(TIME_CONTEXT_EVENTS.clockOffsetsChanged, this.#copy(offsets));
}
/**
* Prints a warning to the console when a deprecated method is used. Limits
* the number of times a warning is printed per unique method and newMethod
* combination.
* @param {string} method the deprecated method
* @param {string} [newMethod] the new method to use instead
* @returns
*/
#warnMethodDeprecated(method, newMethod) {
const MAX_CALLS = 1; // Only warn once per unique method and newMethod combination
@ -683,11 +673,6 @@ class TimeContext extends EventEmitter {
console.warn(message);
}
/**
* Deep copy an object.
* @param {object} object The object to copy
* @returns {object} The copied object
*/
#copy(object) {
return JSON.parse(JSON.stringify(object));
}

View File

@ -212,7 +212,7 @@ export default {
this.openmct.time.on('timeSystem', this.updateTimeSystem);
this.timestampKey = this.openmct.time.getTimeSystem().key;
this.timestampKey = this.openmct.time.timeSystem().key;
this.valueMetadata = undefined;

View File

@ -253,7 +253,7 @@ export default {
};
},
getOptions() {
const { start, end } = this.timeContext.getBounds();
const { start, end } = this.timeContext.bounds();
return {
end,
@ -372,13 +372,13 @@ export default {
this.setTrace(key, telemetryObject.name, axisMetadata, xValues, yValues);
},
isDataInTimeRange(datum, key, telemetryObject) {
const timeSystemKey = this.timeContext.getTimeSystem().key;
const timeSystemKey = this.timeContext.timeSystem().key;
const metadata = this.openmct.telemetry.getMetadata(telemetryObject);
let metadataValue = metadata.value(timeSystemKey) || { key: timeSystemKey };
let currentTimestamp = this.parse(key, metadataValue.key, datum);
return currentTimestamp && this.timeContext.getBounds().end >= currentTimestamp;
return currentTimestamp && this.timeContext.bounds().end >= currentTimestamp;
},
format(telemetryObjectKey, metadataKey, data) {
const formats = this.telemetryObjectFormats[telemetryObjectKey];

View File

@ -306,7 +306,7 @@ export default {
this.trace = [trace];
},
getTimestampForDatum(datum, key, telemetryObject) {
const timeSystemKey = this.timeContext.getTimeSystem().key;
const timeSystemKey = this.timeContext.timeSystem().key;
const metadata = this.openmct.telemetry.getMetadata(telemetryObject);
let metadataValue = metadata.value(timeSystemKey) || { format: timeSystemKey };
@ -327,7 +327,7 @@ export default {
return formats[metadataKey].parse(datum);
},
getOptions() {
const { start, end } = this.timeContext.getBounds();
const { start, end } = this.timeContext.bounds();
return {
end,

View File

@ -245,7 +245,7 @@ export default class Condition extends EventEmitter {
latestTimestamp,
updatedCriterion.data,
this.timeSystems,
this.openmct.time.getTimeSystem()
this.openmct.time.timeSystem()
);
this.conditionManager.updateCurrentCondition(latestTimestamp);
}
@ -309,7 +309,7 @@ export default class Condition extends EventEmitter {
latestTimestamp,
data,
this.timeSystems,
this.openmct.time.getTimeSystem()
this.openmct.time.timeSystem()
);
});

View File

@ -113,7 +113,7 @@ export default class ConditionManager extends EventEmitter {
{},
{},
this.timeSystems,
this.openmct.time.getTimeSystem()
this.openmct.time.timeSystem()
);
this.updateConditionResults({ id: id });
this.updateCurrentCondition(latestTimestamp);
@ -383,7 +383,7 @@ export default class ConditionManager extends EventEmitter {
latestTimestamp,
data,
this.timeSystems,
this.openmct.time.getTimeSystem()
this.openmct.time.timeSystem()
);
});

View File

@ -227,7 +227,7 @@ export default class AllTelemetryCriterion extends TelemetryCriterion {
return Promise.all(telemetryRequests).then((telemetryRequestsResults) => {
let latestTimestamp;
const timeSystems = this.openmct.time.getAllTimeSystems();
const timeSystem = this.openmct.time.getTimeSystem();
const timeSystem = this.openmct.time.timeSystem();
telemetryRequestsResults.forEach((results, index) => {
const latestDatum =

View File

@ -280,7 +280,7 @@ export default {
await this.$nextTick();
},
formattedValueForCopy() {
const timeFormatterKey = this.openmct.time.getTimeSystem().key;
const timeFormatterKey = this.openmct.time.timeSystem().key;
const timeFormatter = this.formats[timeFormatterKey];
const unit = this.unit ? ` ${this.unit}` : '';

View File

@ -363,7 +363,7 @@ export default {
rangeLow: gaugeController.min,
gaugeType: gaugeController.gaugeType,
showUnits: gaugeController.showUnits,
activeTimeSystem: this.openmct.time.getTimeSystem(),
activeTimeSystem: this.openmct.time.timeSystem(),
units: ''
};
},
@ -726,7 +726,7 @@ export default {
return;
}
const { start, end } = this.openmct.time.getBounds();
const { start, end } = this.openmct.time.bounds();
const parsedValue = this.timeFormatter.parse(this.datum);
const beforeStartOfBounds = parsedValue < start;

View File

@ -49,7 +49,7 @@ export default {
mixins: [imageryData],
inject: ['openmct', 'domainObject', 'objectPath'],
data() {
let timeSystem = this.openmct.time.getTimeSystem();
let timeSystem = this.openmct.time.timeSystem();
this.metadata = {};
this.requestCount = 0;
@ -148,10 +148,10 @@ export default {
return clientWidth;
},
updateViewBounds(bounds, isTick) {
this.viewBounds = this.timeContext.getBounds();
this.viewBounds = this.timeContext.bounds();
if (this.timeSystem === undefined) {
this.timeSystem = this.timeContext.getTimeSystem();
this.timeSystem = this.timeContext.timeSystem();
}
this.setScaleAndPlotImagery(this.timeSystem, !isTick);
@ -216,7 +216,7 @@ export default {
}
if (timeSystem === undefined) {
timeSystem = this.timeContext.getTimeSystem();
timeSystem = this.timeContext.timeSystem();
}
if (timeSystem.isUTCBased) {

View File

@ -44,7 +44,7 @@ export default class RelatedTelemetry {
this.keys = telemetryKeys;
this._timeFormatter = undefined;
this._timeSystemChange(this.timeContext.getTimeSystem());
this._timeSystemChange(this.timeContext.timeSystem());
// grab related telemetry metadata
for (let key of this.keys) {
@ -110,10 +110,10 @@ export default class RelatedTelemetry {
// and set bounds.
ephemeralContext.resetContext();
const newBounds = {
start: this.timeContext.getBounds().start,
start: this.timeContext.bounds().start,
end: this._parseTime(datum)
};
ephemeralContext.setBounds(newBounds);
ephemeralContext.bounds(newBounds);
const options = {
start: newBounds.start,

View File

@ -171,7 +171,7 @@ export default {
this.bounds = bounds; // setting bounds for ImageryView watcher
},
timeSystemChanged() {
this.timeSystem = this.timeContext.getTimeSystem();
this.timeSystem = this.timeContext.timeSystem();
this.timeKey = this.timeSystem.key;
this.timeFormatter = this.getFormatter(this.timeKey);
this.durationFormatter = this.getFormatter(

View File

@ -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', () => {

View File

@ -680,7 +680,7 @@ export default {
} else if (domainObjectData) {
// plain domain object
const objectPath = JSON.parse(domainObjectData);
const bounds = this.openmct.time.getBounds();
const bounds = this.openmct.time.bounds();
const snapshotMeta = {
bounds,
link: null,

View File

@ -275,7 +275,7 @@ export default {
}
const hash = this.embed.historicLink;
const bounds = this.openmct.time.getBounds();
const bounds = this.openmct.time.bounds();
const isTimeBoundChanged =
this.embed.bounds.start !== bounds.start || this.embed.bounds.end !== bounds.end;
const isFixedTimespanMode = !this.openmct.time.clock();

View File

@ -369,7 +369,7 @@ export default {
},
methods: {
async addNewEmbed(objectPath) {
const bounds = this.openmct.time.getBounds();
const bounds = this.openmct.time.bounds();
const snapshotMeta = {
bounds,
link: null,

View File

@ -123,7 +123,7 @@ export default {
const objectPath = this.objectPath || this.openmct.router.path;
const link = this.isPreview ? this.getPreviewObjectLink() : window.location.hash;
const snapshotMeta = {
bounds: this.openmct.time.getBounds(),
bounds: this.openmct.time.bounds(),
link,
objectPath,
openmct: this.openmct

View File

@ -140,7 +140,7 @@ export function createNewImageEmbed(image, openmct, imageName = '') {
};
const embedMetaData = {
bounds: openmct.time.getBounds(),
bounds: openmct.time.bounds(),
link: null,
objectPath: null,
openmct,

View File

@ -46,7 +46,7 @@ export default class PainterroInstance {
this.config.id = this.elementId;
this.config.saveHandler = this.saveHandler.bind(this);
this.painterro = Painterro(this.config);
this.painterro = Painterro.default(this.config);
}
save(callback) {

View File

@ -710,7 +710,7 @@ class CouchObjectProvider {
this.objectQueue[key].pending = true;
const queued = this.objectQueue[key].dequeue();
let couchDocument = new CouchDocument(key, queued.model);
couchDocument.metadata.created = Date.now();
this.#enqueueForPersistence({
key,
document: couchDocument

View File

@ -196,7 +196,7 @@ export default {
this.followTimeContext();
},
followTimeContext() {
this.updateViewBounds(this.timeContext.getBounds());
this.updateViewBounds(this.timeContext.bounds());
this.timeContext.on('timeSystem', this.setScaleAndGenerateActivities);
this.timeContext.on('bounds', this.updateViewBounds);
@ -319,7 +319,7 @@ export default {
}
if (this.timeSystem === null) {
this.timeSystem = this.openmct.time.getTimeSystem();
this.timeSystem = this.openmct.time.timeSystem();
}
this.setScaleAndGenerateActivities();
@ -344,7 +344,7 @@ export default {
}
if (!timeSystem) {
timeSystem = this.openmct.time.getTimeSystem();
timeSystem = this.openmct.time.timeSystem();
}
if (timeSystem.isUTCBased) {

View File

@ -116,7 +116,7 @@ export default {
}
},
setFormatters() {
let timeSystem = this.openmct.time.getTimeSystem();
let timeSystem = this.openmct.time.timeSystem();
this.timeFormatter = this.openmct.telemetry.getValueFormatter({
format: timeSystem.timeFormat
}).formatter;

View File

@ -661,7 +661,7 @@ export default {
this.offsetWidth = this.$parent.$refs.plotWrapper.offsetWidth;
this.startLoading();
const bounds = this.timeContext.getBounds();
const bounds = this.timeContext.bounds();
const options = {
size: this.$parent.$refs.plotWrapper.offsetWidth,
domain: this.config.xAxis.get('key'),

View File

@ -614,7 +614,7 @@ export default {
const yAxisId = series.get('yAxisId') || mainYAxisId;
let offset = this.offset[yAxisId];
return new MCTChartAlarmLineSet(series, this, offset, this.openmct.time.getBounds());
return new MCTChartAlarmLineSet(series, this, offset, this.openmct.time.bounds());
},
pointSetForSeries(series) {
const mainYAxisId = this.config.yAxis.get('id');

View File

@ -93,7 +93,7 @@ export default class XAxisModel extends Model {
* @override
*/
defaultModel(options) {
const bounds = options.openmct.time.getBounds();
const bounds = options.openmct.time.bounds();
const timeSystem = options.openmct.time.getTimeSystem();
const format = options.openmct.telemetry.getFormatter(timeSystem.timeFormat);

View File

@ -134,7 +134,7 @@ export default class RemoteClock extends DefaultClock {
* @private
*/
_timeSystemChange() {
let timeSystem = this.openmct.time.getTimeSystem();
let timeSystem = this.openmct.time.timeSystem();
let timeKey = timeSystem.key;
let metadataValue = this.metadata.value(timeKey);
let timeFormatter = this.openmct.telemetry.getValueFormatter(metadataValue);
@ -149,11 +149,13 @@ export default class RemoteClock extends DefaultClock {
/**
* Waits for the clock to have a non-default tick value.
*
* @private
*/
#waitForReady() {
const waitForInitialTick = (resolve) => {
if (this.lastTick > 0) {
const offsets = this.openmct.time.getClockOffsets();
const offsets = this.openmct.time.clockOffsets();
resolve({
start: this.lastTick + offsets.start,
end: this.lastTick + offsets.end

View File

@ -62,7 +62,7 @@ SummaryWidgetEvaluator.prototype.subscribe = function (callback) {
}
const updateCallback = function () {
const datum = this.evaluateState(realtimeStates, this.openmct.time.getTimeSystem().key);
const datum = this.evaluateState(realtimeStates, this.openmct.time.timeSystem().key);
if (datum) {
callback(datum);
}

View File

@ -611,11 +611,11 @@ describe('The Mean Telemetry Provider', function () {
}
function createMockTimeApi() {
return jasmine.createSpyObj('timeApi', ['getTimeSystem', 'setTimeSystem']);
return jasmine.createSpyObj('timeApi', ['timeSystem']);
}
function setTimeSystemTo(timeSystemKey) {
mockApi.time.getTimeSystem.and.returnValue({
mockApi.time.timeSystem.and.returnValue({
key: timeSystemKey
});
}

View File

@ -92,7 +92,7 @@ TelemetryAverager.prototype.calculateMean = function () {
* @private
*/
TelemetryAverager.prototype.setDomainKeyAndFormatter = function () {
const domainKey = this.timeAPI.getTimeSystem().key;
const domainKey = this.timeAPI.timeSystem().key;
if (domainKey !== this.domainKey) {
this.domainKey = domainKey;
this.domainFormatter = this.getFormatter(domainKey);

View File

@ -134,7 +134,7 @@ export default class TelemetryTable extends EventEmitter {
//If no persisted sort order, default to sorting by time system, descending.
sortOptions = sortOptions || {
key: this.openmct.time.getTimeSystem().key,
key: this.openmct.time.timeSystem().key,
direction: 'desc'
};
@ -171,10 +171,6 @@ export default class TelemetryTable extends EventEmitter {
this.removeTelemetryCollection(keyString);
let sortOptions = this.configuration.getConfiguration().sortOptions;
requestOptions.order =
sortOptions?.direction ?? (this.telemetryMode === 'performance' ? 'desc' : 'asc');
if (this.telemetryMode === 'performance') {
requestOptions.size = this.rowLimit;
requestOptions.enforceSize = true;

View File

@ -40,8 +40,6 @@ export default class TelemetryTableConfiguration extends EventEmitter {
'configuration',
this.objectMutated
);
this.notPersistable = !this.openmct.objects.isPersistable(this.domainObject.identifier);
}
getConfiguration() {
@ -54,19 +52,14 @@ export default class TelemetryTableConfiguration extends EventEmitter {
// anything that doesn't have a telemetryMode existed before the change and should
// take the properties of any passed in defaults or the defaults from the plugin
configuration.telemetryMode = configuration.telemetryMode ?? this.defaultOptions.telemetryMode;
configuration.persistModeChange = this.notPersistable
? false
: configuration.persistModeChange ?? this.defaultOptions.persistModeChange;
configuration.persistModeChange =
configuration.persistModeChange ?? this.defaultOptions.persistModeChange;
configuration.rowLimit = configuration.rowLimit ?? this.defaultOptions.rowLimit;
return configuration;
}
updateConfiguration(configuration) {
if (this.notPersistable) {
return;
}
this.openmct.objects.mutate(this.domainObject, 'configuration', configuration);
}

View File

@ -141,6 +141,7 @@ export default {
data() {
const bounds = this.openmct.time.getBounds();
const timeSystem = this.openmct.time.getTimeSystem();
// const isFixed = this.openmct.time.isFixed();
return {
timeSystem,

View File

@ -173,16 +173,16 @@ export default {
});
},
getBoundsForTimeSystem(timeSystem) {
const currentBounds = this.timeContext.getBounds();
const currentBounds = this.timeContext.bounds();
//TODO: Some kind of translation via an offset? of current bounds to target timeSystem
return currentBounds;
},
updateViewBounds() {
const bounds = this.timeContext.getBounds();
const bounds = this.timeContext.bounds();
this.updateContentHeight();
let currentTimeSystemIndex = this.timeSystems.findIndex(
(item) => item.timeSystem.key === this.openmct.time.getTimeSystem().key
(item) => item.timeSystem.key === this.openmct.time.timeSystem().key
);
if (currentTimeSystemIndex > -1) {
let currentTimeSystem = {

View File

@ -97,7 +97,7 @@ const headerItems = [
property: 'start',
name: 'Start Time',
format: function (value, object, key, openmct, options = {}) {
const timeFormat = openmct.time.getTimeSystem().timeFormat;
const timeFormat = openmct.time.timeSystem().timeFormat;
const timeFormatter = openmct.telemetry.getValueFormatter({ format: timeFormat }).formatter;
if (options.skipDateForToday) {
return timeFormatter.format(value, SAME_DAY_PRECISION_SECONDS);
@ -112,7 +112,7 @@ const headerItems = [
property: 'end',
name: 'End Time',
format: function (value, object, key, openmct, options = {}) {
const timeFormat = openmct.time.getTimeSystem().timeFormat;
const timeFormat = openmct.time.timeSystem().timeFormat;
const timeFormatter = openmct.telemetry.getValueFormatter({ format: timeFormat }).formatter;
if (options.skipDateForToday) {
return timeFormatter.format(value, SAME_DAY_PRECISION_SECONDS);
@ -425,14 +425,14 @@ export default {
},
isActivityInBounds(activity) {
const startInBounds =
activity.start >= this.timeContext.getBounds()?.start &&
activity.start <= this.timeContext.getBounds()?.end;
activity.start >= this.timeContext.bounds()?.start &&
activity.start <= this.timeContext.bounds()?.end;
const endInBounds =
activity.end >= this.timeContext.getBounds()?.start &&
activity.end <= this.timeContext.getBounds()?.end;
activity.end >= this.timeContext.bounds()?.start &&
activity.end <= this.timeContext.bounds()?.end;
const middleInBounds =
activity.start <= this.timeContext.getBounds()?.start &&
activity.end >= this.timeContext.getBounds()?.end;
activity.start <= this.timeContext.bounds()?.start &&
activity.end >= this.timeContext.bounds()?.end;
return startInBounds || endInBounds || middleInBounds;
},

View File

@ -33,7 +33,7 @@ $tabletItemH: floor(math.div($gridItemMobile, 3));
$shellTimeConductorMobileH: 90px;
/************************** MOBILE TREE MENU DIMENSIONS */
$mobileTreeItemH: 35px;
$mobileTreeItemH: 30px;
$mobileTreeItemIndent: 15px;
$mobileTreeRightArrowW: 30px;

View File

@ -97,7 +97,7 @@
></button>
</template>
<multipane type="vertical">
<pane>
<pane class="l-pane__browse-tree">
<mct-tree
ref="mctTree"
:sync-tree-navigation="triggerSync"
@ -107,6 +107,7 @@
</pane>
<pane
handle="before"
class="l-pane__recently-viewed"
label="Recently Viewed"
:persist-position="true"
collapse-type="horizontal"

View File

@ -26,30 +26,33 @@
:class="isAlias"
:aria-label="`${domainObject.name}`"
>
<div
class="c-recentobjects-listitem__type-icon recent-object-icon"
:class="resultTypeIcon"
></div>
<div class="c-recentobjects-listitem__body">
<span
ref="recentObjectName"
class="c-recentobjects-listitem__title"
:name="domainObject.name"
draggable="true"
@dragstart="dragStart"
@click.prevent="clickedRecent"
@mouseover.ctrl="showToolTip"
@mouseleave="hideToolTip"
>
{{ domainObject.name }}
</span>
<ObjectPath
class="c-recentobjects-listitem__object-path"
:read-only="false"
:domain-object="domainObject"
:object-path="objectPath"
/>
<div class="c-recentobjects-listitem__outerwrapper">
<div class="c-recentobjects-listitem__wrapper">
<div
class="c-recentobjects-listitem__type-icon recent-object-icon"
:class="resultTypeIcon"
></div>
<span
ref="recentObjectName"
class="c-recentobjects-listitem__title"
:name="domainObject.name"
draggable="true"
@dragstart="dragStart"
@click.prevent="clickedRecent"
@mouseover.ctrl="showToolTip"
@mouseleave="hideToolTip"
>
{{ domainObject.name }}
</span>
</div>
<div class="c-recentobjects-listitem__object-path">
<ObjectPath
class="c-recentobjects-listitem__object-path"
:read-only="false"
:domain-object="domainObject"
:object-path="objectPath"
/>
</div>
</div>
<div class="c-recentobjects-listitem__target-button">
<button

View File

@ -133,10 +133,9 @@
&__pane-tree {
background: linear-gradient(90deg, transparent 70%, rgba(black, 0.2) 99%, rgba(black, 0.3));
.l-pane__header {
// Hide all buttons except the collapse button
> :not(.l-pane__collapse-button) {
display: none;
.l-pane__header {
> :not(.l-pane__collapse-button) { // On default, show all the header icons.
display: block;
}
}
@ -148,6 +147,11 @@
[class*='collapse-button'] {
right: -8px;
}
.l-pane__header{ // If pane is collapsed, hide all the icons except the collapse button, which gets replaced into the hamburger menu.
> :not(.l-pane__collapse-button) {
display: none;
}
}
}
}
}

View File

@ -2,89 +2,101 @@
/**************************** BASE - MOBILE AND DESKTOP */
.l-multipane {
display: flex;
flex: 1 1 auto;
overflow: hidden;
display: flex;
flex: 1 1 auto;
overflow: hidden;
&--horizontal,
> .l-pane {
flex-flow: row nowrap;
}
&--horizontal,
> .l-pane {
flex-flow: row nowrap;
}
&--vertical,
> .l-pane {
flex-flow: column nowrap;
}
&--vertical,
> .l-pane {
flex-flow: column nowrap;
}
&--vertical {
height: 100%;
}
&--vertical {
height: 100%;
body.mobile & {
gap: $interiorMargin;
}
> .l-pane .l-pane__contents {
padding-right: $interiorMarginSm; // Fend off scrollbar
}
}
}
.l-pane {
backface-visibility: hidden;
display: flex;
min-width: 0px;
min-height: 0px;
opacity: 1;
pointer-events: inherit;
&__handle,
&__label {
// __handle and __label don't appear in mobile
display: none;
}
&__header {
backface-visibility: hidden;
display: flex;
align-items: center;
@include desktop() {
margin-bottom: $interiorMargin;
min-width: 0px;
min-height: 0px;
opacity: 1;
pointer-events: inherit;
&__handle,
&__label {
// __handle and __label don't appear in mobile
display: none;
}
}
&--reacts {
// This is the pane that doesn't hold the handle
// It reacts to other panes that are able to resize
flex: 1 1 0;
}
&--collapsed {
flex-basis: 0px !important;
transition: all 350ms ease;
.l-pane__contents {
transition: opacity 150ms ease;
opacity: 0;
pointer-events: none;
overflow: hidden; // Prevents toolbar from extending into Inspector
> * {
min-width: 0 !important;
min-height: 0 !important;
}
&__header {
display: flex;
align-items: center;
@include desktop() {
margin-bottom: $interiorMargin;
}
}
}
&[class*='--horizontal'] {
padding-left: $interiorMargin;
padding-right: $interiorMargin;
&.l-pane--collapsed {
padding-left: 0 !important;
padding-right: 0 !important;
&--reacts {
// This is the pane that doesn't hold the handle
// It reacts to other panes that are able to resize
flex: 1 1 0;
}
}
&[class*='--vertical'] {
padding-top: $interiorMargin;
padding-bottom: $interiorMargin;
min-height: 30px; // For Recents holder
&--collapsed {
flex-basis: 0px !important;
transition: all 350ms ease;
&.l-pane--collapsed {
padding-top: 0 !important;
padding-bottom: 0 !important;
.l-pane__contents {
transition: opacity 150ms ease;
opacity: 0;
pointer-events: none;
overflow: hidden; // Prevents toolbar from extending into Inspector
> * {
min-width: 0 !important;
min-height: 0 !important;
}
}
}
&[class*='--horizontal'] {
padding-left: $interiorMargin;
padding-right: $interiorMargin;
&.l-pane--collapsed {
padding-left: 0 !important;
padding-right: 0 !important;
}
}
&[class*='--vertical'] {
padding-top: $interiorMargin;
padding-bottom: $interiorMargin;
min-height: 30px; // For Recents holder
body.mobile & {
border-top: 2px solid $colorInteriorBorder; // Adds non-resizable splitter
}
&.l-pane--collapsed {
padding-top: 0 !important;
padding-bottom: 0 !important;
}
}
}
/************************ CONTENTS */
&__contents {
@ -95,269 +107,303 @@
pointer-events: inherit;
transition: opacity 250ms ease 250ms;
.l-pane__contents {
// Don't pad all nested __contents
padding: 0;
}
}
/************************************************ DESKTOP STYLES */
body.desktop & {
&__handle {
background-color: $colorSplitterBg;
display: block;
position: absolute;
@include transition(background-color, $transOutTime);
&:before {
// Extended hit area
content: '';
display: block;
position: absolute;
z-index: -1;
}
&:hover {
background-color: $colorSplitterHover;
@include transition(background-color);
}
.l-pane__contents {
// Don't pad all nested __contents
padding: 0;
}
}
&__header {
font-size: 11px;
}
&__label {
// Name of the pane
@include ellipsize();
@include userSelectNone();
color: $splitterBtnLabelColorFg;
display: block;
text-transform: uppercase;
flex: 1 1 auto;
}
[class*='expand-button'] {
display: none; // Hidden by default
background-color: $splitterCollapsedBtnColorBg;
color: $splitterCollapsedBtnColorFg;
&:before {
// '+' icon
font-size: 0.8em;
margin-bottom: $interiorMarginSm; // margin-bottom is needed for Tree and Inspector
margin-right: $interiorMarginSm; // margin-right and margin-left are needed for Recent Objects
margin-left: $interiorMarginSm;
}
&:hover {
background-color: $splitterCollapsedBtnColorBgHov;
color: $splitterCollapsedBtnColorFgHov;
@include transition(background-color);
}
}
&--resizing {
// User is dragging the handle and resizing a pane
@include userSelectNone();
+ .l-pane {
@include userSelectNone();
}
.l-pane {
/************************************************ DESKTOP STYLES */
body.desktop & {
&__handle {
background-color: $colorSplitterHover;
}
}
}
background-color: $colorSplitterBg;
display: block;
position: absolute;
@include transition(background-color, $transOutTime);
&[class*='--collapsed'] {
/********************************* STYLES FOR DESKTOP COLLAPSED PANES, ALL ORIENTATIONS */
$d: nth($splitterBtnD, 1);
flex-basis: $d;
min-width: $d;
min-height: $d;
&:before {
// Extended hit area
content: '';
display: block;
position: absolute;
z-index: -1;
}
> .l-pane__handle {
display: none;
}
[class*='collapse-button'] {
display: none;
}
[class*='expand-button'] {
display: block;
}
}
&[class*='--collapsed'] { // For Recent Objects Button
&.collapse-horizontal {
[class*='expand-button'] {
display: block;
position: absolute;
top: 0;
width: 100%;
border-top-right-radius: $controlCr;
border-top-left-radius: $controlCr;
}
}
[class*='expand-button'] {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
height: auto;
width: 100%;
padding: $interiorMarginSm 1px;
font-size: 11px;
[class*='label'] {
text-orientation: mixed;
text-transform: uppercase;
writing-mode: horizontal-tb;
}
}
}
&[class*='--horizontal'] {
> .l-pane__handle {
cursor: col-resize;
top: 0;
bottom: 0;
width: $splitterHandleD;
&:before {
// Extended hit area
top: 0;
right: $splitterHandleHitMargin * -1;
bottom: 0;
left: $splitterHandleHitMargin * -1;
}
}
.l-pane__collapse-button {
&:before {
content: $glyph-icon-line-horz;
}
}
&[class*='--collapsed'] {
/************************ COLLAPSED HORIZONTAL SPLITTER, EITHER DIRECTION */
[class*='__header'] {
display: none;
&:hover {
background-color: $colorSplitterHover;
@include transition(background-color);
}
}
[class*='expand-button'] {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
height: auto;
width: 100%;
padding: $interiorMargin 1px;
font-size: 11px;
&__header {
font-size: 11px;
}
[class*='label'] {
text-orientation: mixed;
&__label {
// Name of the pane
@include ellipsize();
@include userSelectNone();
color: $splitterBtnLabelColorFg;
display: block;
text-transform: uppercase;
writing-mode: vertical-lr;
}
}
}
/************************** Horizontal Splitter Before */
// Example: Inspector pane
&[class*='-before'] {
margin-left: nth($shellPanePad, 2);
padding-left: nth($shellPanePad, 2);
> .l-pane__handle {
left: 0;
transform: translateX(floor(math.div($splitterHandleD, -2))); // Center over the pane edge
flex: 1 1 auto;
}
[class*='expand-button'] {
border-top-left-radius: $controlCr;
border-bottom-left-radius: $controlCr;
}
}
display: none; // Hidden by default
background-color: $splitterCollapsedBtnColorBg;
color: $splitterCollapsedBtnColorFg;
/************************** Horizontal Splitter After */
// Example: Tree pane and Recent Objects
&[class*='-after'] {
margin-right: nth($shellPanePad, 2);
padding-right: nth($shellPanePad, 2);
&:before {
// '+' icon
font-size: 0.8em;
margin-bottom: $interiorMarginSm; // margin-bottom is needed for Tree and Inspector
margin-right: $interiorMarginSm; // margin-right and margin-left are needed for Recent Objects
}
> .l-pane__handle {
right: 0;
transform: translateX(floor(math.div($splitterHandleD, 2)));
&:hover {
background-color: $splitterCollapsedBtnColorBgHov;
color: $splitterCollapsedBtnColorFgHov;
@include transition(background-color);
}
}
[class*='expand-button'] {
border-top-right-radius: $controlCr;
border-bottom-right-radius: $controlCr;
&--resizing {
// User is dragging the handle and resizing a pane
@include userSelectNone();
+ .l-pane {
@include userSelectNone();
}
.l-pane {
&__handle {
background-color: $colorSplitterHover;
}
}
}
&[class*='--collapsed'] {
/********************************* STYLES FOR DESKTOP COLLAPSED PANES, ALL ORIENTATIONS */
$d: nth($splitterBtnD, 1);
flex-basis: $d;
min-width: $d;
min-height: $d;
> .l-pane__handle {
display: none;
}
[class*='collapse-button'] {
display: none;
}
[class*='expand-button'] {
display: block;
}
}
&[class*='--collapsed'] { // For Recent Objects Button
&.collapse-horizontal {
[class*='expand-button'] {
display: flex;
align-items: center;
position: absolute;
top: 0;
width: 100%;
border-top-right-radius: $controlCr;
border-top-left-radius: $controlCr;
border-bottom-right-radius: $controlCr;
border-bottom-left-radius: $controlCr;
padding: $interiorMarginSm $interiorMargin;
&:before {
font-size: 0.7em;
margin-bottom: 0px;
}
}
}
[class*='expand-button'] {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
height: auto;
width: 100%;
padding: $interiorMarginSm 1px;
font-size: 11px;
[class*='label'] {
text-orientation: mixed;
text-transform: uppercase;
writing-mode: horizontal-tb;
}
}
}
&[class*='--horizontal'] {
> .l-pane__handle {
cursor: col-resize;
top: 0;
bottom: 0;
width: $splitterHandleD;
&:before {
// Extended hit area
top: 0;
right: $splitterHandleHitMargin * -1;
bottom: 0;
left: $splitterHandleHitMargin * -1;
}
}
.l-pane__collapse-button {
&:before {
content: $glyph-icon-line-horz;
}
}
&[class*='--collapsed'] {
/************************ COLLAPSED HORIZONTAL SPLITTER, EITHER DIRECTION */
[class*='__header'] {
display: none;
}
[class*='expand-button'] {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
height: auto;
width: 100%;
padding: $interiorMargin 1px;
font-size: 11px;
[class*='label'] {
text-orientation: mixed;
text-transform: uppercase;
writing-mode: vertical-lr;
}
}
}
/************************** Horizontal Splitter Before */
// Example: Inspector pane
&[class*='-before'] {
margin-left: nth($shellPanePad, 2);
padding-left: nth($shellPanePad, 2);
> .l-pane__handle {
left: 0;
transform: translateX(floor(math.div($splitterHandleD, -2))); // Center over the pane edge
}
[class*='expand-button'] {
border-top-left-radius: $controlCr;
border-bottom-left-radius: $controlCr;
}
}
/************************** Horizontal Splitter After */
// Example: Tree pane and Recent Objects
&[class*='-after'] {
margin-right: nth($shellPanePad, 2);
padding-right: nth($shellPanePad, 2);
> .l-pane__handle {
right: 0;
transform: translateX(floor(math.div($splitterHandleD, 2)));
}
[class*='expand-button'] {
border-top-right-radius: $controlCr;
border-bottom-right-radius: $controlCr;
}
}
}
&[class*='--vertical'] {
// l-pane--vertical
> .l-pane__handle {
cursor: row-resize;
left: 0;
right: 0;
height: $splitterHandleD;
&:before {
// Extended hit area
left: 0;
top: $splitterHandleHitMargin * -1;
right: 0;
bottom: $splitterHandleHitMargin * -1;
}
}
/************************** Vertical Splitter Before */
// Pane collapses downward. Used by Recent Objects in Tree
&[class*='-before'] {
$m: $interiorMarginLg;
margin-top: $m;
padding-top: $m;
padding-bottom: 0;
> .l-pane__handle {
top: 0;
transform: translateY(floor(math.div($splitterHandleD, -1)));
}
.l-pane__collapse-button:before {
content: $glyph-icon-line-horz;
}
&.l-pane--collapsed {
> .l-pane__collapse-button {
transform: scaleY(-1);
}
}
}
/************************** Vertical Splitter After */
// Pane collapses upward. Not sure we'll ever use this...
&[class*='-after'] {
> .l-pane__handle {
bottom: 0;
transform: translateY(floor(math.div($splitterHandleD, 1)));
}
&:not(.l-pane--collapsed) > .l-pane__collapse-button {
&:after {
transform: scaleY(-1);
}
}
}
}
}
}
&[class*='--vertical'] {
// l-pane--vertical
// Ends .body.desktop
}
> .l-pane__handle {
cursor: row-resize;
left: 0;
right: 0;
height: $splitterHandleD;
// Ends .l-pane
&:before {
// Extended hit area
left: 0;
top: $splitterHandleHitMargin * -1;
right: 0;
bottom: $splitterHandleHitMargin * -1;
}
}
/************************** Vertical Splitter Before */
// Pane collapses downward. Used by Recent Objects in Tree
&[class*='-before'] {
$m: $interiorMarginLg;
margin-top: $m;
padding-top: $m;
> .l-pane__handle {
top: 0;
transform: translateY(floor(math.div($splitterHandleD, -1)));
body.mobile {
.l-pane__header {
> * {
@include ellipsize();
@include userSelectNone();
text-transform: uppercase;
}
.l-pane__collapse-button:before {
content: $glyph-icon-line-horz;
.l-pane__label {
margin-right: auto;
}
&.l-pane--collapsed {
> .l-pane__collapse-button {
transform: scaleY(-1);
}
}
}
/************************** Vertical Splitter After */
// Pane collapses upward. Not sure we'll ever use this...
&[class*='-after'] {
> .l-pane__handle {
bottom: 0;
transform: translateY(floor(math.div($splitterHandleD, 1)));
}
&:not(.l-pane--collapsed) > .l-pane__collapse-button {
&:after {
transform: scaleY(-1);
}
}
}
}
} // Ends .body.desktop
} // Ends .l-pane
.l-pane__recently-viewed {
height: 50% !important;
max-height: 200px;
}
}

View File

@ -24,7 +24,7 @@
display: flex;
padding: $interiorMargin $interiorMarginSm;
align-items: flex-start;
> * + * {
margin-left: $interiorMarginSm;
}
@ -43,12 +43,23 @@
}
}
&__outerwrapper{
display: flex;
flex-direction: column;
width: 100%;
}
&__wrapper{
display: flex;
}
&__object-path {
padding: 0 $interiorMarginSm;
padding: 0 $interiorMarginLg;
}
&__target-button {
opacity: 0;
margin-left: auto;
}
&__type-icon,
@ -59,6 +70,11 @@
&__type-icon {
color: $colorItemTreeIcon;
font-size: 1.25em;
height: 100%; // When pane is small, this avoids the alias icons from wrapping down
min-width: $treeTypeIconW;
body.mobile &{
height: auto;
}
// TEMP: uses object-label component, hide label part
.c-object-label__name {
@ -102,7 +118,8 @@
border-radius: $basicCr;
color: $colorItemTreeFg;
cursor: pointer;
padding: $interiorMarginSm;
padding: 2px $interiorMarginSm;
margin-left: $interiorMarginSm;
&:hover {
background-color: $colorItemTreeHoverBg;
@ -117,3 +134,22 @@
.c-recentobjects-listitem:hover .c-recentobjects-listitem__target-button {
opacity: 100;
}
body.mobile {
.c-recentobjects-listitem__target-button{
display: none;
}
.c-recentobjects-listitem__wrapper{
height: $mobileTreeItemH;
align-items: center;
margin-bottom: $interiorMarginSm;
background: rgba(172, 172, 172, 0.1);
border-radius: $interiorMarginSm;
}
.c-recentobjects-listitem{
border-top: none;
}
.c-recentobjects-listitem__type-icon{
margin-left: $interiorMargin;
}
}

View File

@ -33,7 +33,7 @@ export default class StalenessUtils {
shouldUpdateStaleness(stalenessResponse, id) {
const stalenessResponseTime = this.parseTime(stalenessResponse);
const { start } = this.openmct.time.getBounds();
const { start } = this.openmct.time.bounds();
const isStalenessInCurrentClock = stalenessResponseTime > start;
if (stalenessResponseTime > this.lastStalenessResponseTime && isStalenessInCurrentClock) {

View File

@ -16,22 +16,14 @@
"noImplicitAny": false,
"module": "esnext",
"moduleResolution": "node",
"outFile": "dist/types/index.d.ts",
"outDir": "dist",
"skipLibCheck": true,
"target": "ES2015",
"paths": {
// matches the alias in webpack config, so that types for those imports are visible.
"@/*": [
"src/*"
]
"@/*": ["src/*"]
}
},
"include": [
"src/api/**/*.js"
],
"exclude": [
"node_modules",
"dist",
"**/*Spec.js"
]
}
"include": ["src/api/**/*.js"],
"exclude": ["node_modules", "dist", "**/*Spec.js"]
}