Compare commits
9 Commits
fix-github
...
playwright
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b02d063b8b | ||
|
|
079956d7ae | ||
|
|
c81c5f7c25 | ||
|
|
986083f3c7 | ||
|
|
cb7c9fd4f9 | ||
|
|
8b8f5c4df4 | ||
|
|
80fc393b54 | ||
|
|
58138e8554 | ||
|
|
54bed23267 |
@@ -2,7 +2,7 @@ version: 2.1
|
||||
executors:
|
||||
pw-focal-development:
|
||||
docker:
|
||||
- image: mcr.microsoft.com/playwright:v1.19.2-focal
|
||||
- image: mcr.microsoft.com/playwright:v1.19.1-focal
|
||||
environment:
|
||||
NODE_ENV: development # Needed to ensure 'dist' folder created and devDependencies installed
|
||||
parameters:
|
||||
@@ -21,7 +21,7 @@ commands:
|
||||
- restore_cache_cmd:
|
||||
node-version: << parameters.node-version >>
|
||||
- node/install:
|
||||
install-npm: true
|
||||
install-npm: false
|
||||
node-version: << parameters.node-version >>
|
||||
- run: npm install
|
||||
restore_cache_cmd:
|
||||
@@ -47,6 +47,7 @@ commands:
|
||||
paths:
|
||||
- ~/.npm
|
||||
- node_modules
|
||||
- ~/.cache/ms-playwright
|
||||
generate_and_store_version_and_filesystem_artifacts:
|
||||
description: "Track important packages and files"
|
||||
steps:
|
||||
@@ -64,7 +65,6 @@ commands:
|
||||
- run: curl -Os https://uploader.codecov.io/latest/linux/codecov;chmod +x codecov;./codecov
|
||||
orbs:
|
||||
node: circleci/node@4.9.0
|
||||
browser-tools: circleci/browser-tools@1.2.3
|
||||
jobs:
|
||||
npm-audit:
|
||||
parameters:
|
||||
@@ -96,26 +96,9 @@ jobs:
|
||||
steps:
|
||||
- build_and_install:
|
||||
node-version: <<parameters.node-version>>
|
||||
- when:
|
||||
condition:
|
||||
equal: [ "FirefoxESR", <<parameters.browser>> ]
|
||||
steps:
|
||||
- browser-tools/install-firefox:
|
||||
version: "91.7.1esr" #https://archive.mozilla.org/pub/firefox/releases/
|
||||
- when:
|
||||
condition:
|
||||
equal: [ "FirefoxHeadless", <<parameters.browser>> ]
|
||||
steps:
|
||||
- browser-tools/install-firefox
|
||||
- when:
|
||||
condition:
|
||||
equal: [ "ChromeHeadless", <<parameters.browser>> ]
|
||||
steps:
|
||||
- browser-tools/install-chrome:
|
||||
replace-existing: false
|
||||
- run: npm run test -- --browsers=<<parameters.browser>>
|
||||
- save_cache_cmd:
|
||||
node-version: <<parameters.node-version>>
|
||||
- run: npm run test:coverage -- --browsers=<<parameters.browser>>
|
||||
- store_test_results:
|
||||
path: dist/reports/tests/
|
||||
- store_artifacts:
|
||||
@@ -142,12 +125,15 @@ workflows:
|
||||
overall-circleci-commit-status: #These jobs run on every commit
|
||||
jobs:
|
||||
- lint:
|
||||
name: node16-lint
|
||||
node-version: lts/gallium
|
||||
- unit-test:
|
||||
name: node12-chrome
|
||||
node-version: lts/erbium
|
||||
browser: PlaywrightChrome
|
||||
- unit-test:
|
||||
name: node14-chrome
|
||||
node-version: lts/fermium
|
||||
browser: ChromeHeadless
|
||||
browser: PlaywrightChrome
|
||||
post-steps:
|
||||
- upload_code_covio
|
||||
- unit-test:
|
||||
@@ -158,20 +144,24 @@ workflows:
|
||||
name: e2e-ci
|
||||
node-version: lts/gallium
|
||||
suite: ci
|
||||
the-nightly: #These jobs do not run on PRs, but against master at night
|
||||
the-nightly: #These jobs do not run on PRs, but against master each night
|
||||
jobs:
|
||||
- unit-test:
|
||||
name: node16-firefoxESR-nightly
|
||||
node-version: lts/gallium
|
||||
browser: FirefoxESR
|
||||
name: node12-chrome-nightly
|
||||
node-version: lts/erbium
|
||||
browser: PlaywrightChrome
|
||||
- unit-test:
|
||||
name: node14-firefox-nightly
|
||||
node-version: lts/fermium
|
||||
browser: FirefoxHeadless
|
||||
browser: PlaywrightFirefox
|
||||
- unit-test:
|
||||
name: node14-chrome-nightly
|
||||
name: node14-chrome-canary-nightly
|
||||
node-version: lts/fermium
|
||||
browser: ChromeHeadless
|
||||
browser: PlaywrightChromeCanary
|
||||
- unit-test:
|
||||
name: node14-safari-nightly
|
||||
node-version: lts/fermium
|
||||
browser: PlaywrightSafari
|
||||
- unit-test:
|
||||
name: node16-chrome-nightly
|
||||
node-version: lts/gallium
|
||||
|
||||
14
.github/workflows/e2e-pr.yml
vendored
14
.github/workflows/e2e-pr.yml
vendored
@@ -8,7 +8,7 @@ on:
|
||||
|
||||
jobs:
|
||||
e2e-full:
|
||||
if: ${{ github.event_name == 'pull_request' && github.event.action == 'labeled' && github.event.label.name == 'pr:e2e' }} || ${{ github.event_name == 'pull_request' && github.event.action == 'opened' }}
|
||||
if: ${{ github.event.label.name == 'pr:e2e' }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -30,18 +30,8 @@ jobs:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16'
|
||||
check-latest: true
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
|
||||
- run: npx playwright@1.19.2 install
|
||||
- run: npm install
|
||||
- run: npx playwright install
|
||||
- run: npm run test:e2e:full
|
||||
- name: Archive test results
|
||||
uses: actions/upload-artifact@v2
|
||||
|
||||
14
.github/workflows/e2e-visual.yml
vendored
14
.github/workflows/e2e-visual.yml
vendored
@@ -10,25 +10,15 @@ on:
|
||||
|
||||
jobs:
|
||||
e2e-visual:
|
||||
if: ${{ github.event.label.name == 'pr:visual' }} || ${{ github.event.workflow_dispatch }} || ${{ github.event.schedule }} || ${{ github.event_name == 'pull_request' && github.event.action == 'opened' }}
|
||||
if: ${{ github.event.label.name == 'pr:visual' }} || ${{ github.event.workflow_dispatch }} || ${{ github.event.schedule }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16'
|
||||
check-latest: true
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
|
||||
- run: npx playwright@1.19.2 install
|
||||
- run: npm install
|
||||
- run: npx playwright install
|
||||
- name: Run the e2e visual tests
|
||||
run: npm run test:e2e:visual
|
||||
env:
|
||||
|
||||
21
.github/workflows/e2e.yml
vendored
Normal file
21
.github/workflows/e2e.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
name: "e2e"
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Which branch do you want to test?' # Limited to branch for now
|
||||
required: false
|
||||
default: 'master'
|
||||
jobs:
|
||||
e2e:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.inputs.version }}
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16'
|
||||
- run: npm install
|
||||
- name: Run the e2e tests
|
||||
run: npm run test:e2e:ci
|
||||
19
.github/workflows/lighthouse.yml
vendored
19
.github/workflows/lighthouse.yml
vendored
@@ -6,6 +6,11 @@ on:
|
||||
description: 'Which branch do you want to test?' # Limited to branch for now
|
||||
required: false
|
||||
default: 'master'
|
||||
pull_request:
|
||||
types:
|
||||
- labeled
|
||||
schedule:
|
||||
- cron: '28 21 * * 1-5'
|
||||
jobs:
|
||||
lighthouse-pr:
|
||||
if: ${{ github.event.label.name == 'pr:lighthouse' }}
|
||||
@@ -15,10 +20,10 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: master #explicitly checkout master for baseline
|
||||
- name: Install Node 16
|
||||
- name: Install Node 14
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16'
|
||||
node-version: '14'
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
@@ -49,11 +54,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install Node 16
|
||||
- name: Install Node 14
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16'
|
||||
check-latest: true
|
||||
node-version: '14'
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
@@ -79,10 +83,9 @@ jobs:
|
||||
- name: Install Node 14
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16'
|
||||
check-latest: true
|
||||
node-version: '14'
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
|
||||
1
.github/workflows/npm-prerelease.yml
vendored
1
.github/workflows/npm-prerelease.yml
vendored
@@ -27,7 +27,6 @@ jobs:
|
||||
with:
|
||||
node-version: 16
|
||||
registry-url: https://registry.npmjs.org/
|
||||
check-latest: true
|
||||
- run: npm install
|
||||
- run: npm publish --access public --tag unstable
|
||||
env:
|
||||
|
||||
15
.github/workflows/pr-platform.yml
vendored
15
.github/workflows/pr-platform.yml
vendored
@@ -12,14 +12,13 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-20.04 #MMOC Ubuntu version
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
- macos-10.15
|
||||
- windows-latest
|
||||
node_version:
|
||||
- 12
|
||||
- 14
|
||||
- 16
|
||||
- 17
|
||||
architecture:
|
||||
- x64
|
||||
name: Node ${{ matrix.node_version }} - ${{ matrix.architecture }} on ${{ matrix.os }}
|
||||
@@ -30,16 +29,6 @@ jobs:
|
||||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
architecture: ${{ matrix.architecture }}
|
||||
check-latest: true
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-${{ matrix.node_version }}--build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ matrix.node_version }}--build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
|
||||
- run: npm install
|
||||
- run: npm test
|
||||
- run: npm run lint -- --quiet
|
||||
|
||||
61
.npmignore
61
.npmignore
@@ -1,27 +1,44 @@
|
||||
# Ignore everything first (will not ignore special files like LICENSE.md,
|
||||
# README.md, and package.json)...
|
||||
/**/*
|
||||
*.scssc
|
||||
*.zip
|
||||
*.gzip
|
||||
*.tgz
|
||||
*.DS_Store
|
||||
|
||||
# ...but include these folders...
|
||||
!/dist/**/*
|
||||
!/src/**/*
|
||||
*.sass-cache
|
||||
*COMPILE.css
|
||||
|
||||
# We might be able to remove this if it is not imported by any project directly.
|
||||
# https://github.com/nasa/openmct/issues/4992
|
||||
!/example/**/*
|
||||
# Intellij project configuration files
|
||||
*.idea
|
||||
*.iml
|
||||
|
||||
# We will remove this in https://github.com/nasa/openmct/issues/4922
|
||||
!/app.js
|
||||
# External dependencies
|
||||
|
||||
# ...except for these files in the above folders.
|
||||
/src/**/*Spec.js
|
||||
/src/**/test/
|
||||
# TODO move test utils into test/ folders
|
||||
/src/utils/testing.js
|
||||
# Build output
|
||||
target
|
||||
|
||||
# Also include these special top-level files.
|
||||
!copyright-notice.js
|
||||
!copyright-notice.html
|
||||
!index.html
|
||||
!openmct.js
|
||||
!SECURITY.md
|
||||
# Mac OS X Finder
|
||||
.DS_Store
|
||||
|
||||
# Closed source libraries
|
||||
closed-lib
|
||||
|
||||
# Node, Bower dependencies
|
||||
node_modules
|
||||
bower_components
|
||||
|
||||
Procfile
|
||||
|
||||
# Protractor logs
|
||||
protractor/logs
|
||||
|
||||
# npm-debug log
|
||||
npm-debug.log
|
||||
|
||||
# Infra and tests
|
||||
.circleci
|
||||
.github
|
||||
e2e
|
||||
codecov.yml
|
||||
lighthouserc.yml
|
||||
*.Spec.js
|
||||
karma.conf.js
|
||||
|
||||
2
app.js
2
app.js
@@ -64,7 +64,7 @@ app.use(require('webpack-dev-middleware')(
|
||||
compiler,
|
||||
{
|
||||
publicPath: '/dist',
|
||||
stats: 'errors-warnings'
|
||||
logLevel: 'warn'
|
||||
}
|
||||
));
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ const config = {
|
||||
reporter: [
|
||||
['list'],
|
||||
['junit', { outputFile: 'test-results/results.xml' }],
|
||||
['allure-playwright']
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
@@ -157,10 +157,5 @@ test.describe('Sine Wave Generator', () => {
|
||||
y: 28
|
||||
}
|
||||
});
|
||||
|
||||
// Verify that where we click on canvas shows the number we clicked on
|
||||
// Note that any number will do, we just care that a number exists
|
||||
await expect(page.locator('.value-to-display-nearestValue')).toContainText(/[+-]?([0-9]*[.])?[0-9]+/);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,42 +1,42 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2022, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
This test suite is dedicated to tests which verify the basic operations surrounding moving objects.
|
||||
*/
|
||||
|
||||
const { test, expect } = require('@playwright/test');
|
||||
|
||||
test.describe('Move item tests', () => {
|
||||
test.fixme('Create a basic object and verify that it can be moved to another Folder', async ({ page }) => {
|
||||
//Create and save Folder
|
||||
//Create and save Domain Object
|
||||
//Verify that the newly created domain object can be moved to Folder from Step 1.
|
||||
//Verify that newly moved object appears in the correct point in Tree
|
||||
//Verify that newly moved object appears correctly in Inspector panel
|
||||
});
|
||||
test.fixme('Create a basic object and verify that it cannot be moved to object without Composition Provider', async ({ page }) => {
|
||||
//Create and save Telemetry Object
|
||||
//Create and save Domain Object
|
||||
//Verify that the newly created domain object cannot be moved to Telemetry Object from step 1.
|
||||
});
|
||||
});
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2022, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
This test suite is dedicated to tests which verify the basic operations surrounding moving objects.
|
||||
*/
|
||||
|
||||
const { test, expect } = require('@playwright/test');
|
||||
|
||||
test.describe('Move item tests', () => {
|
||||
test.fixme('Create a basic object and verify that it can be moved to another Folder', async ({ page }) => {
|
||||
//Create and save Folder
|
||||
//Create and save Domain Object
|
||||
//Verify that the newly created domain object can be moved to Folder from Step 1.
|
||||
//Verify that newly moved object appears in the correct point in Tree
|
||||
//Verify that newly moved object appears correctly in Inspector panel
|
||||
});
|
||||
test.fixme('Create a basic object and verify that it cannot be moved to object without Composition Provider', async ({ page }) => {
|
||||
//Create and save Telemetry Object
|
||||
//Create and save Domain Object
|
||||
//Verify that the newly created domain object cannot be moved to Telemetry Object from step 1.
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,46 +1,46 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2022, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
This test suite is dedicated to tests which verify the basic operations surrounding importAsJSON.
|
||||
*/
|
||||
|
||||
const { test, expect } = require('@playwright/test');
|
||||
|
||||
test.describe('ExportAsJSON', () => {
|
||||
test.fixme('Verify that domain object can be importAsJSON from Tree', async ({ page }) => {
|
||||
//Verify that an testdata JSON file can be imported from Tree
|
||||
//Verify correctness of imported domain object
|
||||
});
|
||||
test.fixme('Verify that domain object can be importAsJSON from 3 dot menu on folder', async ({ page }) => {
|
||||
//Verify that an testdata JSON file can be imported from 3 dot menu on folder domain object
|
||||
//Verify correctness of imported domain object
|
||||
});
|
||||
test.fixme('Verify that a nested Objects can be importAsJSON', async ({ page }) => {
|
||||
// Testdata with hierarchy
|
||||
// ImportAsJSON on Tree
|
||||
// Verify Hierarchy
|
||||
});
|
||||
test.fixme('Verify that the ImportAsJSON dropdown does not appear for the item X', async ({ page }) => {
|
||||
// Other than non-persistible objects
|
||||
});
|
||||
});
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2022, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
This test suite is dedicated to tests which verify the basic operations surrounding importAsJSON.
|
||||
*/
|
||||
|
||||
const { test, expect } = require('@playwright/test');
|
||||
|
||||
test.describe('ExportAsJSON', () => {
|
||||
test.fixme('Verify that domain object can be importAsJSON from Tree', async ({ page }) => {
|
||||
//Verify that an testdata JSON file can be imported from Tree
|
||||
//Verify correctness of imported domain object
|
||||
});
|
||||
test.fixme('Verify that domain object can be importAsJSON from 3 dot menu on folder', async ({ page }) => {
|
||||
//Verify that an testdata JSON file can be imported from 3 dot menu on folder domain object
|
||||
//Verify correctness of imported domain object
|
||||
});
|
||||
test.fixme('Verify that a nested Objects can be importAsJSON', async ({ page }) => {
|
||||
// Testdata with hierarchy
|
||||
// ImportAsJSON on Tree
|
||||
// Verify Hierarchy
|
||||
});
|
||||
test.fixme('Verify that the ImportAsJSON dropdown does not appear for the item X', async ({ page }) => {
|
||||
// Other than non-persistible objects
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2022, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
This test suite is dedicated to tests which verify the basic operations surrounding Clock.
|
||||
*/
|
||||
|
||||
const { test, expect } = require('@playwright/test');
|
||||
|
||||
test.describe('Clock Generator', () => {
|
||||
|
||||
test('Timezone dropdown will collapse when clicked outside or on dropdown icon again', async ({ page }) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/4878'
|
||||
});
|
||||
//Go to baseURL
|
||||
await page.goto('/', { waitUntil: 'networkidle' });
|
||||
|
||||
//Click the Create button
|
||||
await page.click('button:has-text("Create")');
|
||||
|
||||
// Click Clock
|
||||
await page.click('text=Clock');
|
||||
|
||||
// Click .icon-arrow-down
|
||||
await page.locator('.icon-arrow-down').click();
|
||||
//verify if the autocomplete dropdown is visible
|
||||
await expect(page.locator(".optionPreSelected")).toBeVisible();
|
||||
// Click .icon-arrow-down
|
||||
await page.locator('.icon-arrow-down').click();
|
||||
|
||||
// Verify clicking on the autocomplete arrow collapses the dropdown
|
||||
await expect(page.locator(".optionPreSelected")).not.toBeVisible();
|
||||
|
||||
// Click timezone input to open dropdown
|
||||
await page.locator('.autocompleteInput').click();
|
||||
//verify if the autocomplete dropdown is visible
|
||||
await expect(page.locator(".optionPreSelected")).toBeVisible();
|
||||
|
||||
// Verify clicking outside the autocomplete dropdown collapses it
|
||||
await page.locator('text=Timezone').click();
|
||||
// Verify clicking on the autocomplete arrow collapses the dropdown
|
||||
await expect(page.locator(".optionPreSelected")).not.toBeVisible();
|
||||
|
||||
});
|
||||
});
|
||||
@@ -1,217 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2022, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
This test suite is dedicated to tests which verify the basic operations surrounding imagery,
|
||||
but only assume that example imagery is present.
|
||||
*/
|
||||
|
||||
const { test, expect } = require('@playwright/test');
|
||||
|
||||
test.describe('Example Imagery', () => {
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
page.on('console', msg => console.log(msg.text()))
|
||||
//Go to baseURL
|
||||
await page.goto('/', { waitUntil: 'networkidle' });
|
||||
|
||||
//Click the Create button
|
||||
await page.click('button:has-text("Create")');
|
||||
|
||||
// Click text=Example Imagery
|
||||
await page.click('text=Example Imagery');
|
||||
|
||||
// Click text=OK
|
||||
await Promise.all([
|
||||
page.waitForNavigation(/*{ url: 'http://localhost:8080/#/browse/mine/dab945d4-5a84-480e-8180-222b4aa730fa?tc.mode=fixed&tc.startBound=1639696164435&tc.endBound=1639697964435&tc.timeSystem=utc&view=conditionSet.view' }*/),
|
||||
page.click('text=OK')
|
||||
]);
|
||||
|
||||
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery');
|
||||
});
|
||||
|
||||
const backgroundImageSelector = '.c-imagery__main-image__background-image';
|
||||
test('Can use Mouse Wheel to zoom in and out of latest image', async ({ page }) => {
|
||||
const bgImageLocator = await page.locator(backgroundImageSelector);
|
||||
const deltaYStep = 100; //equivalent to 1x zoom
|
||||
await bgImageLocator.hover();
|
||||
const originalImageDimensions = await page.locator(backgroundImageSelector).boundingBox();
|
||||
// zoom in
|
||||
await bgImageLocator.hover();
|
||||
await page.mouse.wheel(0, deltaYStep * 2);
|
||||
// wait for zoom animation to finish
|
||||
await bgImageLocator.hover();
|
||||
const imageMouseZoomedIn = await page.locator(backgroundImageSelector).boundingBox();
|
||||
// zoom out
|
||||
await bgImageLocator.hover();
|
||||
await page.mouse.wheel(0, -deltaYStep);
|
||||
// wait for zoom animation to finish
|
||||
await bgImageLocator.hover();
|
||||
const imageMouseZoomedOut = await page.locator(backgroundImageSelector).boundingBox();
|
||||
|
||||
expect(imageMouseZoomedIn.height).toBeGreaterThan(originalImageDimensions.height);
|
||||
expect(imageMouseZoomedIn.width).toBeGreaterThan(originalImageDimensions.width);
|
||||
expect(imageMouseZoomedOut.height).toBeLessThan(imageMouseZoomedIn.height);
|
||||
expect(imageMouseZoomedOut.width).toBeLessThan(imageMouseZoomedIn.width);
|
||||
|
||||
});
|
||||
|
||||
test('Can use alt+drag to move around image once zoomed in', async ({ page }) => {
|
||||
const deltaYStep = 100; //equivalent to 1x zoom
|
||||
|
||||
const bgImageLocator = await page.locator(backgroundImageSelector);
|
||||
await bgImageLocator.hover();
|
||||
// zoom in
|
||||
await page.mouse.wheel(0, deltaYStep * 2);
|
||||
await bgImageLocator.hover();
|
||||
const zoomedBoundingBox = await bgImageLocator.boundingBox();
|
||||
const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2;
|
||||
const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2;
|
||||
// move to the right
|
||||
|
||||
// center the mouse pointer
|
||||
await page.mouse.move(imageCenterX, imageCenterY);
|
||||
|
||||
// pan right
|
||||
await page.keyboard.down('Alt');
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(imageCenterX - 200, imageCenterY, 10);
|
||||
await page.mouse.up();
|
||||
await page.keyboard.up('Alt');
|
||||
const afterRightPanBoundingBox = await bgImageLocator.boundingBox();
|
||||
expect(zoomedBoundingBox.x).toBeGreaterThan(afterRightPanBoundingBox.x);
|
||||
|
||||
// pan left
|
||||
await page.keyboard.down('Alt');
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(imageCenterX, imageCenterY, 10);
|
||||
await page.mouse.up();
|
||||
await page.keyboard.up('Alt');
|
||||
const afterLeftPanBoundingBox = await bgImageLocator.boundingBox();
|
||||
expect(afterRightPanBoundingBox.x).toBeLessThan(afterLeftPanBoundingBox.x);
|
||||
|
||||
// pan up
|
||||
await page.mouse.move(imageCenterX, imageCenterY);
|
||||
await page.keyboard.down('Alt');
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(imageCenterX, imageCenterY + 200, 10);
|
||||
await page.mouse.up();
|
||||
await page.keyboard.up('Alt');
|
||||
const afterUpPanBoundingBox = await bgImageLocator.boundingBox();
|
||||
expect(afterUpPanBoundingBox.y).toBeGreaterThan(afterLeftPanBoundingBox.y);
|
||||
|
||||
// pan down
|
||||
await page.keyboard.down('Alt');
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(imageCenterX, imageCenterY - 200, 10);
|
||||
await page.mouse.up();
|
||||
await page.keyboard.up('Alt');
|
||||
const afterDownPanBoundingBox = await bgImageLocator.boundingBox();
|
||||
expect(afterDownPanBoundingBox.y).toBeLessThan(afterUpPanBoundingBox.y);
|
||||
|
||||
});
|
||||
|
||||
test('Can use + - buttons to zoom on the image', async ({ page }) => {
|
||||
const bgImageLocator = await page.locator(backgroundImageSelector);
|
||||
await bgImageLocator.hover();
|
||||
const zoomInBtn = await page.locator('.t-btn-zoom-in');
|
||||
const zoomOutBtn = await page.locator('.t-btn-zoom-out');
|
||||
const initialBoundingBox = await bgImageLocator.boundingBox();
|
||||
|
||||
await zoomInBtn.click();
|
||||
await zoomInBtn.click();
|
||||
// wait for zoom animation to finish
|
||||
await bgImageLocator.hover();
|
||||
const zoomedInBoundingBox = await bgImageLocator.boundingBox();
|
||||
expect(zoomedInBoundingBox.height).toBeGreaterThan(initialBoundingBox.height);
|
||||
expect(zoomedInBoundingBox.width).toBeGreaterThan(initialBoundingBox.width);
|
||||
|
||||
await zoomOutBtn.click();
|
||||
// wait for zoom animation to finish
|
||||
await bgImageLocator.hover();
|
||||
const zoomedOutBoundingBox = await bgImageLocator.boundingBox();
|
||||
expect(zoomedOutBoundingBox.height).toBeLessThan(zoomedInBoundingBox.height);
|
||||
expect(zoomedOutBoundingBox.width).toBeLessThan(zoomedInBoundingBox.width);
|
||||
|
||||
});
|
||||
|
||||
test('Can use the reset button to reset the image', async ({ page }) => {
|
||||
const bgImageLocator = await page.locator(backgroundImageSelector);
|
||||
await bgImageLocator.hover();
|
||||
const zoomInBtn = await page.locator('.t-btn-zoom-in');
|
||||
const zoomResetBtn = await page.locator('.t-btn-zoom-reset');
|
||||
const initialBoundingBox = await bgImageLocator.boundingBox();
|
||||
|
||||
await zoomInBtn.click();
|
||||
await zoomInBtn.click();
|
||||
// wait for zoom animation to finish
|
||||
await bgImageLocator.hover();
|
||||
const zoomedInBoundingBox = await bgImageLocator.boundingBox();
|
||||
expect.soft(zoomedInBoundingBox.height).toBeGreaterThan(initialBoundingBox.height);
|
||||
expect.soft(zoomedInBoundingBox.width).toBeGreaterThan(initialBoundingBox.width);
|
||||
|
||||
await zoomResetBtn.click();
|
||||
await bgImageLocator.hover();
|
||||
|
||||
const resetBoundingBox = await bgImageLocator.boundingBox();
|
||||
expect.soft(resetBoundingBox.height).toBeLessThan(zoomedInBoundingBox.height);
|
||||
expect.soft(resetBoundingBox.width).toBeLessThan(zoomedInBoundingBox.width);
|
||||
|
||||
expect.soft(resetBoundingBox.height).toEqual(initialBoundingBox.height);
|
||||
expect(resetBoundingBox.width).toEqual(initialBoundingBox.width);
|
||||
});
|
||||
|
||||
//test('Can use Mouse Wheel to zoom in and out of previous image');
|
||||
//test('Can zoom into the latest image and the real-time/fixed-time imagery will pause');
|
||||
//test.skip('Can zoom into a previous image from thumbstrip in real-time or fixed-time');
|
||||
//test.skip('Clicking on the left arrow should pause the imagery and go to previous image');
|
||||
//test.skip('If the imagery view is in pause mode, it should not be updated when new images come in');
|
||||
//test.skip('If the imagery view is not in pause mode, it should be updated when new images come in');
|
||||
});
|
||||
|
||||
test.describe('Example Imagery in Display layout', () => {
|
||||
test.skip('Can use Mouse Wheel to zoom in and out of previous image');
|
||||
test.skip('Can use alt+drag to move around image once zoomed in');
|
||||
test.skip('Can zoom into the latest image and the real-time/fixed-time imagery will pause');
|
||||
test.skip('Clicking on the left arrow should pause the imagery and go to previous image');
|
||||
test.skip('If the imagery view is in pause mode, it should not be updated when new images come in');
|
||||
test.skip('If the imagery view is not in pause mode, it should be updated when new images come in');
|
||||
});
|
||||
|
||||
test.describe('Example Imagery in Flexible layout', () => {
|
||||
test.skip('Can use Mouse Wheel to zoom in and out of previous image');
|
||||
test.skip('Can use alt+drag to move around image once zoomed in');
|
||||
test.skip('Can zoom into the latest image and the real-time/fixed-time imagery will pause');
|
||||
test.skip('Clicking on the left arrow should pause the imagery and go to previous image');
|
||||
test.skip('If the imagery view is in pause mode, it should not be updated when new images come in');
|
||||
test.skip('If the imagery view is not in pause mode, it should be updated when new images come in');
|
||||
});
|
||||
|
||||
test.describe('Example Imagery in Tabs view', () => {
|
||||
test.skip('Can use Mouse Wheel to zoom in and out of previous image');
|
||||
test.skip('Can use alt+drag to move around image once zoomed in');
|
||||
test.skip('Can zoom into the latest image and the real-time/fixed-time imagery will pause');
|
||||
test.skip('Can zoom into a previous image from thumbstrip in real-time or fixed-time');
|
||||
test.skip('Clicking on the left arrow should pause the imagery and go to previous image');
|
||||
test.skip('If the imagery view is in pause mode, it should not be updated when new images come in');
|
||||
test.skip('If the imagery view is not in pause mode, it should be updated when new images come in');
|
||||
});
|
||||
@@ -22,14 +22,14 @@
|
||||
|
||||
/*
|
||||
Collection of Visual Tests set to run in a default context. The tests within this suite
|
||||
are only meant to run against openmct's app.js started by `npm run start` within the
|
||||
are only meant to run against openmct's app.js started by `npm run start` within the
|
||||
`./e2e/playwright-visual.config.js` file.
|
||||
|
||||
These should only use functional expect statements to verify assumptions about the state
|
||||
These should only use functional expect statements to verify assumptions about the state
|
||||
in a test and not for functional verification of correctness. Visual tests are not supposed
|
||||
to "fail" on assertions. Instead, they should be used to detect changes between builds or branches.
|
||||
|
||||
Note: Larger testsuite sizes are OK due to the setup time associated with these tests.
|
||||
Note: Larger testsuite sizes are OK due to the setup time associated with these tests.
|
||||
*/
|
||||
|
||||
const { test, expect } = require('@playwright/test');
|
||||
|
||||
@@ -35,8 +35,8 @@ define([
|
||||
phase: 0
|
||||
};
|
||||
|
||||
function GeneratorProvider(openmct) {
|
||||
this.workerInterface = new WorkerInterface(openmct);
|
||||
function GeneratorProvider() {
|
||||
this.workerInterface = new WorkerInterface();
|
||||
}
|
||||
|
||||
GeneratorProvider.prototype.canProvideTelemetry = function (domainObject) {
|
||||
|
||||
@@ -21,13 +21,20 @@
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
'raw-loader!./generatorWorker.js',
|
||||
'uuid'
|
||||
], function (
|
||||
workerText,
|
||||
uuid
|
||||
) {
|
||||
function WorkerInterface(openmct) {
|
||||
// eslint-disable-next-line no-undef
|
||||
const workerUrl = `${openmct.getAssetPath()}${__OPENMCT_ROOT_RELATIVE__}generatorWorker.js`;
|
||||
|
||||
var workerBlob = new Blob(
|
||||
[workerText],
|
||||
{type: 'application/javascript'}
|
||||
);
|
||||
var workerUrl = URL.createObjectURL(workerBlob);
|
||||
|
||||
function WorkerInterface() {
|
||||
this.worker = new Worker(workerUrl);
|
||||
this.worker.onmessage = this.onMessage.bind(this);
|
||||
this.callbacks = {};
|
||||
|
||||
@@ -146,7 +146,7 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
openmct.telemetry.addProvider(new GeneratorProvider(openmct));
|
||||
openmct.telemetry.addProvider(new GeneratorProvider());
|
||||
openmct.telemetry.addProvider(new GeneratorMetadataProvider());
|
||||
openmct.telemetry.addProvider(new SinewaveLimitProvider());
|
||||
};
|
||||
|
||||
@@ -38,10 +38,6 @@ module.exports = (config) => {
|
||||
{
|
||||
pattern: 'dist/inMemorySearchWorker.js*',
|
||||
included: false
|
||||
},
|
||||
{
|
||||
pattern: 'dist/generatorWorker.js*',
|
||||
included: false
|
||||
}
|
||||
],
|
||||
port: 9876,
|
||||
@@ -62,6 +58,22 @@ module.exports = (config) => {
|
||||
FirefoxESR: {
|
||||
base: 'FirefoxHeadless',
|
||||
name: 'FirefoxESR'
|
||||
},
|
||||
PlaywrightChrome: {
|
||||
base: 'ChromiumHeadless'
|
||||
},
|
||||
PlaywrightSafari: {
|
||||
base: 'WebKitHeadless'
|
||||
},
|
||||
PlaywrightChromeCanary: {
|
||||
base: 'ChromiumHeadless',
|
||||
launchOptions: {
|
||||
channel: 'chrome-canary'
|
||||
}
|
||||
},
|
||||
PlaywrightFirefox: {
|
||||
base: 'FirefoxHeadless',
|
||||
debug: true
|
||||
}
|
||||
},
|
||||
colors: true,
|
||||
@@ -91,12 +103,17 @@ module.exports = (config) => {
|
||||
showSpecTiming: true,
|
||||
failFast: false
|
||||
},
|
||||
plugins: [
|
||||
'karma-*',
|
||||
'@onslip/karma-playwright-launcher'
|
||||
],
|
||||
preprocessors: {
|
||||
'indexTest.js': ['webpack', 'sourcemap']
|
||||
},
|
||||
webpack: webpackConfig,
|
||||
webpackMiddleware: {
|
||||
stats: 'errors-warnings'
|
||||
stats: 'errors-only',
|
||||
logLevel: 'warn'
|
||||
},
|
||||
concurrency: 1,
|
||||
singleRun: true,
|
||||
|
||||
41
package.json
41
package.json
@@ -1,18 +1,14 @@
|
||||
{
|
||||
"name": "openmct",
|
||||
"version": "2.0.2-SNAPSHOT",
|
||||
"version": "2.0.1-SNAPSHOT",
|
||||
"description": "The Open MCT core platform",
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "7.16.3",
|
||||
"@braintree/sanitize-url": "6.0.0",
|
||||
"@percy/cli": "1.0.0-beta.76",
|
||||
"@onslip/karma-playwright-launcher": "0.2.1",
|
||||
"@percy/cli": "1.0.0-beta.75",
|
||||
"@percy/playwright": "1.0.1",
|
||||
"@playwright/test": "1.19.2",
|
||||
"@types/eventemitter3": "^1.0.0",
|
||||
"@types/jasmine": "^4.0.1",
|
||||
"@types/karma": "^6.3.2",
|
||||
"@types/lodash": "^4.14.178",
|
||||
"@types/mocha": "^9.1.0",
|
||||
"allure-playwright": "2.0.0-beta.15",
|
||||
"babel-loader": "8.2.3",
|
||||
"babel-plugin-istanbul": "6.1.1",
|
||||
@@ -32,14 +28,16 @@
|
||||
"eventemitter3": "1.2.0",
|
||||
"exports-loader": "0.7.0",
|
||||
"express": "4.13.1",
|
||||
"file-loader": "6.2.0",
|
||||
"file-saver": "2.0.5",
|
||||
"git-rev-sync": "3.0.2",
|
||||
"html-loader": "0.5.5",
|
||||
"html2canvas": "1.4.1",
|
||||
"imports-loader": "0.8.0",
|
||||
"jasmine-core": "4.0.1",
|
||||
"jsdoc": "3.5.5",
|
||||
"karma": "6.3.15",
|
||||
"karma-chrome-launcher": "3.1.1",
|
||||
"karma-chrome-launcher": "3.1.0",
|
||||
"karma-cli": "2.0.0",
|
||||
"karma-coverage": "2.1.1",
|
||||
"karma-coverage-istanbul-reporter": "3.0.3",
|
||||
@@ -49,22 +47,22 @@
|
||||
"karma-sourcemap-loader": "0.3.8",
|
||||
"karma-spec-reporter": "0.0.33",
|
||||
"karma-webpack": "5.0.0",
|
||||
"lighthouse": "9.5.0",
|
||||
"location-bar": "3.0.1",
|
||||
"lodash": "4.17.21",
|
||||
"mini-css-extract-plugin": "2.6.0",
|
||||
"lodash": "4.17.12",
|
||||
"mini-css-extract-plugin": "2.4.5",
|
||||
"moment": "2.29.1",
|
||||
"moment-duration-format": "2.2.2",
|
||||
"moment-timezone": "0.5.34",
|
||||
"moment-timezone": "0.5.28",
|
||||
"node-bourbon": "4.2.3",
|
||||
"painterro": "1.2.56",
|
||||
"plotly.js-basic-dist": "2.5.0",
|
||||
"plotly.js-gl2d-dist": "2.5.0",
|
||||
"printj": "1.3.1",
|
||||
"request": "2.88.2",
|
||||
"raw-loader": "4.0.2",
|
||||
"request": "2.69.0",
|
||||
"resolve-url-loader": "4.0.0",
|
||||
"sass": "1.49.9",
|
||||
"sass-loader": "12.6.0",
|
||||
"sass": "1.49.0",
|
||||
"sass-loader": "12.4.0",
|
||||
"sinon": "13.0.1",
|
||||
"style-loader": "^1.0.1",
|
||||
"uuid": "3.3.3",
|
||||
@@ -74,13 +72,13 @@
|
||||
"vue-template-compiler": "2.6.14",
|
||||
"webpack": "5.68.0",
|
||||
"webpack-cli": "4.9.2",
|
||||
"webpack-dev-middleware": "5.3.1",
|
||||
"webpack-hot-middleware": "2.25.1",
|
||||
"webpack-dev-middleware": "3.7.3",
|
||||
"webpack-hot-middleware": "2.22.3",
|
||||
"webpack-merge": "5.8.0",
|
||||
"zepto": "1.2.0"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf ./dist ./node_modules ./package-lock.json",
|
||||
"clean": "rm -rf ./dist ./node_modules; rm package-lock.json",
|
||||
"clean-test-lint": "npm run clean; npm install; npm run test; npm run lint",
|
||||
"start": "node app.js",
|
||||
"lint": "eslint example src --ext .js,.vue openmct.js",
|
||||
@@ -93,6 +91,11 @@
|
||||
"test": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run",
|
||||
"test:firefox": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run --browsers=FirefoxHeadless",
|
||||
"test:debug": "cross-env NODE_ENV=debug karma start --no-single-run",
|
||||
"test:coverage": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" COVERAGE=true karma start --single-run",
|
||||
"test:coverage:pw": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" COVERAGE=true karma start --single-run --browsers=PlaywrightChrome",
|
||||
"test:coverage:pwsafari": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" COVERAGE=true karma start --single-run --browsers=PlaywrightSafari",
|
||||
"test:coverage:pwcanary": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" COVERAGE=true karma start --single-run --browsers=PlaywrightChromeCanary",
|
||||
"test:coverage:pwfirefox": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run --browsers=PlaywrightFirefox",
|
||||
"test:e2e:ci": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome smoke default condition timeConductor",
|
||||
"test:e2e:local": "npx playwright test --config=e2e/playwright-local.config.js --project=chrome",
|
||||
"test:e2e:visual": "percy exec --config ./e2e/.percy.yml -- npx playwright test --config=e2e/playwright-visual.config.js default",
|
||||
@@ -110,7 +113,7 @@
|
||||
"url": "https://github.com/nasa/openmct.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.19.1"
|
||||
"node": ">=12.22.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"Firefox ESR",
|
||||
|
||||
@@ -65,11 +65,10 @@ export default class FormControl {
|
||||
*/
|
||||
_getControlViewProvider(control) {
|
||||
const self = this;
|
||||
let rowComponent;
|
||||
|
||||
return {
|
||||
show(element, model, onChange) {
|
||||
rowComponent = new Vue({
|
||||
const rowComponent = new Vue({
|
||||
el: element,
|
||||
components: {
|
||||
FormControlComponent: DEFAULT_CONTROLS_MAP[control]
|
||||
@@ -87,9 +86,6 @@ export default class FormControl {
|
||||
});
|
||||
|
||||
return rowComponent;
|
||||
},
|
||||
destroy() {
|
||||
rowComponent.$destroy();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -22,19 +22,17 @@
|
||||
|
||||
<template>
|
||||
<div class="form-control autocomplete">
|
||||
<span class="autocompleteInputAndArrow">
|
||||
<input
|
||||
v-model="field"
|
||||
class="autocompleteInput"
|
||||
type="text"
|
||||
@click="inputClicked()"
|
||||
@keydown="keyDown($event)"
|
||||
>
|
||||
<span
|
||||
class="icon-arrow-down"
|
||||
@click="arrowClicked()"
|
||||
></span>
|
||||
</span>
|
||||
<input
|
||||
v-model="field"
|
||||
class="autocompleteInput"
|
||||
type="text"
|
||||
@click="inputClicked()"
|
||||
@keydown="keyDown($event)"
|
||||
>
|
||||
<span
|
||||
class="icon-arrow-down"
|
||||
@click="arrowClicked()"
|
||||
></span>
|
||||
<div
|
||||
class="autocompleteOptions"
|
||||
@blur="hideOptions = true"
|
||||
@@ -110,21 +108,10 @@ export default {
|
||||
|
||||
this.$emit('onChange', data);
|
||||
}
|
||||
},
|
||||
hideOptions(newValue) {
|
||||
if (!newValue) {
|
||||
// adding a event listener when the hideOpntions is false (dropdown is visible)
|
||||
// handleoutsideclick can collapse the dropdown when clicked outside autocomplete
|
||||
document.body.addEventListener('click', this.handleOutsideClick);
|
||||
} else {
|
||||
//removing event listener when hideOptions become true (dropdown is collapsed)
|
||||
document.body.removeEventListener('click', this.handleOutsideClick);
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.options = this.model.options;
|
||||
this.autocompleteInputAndArrow = this.$el.getElementsByClassName('autocompleteInputAndArrow')[0];
|
||||
this.autocompleteInputElement = this.$el.getElementsByClassName('autocompleteInput')[0];
|
||||
if (this.options[0].name) {
|
||||
// If "options" include name, value pair
|
||||
@@ -136,9 +123,6 @@ export default {
|
||||
this.optionNames = this.options;
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
document.body.removeEventListener('click', this.handleOutsideClick);
|
||||
},
|
||||
methods: {
|
||||
decrementOptionIndex() {
|
||||
if (this.optionIndex === 0) {
|
||||
@@ -192,21 +176,7 @@ export default {
|
||||
// to show them all the options
|
||||
this.showFilteredOptions = false;
|
||||
this.autocompleteInputElement.select();
|
||||
|
||||
if (this.hideOptions) {
|
||||
this.showOptions();
|
||||
} else {
|
||||
this.hideOptions = true;
|
||||
}
|
||||
|
||||
},
|
||||
handleOutsideClick(event) {
|
||||
// if click event is detected outside autocomplete (both input & arrow) while the
|
||||
// dropdown is visible, this will collapse the dropdown.
|
||||
const clickedInsideAutocomplete = this.autocompleteInputAndArrow.contains(event.target);
|
||||
if (!clickedInsideAutocomplete && !this.hideOptions) {
|
||||
this.hideOptions = true;
|
||||
}
|
||||
this.showOptions();
|
||||
},
|
||||
optionMouseover(optionId) {
|
||||
this.optionIndex = optionId;
|
||||
|
||||
@@ -365,7 +365,3 @@ class TimeContext extends EventEmitter {
|
||||
}
|
||||
|
||||
export default TimeContext;
|
||||
|
||||
/**
|
||||
@typedef {{start: number, end: number}} Bounds
|
||||
*/
|
||||
|
||||
@@ -15,8 +15,7 @@ export default function (folderName, couchPlugin, searchFilter) {
|
||||
return Promise.resolve({
|
||||
identifier,
|
||||
type: 'folder',
|
||||
name: folderName || "CouchDB Documents",
|
||||
location: 'ROOT'
|
||||
name: folderName || "CouchDB Documents"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,8 +85,7 @@ describe('the plugin', function () {
|
||||
expect(object).toEqual({
|
||||
identifier,
|
||||
type: 'folder',
|
||||
name: 'CouchDB Documents',
|
||||
location: 'ROOT'
|
||||
name: "CouchDB Documents"
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -166,7 +166,7 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
gridSize() {
|
||||
return this.domainObject.configuration.layoutGrid.map(Number);
|
||||
return this.domainObject.configuration.layoutGrid;
|
||||
},
|
||||
layoutItems() {
|
||||
return this.domainObject.configuration.items;
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
<template v-for="(container, index) in containers">
|
||||
<drop-hint
|
||||
v-if="index === 0 && containers.length > 1"
|
||||
:key="`hint-top-${container.id}`"
|
||||
:key="index"
|
||||
class="c-fl-frame__drop-hint"
|
||||
:index="-1"
|
||||
:allow-drop="allowContainerDrop"
|
||||
@@ -51,7 +51,7 @@
|
||||
/>
|
||||
|
||||
<container-component
|
||||
:key="`component-${container.id}`"
|
||||
:key="container.id"
|
||||
class="c-fl__container"
|
||||
:index="index"
|
||||
:container="container"
|
||||
@@ -66,7 +66,7 @@
|
||||
|
||||
<resize-handle
|
||||
v-if="index !== (containers.length - 1)"
|
||||
:key="`handle-${container.id}`"
|
||||
:key="index"
|
||||
:index="index"
|
||||
:orientation="rowsLayout ? 'vertical' : 'horizontal'"
|
||||
:is-editing="isEditing"
|
||||
@@ -77,7 +77,7 @@
|
||||
|
||||
<drop-hint
|
||||
v-if="containers.length > 1"
|
||||
:key="`hint-bottom-${container.id}`"
|
||||
:key="index"
|
||||
class="c-fl-frame__drop-hint"
|
||||
:index="index"
|
||||
:allow-drop="allowContainerDrop"
|
||||
|
||||
@@ -1,273 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2022, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div class="h-local-controls h-local-controls--overlay-content c-local-controls--show-on-hover c-image-controls__controls">
|
||||
<div class="c-image-controls__control c-image-controls__zoom icon-magnify">
|
||||
<div class="c-button-set c-button-set--strip-h">
|
||||
<button
|
||||
class="c-button t-btn-zoom-out icon-minus"
|
||||
title="Zoom out"
|
||||
@click="zoomOut"
|
||||
></button>
|
||||
|
||||
<button
|
||||
class="c-button t-btn-zoom-in icon-plus"
|
||||
title="Zoom in"
|
||||
@click="zoomIn"
|
||||
></button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="c-button t-btn-zoom-lock"
|
||||
title="Lock current zoom and pan across all images"
|
||||
:class="{'icon-unlocked': !panZoomLocked, 'icon-lock': panZoomLocked}"
|
||||
@click="toggleZoomLock"
|
||||
></button>
|
||||
|
||||
<button
|
||||
class="c-button icon-reset t-btn-zoom-reset"
|
||||
title="Remove zoom and pan"
|
||||
@click="handleResetImage"
|
||||
></button>
|
||||
|
||||
<span class="c-image-controls__zoom-factor">x{{ formattedZoomFactor }}</span>
|
||||
</div>
|
||||
<div class="c-image-controls__control c-image-controls__brightness-contrast">
|
||||
<span
|
||||
class="c-image-controls__sliders"
|
||||
draggable="true"
|
||||
@dragstart.stop.prevent
|
||||
>
|
||||
<div class="c-image-controls__input icon-brightness">
|
||||
<input
|
||||
v-model="filters.contrast"
|
||||
type="range"
|
||||
min="0"
|
||||
max="500"
|
||||
@change="notifyFiltersChanged"
|
||||
>
|
||||
</div>
|
||||
<div class="c-image-controls__input icon-contrast">
|
||||
<input
|
||||
v-model="filters.brightness"
|
||||
type="range"
|
||||
min="0"
|
||||
max="500"
|
||||
@change="notifyFiltersChanged"
|
||||
>
|
||||
</div>
|
||||
</span>
|
||||
<span class="t-reset-btn-holder c-imagery__lc__reset-btn c-image-controls__btn-reset">
|
||||
<button
|
||||
class="c-icon-link icon-reset t-btn-reset"
|
||||
@click="handleResetFilters"
|
||||
></button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash';
|
||||
|
||||
const DEFAULT_FILTER_VALUES = {
|
||||
brightness: '100',
|
||||
contrast: '100'
|
||||
};
|
||||
|
||||
const ZOOM_LIMITS_MAX_DEFAULT = 20;
|
||||
const ZOOM_LIMITS_MIN_DEFAULT = 1;
|
||||
const ZOOM_STEP = 1;
|
||||
const ZOOM_WHEEL_SENSITIVITY_REDUCTION = 0.01;
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'domainObject'],
|
||||
props: {
|
||||
zoomFactor: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
imageUrl: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
altPressed: false,
|
||||
shiftPressed: false,
|
||||
metaPressed: false,
|
||||
panZoomLocked: false,
|
||||
wheelZooming: false,
|
||||
filters: {
|
||||
brightness: 100,
|
||||
contrast: 100
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
formattedZoomFactor() {
|
||||
return Number.parseFloat(this.zoomFactor).toPrecision(2);
|
||||
},
|
||||
cursorStates() {
|
||||
const isPannable = this.altPressed && this.zoomFactor > 1;
|
||||
const showCursorZoomIn = this.metaPressed && !this.shiftPressed;
|
||||
const showCursorZoomOut = this.metaPressed && this.shiftPressed;
|
||||
const modifierKeyPressed = Boolean(this.metaPressed || this.shiftPressed || this.altPressed);
|
||||
|
||||
return {
|
||||
isPannable,
|
||||
showCursorZoomIn,
|
||||
showCursorZoomOut,
|
||||
modifierKeyPressed
|
||||
};
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
imageUrl(newUrl, oldUrl) {
|
||||
// reset image pan/zoom if newUrl only if not locked
|
||||
if (newUrl && !this.panZoomLocked) {
|
||||
this.$emit('resetImage');
|
||||
}
|
||||
},
|
||||
cursorStates(states) {
|
||||
this.$emit('cursorsUpdated', states);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('keydown', this.handleKeyDown);
|
||||
document.addEventListener('keyup', this.handleKeyUp);
|
||||
this.clearWheelZoom = _.debounce(this.clearWheelZoom, 600);
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener('keydown', this.handleKeyDown);
|
||||
document.removeEventListener('keyup', this.handleKeyUp);
|
||||
},
|
||||
methods: {
|
||||
handleResetImage() {
|
||||
this.$emit('resetImage');
|
||||
},
|
||||
handleUpdatePanZoom(options) {
|
||||
this.$emit('panZoomUpdated', options);
|
||||
},
|
||||
toggleZoomLock() {
|
||||
this.panZoomLocked = !this.panZoomLocked;
|
||||
},
|
||||
notifyFiltersChanged() {
|
||||
this.$emit('filtersUpdated', this.filters);
|
||||
},
|
||||
handleResetFilters() {
|
||||
this.filters = DEFAULT_FILTER_VALUES;
|
||||
this.notifyFiltersChanged();
|
||||
},
|
||||
limitZoomRange(factor) {
|
||||
return Math.min(Math.max(ZOOM_LIMITS_MIN_DEFAULT, factor), ZOOM_LIMITS_MAX_DEFAULT);
|
||||
},
|
||||
// used to increment the zoom without knowledge of current level
|
||||
processZoom(increment, userCoordX, userCoordY) {
|
||||
const newFactor = this.limitZoomRange(this.zoomFactor + increment);
|
||||
this.zoomImage(newFactor, userCoordX, userCoordY);
|
||||
},
|
||||
zoomImage(newScaleFactor, screenClientX, screenClientY) {
|
||||
if (!(newScaleFactor || Number.isInteger(newScaleFactor))) {
|
||||
console.error('Scale factor provided is invalid');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (newScaleFactor > ZOOM_LIMITS_MAX_DEFAULT) {
|
||||
newScaleFactor = ZOOM_LIMITS_MAX_DEFAULT;
|
||||
}
|
||||
|
||||
if (newScaleFactor <= 0 || newScaleFactor <= ZOOM_LIMITS_MIN_DEFAULT) {
|
||||
return this.handleResetImage();
|
||||
}
|
||||
|
||||
this.handleUpdatePanZoom({
|
||||
newScaleFactor,
|
||||
screenClientX,
|
||||
screenClientY
|
||||
});
|
||||
},
|
||||
wheelZoom(e) {
|
||||
// only use x,y coordinates on scrolling in
|
||||
if (this.wheelZooming === false && e.deltaY > 0) {
|
||||
this.wheelZooming = true;
|
||||
// grab first x,y coordinates
|
||||
this.processZoom(e.deltaY * ZOOM_WHEEL_SENSITIVITY_REDUCTION, e.clientX, e.clientY);
|
||||
} else {
|
||||
// ignore subsequent event x,y so scroll drift doesn't occur
|
||||
this.processZoom(e.deltaY * ZOOM_WHEEL_SENSITIVITY_REDUCTION);
|
||||
}
|
||||
|
||||
// debounced method that will only fire after the scroll series is complete
|
||||
this.clearWheelZoom();
|
||||
},
|
||||
/* debounced method so that wheelZooming state will
|
||||
** remain true through a zoom event series
|
||||
*/
|
||||
clearWheelZoom() {
|
||||
this.wheelZooming = false;
|
||||
},
|
||||
handleKeyDown(event) {
|
||||
if (event.key === 'Alt') {
|
||||
this.altPressed = true;
|
||||
}
|
||||
|
||||
if (event.metaKey) {
|
||||
this.metaPressed = true;
|
||||
}
|
||||
|
||||
if (event.shiftKey) {
|
||||
this.shiftPressed = true;
|
||||
}
|
||||
},
|
||||
handleKeyUp(event) {
|
||||
if (event.key === 'Alt') {
|
||||
this.altPressed = false;
|
||||
}
|
||||
|
||||
this.shiftPressed = false;
|
||||
if (!event.metaKey) {
|
||||
this.metaPressed = false;
|
||||
}
|
||||
},
|
||||
zoomIn() {
|
||||
this.processZoom(ZOOM_STEP);
|
||||
},
|
||||
zoomOut() {
|
||||
this.processZoom(-ZOOM_STEP);
|
||||
},
|
||||
// attached to onClick listener in ImageryView
|
||||
handlePanZoomClick(e) {
|
||||
if (this.altPressed) {
|
||||
return this.$emit('startPan', e);
|
||||
}
|
||||
|
||||
if (!(this.metaPressed && e.button === 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newScaleFactor = this.zoomFactor + (this.shiftPressed ? -ZOOM_STEP : ZOOM_STEP);
|
||||
this.zoomImage(newScaleFactor, e.clientX, e.clientY);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -29,78 +29,59 @@
|
||||
@mouseover="focusElement"
|
||||
>
|
||||
<div class="c-imagery__main-image-wrapper has-local-controls">
|
||||
<ImageControls
|
||||
ref="imageControls"
|
||||
:zoom-factor="zoomFactor"
|
||||
:image-url="imageUrl"
|
||||
@resetImage="resetImage"
|
||||
@panZoomUpdated="handlePanZoomUpdate"
|
||||
@filtersUpdated="setFilters"
|
||||
@cursorsUpdated="setCursorStates"
|
||||
@startPan="startPan"
|
||||
/>
|
||||
|
||||
<div class="h-local-controls h-local-controls--overlay-content c-local-controls--show-on-hover c-image-controls__controls">
|
||||
<span
|
||||
class="c-image-controls__sliders"
|
||||
draggable="true"
|
||||
@dragstart="startDrag"
|
||||
>
|
||||
<div class="c-image-controls__slider-wrapper icon-brightness">
|
||||
<input
|
||||
v-model="filters.brightness"
|
||||
type="range"
|
||||
min="0"
|
||||
max="500"
|
||||
>
|
||||
</div>
|
||||
<div class="c-image-controls__slider-wrapper icon-contrast">
|
||||
<input
|
||||
v-model="filters.contrast"
|
||||
type="range"
|
||||
min="0"
|
||||
max="500"
|
||||
>
|
||||
</div>
|
||||
</span>
|
||||
<span class="t-reset-btn-holder c-imagery__lc__reset-btn c-image-controls__btn-reset">
|
||||
<a
|
||||
class="s-icon-button icon-reset t-btn-reset"
|
||||
@click="filters={brightness: 100, contrast: 100}"
|
||||
></a>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
ref="imageBG"
|
||||
class="c-imagery__main-image__bg"
|
||||
:class="{
|
||||
'paused unnsynced': isPaused && !isFixed,
|
||||
'stale': false,
|
||||
'pannable': cursorStates.isPannable,
|
||||
'cursor-zoom-in': cursorStates.showCursorZoomIn,
|
||||
'cursor-zoom-out': cursorStates.showCursorZoomOut
|
||||
}"
|
||||
:class="{'paused unnsynced': isPaused && !isFixed,'stale':false }"
|
||||
@click="expand"
|
||||
>
|
||||
<div
|
||||
v-if="zoomFactor > 1"
|
||||
class="c-imagery__hints"
|
||||
>Alt-drag to pan</div>
|
||||
<div
|
||||
ref="focusedImageWrapper"
|
||||
class="image-wrapper"
|
||||
:style="{
|
||||
'width': `${sizedImageWidth}px`,
|
||||
'height': `${sizedImageHeight}px`
|
||||
'width': `${sizedImageDimensions.width}px`,
|
||||
'height': `${sizedImageDimensions.height}px`
|
||||
}"
|
||||
@mousedown="handlePanZoomClick"
|
||||
>
|
||||
<img
|
||||
ref="focusedImage"
|
||||
class="c-imagery__main-image__image js-imageryView-image "
|
||||
class="c-imagery__main-image__image js-imageryView-image"
|
||||
:src="imageUrl"
|
||||
:draggable="!isSelectable"
|
||||
:style="{
|
||||
'filter': `brightness(${filters.brightness}%) contrast(${filters.contrast}%)`
|
||||
}"
|
||||
:data-openmct-image-timestamp="time"
|
||||
:data-openmct-object-keystring="keyString"
|
||||
>
|
||||
<div
|
||||
v-if="imageUrl"
|
||||
ref="focusedImageElement"
|
||||
class="c-imagery__main-image__background-image"
|
||||
:draggable="!isSelectable"
|
||||
:style="{
|
||||
'filter': `brightness(${filters.brightness}%) contrast(${filters.contrast}%)`,
|
||||
'background-image':
|
||||
`${imageUrl ? (
|
||||
`url(${imageUrl}),
|
||||
repeating-linear-gradient(
|
||||
45deg,
|
||||
transparent,
|
||||
transparent 4px,
|
||||
rgba(125,125,125,.2) 4px,
|
||||
rgba(125,125,125,.2) 8px
|
||||
)`
|
||||
) : ''}`,
|
||||
'transform': `scale(${zoomFactor}) translate(${imageTranslateX}px, ${imageTranslateY}px)`,
|
||||
'transition': `${!pan && animateZoom ? 'transform 250ms ease-in' : 'initial'}`,
|
||||
'width': `${sizedImageWidth}px`,
|
||||
'height': `${sizedImageHeight}px`,
|
||||
|
||||
}"
|
||||
></div>
|
||||
<Compass
|
||||
v-if="shouldDisplayCompass"
|
||||
:compass-rose-sizing-classes="compassRoseSizingClasses"
|
||||
@@ -153,7 +134,7 @@
|
||||
v-if="!isFixed"
|
||||
class="c-button icon-pause pause-play"
|
||||
:class="{'is-paused': isPaused}"
|
||||
@click="paused(!isPaused)"
|
||||
@click="paused(!isPaused, 'button')"
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -175,7 +156,7 @@
|
||||
:key="image.url + image.time"
|
||||
class="c-imagery__thumb c-thumb"
|
||||
:class="{ selected: focusedImageIndex === index && isPaused }"
|
||||
@click="thumbnailClicked(index)"
|
||||
@click="setFocusedImage(index, thumbnailClick)"
|
||||
>
|
||||
<a
|
||||
href=""
|
||||
@@ -201,13 +182,12 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import eventHelpers from '../lib/eventHelpers';
|
||||
import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
|
||||
import RelatedTelemetry from './RelatedTelemetry/RelatedTelemetry';
|
||||
import Compass from './Compass/Compass.vue';
|
||||
import ImageControls from './ImageControls.vue';
|
||||
|
||||
import imageryData from "../../imagery/mixins/imageryData";
|
||||
|
||||
const REFRESH_CSS_MS = 500;
|
||||
@@ -227,12 +207,9 @@ const ARROW_LEFT = 37;
|
||||
|
||||
const SCROLL_LATENCY = 250;
|
||||
|
||||
const ZOOM_SCALE_DEFAULT = 1;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Compass,
|
||||
ImageControls
|
||||
Compass
|
||||
},
|
||||
mixins: [imageryData],
|
||||
inject: ['openmct', 'domainObject', 'objectPath', 'currentView'],
|
||||
@@ -255,6 +232,10 @@ export default {
|
||||
timeSystem: timeSystem,
|
||||
keyString: undefined,
|
||||
autoScroll: true,
|
||||
filters: {
|
||||
brightness: 100,
|
||||
contrast: 100
|
||||
},
|
||||
thumbnailClick: THUMBNAIL_CLICKED,
|
||||
isPaused: false,
|
||||
refreshCSS: false,
|
||||
@@ -266,37 +247,19 @@ export default {
|
||||
focusedImageNaturalAspectRatio: undefined,
|
||||
imageContainerWidth: undefined,
|
||||
imageContainerHeight: undefined,
|
||||
sizedImageWidth: 0,
|
||||
sizedImageHeight: 0,
|
||||
lockCompass: true,
|
||||
resizingWindow: false,
|
||||
timeContext: undefined,
|
||||
zoomFactor: ZOOM_SCALE_DEFAULT,
|
||||
filters: {
|
||||
brightness: 100,
|
||||
contrast: 100
|
||||
},
|
||||
cursorStates: {
|
||||
isPannable: false,
|
||||
showCursorZoomIn: false,
|
||||
showCursorZoomOut: false,
|
||||
modifierKeyPressed: false
|
||||
},
|
||||
imageTranslateX: 0,
|
||||
imageTranslateY: 0,
|
||||
pan: undefined,
|
||||
animateZoom: true,
|
||||
imagePanned: false
|
||||
timeContext: undefined
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
compassRoseSizingClasses() {
|
||||
let compassRoseSizingClasses = '';
|
||||
if (this.sizedImageWidth < 300) {
|
||||
if (this.sizedImageDimensions.width < 300) {
|
||||
compassRoseSizingClasses = '--rose-small --rose-min';
|
||||
} else if (this.sizedImageWidth < 500) {
|
||||
} else if (this.sizedImageDimensions.width < 500) {
|
||||
compassRoseSizingClasses = '--rose-small';
|
||||
} else if (this.sizedImageWidth > 1000) {
|
||||
} else if (this.sizedImageDimensions.width > 1000) {
|
||||
compassRoseSizingClasses = '--rose-max';
|
||||
}
|
||||
|
||||
@@ -365,18 +328,10 @@ export default {
|
||||
return result;
|
||||
},
|
||||
shouldDisplayCompass() {
|
||||
const imageHeightAndWidth = this.sizedImageHeight !== 0
|
||||
&& this.sizedImageWidth !== 0;
|
||||
|
||||
const display = this.focusedImage !== undefined
|
||||
return this.focusedImage !== undefined
|
||||
&& this.focusedImageNaturalAspectRatio !== undefined
|
||||
&& this.imageContainerWidth !== undefined
|
||||
&& this.imageContainerHeight !== undefined
|
||||
&& imageHeightAndWidth
|
||||
&& this.zoomFactor === 1
|
||||
&& this.imagePanned !== true;
|
||||
|
||||
return display;
|
||||
&& this.imageContainerHeight !== undefined;
|
||||
},
|
||||
isSpacecraftPositionFresh() {
|
||||
let isFresh = undefined;
|
||||
@@ -438,6 +393,20 @@ export default {
|
||||
|
||||
return isFresh;
|
||||
},
|
||||
sizedImageDimensions() {
|
||||
let sizedImageDimensions = {};
|
||||
if ((this.imageContainerWidth / this.imageContainerHeight) > this.focusedImageNaturalAspectRatio) {
|
||||
// container is wider than image
|
||||
sizedImageDimensions.width = this.imageContainerHeight * this.focusedImageNaturalAspectRatio;
|
||||
sizedImageDimensions.height = this.imageContainerHeight;
|
||||
} else {
|
||||
// container is taller than image
|
||||
sizedImageDimensions.width = this.imageContainerWidth;
|
||||
sizedImageDimensions.height = this.imageContainerWidth / this.focusedImageNaturalAspectRatio;
|
||||
}
|
||||
|
||||
return sizedImageDimensions;
|
||||
},
|
||||
isFixed() {
|
||||
let clock;
|
||||
if (this.timeContext) {
|
||||
@@ -447,16 +416,6 @@ export default {
|
||||
}
|
||||
|
||||
return clock === undefined;
|
||||
},
|
||||
isSelectable() {
|
||||
return true;
|
||||
|
||||
},
|
||||
sizedImageDimensions() {
|
||||
return {
|
||||
width: this.sizedImageWidth,
|
||||
height: this.sizedImageHeight
|
||||
};
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -465,35 +424,16 @@ export default {
|
||||
const newSize = newHistory.length;
|
||||
let imageIndex;
|
||||
if (this.focusedImageTimestamp !== undefined) {
|
||||
const foundImageIndex = newHistory.findIndex(img => img.time === this.focusedImageTimestamp);
|
||||
imageIndex = foundImageIndex > -1
|
||||
? foundImageIndex
|
||||
: newSize - 1;
|
||||
const foundImageIndex = this.imageHistory.findIndex(image => {
|
||||
return image.time === this.focusedImageTimestamp;
|
||||
});
|
||||
imageIndex = foundImageIndex > -1 ? foundImageIndex : newSize - 1;
|
||||
} else {
|
||||
imageIndex = newSize > 0
|
||||
? newSize - 1
|
||||
: undefined;
|
||||
imageIndex = newSize > 0 ? newSize - 1 : undefined;
|
||||
}
|
||||
|
||||
this.nextImageIndex = imageIndex;
|
||||
|
||||
if (this.previousFocusedImage && newHistory.length) {
|
||||
const matchIndex = this.matchIndexOfPreviousImage(
|
||||
this.previousFocusedImage,
|
||||
newHistory
|
||||
);
|
||||
|
||||
if (matchIndex > -1) {
|
||||
this.setFocusedImage(matchIndex);
|
||||
} else {
|
||||
this.paused();
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.isPaused) {
|
||||
this.setFocusedImage(imageIndex);
|
||||
this.scrollToRight();
|
||||
}
|
||||
this.setFocusedImage(imageIndex, false);
|
||||
this.scrollToRight();
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
@@ -505,10 +445,6 @@ export default {
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
eventHelpers.extend(this);
|
||||
this.focusedImageWrapper = this.$refs.focusedImageWrapper;
|
||||
this.focusedImageElement = this.$refs.focusedImageElement;
|
||||
|
||||
//We only need to use this till the user focuses an image manually
|
||||
if (this.focusedImageTimestamp !== undefined) {
|
||||
this.isPaused = true;
|
||||
@@ -549,7 +485,6 @@ export default {
|
||||
this.thumbWrapperResizeObserver.observe(this.$refs.thumbsWrapper);
|
||||
}
|
||||
|
||||
this.listenTo(this.focusedImageWrapper, 'wheel', this.wheelZoom, this);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.stopFollowingTimeContext();
|
||||
@@ -574,9 +509,6 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.stopListening(this.focusedImageWrapper, 'wheel', this.wheelZoom, this);
|
||||
|
||||
},
|
||||
methods: {
|
||||
setTimeContext() {
|
||||
@@ -593,11 +525,6 @@ export default {
|
||||
}
|
||||
},
|
||||
expand() {
|
||||
// check for modifier keys so it doesnt interfere with the layout
|
||||
if (this.cursorStates.modifierKeyPressed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const actionCollection = this.openmct.actions.getActionsCollection(this.objectPath, this.currentView);
|
||||
const visibleActions = actionCollection.getVisibleActions();
|
||||
const viewLargeAction = visibleActions
|
||||
@@ -703,7 +630,6 @@ export default {
|
||||
focusElement() {
|
||||
this.$el.focus();
|
||||
},
|
||||
|
||||
handleScroll() {
|
||||
const thumbsWrapper = this.$refs.thumbsWrapper;
|
||||
if (!thumbsWrapper || this.resizingWindow) {
|
||||
@@ -714,15 +640,20 @@ export default {
|
||||
const disableScroll = scrollWidth > Math.ceil(scrollLeft + clientWidth);
|
||||
this.autoScroll = !disableScroll;
|
||||
},
|
||||
paused(state) {
|
||||
this.isPaused = Boolean(state);
|
||||
paused(state, type) {
|
||||
this.isPaused = state;
|
||||
|
||||
if (!state) {
|
||||
this.previousFocusedImage = null;
|
||||
this.setFocusedImage(this.nextImageIndex);
|
||||
this.autoScroll = true;
|
||||
this.scrollToRight();
|
||||
if (type === 'button') {
|
||||
this.setFocusedImage(this.imageHistory.length - 1);
|
||||
}
|
||||
|
||||
if (this.nextImageIndex) {
|
||||
this.setFocusedImage(this.nextImageIndex);
|
||||
delete this.nextImageIndex;
|
||||
}
|
||||
|
||||
this.autoScroll = true;
|
||||
this.scrollToRight();
|
||||
},
|
||||
scrollToFocused() {
|
||||
const thumbsWrapper = this.$refs.thumbsWrapper;
|
||||
@@ -761,24 +692,51 @@ export default {
|
||||
&& x.time === previous.time
|
||||
));
|
||||
},
|
||||
thumbnailClicked(index) {
|
||||
this.setFocusedImage(index);
|
||||
this.paused(true);
|
||||
|
||||
this.setPreviousFocusedImage(index);
|
||||
},
|
||||
setPreviousFocusedImage(index) {
|
||||
this.focusedImageTimestamp = undefined;
|
||||
this.previousFocusedImage = this.imageHistory[index]
|
||||
? JSON.parse(JSON.stringify(this.imageHistory[index]))
|
||||
: undefined;
|
||||
},
|
||||
setFocusedImage(index) {
|
||||
setFocusedImage(index, thumbnailClick = false) {
|
||||
let focusedIndex = index;
|
||||
if (!(Number.isInteger(index) && index > -1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.focusedImageIndex = index;
|
||||
if (thumbnailClick) {
|
||||
//We use the props till the user changes what they want to see
|
||||
this.focusedImageTimestamp = undefined;
|
||||
//set the previousFocusedImage when a user chooses an image
|
||||
this.previousFocusedImage = this.imageHistory[focusedIndex] ? JSON.parse(JSON.stringify(this.imageHistory[focusedIndex])) : undefined;
|
||||
}
|
||||
|
||||
if (this.previousFocusedImage) {
|
||||
// determine if the previous image exists in the new bounds of imageHistory
|
||||
if (!thumbnailClick) {
|
||||
const matchIndex = this.matchIndexOfPreviousImage(
|
||||
this.previousFocusedImage,
|
||||
this.imageHistory
|
||||
);
|
||||
focusedIndex = matchIndex > -1 ? matchIndex : this.imageHistory.length - 1;
|
||||
}
|
||||
|
||||
if (!(this.isPaused || thumbnailClick)
|
||||
|| focusedIndex === this.imageHistory.length - 1) {
|
||||
delete this.previousFocusedImage;
|
||||
}
|
||||
}
|
||||
|
||||
this.focusedImageIndex = focusedIndex;
|
||||
|
||||
//TODO: do we even need this anymore?
|
||||
if (this.isPaused && !thumbnailClick && this.focusedImageTimestamp === undefined) {
|
||||
this.nextImageIndex = focusedIndex;
|
||||
//this could happen if bounds changes
|
||||
if (this.focusedImageIndex > this.imageHistory.length - 1) {
|
||||
this.focusedImageIndex = focusedIndex;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (thumbnailClick && !this.isPaused) {
|
||||
this.paused(true);
|
||||
}
|
||||
},
|
||||
trackDuration() {
|
||||
if (this.canTrackDuration) {
|
||||
@@ -816,7 +774,7 @@ export default {
|
||||
|
||||
let index = this.focusedImageIndex;
|
||||
|
||||
this.thumbnailClicked(++index);
|
||||
this.setFocusedImage(++index, THUMBNAIL_CLICKED);
|
||||
if (index === this.imageHistory.length - 1) {
|
||||
this.paused(false);
|
||||
}
|
||||
@@ -829,50 +787,14 @@ export default {
|
||||
let index = this.focusedImageIndex;
|
||||
|
||||
if (index === this.imageHistory.length - 1) {
|
||||
this.thumbnailClicked(this.imageHistory.length - 2);
|
||||
this.setFocusedImage(this.imageHistory.length - 2, THUMBNAIL_CLICKED);
|
||||
} else {
|
||||
this.thumbnailClicked(--index);
|
||||
this.setFocusedImage(--index, THUMBNAIL_CLICKED);
|
||||
}
|
||||
},
|
||||
resetImage() {
|
||||
this.imagePanned = false;
|
||||
this.zoomFactor = ZOOM_SCALE_DEFAULT;
|
||||
this.imageTranslateX = 0;
|
||||
this.imageTranslateY = 0;
|
||||
},
|
||||
handlePanZoomUpdate({ newScaleFactor, screenClientX, screenClientY }) {
|
||||
if (!this.isPaused) {
|
||||
this.paused(true);
|
||||
}
|
||||
|
||||
if (!(screenClientX || screenClientY)) {
|
||||
return this.updatePanZoom(newScaleFactor, 0, 0);
|
||||
}
|
||||
|
||||
// handle mouse events
|
||||
const imageRect = this.focusedImageWrapper.getBoundingClientRect();
|
||||
const imageContainerX = screenClientX - imageRect.left;
|
||||
const imageContainerY = screenClientY - imageRect.top;
|
||||
const offsetFromCenterX = (imageRect.width / 2) - imageContainerX;
|
||||
const offsetFromCenterY = (imageRect.height / 2) - imageContainerY;
|
||||
|
||||
this.updatePanZoom(newScaleFactor, offsetFromCenterX, offsetFromCenterY);
|
||||
},
|
||||
updatePanZoom(newScaleFactor, offsetFromCenterX, offsetFromCenterY) {
|
||||
const currentScale = this.zoomFactor;
|
||||
const previousTranslateX = this.imageTranslateX;
|
||||
const previousTranslateY = this.imageTranslateY;
|
||||
|
||||
const offsetXInOriginalScale = offsetFromCenterX / currentScale;
|
||||
const offsetYInOriginalScale = offsetFromCenterY / currentScale;
|
||||
const translateX = offsetXInOriginalScale + previousTranslateX;
|
||||
const translateY = offsetYInOriginalScale + previousTranslateY;
|
||||
this.imageTranslateX = translateX;
|
||||
this.imageTranslateY = translateY;
|
||||
this.zoomFactor = newScaleFactor;
|
||||
},
|
||||
handlePanZoomClick(e) {
|
||||
this.$refs.imageControls.handlePanZoomClick(e);
|
||||
startDrag(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
},
|
||||
arrowDownHandler(event) {
|
||||
let key = event.keyCode;
|
||||
@@ -935,7 +857,7 @@ export default {
|
||||
|
||||
// TODO - should probably cache this
|
||||
img.addEventListener('load', () => {
|
||||
this.setSizedImageDimensions();
|
||||
this.focusedImageNaturalAspectRatio = img.naturalWidth / img.naturalHeight;
|
||||
}, { once: true });
|
||||
},
|
||||
resizeImageContainer() {
|
||||
@@ -950,21 +872,6 @@ export default {
|
||||
if (this.$refs.imageBG.clientHeight !== this.imageContainerHeight) {
|
||||
this.imageContainerHeight = this.$refs.imageBG.clientHeight;
|
||||
}
|
||||
|
||||
this.setSizedImageDimensions();
|
||||
},
|
||||
setSizedImageDimensions() {
|
||||
this.focusedImageNaturalAspectRatio = this.$refs.focusedImage.naturalWidth / this.$refs.focusedImage.naturalHeight;
|
||||
|
||||
if ((this.imageContainerWidth / this.imageContainerHeight) > this.focusedImageNaturalAspectRatio) {
|
||||
// container is wider than image
|
||||
this.sizedImageWidth = this.imageContainerHeight * this.focusedImageNaturalAspectRatio;
|
||||
this.sizedImageHeight = this.imageContainerHeight;
|
||||
} else {
|
||||
// container is taller than image
|
||||
this.sizedImageWidth = this.imageContainerWidth;
|
||||
this.sizedImageHeight = this.imageContainerWidth / this.focusedImageNaturalAspectRatio;
|
||||
}
|
||||
},
|
||||
handleThumbWindowResizeStart() {
|
||||
if (!this.autoScroll) {
|
||||
@@ -983,73 +890,6 @@ export default {
|
||||
this.$nextTick(() => {
|
||||
this.resizingWindow = false;
|
||||
});
|
||||
},
|
||||
// debounced method
|
||||
clearWheelZoom() {
|
||||
this.$refs.imageControls.clearWheelZoom();
|
||||
},
|
||||
wheelZoom(e) {
|
||||
e.preventDefault();
|
||||
if (!this.isPaused) {
|
||||
this.paused(true);
|
||||
}
|
||||
|
||||
this.$refs.imageControls.wheelZoom(e);
|
||||
},
|
||||
startPan(e) {
|
||||
e.preventDefault();
|
||||
if (!this.pan && this.zoomFactor > 1) {
|
||||
this.animateZoom = false;
|
||||
this.imagePanned = true;
|
||||
this.pan = {
|
||||
x: e.clientX,
|
||||
y: e.clientY
|
||||
};
|
||||
this.listenTo(window, 'mouseup', this.onMouseUp, this);
|
||||
this.listenTo(window, 'mousemove', this.trackMousePosition, this);
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
trackMousePosition(e) {
|
||||
if (!e.altKey) {
|
||||
return this.onMouseUp(e);
|
||||
}
|
||||
|
||||
this.updatePan(e);
|
||||
e.preventDefault();
|
||||
|
||||
},
|
||||
updatePan(e) {
|
||||
if (!this.pan) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dX = e.clientX - this.pan.x;
|
||||
const dY = e.clientY - this.pan.y;
|
||||
this.pan = {
|
||||
x: e.clientX,
|
||||
y: e.clientY
|
||||
};
|
||||
this.updatePanZoom(this.zoomFactor, dX, dY);
|
||||
},
|
||||
endPan() {
|
||||
this.pan = undefined;
|
||||
this.animateZoom = true;
|
||||
},
|
||||
onMouseUp(event) {
|
||||
this.stopListening(window, 'mouseup', this.onMouseUp, this);
|
||||
this.stopListening(window, 'mousemove', this.trackMousePosition, this);
|
||||
|
||||
if (this.pan) {
|
||||
return this.endPan(event);
|
||||
}
|
||||
},
|
||||
setFilters(filtersObj) {
|
||||
this.filters = filtersObj;
|
||||
},
|
||||
setCursorStates(states) {
|
||||
this.cursorStates = states;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -18,11 +18,6 @@
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.image-wrapper {
|
||||
overflow: visible clip;
|
||||
background-image: repeating-linear-gradient(45deg, transparent, transparent 4px, rgba(125, 125, 125, 0.2) 4px, rgba(125, 125, 125, 0.2) 8px);
|
||||
}
|
||||
|
||||
&__main-image {
|
||||
&__bg {
|
||||
background-color: $colorPlotBg;
|
||||
@@ -32,46 +27,18 @@
|
||||
justify-content: center;
|
||||
flex: 1 1 auto;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
|
||||
&.unnsynced{
|
||||
@include sUnsynced();
|
||||
}
|
||||
&.cursor-zoom-in {
|
||||
cursor: zoom-in;
|
||||
}
|
||||
&.cursor-zoom-out {
|
||||
cursor: zoom-out;
|
||||
}
|
||||
&.pannable {
|
||||
@include cursorGrab();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
&__background-image {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
&__image {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
visibility: hidden;
|
||||
display: contents;
|
||||
}
|
||||
}
|
||||
|
||||
&__hints {
|
||||
$m: $interiorMargin;
|
||||
background: rgba(black, 0.2);
|
||||
border-radius: $smallCr;
|
||||
padding: 2px $interiorMargin;
|
||||
position: absolute;
|
||||
right: $m;
|
||||
top: $m;
|
||||
opacity: 0.9;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&__control-bar,
|
||||
&__time {
|
||||
display: flex;
|
||||
@@ -210,8 +177,8 @@
|
||||
z-index: 2;
|
||||
background: $colorLocalControlOvrBg;
|
||||
border-radius: $basicCr;
|
||||
max-width: 250px;
|
||||
min-width: 170px;
|
||||
max-width: 200px;
|
||||
min-width: 70px;
|
||||
width: 35%;
|
||||
align-items: center;
|
||||
padding: $interiorMargin $interiorMarginLg;
|
||||
@@ -235,7 +202,6 @@
|
||||
|
||||
&__lc {
|
||||
&__reset-btn {
|
||||
// Span that holds bracket graphics and button
|
||||
$bc: $scrollbarTrackColorBg;
|
||||
|
||||
&:before,
|
||||
@@ -256,50 +222,18 @@
|
||||
border-bottom: 1px solid $bc;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.c-icon-link {
|
||||
color: $colorBtnFg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.c-image-controls {
|
||||
// Brightness/contrast
|
||||
|
||||
&__controls {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
|
||||
> * + * {
|
||||
margin-top: $interiorMargin;
|
||||
}
|
||||
|
||||
[class*='c-button'] { flex: 0 0 auto; }
|
||||
}
|
||||
|
||||
&__control,
|
||||
&__input {
|
||||
// Sliders and reset element
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
&:before {
|
||||
color: rgba($colorMenuFg, 0.5);
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&__input {
|
||||
// A wrapper is needed to add the type icon to left of each control
|
||||
|
||||
input[type='range'] {
|
||||
//width: 100%; // Do we need this?
|
||||
}
|
||||
}
|
||||
|
||||
&__zoom {
|
||||
> * + * { margin-left: $interiorMargin; }
|
||||
margin-right: $interiorMargin; // Need some extra space due to proximity to close button
|
||||
}
|
||||
|
||||
&__sliders {
|
||||
@@ -312,6 +246,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__slider-wrapper {
|
||||
// A wrapper is needed to add the type icon to left of each range input
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&:before {
|
||||
color: rgba($colorMenuFg, 0.5);
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
|
||||
input[type='range'] {
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
&__btn-reset {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2021, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([], function () {
|
||||
const helperFunctions = {
|
||||
listenTo: function (object, event, callback, context) {
|
||||
if (!this._listeningTo) {
|
||||
this._listeningTo = [];
|
||||
}
|
||||
|
||||
const listener = {
|
||||
object: object,
|
||||
event: event,
|
||||
callback: callback,
|
||||
context: context,
|
||||
_cb: context ? callback.bind(context) : callback
|
||||
};
|
||||
if (object.$watch && event.indexOf('change:') === 0) {
|
||||
const scopePath = event.replace('change:', '');
|
||||
listener.unlisten = object.$watch(scopePath, listener._cb, true);
|
||||
} else if (object.$on) {
|
||||
listener.unlisten = object.$on(event, listener._cb);
|
||||
} else if (object.addEventListener) {
|
||||
object.addEventListener(event, listener._cb);
|
||||
} else {
|
||||
object.on(event, listener._cb);
|
||||
}
|
||||
|
||||
this._listeningTo.push(listener);
|
||||
},
|
||||
|
||||
stopListening: function (object, event, callback, context) {
|
||||
if (!this._listeningTo) {
|
||||
this._listeningTo = [];
|
||||
}
|
||||
|
||||
this._listeningTo.filter(function (listener) {
|
||||
if (object && object !== listener.object) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event && event !== listener.event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (callback && callback !== listener.callback) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (context && context !== listener.context) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.map(function (listener) {
|
||||
if (listener.unlisten) {
|
||||
listener.unlisten();
|
||||
} else if (listener.object.removeEventListener) {
|
||||
listener.object.removeEventListener(listener.event, listener._cb);
|
||||
} else {
|
||||
listener.object.off(listener.event, listener._cb);
|
||||
}
|
||||
|
||||
return listener;
|
||||
})
|
||||
.forEach(function (listener) {
|
||||
this._listeningTo.splice(this._listeningTo.indexOf(listener), 1);
|
||||
}, this);
|
||||
},
|
||||
|
||||
extend: function (object) {
|
||||
object.listenTo = helperFunctions.listenTo;
|
||||
object.stopListening = helperFunctions.stopListening;
|
||||
}
|
||||
};
|
||||
|
||||
return helperFunctions;
|
||||
});
|
||||
@@ -483,42 +483,6 @@ describe("The Imagery View Layouts", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
xit('should change the image zoom factor when using the zoom buttons', async (done) => {
|
||||
await Vue.nextTick();
|
||||
let imageSizeBefore;
|
||||
let imageSizeAfter;
|
||||
|
||||
// test clicking the zoom in button
|
||||
imageSizeBefore = parent.querySelector('.c-imagery_main-image_background-image').getBoundingClientRect();
|
||||
parent.querySelector('.t-btn-zoom-in').click();
|
||||
await Vue.nextTick();
|
||||
imageSizeAfter = parent.querySelector('.c-imagery_main-image_background-image').getBoundingClientRect();
|
||||
expect(imageSizeAfter.height).toBeGreaterThan(imageSizeBefore.height);
|
||||
expect(imageSizeAfter.width).toBeGreaterThan(imageSizeBefore.width);
|
||||
// test clicking the zoom out button
|
||||
imageSizeBefore = parent.querySelector('.c-imagery_main-image_background-image').getBoundingClientRect();
|
||||
parent.querySelector('.t-btn-zoom-out').click();
|
||||
await Vue.nextTick();
|
||||
imageSizeAfter = parent.querySelector('.c-imagery_main-image_background-image').getBoundingClientRect();
|
||||
expect(imageSizeAfter.height).toBeLessThan(imageSizeBefore.height);
|
||||
expect(imageSizeAfter.width).toBeLessThan(imageSizeBefore.width);
|
||||
done();
|
||||
});
|
||||
xit('should reset the zoom factor on the image when clicking the zoom button', async (done) => {
|
||||
await Vue.nextTick();
|
||||
// test clicking the zoom reset button
|
||||
// zoom in to scale up the image dimensions
|
||||
parent.querySelector('.t-btn-zoom-in').click();
|
||||
await Vue.nextTick();
|
||||
let imageSizeBefore = parent.querySelector('.c-imagery_main-image_background-image').getBoundingClientRect();
|
||||
await Vue.nextTick();
|
||||
parent.querySelector('.t-btn-zoom-reset').click();
|
||||
let imageSizeAfter = parent.querySelector('.c-imagery_main-image_background-image').getBoundingClientRect();
|
||||
expect(imageSizeAfter.height).toBeLessThan(imageSizeBefore.height);
|
||||
expect(imageSizeAfter.width).toBeLessThan(imageSizeBefore.width);
|
||||
done();
|
||||
});
|
||||
|
||||
it('clear data action is installed', () => {
|
||||
expect(clearDataAction).toBeDefined();
|
||||
});
|
||||
|
||||
@@ -90,17 +90,6 @@ export default class LinkAction {
|
||||
|
||||
validate(currentParent) {
|
||||
return (data) => {
|
||||
|
||||
// default current parent to ROOT, if it's undefined, then it's a root level item
|
||||
if (currentParent === undefined) {
|
||||
currentParent = {
|
||||
identifier: {
|
||||
key: 'ROOT',
|
||||
namespace: ''
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const parentCandidate = data.value[0];
|
||||
const currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier);
|
||||
const parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier);
|
||||
|
||||
@@ -76,9 +76,6 @@
|
||||
<div
|
||||
ref="chartContainer"
|
||||
class="gl-plot-chart-wrapper"
|
||||
:class="[
|
||||
{ 'alt-pressed': altPressed },
|
||||
]"
|
||||
>
|
||||
<mct-chart
|
||||
:rectangles="rectangles"
|
||||
@@ -233,7 +230,6 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
altPressed: false,
|
||||
highlights: [],
|
||||
lockHighlightPoint: false,
|
||||
tickWidth: 0,
|
||||
@@ -272,8 +268,6 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('keydown', this.handleKeyDown);
|
||||
document.addEventListener('keyup', this.handleKeyUp);
|
||||
eventHelpers.extend(this);
|
||||
this.updateRealTime = this.updateRealTime.bind(this);
|
||||
this.updateDisplayBounds = this.updateDisplayBounds.bind(this);
|
||||
@@ -305,21 +299,9 @@ export default {
|
||||
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener('keydown', this.handleKeyDown);
|
||||
document.removeEventListener('keyup', this.handleKeyUp);
|
||||
this.destroy();
|
||||
},
|
||||
methods: {
|
||||
handleKeyDown(event) {
|
||||
if (event.key === 'Alt') {
|
||||
this.altPressed = true;
|
||||
}
|
||||
},
|
||||
handleKeyUp(event) {
|
||||
if (event.key === 'Alt') {
|
||||
this.altPressed = false;
|
||||
}
|
||||
},
|
||||
setTimeContext() {
|
||||
this.stopFollowingTimeContext();
|
||||
|
||||
@@ -661,7 +643,7 @@ export default {
|
||||
this.positionOverElement = {
|
||||
x: event.clientX - this.chartElementBounds.left,
|
||||
y: this.chartElementBounds.height
|
||||
- (event.clientY - this.chartElementBounds.top)
|
||||
- (event.clientY - this.chartElementBounds.top)
|
||||
};
|
||||
|
||||
this.positionOverPlot = {
|
||||
|
||||
@@ -112,10 +112,6 @@ export default {
|
||||
mounted() {
|
||||
eventHelpers.extend(this);
|
||||
|
||||
if (!this.axisType) {
|
||||
throw new Error("axis-type prop expected");
|
||||
}
|
||||
|
||||
this.axis = this.getAxisFromConfig();
|
||||
|
||||
this.tickCount = 4;
|
||||
@@ -130,16 +126,15 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
getAxisFromConfig() {
|
||||
const configId = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||
|
||||
/** @type {import('./configuration/PlotConfigurationModel').default} */
|
||||
let config = configStore.get(configId);
|
||||
|
||||
if (!config) {
|
||||
throw new Error('config is missing');
|
||||
if (!this.axisType) {
|
||||
return;
|
||||
}
|
||||
|
||||
return config[this.axisType];
|
||||
const configId = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||
let config = configStore.get(configId);
|
||||
if (config) {
|
||||
return config[this.axisType];
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Determine whether ticks should be regenerated for a given range.
|
||||
@@ -215,8 +210,8 @@ export default {
|
||||
if (this.shouldRegenerateTicks(range, forceRegeneration)) {
|
||||
let newTicks = this.getTicks();
|
||||
this.tickRange = {
|
||||
min: Math.min(...newTicks),
|
||||
max: Math.max(...newTicks),
|
||||
min: Math.min.apply(Math, newTicks),
|
||||
max: Math.max.apply(Math, newTicks),
|
||||
step: newTicks[1] - newTicks[0]
|
||||
};
|
||||
|
||||
|
||||
@@ -23,9 +23,6 @@
|
||||
import eventHelpers from '../lib/eventHelpers';
|
||||
|
||||
export default class MCTChartAlarmLineSet {
|
||||
/**
|
||||
* @param {Bounds} bounds
|
||||
*/
|
||||
constructor(series, chart, offset, bounds) {
|
||||
this.series = series;
|
||||
this.chart = chart;
|
||||
@@ -43,9 +40,6 @@ export default class MCTChartAlarmLineSet {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Bounds} bounds
|
||||
*/
|
||||
updateBounds(bounds) {
|
||||
this.bounds = bounds;
|
||||
this.getLimitPoints(this.series);
|
||||
@@ -112,7 +106,3 @@ export default class MCTChartAlarmLineSet {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@typedef {import('@/api/time/TimeContext').Bounds} Bounds
|
||||
*/
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
import MCTChartSeriesElement from './MCTChartSeriesElement';
|
||||
|
||||
export default class MCTChartLineLinear extends MCTChartSeriesElement {
|
||||
addPoint(point, start) {
|
||||
addPoint(point, start, count) {
|
||||
this.buffer[start] = point.x;
|
||||
this.buffer[start + 1] = point.y;
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ export default class MCTChartLineStepAfter extends MCTChartSeriesElement {
|
||||
return 2 + ((index - 1) * 4);
|
||||
}
|
||||
|
||||
addPoint(point, start) {
|
||||
addPoint(point, start, count) {
|
||||
if (start === 0 && this.count === 0) {
|
||||
// First point is easy.
|
||||
this.buffer[start] = point.x;
|
||||
|
||||
@@ -24,7 +24,7 @@ import MCTChartSeriesElement from './MCTChartSeriesElement';
|
||||
|
||||
// TODO: Is this needed? This is identical to MCTChartLineLinear. Why is it a different class?
|
||||
export default class MCTChartPointSet extends MCTChartSeriesElement {
|
||||
addPoint(point, start) {
|
||||
addPoint(point, start, count) {
|
||||
this.buffer[start] = point.x;
|
||||
this.buffer[start + 1] = point.y;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
|
||||
import eventHelpers from '../lib/eventHelpers';
|
||||
|
||||
/** @abstract */
|
||||
export default class MCTChartSeriesElement {
|
||||
constructor(series, chart, offset) {
|
||||
this.series = series;
|
||||
@@ -73,11 +72,9 @@ export default class MCTChartSeriesElement {
|
||||
}
|
||||
}
|
||||
|
||||
/** @abstract */
|
||||
removePoint(index) {}
|
||||
|
||||
/** @abstract */
|
||||
addPoint(point, index) {}
|
||||
removePoint(point, index, count) {
|
||||
// by default, do nothing.
|
||||
}
|
||||
|
||||
remove(point, index, series) {
|
||||
const vertexCount = this.vertexCountForPointAtIndex(index);
|
||||
@@ -158,18 +155,3 @@ export default class MCTChartSeriesElement {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** @typedef {any} TODO */
|
||||
|
||||
/** @typedef {import('../configuration/PlotSeries').default} PlotSeries */
|
||||
|
||||
/**
|
||||
@typedef {{
|
||||
x: (x: number) => number
|
||||
y: (y: number) => number
|
||||
xVal: (point: Point, pSeries: PlotSeries) => number
|
||||
yVal: (point: Point, pSeries: PlotSeries) => number
|
||||
xKey: TODO
|
||||
yKey: TODO
|
||||
}} Offset
|
||||
*/
|
||||
|
||||
@@ -21,17 +21,11 @@
|
||||
*****************************************************************************/
|
||||
import Model from './Model';
|
||||
|
||||
/**
|
||||
* @template {object} T
|
||||
* @template {object} O
|
||||
* @extends {Model<T, O>}
|
||||
*/
|
||||
export default class Collection extends Model {
|
||||
/** @type {Constructor} */
|
||||
modelClass = Model;
|
||||
|
||||
initialize(options) {
|
||||
super.initialize(options);
|
||||
this.modelClass = Model;
|
||||
if (options.models) {
|
||||
this.models = options.models.map(this.modelFn, this);
|
||||
} else {
|
||||
@@ -113,7 +107,3 @@ export default class Collection extends Model {
|
||||
this.stopListening();
|
||||
}
|
||||
}
|
||||
|
||||
/** @typedef {any} TODO */
|
||||
|
||||
/** @typedef {new (...args: any[]) => object} Constructor */
|
||||
|
||||
@@ -19,47 +19,32 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
class ConfigStore {
|
||||
/** @type {Record<string, Destroyable>} */
|
||||
store = {};
|
||||
|
||||
/**
|
||||
@param {string} id
|
||||
*/
|
||||
deleteStore(id) {
|
||||
const obj = this.store[id];
|
||||
|
||||
if (obj) {
|
||||
if (obj.destroy) {
|
||||
obj.destroy();
|
||||
}
|
||||
|
||||
delete this.store[id];
|
||||
}
|
||||
}
|
||||
|
||||
deleteAll() {
|
||||
Object.keys(this.store).forEach(id => this.deleteStore(id));
|
||||
}
|
||||
|
||||
/**
|
||||
@param {string} id
|
||||
@param {any} config
|
||||
*/
|
||||
add(id, config) {
|
||||
this.store[id] = config;
|
||||
}
|
||||
|
||||
/**
|
||||
@param {string} id
|
||||
*/
|
||||
get(id) {
|
||||
return this.store[id];
|
||||
}
|
||||
function ConfigStore() {
|
||||
this.store = {};
|
||||
}
|
||||
|
||||
ConfigStore.prototype.deleteStore = function (id) {
|
||||
if (this.store[id]) {
|
||||
if (this.store[id].destroy) {
|
||||
this.store[id].destroy();
|
||||
}
|
||||
|
||||
delete this.store[id];
|
||||
}
|
||||
};
|
||||
|
||||
ConfigStore.prototype.deleteAll = function () {
|
||||
Object.keys(this.store).forEach(id => this.deleteStore(id));
|
||||
};
|
||||
|
||||
ConfigStore.prototype.add = function (id, config) {
|
||||
this.store[id] = config;
|
||||
};
|
||||
|
||||
ConfigStore.prototype.get = function (id) {
|
||||
return this.store[id];
|
||||
};
|
||||
|
||||
const STORE = new ConfigStore();
|
||||
|
||||
export default STORE;
|
||||
|
||||
/** @typedef {{destroy?(): void}} Destroyable */
|
||||
|
||||
@@ -42,10 +42,7 @@ export default class LegendModel extends Model {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
defaultModel(options) {
|
||||
defaults(options) {
|
||||
return {
|
||||
position: 'top',
|
||||
expandByDefault: false,
|
||||
|
||||
@@ -20,18 +20,11 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import EventEmitter from 'EventEmitter';
|
||||
import eventHelpers from "../lib/eventHelpers";
|
||||
import _ from 'lodash';
|
||||
|
||||
/**
|
||||
* @template {object} T
|
||||
* @template {object} O
|
||||
*/
|
||||
export default class Model extends EventEmitter {
|
||||
/**
|
||||
* @param {ModelOptions<T, O>} options
|
||||
*/
|
||||
constructor(options) {
|
||||
super();
|
||||
|
||||
@@ -42,14 +35,10 @@ export default class Model extends EventEmitter {
|
||||
options = {};
|
||||
}
|
||||
|
||||
// FIXME: this.id is defined as a method further below, but here it is
|
||||
// assigned a possibly-undefined value. Is this code unused?
|
||||
this.id = options.id;
|
||||
|
||||
/** @type {ModelType<T>} */
|
||||
this.model = options.model;
|
||||
this.collection = options.collection;
|
||||
const defaults = this.defaultModel(options);
|
||||
const defaults = this.defaults(options);
|
||||
if (!this.model) {
|
||||
this.model = options.model = defaults;
|
||||
} else {
|
||||
@@ -57,23 +46,14 @@ export default class Model extends EventEmitter {
|
||||
}
|
||||
|
||||
this.initialize(options);
|
||||
|
||||
/** @type {keyof ModelType<T> } */
|
||||
this.idAttr = 'id';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ModelOptions<T, O>} options
|
||||
* @returns {ModelType<T>}
|
||||
*/
|
||||
defaultModel(options) {
|
||||
defaults(options) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ModelOptions<T, O>} options
|
||||
*/
|
||||
initialize(options) {
|
||||
initialize(model) {
|
||||
|
||||
}
|
||||
|
||||
@@ -89,29 +69,14 @@ export default class Model extends EventEmitter {
|
||||
return this.get(this.idAttr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {keyof ModelType<T>} K
|
||||
* @param {K} attribute
|
||||
* @returns {ModelType<T>[K]}
|
||||
*/
|
||||
get(attribute) {
|
||||
return this.model[attribute];
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {keyof ModelType<T>} K
|
||||
* @param {K} attribute
|
||||
* @returns boolean
|
||||
*/
|
||||
has(attribute) {
|
||||
return _.has(this.model, attribute);
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {keyof ModelType<T>} K
|
||||
* @param {K} attribute
|
||||
* @param {ModelType<T>[K]} value
|
||||
*/
|
||||
set(attribute, value) {
|
||||
const oldValue = this.model[attribute];
|
||||
this.model[attribute] = value;
|
||||
@@ -119,10 +84,6 @@ export default class Model extends EventEmitter {
|
||||
this.emit('change:' + attribute, value, oldValue, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {keyof ModelType<T>} K
|
||||
* @param {K} attribute
|
||||
*/
|
||||
unset(attribute) {
|
||||
const oldValue = this.model[attribute];
|
||||
delete this.model[attribute];
|
||||
@@ -130,26 +91,3 @@ export default class Model extends EventEmitter {
|
||||
this.emit('change:' + attribute, undefined, oldValue, this);
|
||||
}
|
||||
}
|
||||
|
||||
/** @typedef {any} TODO */
|
||||
|
||||
/** @typedef {TODO} OpenMCT */
|
||||
|
||||
/**
|
||||
@template {object} T
|
||||
@typedef {{
|
||||
id?: string
|
||||
} & T} ModelType
|
||||
*/
|
||||
|
||||
/**
|
||||
@template {object} T
|
||||
@template {object} O
|
||||
@typedef {{
|
||||
model?: ModelType<T>
|
||||
models?: T[]
|
||||
openmct: OpenMCT
|
||||
id?: string
|
||||
[k: string]: unknown
|
||||
} & O} ModelOptions
|
||||
*/
|
||||
|
||||
@@ -26,30 +26,20 @@ import SeriesCollection from "./SeriesCollection";
|
||||
import XAxisModel from "./XAxisModel";
|
||||
import YAxisModel from "./YAxisModel";
|
||||
import LegendModel from "./LegendModel";
|
||||
|
||||
/**
|
||||
* PlotConfiguration model stores the configuration of a plot and some
|
||||
* limited state. The indiidual parts of the plot configuration model
|
||||
* handle setting defaults and updating in response to various changes.
|
||||
*
|
||||
* @extends {Model<PlotConfigModelType, PlotConfigModelOptions>}
|
||||
*/
|
||||
export default class PlotConfigurationModel extends Model {
|
||||
/**
|
||||
* Initializes all sub models and then passes references to submodels
|
||||
* to those that need it.
|
||||
*
|
||||
* @override
|
||||
* @param {import('./Model').ModelOptions<PlotConfigModelType, PlotConfigModelOptions>} options
|
||||
*/
|
||||
initialize(options) {
|
||||
this.openmct = options.openmct;
|
||||
|
||||
// This is a type assertion for TypeScript, this error is never thrown in practice.
|
||||
if (!options.model) {
|
||||
throw new Error('Not a collection model.');
|
||||
}
|
||||
|
||||
this.xAxis = new XAxisModel({
|
||||
model: options.model.xAxis,
|
||||
plot: this,
|
||||
@@ -86,8 +76,6 @@ export default class PlotConfigurationModel extends Model {
|
||||
}
|
||||
/**
|
||||
* Retrieve the persisted series config for a given identifier.
|
||||
* @param {import('./PlotSeries').Identifier} identifier
|
||||
* @returns {import('./PlotSeries').PlotSeriesModelType=}
|
||||
*/
|
||||
getPersistedSeriesConfig(identifier) {
|
||||
const domainObject = this.get('domainObject');
|
||||
@@ -135,48 +123,15 @@ export default class PlotConfigurationModel extends Model {
|
||||
/**
|
||||
* Return defaults, which are extracted from the passed in domain
|
||||
* object.
|
||||
* @override
|
||||
* @param {import('./Model').ModelOptions<PlotConfigModelType, PlotConfigModelOptions>} options
|
||||
*/
|
||||
defaultModel(options) {
|
||||
defaults(options) {
|
||||
return {
|
||||
series: [],
|
||||
domainObject: options.domainObject,
|
||||
xAxis: {},
|
||||
yAxis: _.cloneDeep(options.domainObject.configuration?.yAxis ?? {}),
|
||||
legend: _.cloneDeep(options.domainObject.configuration?.legend ?? {})
|
||||
xAxis: {
|
||||
},
|
||||
yAxis: _.cloneDeep(_.get(options.domainObject, 'configuration.yAxis', {})),
|
||||
legend: _.cloneDeep(_.get(options.domainObject, 'configuration.legend', {}))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/** @typedef {any} TODO */
|
||||
|
||||
/** @typedef {import('./PlotSeries').default} PlotSeries */
|
||||
|
||||
/**
|
||||
@typedef {{
|
||||
configuration: {
|
||||
series: import('./PlotSeries').PlotSeriesModelType[]
|
||||
}
|
||||
}} SomeDomainObject_NeedsName
|
||||
*/
|
||||
|
||||
/**
|
||||
@typedef {{
|
||||
xAxis: import('./XAxisModel').XAxisModelType
|
||||
yAxis: import('./YAxisModel').YAxisModelType
|
||||
legend: TODO
|
||||
series: PlotSeries[]
|
||||
domainObject: SomeDomainObject_NeedsName
|
||||
}} PlotConfigModelType
|
||||
*/
|
||||
|
||||
/** @typedef {TODO} SomeOtherDomainObject */
|
||||
|
||||
/**
|
||||
TODO: Is SomeOtherDomainObject the same domain object as with SomeDomainObject_NeedsName?
|
||||
@typedef {{
|
||||
plot: import('./PlotConfigurationModel').default
|
||||
domainObject: SomeOtherDomainObject
|
||||
}} PlotConfigModelOptions
|
||||
*/
|
||||
|
||||
@@ -59,15 +59,9 @@ import configStore from "../configuration/ConfigStore";
|
||||
* `metadata`: the Open MCT Telemetry Metadata Manager for the associated
|
||||
* telemetry point.
|
||||
* `formats`: the Open MCT format map for this telemetry point.
|
||||
*
|
||||
* @extends {Model<PlotSeriesModelType, PlotSeriesModelOptions>}
|
||||
*/
|
||||
export default class PlotSeries extends Model {
|
||||
/**
|
||||
@param {import('./Model').ModelOptions<PlotSeriesModelType, PlotSeriesModelOptions>} options
|
||||
*/
|
||||
constructor(options) {
|
||||
|
||||
super(options);
|
||||
|
||||
this.listenTo(this, 'change:xKey', this.onXKeyChange, this);
|
||||
@@ -82,10 +76,8 @@ export default class PlotSeries extends Model {
|
||||
|
||||
/**
|
||||
* Set defaults for telemetry series.
|
||||
* @param {import('./Model').ModelOptions<PlotSeriesModelType, PlotSeriesModelOptions>} options
|
||||
* @override
|
||||
*/
|
||||
defaultModel(options) {
|
||||
defaults(options) {
|
||||
this.metadata = options
|
||||
.openmct
|
||||
.telemetry
|
||||
@@ -117,21 +109,13 @@ export default class PlotSeries extends Model {
|
||||
|
||||
/**
|
||||
* Remove real-time subscription when destroyed.
|
||||
* @override
|
||||
*/
|
||||
destroy() {
|
||||
super.destroy();
|
||||
|
||||
onDestroy(model) {
|
||||
if (this.unsubscribe) {
|
||||
this.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set defaults for telemetry series.
|
||||
* @override
|
||||
* @param {import('./Model').ModelOptions<PlotSeriesModelType, PlotSeriesModelOptions>} options
|
||||
*/
|
||||
initialize(options) {
|
||||
this.openmct = options.openmct;
|
||||
this.domainObject = options.domainObject;
|
||||
@@ -152,11 +136,9 @@ export default class PlotSeries extends Model {
|
||||
|
||||
});
|
||||
this.openmct.time.on('bounds', this.updateLimits);
|
||||
this.on('destroy', this.onDestroy, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Bounds} bounds
|
||||
*/
|
||||
updateLimits(bounds) {
|
||||
this.emit('limitBounds', bounds);
|
||||
}
|
||||
@@ -206,7 +188,7 @@ export default class PlotSeries extends Model {
|
||||
return this.openmct
|
||||
.telemetry
|
||||
.request(this.domainObject, options)
|
||||
.then((points) => {
|
||||
.then(function (points) {
|
||||
const data = this.getSeriesData();
|
||||
const newPoints = _(data)
|
||||
.concat(points)
|
||||
@@ -214,7 +196,7 @@ export default class PlotSeries extends Model {
|
||||
.uniq(true, point => [this.getXVal(point), this.getYVal(point)].join())
|
||||
.value();
|
||||
this.reset(newPoints);
|
||||
})
|
||||
}.bind(this))
|
||||
.catch((error) => {
|
||||
console.warn('Error fetching data', error);
|
||||
});
|
||||
@@ -519,55 +501,8 @@ export default class PlotSeries extends Model {
|
||||
|
||||
/**
|
||||
* Update the series data with the given value.
|
||||
* @returns {Array<{
|
||||
cos: number
|
||||
sin: number
|
||||
mctLimitState: {
|
||||
cssClass: string
|
||||
high: number
|
||||
low: {sin: number, cos: number}
|
||||
name: string
|
||||
}
|
||||
utc: number
|
||||
wavelength: number
|
||||
yesterday: number
|
||||
}>}
|
||||
*/
|
||||
getSeriesData() {
|
||||
return configStore.get(this.dataStoreId) || [];
|
||||
}
|
||||
}
|
||||
|
||||
/** @typedef {any} TODO */
|
||||
|
||||
/** @typedef {{key: string, namespace: string}} Identifier */
|
||||
|
||||
/**
|
||||
@typedef {{
|
||||
identifier: Identifier
|
||||
name: string
|
||||
unit: string
|
||||
xKey: string
|
||||
yKey: string
|
||||
markers: boolean
|
||||
markerShape: keyof typeof MARKER_SHAPES
|
||||
markerSize: number
|
||||
alarmMarkers: boolean
|
||||
limitLines: boolean
|
||||
interpolate: boolean
|
||||
stats: TODO
|
||||
}} PlotSeriesModelType
|
||||
*/
|
||||
|
||||
/**
|
||||
@typedef {{
|
||||
model: PlotSeriesModelType
|
||||
collection: import('./SeriesCollection').default
|
||||
persistedConfig: PlotSeriesModelType
|
||||
filters: TODO
|
||||
}} PlotSeriesModelOptions
|
||||
*/
|
||||
|
||||
/**
|
||||
@typedef {import('@/api/time/TimeContext').Bounds} Bounds
|
||||
*/
|
||||
|
||||
@@ -26,14 +26,8 @@ import Collection from "./Collection";
|
||||
import Color from "@/ui/color/Color";
|
||||
import ColorPalette from "@/ui/color/ColorPalette";
|
||||
|
||||
/**
|
||||
* @extends {Collection<SeriesCollectionModelType, SeriesCollectionOptions>}
|
||||
*/
|
||||
export default class SeriesCollection extends Collection {
|
||||
/**
|
||||
@override
|
||||
@param {import('./Model').ModelOptions<SeriesCollectionModelType, SeriesCollectionOptions>} options
|
||||
*/
|
||||
|
||||
initialize(options) {
|
||||
super.initialize(options);
|
||||
this.modelClass = PlotSeries;
|
||||
@@ -89,15 +83,11 @@ export default class SeriesCollection extends Collection {
|
||||
// Clone to prevent accidental mutation by ref.
|
||||
seriesConfig = JSON.parse(JSON.stringify(seriesConfig));
|
||||
|
||||
if (!seriesConfig) {
|
||||
throw "not possible";
|
||||
}
|
||||
|
||||
this.add(new PlotSeries({
|
||||
model: seriesConfig,
|
||||
domainObject: domainObject,
|
||||
openmct: this.openmct,
|
||||
collection: this,
|
||||
openmct: this.openmct,
|
||||
persistedConfig: this.plot
|
||||
.getPersistedSeriesConfig(domainObject.identifier),
|
||||
filters: filters
|
||||
@@ -173,13 +163,3 @@ export default class SeriesCollection extends Collection {
|
||||
})[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@typedef {PlotSeries} SeriesCollectionModelType
|
||||
*/
|
||||
|
||||
/**
|
||||
@typedef {{
|
||||
plot: import('./PlotConfigurationModel').default
|
||||
}} SeriesCollectionOptions
|
||||
*/
|
||||
|
||||
@@ -20,38 +20,22 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
import Model from "./Model";
|
||||
|
||||
/**
|
||||
* @extends {Model<XAxisModelType, XAxisModelOptions>}
|
||||
*/
|
||||
export default class XAxisModel extends Model {
|
||||
// Despite providing template types to the Model class, we still need to
|
||||
// re-define the type of the following initialize() method's options arg. Tracking
|
||||
// issue for this: https://github.com/microsoft/TypeScript/issues/32082
|
||||
// When they fix it, we can remove the `@param` we have here.
|
||||
/**
|
||||
* @override
|
||||
* @param {import('./Model').ModelOptions<XAxisModelType, XAxisModelOptions>} options
|
||||
* TODO: doc strings.
|
||||
*/
|
||||
export default class XAxisModel extends Model {
|
||||
initialize(options) {
|
||||
this.plot = options.plot;
|
||||
|
||||
// This is a type assertion for TypeScript, this error is not thrown in practice.
|
||||
if (!options.model) {
|
||||
throw new Error('Not a collection model.');
|
||||
}
|
||||
|
||||
this.set('label', options.model.name || '');
|
||||
|
||||
this.on('change:range', (newValue) => {
|
||||
if (!this.get('frozen')) {
|
||||
this.set('displayRange', newValue);
|
||||
this.on('change:range', function (newValue, oldValue, model) {
|
||||
if (!model.get('frozen')) {
|
||||
model.set('displayRange', newValue);
|
||||
}
|
||||
});
|
||||
|
||||
this.on('change:frozen', ((frozen) => {
|
||||
this.on('change:frozen', ((frozen, oldValue, model) => {
|
||||
if (!frozen) {
|
||||
this.set('range', this.get('range'));
|
||||
model.set('range', this.get('range'));
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -61,10 +45,6 @@ export default class XAxisModel extends Model {
|
||||
|
||||
this.listenTo(this, 'change:key', this.changeKey, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} newKey
|
||||
*/
|
||||
changeKey(newKey) {
|
||||
const series = this.plot.series.first();
|
||||
if (series) {
|
||||
@@ -88,17 +68,12 @@ export default class XAxisModel extends Model {
|
||||
plotSeries.reset();
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @param {import('./Model').ModelOptions<XAxisModelType, XAxisModelOptions>} options
|
||||
* @override
|
||||
*/
|
||||
defaultModel(options) {
|
||||
defaults(options) {
|
||||
const bounds = options.openmct.time.bounds();
|
||||
const timeSystem = options.openmct.time.timeSystem();
|
||||
const format = options.openmct.telemetry.getFormatter(timeSystem.timeFormat);
|
||||
|
||||
/** @type {XAxisModelType} */
|
||||
const defaultModel = {
|
||||
return {
|
||||
name: timeSystem.name,
|
||||
key: timeSystem.key,
|
||||
format: format.format.bind(format),
|
||||
@@ -108,42 +83,5 @@ export default class XAxisModel extends Model {
|
||||
},
|
||||
frozen: false
|
||||
};
|
||||
|
||||
return defaultModel;
|
||||
}
|
||||
}
|
||||
|
||||
/** @typedef {any} TODO */
|
||||
|
||||
/** @typedef {TODO} OpenMCT */
|
||||
|
||||
/**
|
||||
@typedef {{
|
||||
min: number
|
||||
max: number
|
||||
}} NumberRange
|
||||
*/
|
||||
|
||||
/**
|
||||
@typedef {import("./Model").ModelType<{
|
||||
range: NumberRange
|
||||
displayRange: NumberRange
|
||||
frozen: boolean
|
||||
label: string
|
||||
format: (n: number) => string
|
||||
values: Array<TODO>
|
||||
}>} AxisModelType
|
||||
*/
|
||||
|
||||
/**
|
||||
@typedef {AxisModelType & {
|
||||
name: string
|
||||
key: string
|
||||
}} XAxisModelType
|
||||
*/
|
||||
|
||||
/**
|
||||
@typedef {{
|
||||
plot: import('./PlotConfigurationModel').default
|
||||
}} XAxisModelOptions
|
||||
*/
|
||||
|
||||
@@ -23,32 +23,27 @@ import _ from 'lodash';
|
||||
import Model from './Model';
|
||||
|
||||
/**
|
||||
* YAxis model
|
||||
*
|
||||
* TODO: docstrings.
|
||||
*
|
||||
* has the following Model properties:
|
||||
*
|
||||
* `autoscale`: boolean, whether or not to autoscale.
|
||||
* `autoscalePadding`: float, percent of padding to display in plots.
|
||||
* `displayRange`: the current display range for the x Axis.
|
||||
* `format`: the formatter for the axis.
|
||||
* `frozen`: boolean, if true, displayRange will not be updated automatically.
|
||||
* Used to temporarily disable automatic updates during user interaction.
|
||||
* `label`: label to display on axis.
|
||||
* `stats`: Min and Max Values of data, automatically updated by observing
|
||||
* plot series.
|
||||
* `values`: for enumerated types, an array of possible display values.
|
||||
* `range`: the user-configured range to use for display, when autoscale is
|
||||
* disabled.
|
||||
*
|
||||
* @extends {Model<YAxisModelType, YAxisModelOptions>}
|
||||
*/
|
||||
export default class YAxisModel extends Model {
|
||||
/**
|
||||
* @override
|
||||
* @param {import('./Model').ModelOptions<YAxisModelType, YAxisModelOptions>} options
|
||||
* YAxis model
|
||||
*
|
||||
* TODO: docstrings.
|
||||
*
|
||||
* has the following Model properties:
|
||||
*
|
||||
* `autoscale`: boolean, whether or not to autoscale.
|
||||
* `autoscalePadding`: float, percent of padding to display in plots.
|
||||
* `displayRange`: the current display range for the x Axis.
|
||||
* `format`: the formatter for the axis.
|
||||
* `frozen`: boolean, if true, displayRange will not be updated automatically.
|
||||
* Used to temporarily disable automatic updates during user interaction.
|
||||
* `label`: label to display on axis.
|
||||
* `stats`: Min and Max Values of data, automatically updated by observing
|
||||
* plot series.
|
||||
* `values`: for enumerated types, an array of possible display values.
|
||||
* `range`: the user-configured range to use for display, when autoscale is
|
||||
* disabled.
|
||||
*
|
||||
*/
|
||||
export default class YAxisModel extends Model {
|
||||
initialize(options) {
|
||||
this.plot = options.plot;
|
||||
this.listenTo(this, 'change:stats', this.calculateAutoscaleExtents, this);
|
||||
@@ -58,9 +53,6 @@ export default class YAxisModel extends Model {
|
||||
this.listenTo(this, 'change:range', this.updateDisplayRange, this);
|
||||
this.updateDisplayRange(this.get('range'));
|
||||
}
|
||||
/**
|
||||
* @param {import('./SeriesCollection').default} seriesCollection
|
||||
*/
|
||||
listenToSeriesCollection(seriesCollection) {
|
||||
this.seriesCollection = seriesCollection;
|
||||
this.listenTo(this.seriesCollection, 'add', (series => {
|
||||
@@ -146,9 +138,6 @@ export default class YAxisModel extends Model {
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
/**
|
||||
* @param {import('./PlotSeries').default} series
|
||||
*/
|
||||
trackSeries(series) {
|
||||
this.listenTo(series, 'change:stats', seriesStats => {
|
||||
if (!seriesStats) {
|
||||
@@ -174,13 +163,12 @@ export default class YAxisModel extends Model {
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Update yAxis format, values, and label from known series.
|
||||
* @param {import('./SeriesCollection').default} seriesCollection
|
||||
*/
|
||||
updateFromSeries(seriesCollection) {
|
||||
* Update yAxis format, values, and label from known series.
|
||||
*/
|
||||
updateFromSeries(series) {
|
||||
const plotModel = this.plot.get('domainObject');
|
||||
const label = _.get(plotModel, 'configuration.yAxis.label');
|
||||
const sampleSeries = seriesCollection.first();
|
||||
const sampleSeries = series.first();
|
||||
if (!sampleSeries) {
|
||||
if (!label) {
|
||||
this.unset('label');
|
||||
@@ -195,7 +183,7 @@ export default class YAxisModel extends Model {
|
||||
this.set('format', yFormat.format.bind(yFormat));
|
||||
this.set('values', yMetadata.values);
|
||||
if (!label) {
|
||||
const labelName = seriesCollection.map(function (s) {
|
||||
const labelName = series.map(function (s) {
|
||||
return s.metadata ? s.metadata.value(s.get('yKey')).name : '';
|
||||
}).reduce(function (a, b) {
|
||||
if (a === undefined) {
|
||||
@@ -215,7 +203,7 @@ export default class YAxisModel extends Model {
|
||||
return;
|
||||
}
|
||||
|
||||
const labelUnits = seriesCollection.map(function (s) {
|
||||
const labelUnits = series.map(function (s) {
|
||||
return s.metadata ? s.metadata.value(s.get('yKey')).units : '';
|
||||
}).reduce(function (a, b) {
|
||||
if (a === undefined) {
|
||||
@@ -236,13 +224,7 @@ export default class YAxisModel extends Model {
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @override
|
||||
* @param {import('./Model').ModelOptions<YAxisModelType, YAxisModelOptions>} options
|
||||
* @returns {YAxisModelType}
|
||||
*/
|
||||
defaultModel(options) {
|
||||
// @ts-ignore incomplete YAxisModelType object used for default value.
|
||||
defaults(options) {
|
||||
return {
|
||||
frozen: false,
|
||||
autoscale: true,
|
||||
@@ -250,20 +232,3 @@ export default class YAxisModel extends Model {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/** @typedef {any} TODO */
|
||||
|
||||
/**
|
||||
@typedef {import('./XAxisModel').AxisModelType & {
|
||||
autoscale: boolean
|
||||
autoscalePadding: number
|
||||
stats: import('./XAxisModel').NumberRange
|
||||
values: Array<TODO>
|
||||
}} YAxisModelType
|
||||
*/
|
||||
|
||||
/**
|
||||
@typedef {{
|
||||
plot: import('./PlotConfigurationModel').default
|
||||
}} YAxisModelOptions
|
||||
*/
|
||||
|
||||
@@ -110,7 +110,6 @@ DrawWebGL.prototype.onContextLost = function (event) {
|
||||
this.emit('error');
|
||||
this.isContextLost = true;
|
||||
this.destroy();
|
||||
// TODO re-initialize and re-draw on context restored
|
||||
};
|
||||
|
||||
DrawWebGL.prototype.initContext = function () {
|
||||
|
||||
@@ -90,10 +90,3 @@ const helperFunctions = {
|
||||
};
|
||||
|
||||
export default helperFunctions;
|
||||
|
||||
/**
|
||||
@typedef {{
|
||||
listenTo: (object: any, event: any, callback: any, context: any) => void
|
||||
stopListening: (object: any, event: any, callback: any, context: any) => void
|
||||
}} EventHelpers
|
||||
*/
|
||||
|
||||
@@ -92,33 +92,23 @@ export default class RemoveAction {
|
||||
this.openmct.editor.save();
|
||||
}
|
||||
|
||||
if (!this.isAlias(child, parent)) {
|
||||
const parentKeyString = this.openmct.objects.makeKeyString(parent.identifier);
|
||||
const isAlias = parentKeyString !== child.location;
|
||||
|
||||
if (!isAlias) {
|
||||
this.openmct.objects.mutate(child, 'location', null);
|
||||
}
|
||||
}
|
||||
|
||||
isAlias(child, parent) {
|
||||
if (parent === undefined) {
|
||||
// then it's a root item, not an alias
|
||||
return false;
|
||||
}
|
||||
|
||||
const parentKeyString = this.openmct.objects.makeKeyString(parent.identifier);
|
||||
const childLocation = child.location;
|
||||
|
||||
return childLocation !== parentKeyString;
|
||||
}
|
||||
|
||||
appliesTo(objectPath) {
|
||||
const parent = objectPath[1];
|
||||
const parentType = parent && this.openmct.types.get(parent.type);
|
||||
const child = objectPath[0];
|
||||
const locked = child.locked ? child.locked : parent && parent.locked;
|
||||
const isEditing = this.openmct.editor.isEditing();
|
||||
const isPersistable = this.openmct.objects.isPersistable(child.identifier);
|
||||
const isAlias = this.isAlias(child, parent);
|
||||
let parent = objectPath[1];
|
||||
let parentType = parent && this.openmct.types.get(parent.type);
|
||||
let child = objectPath[0];
|
||||
let locked = child.locked ? child.locked : parent && parent.locked;
|
||||
let isEditing = this.openmct.editor.isEditing();
|
||||
let isPersistable = this.openmct.objects.isPersistable(child.identifier);
|
||||
|
||||
if (locked || (!isPersistable && !isAlias)) {
|
||||
if (locked || !isPersistable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,12 +32,10 @@
|
||||
<div class="c-conductor__time-bounds">
|
||||
<conductor-inputs-fixed
|
||||
v-if="isFixed"
|
||||
:input-bounds="viewBounds"
|
||||
@updated="saveFixedOffsets"
|
||||
/>
|
||||
<conductor-inputs-realtime
|
||||
v-else
|
||||
:input-bounds="viewBounds"
|
||||
@updated="saveClockOffsets"
|
||||
/>
|
||||
<ConductorModeIcon class="c-conductor__mode-icon" />
|
||||
|
||||
@@ -72,12 +72,6 @@ export default {
|
||||
default() {
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
inputBounds: {
|
||||
type: Object,
|
||||
default() {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -105,12 +99,6 @@ export default {
|
||||
watch: {
|
||||
keyString() {
|
||||
this.setTimeContext();
|
||||
},
|
||||
inputBounds: {
|
||||
handler(newBounds) {
|
||||
this.handleNewBounds(newBounds);
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
ref="startOffset"
|
||||
class="c-button c-conductor__delta-button"
|
||||
title="Set the time offset after now"
|
||||
@click.prevent.stop="showTimePopupStart"
|
||||
@click.prevent="showTimePopupStart"
|
||||
>
|
||||
{{ offsets.start }}
|
||||
</button>
|
||||
@@ -61,7 +61,7 @@
|
||||
ref="endOffset"
|
||||
class="c-button c-conductor__delta-button"
|
||||
title="Set the time offset preceding now"
|
||||
@click.prevent.stop="showTimePopupEnd"
|
||||
@click.prevent="showTimePopupEnd"
|
||||
>
|
||||
{{ offsets.end }}
|
||||
</button>
|
||||
@@ -87,12 +87,6 @@ export default {
|
||||
default() {
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
inputBounds: {
|
||||
type: Object,
|
||||
default() {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -125,12 +119,6 @@ export default {
|
||||
watch: {
|
||||
keyString() {
|
||||
this.setTimeContext();
|
||||
},
|
||||
inputBounds: {
|
||||
handler(newBounds) {
|
||||
this.handleNewBounds(newBounds);
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
@@ -160,10 +160,6 @@ mct-plot {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
.alt-pressed {
|
||||
// When alt is being pressed and user is hovering over the plot, set the cursor
|
||||
@include cursorGrab();
|
||||
}
|
||||
|
||||
.gl-plot-axis-area.gl-plot-x {
|
||||
top: auto;
|
||||
|
||||
@@ -122,9 +122,6 @@
|
||||
flex: 1 1 auto;
|
||||
height: 0; // Chrome 73 overflow bug fix
|
||||
overflow: auto;
|
||||
//To accommodate independent time conductor controls
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.u-fills-container {
|
||||
// Expand component types that fill a container
|
||||
|
||||
@@ -7,14 +7,7 @@
|
||||
"allowJs": true,
|
||||
"checkJs": false,
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"noImplicitOverride": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
|
||||
"paths": {
|
||||
// matches the alias in webpack config, so that types for those imports are visible.
|
||||
"@/*": ["src/*"]
|
||||
}
|
||||
"moduleResolution": "node"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ const gitBranch = require('child_process')
|
||||
const config = {
|
||||
entry: {
|
||||
openmct: './openmct.js',
|
||||
generatorWorker: './example/generator/generatorWorker.js',
|
||||
couchDBChangesFeed: './src/plugins/persistence/couch/CouchChangesFeed.js',
|
||||
inMemorySearchWorker: './src/api/objects/InMemorySearchWorker.js',
|
||||
espressoTheme: './src/plugins/themes/espresso-theme.scss',
|
||||
@@ -99,7 +98,7 @@ const config = {
|
||||
},
|
||||
{
|
||||
test: /\.html$/,
|
||||
type: 'asset/source'
|
||||
use: 'html-loader'
|
||||
},
|
||||
{
|
||||
test: /zepto/,
|
||||
@@ -109,24 +108,25 @@ const config = {
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.(jpg|jpeg|png|svg)$/,
|
||||
type: 'asset/resource',
|
||||
generator: {
|
||||
filename: 'images/[name][ext]'
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.ico$/,
|
||||
type: 'asset/resource',
|
||||
generator: {
|
||||
filename: 'icons/[name][ext]'
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.(woff|woff2?|eot|ttf)$/,
|
||||
type: 'asset/resource',
|
||||
generator: {
|
||||
filename: 'fonts/[name][ext]'
|
||||
test: /\.(jpg|jpeg|png|svg|ico|woff|woff2?|eot|ttf)$/,
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
name: '[name].[ext]',
|
||||
outputPath(url, resourcePath, context) {
|
||||
if (/\.(jpg|jpeg|png|svg)$/.test(url)) {
|
||||
return `images/${url}`;
|
||||
}
|
||||
|
||||
if (/\.ico$/.test(url)) {
|
||||
return `icons/${url}`;
|
||||
}
|
||||
|
||||
if (/\.(woff|woff2?|eot|ttf)$/.test(url)) {
|
||||
return `fonts/${url}`;
|
||||
} else {
|
||||
return `${url}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -134,5 +134,4 @@ const config = {
|
||||
stats: 'errors-warnings'
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
module.exports = config;
|
||||
|
||||
Reference in New Issue
Block a user