mirror of
https://github.com/nasa/openmct.git
synced 2025-06-28 11:51:10 +00:00
Compare commits
620 Commits
notebook-p
...
deep-plotl
Author | SHA1 | Date | |
---|---|---|---|
043d6aa9c3 | |||
ecfab8f7f3 | |||
05c38c37aa | |||
ce78925119 | |||
26aca0f433 | |||
41259bbd40 | |||
580640ff47 | |||
a4aec5d492 | |||
23303c910e | |||
00b3f3ac0b | |||
3282934cf6 | |||
c157fab081 | |||
7c07b66cc9 | |||
7a906ccf5c | |||
ff7debfb81 | |||
92ba103f45 | |||
2c2d8d6b56 | |||
cfadb9f4fd | |||
c185f77a15 | |||
396817b2d1 | |||
96eb6d6b74 | |||
cb5d47f66f | |||
ea90d02d66 | |||
95f73d8eb8 | |||
0dff431f4a | |||
a37c686993 | |||
f12166097c | |||
61d238a097 | |||
d103a22fa0 | |||
04a60cfcbb | |||
8d723960f4 | |||
6d3cd2c699 | |||
87bf94fe0a | |||
af93823b6f | |||
f9deb80350 | |||
4a39ddf425 | |||
83c273b976 | |||
021d730814 | |||
7dd81beb03 | |||
1842d3923c | |||
26838635b6 | |||
ae62b15abf | |||
ba41c1a30e | |||
b9a85d9c4d | |||
11f2c35bb2 | |||
80eab8bad1 | |||
b2d8d640ae | |||
766f48c1ba | |||
da7b93f9b3 | |||
56e6fa66c2 | |||
99c095a69f | |||
f885e83505 | |||
928bc4c68a | |||
d5539c7ae4 | |||
c86a104fb6 | |||
9fa4707c82 | |||
7e2cfa36de | |||
aaa60a1545 | |||
717231fed2 | |||
7fb2bc9729 | |||
46fedc1a30 | |||
addeb635e9 | |||
608d63a7b0 | |||
10679e5f4f | |||
38b8f03b1a | |||
779a42c28c | |||
80c2504768 | |||
80359e3f16 | |||
3b6ef9b44b | |||
66aa4f099f | |||
aa6c6cb88b | |||
4e5cc840d7 | |||
c68edd9b7d | |||
11574b7c40 | |||
abc2cd2413 | |||
5d74882646 | |||
9fe7f230e6 | |||
de4c5b3729 | |||
2a7901914a | |||
73b0fc6f79 | |||
ddef16795c | |||
d188b9a056 | |||
f510f3edd0 | |||
e05b0bb562 | |||
713c5e9fb7 | |||
17bca04560 | |||
61bdadc33c | |||
e0c5bca47d | |||
cdc7c1af64 | |||
3158baa998 | |||
698508fde4 | |||
68a96989e1 | |||
46a6a43234 | |||
d41fc27b55 | |||
24bb96cc90 | |||
483ee173d6 | |||
469e93d916 | |||
f96dfcc942 | |||
063a6c0e51 | |||
7c289d76b6 | |||
c617a440eb | |||
8f81a45b9b | |||
666459be87 | |||
53df89aa5d | |||
8f553f6327 | |||
f91a64483b | |||
de8d63c09d | |||
58b4a6ebf5 | |||
9d45080526 | |||
d3fe2a6811 | |||
97b37edce4 | |||
d3443518d6 | |||
c33314a4bf | |||
59eb034ab4 | |||
67d53fb62b | |||
0fd637c0e9 | |||
169cc6617a | |||
a946325e95 | |||
1beba78111 | |||
2edfeaa606 | |||
2a1f9fd063 | |||
63fe92f8ea | |||
35f303ffa4 | |||
dd70bb470f | |||
250fee125a | |||
956029c786 | |||
b9ab599c35 | |||
8515a411bd | |||
3034ec016a | |||
72a3248123 | |||
b90eb80584 | |||
ebaf702c59 | |||
3956cd1c06 | |||
064cf6747e | |||
8512b634c7 | |||
4220a8a68a | |||
079201273e | |||
e4c9f156a7 | |||
42eeeea374 | |||
984bede43b | |||
072bf361de | |||
4e39d9fb84 | |||
14eaf4e899 | |||
a31d10e708 | |||
f9e88321a3 | |||
9e12203b86 | |||
84874f22e6 | |||
d00e8b68a5 | |||
2907d6d79c | |||
a5a4bb87c5 | |||
389589d7f7 | |||
c02cbd1ba7 | |||
90b475e17b | |||
89a298f5b3 | |||
f990a14a3c | |||
5ec8ac95c1 | |||
5f061001d6 | |||
ef3c4ccf47 | |||
1a86204637 | |||
0a1959df38 | |||
03a690a158 | |||
459a055455 | |||
502d29dd25 | |||
bf947a8835 | |||
f0fd0a9cc7 | |||
7282792da1 | |||
ec0291c54d | |||
4413c29abb | |||
91e1a144ed | |||
53440c31d5 | |||
5128af2531 | |||
4172fdf1d5 | |||
7c200df4c4 | |||
d1b28e079a | |||
23ec838643 | |||
271c619c63 | |||
0552769670 | |||
de8f8088e2 | |||
32c16416d3 | |||
0cf27c349b | |||
015aa8c637 | |||
4a07ddbefc | |||
a81009541c | |||
1b680cfaca | |||
311ff003c0 | |||
0cae61444d | |||
28a603def8 | |||
d2b7407674 | |||
fff89a6384 | |||
953b95f79c | |||
6ff5ce78e1 | |||
0857fd95a7 | |||
a1f2608e7c | |||
b7cea7b955 | |||
32da19a486 | |||
39d7dc8372 | |||
f76f537be7 | |||
a681d67e05 | |||
0a634eb490 | |||
fcca8fa8d9 | |||
829eecf1ae | |||
dc7f83754a | |||
7f49a7bc99 | |||
6f500d0d0b | |||
07d101ac1c | |||
cdf0dd0c10 | |||
c27c347d29 | |||
dc54eef2c9 | |||
57a68a24de | |||
2c1b4b4cfc | |||
437e8a0263 | |||
0a2e912091 | |||
87513a14b7 | |||
168c040f3c | |||
78487a48f6 | |||
a5a197680d | |||
d42bd44485 | |||
77b705ecc8 | |||
48af39a584 | |||
f13714e0c4 | |||
42ac3ef9af | |||
26ffe8efde | |||
87b4000b12 | |||
f790c9bd39 | |||
096c9688d5 | |||
1f19f480ce | |||
44f48a3e2a | |||
60fce4a003 | |||
f04b5b689e | |||
05d981768e | |||
e4a6c21101 | |||
d51dd8b7d0 | |||
aed5377ad2 | |||
cbeb25c583 | |||
b38a9ad4ce | |||
ecf3e19f16 | |||
3b82fd5d8b | |||
e08b4ff0ab | |||
1f3ec77bf1 | |||
3f61db2067 | |||
ce1fdbddda | |||
5332d136b7 | |||
983ed7f0e7 | |||
11978cd869 | |||
00d1b5e69f | |||
6731283cf8 | |||
6b4cd25417 | |||
0cd2799d00 | |||
243b9cac24 | |||
316e0f24cf | |||
05f94edb49 | |||
e22458f09e | |||
cc2df8401b | |||
43a82ec05f | |||
fe2e29d69b | |||
60aecfe27e | |||
5d21a8b6fe | |||
500ab52476 | |||
b0bb723357 | |||
b7fffeab1c | |||
e339d743ed | |||
84f0d49d6f | |||
090e89d524 | |||
90dd53e954 | |||
6ab60ab52e | |||
8975bc8c55 | |||
55e5c49f6e | |||
f090f7ffe7 | |||
94b5617e63 | |||
41c79c6032 | |||
83c648cc26 | |||
76e7fec1a0 | |||
09bfd80f69 | |||
15a7d03e74 | |||
1dc9743484 | |||
8f05c57d1a | |||
81caa27cba | |||
74a7ef2565 | |||
649575fd2d | |||
b75b7a958a | |||
625b39d722 | |||
65f80f4c45 | |||
02cd3048c8 | |||
63feaef988 | |||
6095872682 | |||
dba55867f4 | |||
0da80c2a67 | |||
084df5329a | |||
49ff0c79db | |||
7a4b967a01 | |||
efca7c8e58 | |||
8900072239 | |||
a7e57c62f4 | |||
24bade2284 | |||
27a09239e3 | |||
4a5e106709 | |||
4675fc8ae6 | |||
cf9336dae9 | |||
7f32d196e4 | |||
897d05276a | |||
3e6509ce6f | |||
576b843bd5 | |||
95f855f905 | |||
5b00246cc0 | |||
34a149661c | |||
4c4b587d9c | |||
b8b838f490 | |||
8cb29ba4a9 | |||
ece6223b23 | |||
ecabd00b0c | |||
768df84f10 | |||
f8b3899bb9 | |||
3b046db4f8 | |||
97f829da9f | |||
fb1eed1982 | |||
fa83b4867c | |||
47d4fc9103 | |||
dabd0bff29 | |||
51c70d02d7 | |||
b74733bf3f | |||
84ae65536b | |||
71424dcf8d | |||
2c40396139 | |||
16a0bf9d6c | |||
5498ba8e1e | |||
0f9d7d2832 | |||
9bd1c51a6e | |||
fd74fb0ec4 | |||
3626ff9947 | |||
fd568409d3 | |||
14e3500a88 | |||
83d08ae369 | |||
39bf601ee1 | |||
cfafecdd64 | |||
89ae6ef8c7 | |||
ba780981a5 | |||
ac13bc5850 | |||
e526626e09 | |||
564be6f8ba | |||
371a7d3a3e | |||
8539d60562 | |||
d333fd5822 | |||
364191eddc | |||
583f4dac85 | |||
28255dce01 | |||
c9419d3e2d | |||
b386275330 | |||
d2a45e46f1 | |||
1a409afb03 | |||
e57c18fd69 | |||
3aec9ec6ff | |||
0e9bf74332 | |||
2609a41ee8 | |||
b8dc5acf00 | |||
9d5c7a4015 | |||
fc53e855c4 | |||
467c57b7c6 | |||
a51c0d5139 | |||
d46310ca7d | |||
8f87cc78e8 | |||
ee6e0f310e | |||
f328a1078e | |||
b4cf81a0ef | |||
1b9b7e2345 | |||
4456633010 | |||
cda97d142a | |||
858199e396 | |||
f504a335af | |||
463ec47af6 | |||
ec4d121a98 | |||
fea6d2df96 | |||
598d2b31e9 | |||
25e28ab97c | |||
43056c4068 | |||
614206b10c | |||
30a493d038 | |||
96e433beaa | |||
0915aaea3b | |||
80656c1be0 | |||
acd75f86f4 | |||
486dae54bd | |||
92ecf3af1d | |||
fd0c19026d | |||
3109c8d825 | |||
78cf75323f | |||
b744467f21 | |||
2bb2bb6a1b | |||
11ed7027e7 | |||
36bcfd5a41 | |||
70b5c627ca | |||
f4f1d0387b | |||
a1bf4a92e5 | |||
7d2256d70f | |||
5814d2a35e | |||
6ab84c0bc3 | |||
a7fc9b3caa | |||
67f493f012 | |||
0686e6d38f | |||
7fc825949c | |||
2a9ccdcffd | |||
6db78af69f | |||
038489256c | |||
53b785269b | |||
007b14b5c9 | |||
50b331c451 | |||
44fc62e0ba | |||
2635f085f0 | |||
22161fce7f | |||
386fc75047 | |||
fa6dd84945 | |||
d425bd564c | |||
93e3065b3e | |||
0ad2d59924 | |||
f4468a8233 | |||
dc08877bbb | |||
f08caa6135 | |||
ad7d029ce8 | |||
387912b4d3 | |||
53e0ed4d4a | |||
11c205b5c4 | |||
4ede6351ec | |||
24bbcb466f | |||
b6b5cfe403 | |||
b6ce9c6ed7 | |||
6e5e8f0ce8 | |||
2415d785cc | |||
2b5d6beb84 | |||
86316d8940 | |||
1f2b5ec5c8 | |||
8db6f8f633 | |||
79557165a3 | |||
ec1d4abde9 | |||
07c5e2800a | |||
79811d6662 | |||
67919ece16 | |||
7029dcf09e | |||
fc03b3a79d | |||
096d6371f1 | |||
e580734c95 | |||
2690156a9d | |||
7ac7a40b1b | |||
dc9e572052 | |||
b15ebfd492 | |||
8baee7a0c9 | |||
dc85063467 | |||
be428b326e | |||
dd0e360709 | |||
04da88e3b4 | |||
9bcab02e35 | |||
1ff4d41b7c | |||
04a5c8f69f | |||
8886a94a01 | |||
f25eebdf3f | |||
f9ba46fe85 | |||
6f6fb859d6 | |||
1e3389b427 | |||
c977c64139 | |||
e419149378 | |||
a5a3e41d21 | |||
ecef8eaf86 | |||
de03cfbe64 | |||
03a6de55d6 | |||
3c5047df5e | |||
3cc630d4c2 | |||
b3488c54cd | |||
01b1d66bea | |||
bc9cadaa77 | |||
f42ec7e2c5 | |||
d6a422fbdb | |||
d98b54bea7 | |||
0beda1d053 | |||
e912ab8f4e | |||
5055a18ca1 | |||
96746f4042 | |||
b22ad3ded9 | |||
7e0f475c63 | |||
efb3c2b71e | |||
862ea6986f | |||
cfa5dcb02e | |||
23aaada79d | |||
9e4458db10 | |||
a8da06033c | |||
0bf3597147 | |||
cfd9730055 | |||
e88ead30dc | |||
67b24ce846 | |||
709c3fff65 | |||
ab6e87ae6b | |||
cdb7066bed | |||
73d0507f1f | |||
4d263bcf32 | |||
2d8f61172d | |||
621c1dc11e | |||
dd136a5ff4 | |||
8fc785bbd6 | |||
82be503f4f | |||
7feb933519 | |||
e806e5a293 | |||
770951c6da | |||
7b7c7b528a | |||
a554aa20f8 | |||
ff1ef1f184 | |||
bf1efaf912 | |||
ff2bc41317 | |||
bdaf8aff15 | |||
1dc4f9f6bb | |||
d6320f5da1 | |||
276be5e857 | |||
3101e77ecc | |||
ab6dae16f1 | |||
36222d79c6 | |||
08656a6674 | |||
8034317796 | |||
a59f3a550e | |||
415b967c0b | |||
642499d519 | |||
fa0a54eee7 | |||
82f175f6c7 | |||
8df549e8d9 | |||
9fd720777b | |||
4c68c725b1 | |||
06a5207c6d | |||
78b885c508 | |||
a18a3b6099 | |||
68949e070c | |||
654333dabe | |||
81b8a76f1b | |||
31736fa194 | |||
33632ef1dc | |||
94305ed82c | |||
c8abc45e25 | |||
cd25459ac9 | |||
aaf1eb8059 | |||
1589e4236a | |||
8ca202d0a9 | |||
d2f7904118 | |||
2d059fb856 | |||
8bbd7898bb | |||
ea6f8c9a50 | |||
d152440436 | |||
1ffe76a525 | |||
a6825f530c | |||
7cf6dc386f | |||
5d8252bb07 | |||
e1e1e0fb2f | |||
4f7345563f | |||
68a2b9f3a8 | |||
d79402c568 | |||
d0e8f650be | |||
d819c6efe2 | |||
91c877f234 | |||
55a674ba7b | |||
36055b7c04 | |||
fe3cc661d3 | |||
eb7efae1cc | |||
63f8fb54d4 | |||
097fa2e655 | |||
3d0b4d51c2 | |||
37650487f7 | |||
6ccc0b4fbf | |||
79fe95372d | |||
6adb190d0e | |||
c094e6c6f4 | |||
8c796b4e57 | |||
c08e9a89ff | |||
cc8ba18ccc | |||
57c671a42e | |||
1ee6ecf3ae | |||
5f80b3773b | |||
8452455050 | |||
e5d8f60cdb | |||
de466000a0 | |||
49664c011c | |||
cf34d6b127 | |||
e52f6ce099 | |||
1ecdc4c487 | |||
d38e2c49cb | |||
f8464fa76f | |||
308ae2cb2e | |||
88219659fb | |||
c34c2df061 | |||
99c7bd4c10 | |||
f93d5a6fbf | |||
cd116667be | |||
2f2de3952d | |||
45e56798c5 | |||
0664d480e6 | |||
283599ddf5 | |||
09e3ceefa0 | |||
87f76ebfe4 | |||
55a195b841 | |||
c7946fd7b3 | |||
5d3ba3199c | |||
f0d10306fc | |||
161943b5b8 | |||
e545043a26 | |||
40fb58b5b7 | |||
1f9d4708b3 | |||
162809e081 | |||
482c871ac2 | |||
f0b3311630 | |||
656d6d6c3f | |||
ea45f0f4aa | |||
6a25cb0a58 | |||
4a1901420d | |||
ad64f00608 | |||
65aea29cb9 | |||
7981424e9a | |||
10c4340475 | |||
0a95db1a51 | |||
ace77dce65 | |||
c1d58bb25f | |||
fbcafe0f62 | |||
9a9d9222a9 | |||
221e5b4f6c | |||
5df74aee68 | |||
17838d8040 | |||
f82ca91a61 | |||
b06c234b59 | |||
31a7ebd4f1 |
@ -20,8 +20,8 @@ jobs:
|
|||||||
paths:
|
paths:
|
||||||
- node_modules
|
- node_modules
|
||||||
- run:
|
- run:
|
||||||
name: npm run test
|
name: npm run test:coverage
|
||||||
command: npm run test
|
command: npm run test:coverage
|
||||||
- run:
|
- run:
|
||||||
name: npm run lint
|
name: npm run lint
|
||||||
command: npm run lint
|
command: npm run lint
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
Open MCT, Copyright (c) 2014-2017, United States Government
|
Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
as represented by the Administrator of the National Aeronautics and Space
|
as represented by the Administrator of the National Aeronautics and Space
|
||||||
Administration. All rights reserved.
|
Administration. All rights reserved.
|
||||||
|
|
||||||
@ -43,9 +43,9 @@
|
|||||||
openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
|
openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
openmct.install(openmct.plugins.LocalStorage());
|
||||||
openmct.install(openmct.plugins.Espresso());
|
openmct.install(openmct.plugins.Espresso());
|
||||||
openmct.install(openmct.plugins.MyItems());
|
openmct.install(openmct.plugins.MyItems());
|
||||||
openmct.install(openmct.plugins.LocalStorage());
|
|
||||||
openmct.install(openmct.plugins.Generator());
|
openmct.install(openmct.plugins.Generator());
|
||||||
openmct.install(openmct.plugins.ExampleImagery());
|
openmct.install(openmct.plugins.ExampleImagery());
|
||||||
openmct.install(openmct.plugins.UTCTimeSystem());
|
openmct.install(openmct.plugins.UTCTimeSystem());
|
||||||
|
@ -24,16 +24,27 @@
|
|||||||
|
|
||||||
const devMode = process.env.NODE_ENV !== 'production';
|
const devMode = process.env.NODE_ENV !== 'production';
|
||||||
const browsers = [process.env.NODE_ENV === 'debug' ? 'ChromeDebugging' : 'ChromeHeadless'];
|
const browsers = [process.env.NODE_ENV === 'debug' ? 'ChromeDebugging' : 'ChromeHeadless'];
|
||||||
|
const coverageEnabled = process.env.COVERAGE === 'true';
|
||||||
|
const reporters = ['progress', 'html'];
|
||||||
|
|
||||||
|
if (coverageEnabled) {
|
||||||
|
reporters.push('coverage-istanbul');
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = (config) => {
|
module.exports = (config) => {
|
||||||
const webpackConfig = require('./webpack.config.js');
|
const webpackConfig = require('./webpack.config.js');
|
||||||
delete webpackConfig.output;
|
delete webpackConfig.output;
|
||||||
|
|
||||||
if (!devMode) {
|
if (!devMode || coverageEnabled) {
|
||||||
webpackConfig.module.rules.push({
|
webpackConfig.module.rules.push({
|
||||||
test: /\.js$/,
|
test: /\.js$/,
|
||||||
exclude: /node_modules|example/,
|
exclude: /node_modules|example|lib|dist/,
|
||||||
use: 'istanbul-instrumenter-loader'
|
use: {
|
||||||
|
loader: 'istanbul-instrumenter-loader',
|
||||||
|
options: {
|
||||||
|
esModules: true
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,11 +56,7 @@ module.exports = (config) => {
|
|||||||
'src/**/*Spec.js'
|
'src/**/*Spec.js'
|
||||||
],
|
],
|
||||||
port: 9876,
|
port: 9876,
|
||||||
reporters: [
|
reporters: reporters,
|
||||||
'progress',
|
|
||||||
'coverage',
|
|
||||||
'html'
|
|
||||||
],
|
|
||||||
browsers: browsers,
|
browsers: browsers,
|
||||||
customLaunchers: {
|
customLaunchers: {
|
||||||
ChromeDebugging: {
|
ChromeDebugging: {
|
||||||
@ -61,25 +68,25 @@ module.exports = (config) => {
|
|||||||
colors: true,
|
colors: true,
|
||||||
logLevel: config.LOG_INFO,
|
logLevel: config.LOG_INFO,
|
||||||
autoWatch: true,
|
autoWatch: true,
|
||||||
coverageReporter: {
|
|
||||||
dir: process.env.CIRCLE_ARTIFACTS ?
|
|
||||||
process.env.CIRCLE_ARTIFACTS + '/coverage' :
|
|
||||||
"dist/reports/coverage",
|
|
||||||
check: {
|
|
||||||
global: {
|
|
||||||
lines: 80,
|
|
||||||
excludes: ['src/plugins/plot/**/*.js']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// HTML test reporting.
|
// HTML test reporting.
|
||||||
htmlReporter: {
|
htmlReporter: {
|
||||||
outputDir: "dist/reports/tests",
|
outputDir: "dist/reports/tests",
|
||||||
preserveDescribeNesting: true,
|
preserveDescribeNesting: true,
|
||||||
foldAll: false
|
foldAll: false
|
||||||
},
|
},
|
||||||
|
coverageIstanbulReporter: {
|
||||||
|
fixWebpackSourcePaths: true,
|
||||||
|
dir: process.env.CIRCLE_ARTIFACTS ?
|
||||||
|
process.env.CIRCLE_ARTIFACTS + '/coverage' :
|
||||||
|
"dist/reports/coverage",
|
||||||
|
reports: ['html', 'lcovonly', 'text-summary'],
|
||||||
|
thresholds: {
|
||||||
|
global: {
|
||||||
|
lines: 62
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
preprocessors: {
|
preprocessors: {
|
||||||
// add webpack as preprocessor
|
|
||||||
'platform/**/*Spec.js': ['webpack', 'sourcemap'],
|
'platform/**/*Spec.js': ['webpack', 'sourcemap'],
|
||||||
'src/**/*Spec.js': ['webpack', 'sourcemap']
|
'src/**/*Spec.js': ['webpack', 'sourcemap']
|
||||||
},
|
},
|
||||||
|
18
package.json
18
package.json
@ -2,10 +2,11 @@
|
|||||||
"name": "openmct",
|
"name": "openmct",
|
||||||
"version": "1.0.0-snapshot",
|
"version": "1.0.0-snapshot",
|
||||||
"description": "The Open MCT core platform",
|
"description": "The Open MCT core platform",
|
||||||
"dependencies": {},
|
"dependencies": {
|
||||||
|
"plotly.js-dist": "^1.54.1"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"acorn": "6.2.0",
|
"angular": "1.7.9",
|
||||||
"angular": "1.4.14",
|
|
||||||
"angular-route": "1.4.14",
|
"angular-route": "1.4.14",
|
||||||
"babel-eslint": "8.2.6",
|
"babel-eslint": "8.2.6",
|
||||||
"comma-separated-values": "^3.6.4",
|
"comma-separated-values": "^3.6.4",
|
||||||
@ -43,6 +44,7 @@
|
|||||||
"karma-chrome-launcher": "^2.2.0",
|
"karma-chrome-launcher": "^2.2.0",
|
||||||
"karma-cli": "^1.0.1",
|
"karma-cli": "^1.0.1",
|
||||||
"karma-coverage": "^1.1.2",
|
"karma-coverage": "^1.1.2",
|
||||||
|
"karma-coverage-istanbul-reporter": "^2.1.1",
|
||||||
"karma-html-reporter": "^0.2.7",
|
"karma-html-reporter": "^0.2.7",
|
||||||
"karma-jasmine": "^1.1.2",
|
"karma-jasmine": "^1.1.2",
|
||||||
"karma-sourcemap-loader": "^0.3.7",
|
"karma-sourcemap-loader": "^0.3.7",
|
||||||
@ -53,7 +55,7 @@
|
|||||||
"marked": "^0.3.5",
|
"marked": "^0.3.5",
|
||||||
"mini-css-extract-plugin": "^0.4.1",
|
"mini-css-extract-plugin": "^0.4.1",
|
||||||
"minimist": "^1.1.1",
|
"minimist": "^1.1.1",
|
||||||
"moment": "^2.11.1",
|
"moment": "^2.25.3",
|
||||||
"moment-duration-format": "^2.2.2",
|
"moment-duration-format": "^2.2.2",
|
||||||
"moment-timezone": "^0.5.21",
|
"moment-timezone": "^0.5.21",
|
||||||
"node-bourbon": "^4.2.3",
|
"node-bourbon": "^4.2.3",
|
||||||
@ -76,14 +78,16 @@
|
|||||||
"zepto": "^1.2.0"
|
"zepto": "^1.2.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"clean": "rm -rf ./dist",
|
||||||
"start": "node app.js",
|
"start": "node app.js",
|
||||||
"lint": "eslint platform example src/**/*.{js,vue} openmct.js",
|
"lint": "eslint platform example src --ext .js,.vue openmct.js",
|
||||||
"lint:fix": "eslint platform example src/**/*.{js,vue} openmct.js --fix",
|
"lint:fix": "eslint platform example src --ext .js,.vue openmct.js --fix",
|
||||||
"build:prod": "cross-env NODE_ENV=production webpack",
|
"build:prod": "cross-env NODE_ENV=production webpack",
|
||||||
"build:dev": "webpack",
|
"build:dev": "webpack",
|
||||||
"build:watch": "webpack --watch",
|
"build:watch": "webpack --watch",
|
||||||
"test": "karma start --single-run",
|
"test": "karma start --single-run",
|
||||||
"test-debug": "cross-env NODE_ENV=debug karma start --no-single-run",
|
"test:debug": "cross-env NODE_ENV=debug karma start --no-single-run",
|
||||||
|
"test:coverage": "./scripts/test-coverage.sh",
|
||||||
"test:watch": "karma start --no-single-run",
|
"test:watch": "karma start --no-single-run",
|
||||||
"verify": "concurrently 'npm:test' 'npm:lint'",
|
"verify": "concurrently 'npm:test' 'npm:lint'",
|
||||||
"jsdoc": "jsdoc -c jsdoc.json -R API.md -r -d dist/docs/api",
|
"jsdoc": "jsdoc -c jsdoc.json -R API.md -r -d dist/docs/api",
|
||||||
|
@ -87,6 +87,11 @@ define([
|
|||||||
bootstrapper
|
bootstrapper
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Override of angular1.6 ! hashPrefix
|
||||||
|
app.config(['$locationProvider', function ($locationProvider) {
|
||||||
|
$locationProvider.hashPrefix('');
|
||||||
|
}]);
|
||||||
|
|
||||||
// Apply logging levels; this must be done now, before the
|
// Apply logging levels; this must be done now, before the
|
||||||
// first log statement.
|
// first log statement.
|
||||||
new LogLevel(logLevel).configure(app, $log);
|
new LogLevel(logLevel).configure(app, $log);
|
||||||
|
@ -33,7 +33,7 @@ define(
|
|||||||
var CONNECTED = {
|
var CONNECTED = {
|
||||||
text: "Connected",
|
text: "Connected",
|
||||||
glyphClass: "ok",
|
glyphClass: "ok",
|
||||||
statusClass: "s-status-ok",
|
statusClass: "s-status-on",
|
||||||
description: "Connected to the domain object database."
|
description: "Connected to the domain object database."
|
||||||
},
|
},
|
||||||
DISCONNECTED = {
|
DISCONNECTED = {
|
||||||
|
@ -71,7 +71,7 @@ define([
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "ELASTIC_PATH",
|
"key": "ELASTIC_PATH",
|
||||||
"value": "mct/domain_object",
|
"value": "mct/_doc",
|
||||||
"priority": "fallback"
|
"priority": "fallback"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -32,7 +32,7 @@ define(
|
|||||||
var CONNECTED = {
|
var CONNECTED = {
|
||||||
text: "Connected",
|
text: "Connected",
|
||||||
glyphClass: "ok",
|
glyphClass: "ok",
|
||||||
statusClass: "s-status-ok",
|
statusClass: "s-status-on",
|
||||||
description: "Connected to the domain object database."
|
description: "Connected to the domain object database."
|
||||||
},
|
},
|
||||||
DISCONNECTED = {
|
DISCONNECTED = {
|
||||||
|
@ -32,9 +32,9 @@ define(
|
|||||||
// JSLint doesn't like underscore-prefixed properties,
|
// JSLint doesn't like underscore-prefixed properties,
|
||||||
// so hide them here.
|
// so hide them here.
|
||||||
var SRC = "_source",
|
var SRC = "_source",
|
||||||
REV = "_version",
|
CONFLICT = 409,
|
||||||
ID = "_id",
|
SEQ_NO = "_seq_no",
|
||||||
CONFLICT = 409;
|
PRIMARY_TERM = "_primary_term";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ElasticPersistenceProvider reads and writes JSON documents
|
* The ElasticPersistenceProvider reads and writes JSON documents
|
||||||
@ -104,7 +104,8 @@ define(
|
|||||||
// Get a domain object model out of ElasticSearch's response
|
// Get a domain object model out of ElasticSearch's response
|
||||||
ElasticPersistenceProvider.prototype.getModel = function (response) {
|
ElasticPersistenceProvider.prototype.getModel = function (response) {
|
||||||
if (response && response[SRC]) {
|
if (response && response[SRC]) {
|
||||||
this.revs[response[ID]] = response[REV];
|
this.revs[response[SEQ_NO]] = response[SEQ_NO];
|
||||||
|
this.revs[response[PRIMARY_TERM]] = response[PRIMARY_TERM];
|
||||||
return response[SRC];
|
return response[SRC];
|
||||||
} else {
|
} else {
|
||||||
return undefined;
|
return undefined;
|
||||||
@ -116,7 +117,8 @@ define(
|
|||||||
// indicate that the request failed.
|
// indicate that the request failed.
|
||||||
ElasticPersistenceProvider.prototype.checkResponse = function (response, key) {
|
ElasticPersistenceProvider.prototype.checkResponse = function (response, key) {
|
||||||
if (response && !response.error) {
|
if (response && !response.error) {
|
||||||
this.revs[key] = response[REV];
|
this.revs[SEQ_NO] = response[SEQ_NO];
|
||||||
|
this.revs[PRIMARY_TERM] = response[PRIMARY_TERM];
|
||||||
return response;
|
return response;
|
||||||
} else {
|
} else {
|
||||||
return this.handleError(response, key);
|
return this.handleError(response, key);
|
||||||
@ -147,7 +149,7 @@ define(
|
|||||||
function checkUpdate(response) {
|
function checkUpdate(response) {
|
||||||
return self.checkResponse(response, key);
|
return self.checkResponse(response, key);
|
||||||
}
|
}
|
||||||
return this.put(key, value, { version: this.revs[key] })
|
return this.put(key, value)
|
||||||
.then(checkUpdate);
|
.then(checkUpdate);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ define(
|
|||||||
it("allows object creation", function () {
|
it("allows object creation", function () {
|
||||||
var model = { someKey: "some value" };
|
var model = { someKey: "some value" };
|
||||||
mockHttp.and.returnValue(mockPromise({
|
mockHttp.and.returnValue(mockPromise({
|
||||||
data: { "_id": "abc", "_version": 1 }
|
data: { "_id": "abc", "_seq_no": 1, "_primary_term": 1 }
|
||||||
}));
|
}));
|
||||||
provider.createObject("testSpace", "abc", model).then(capture);
|
provider.createObject("testSpace", "abc", model).then(capture);
|
||||||
expect(mockHttp).toHaveBeenCalledWith({
|
expect(mockHttp).toHaveBeenCalledWith({
|
||||||
@ -100,7 +100,7 @@ define(
|
|||||||
it("allows object models to be read back", function () {
|
it("allows object models to be read back", function () {
|
||||||
var model = { someKey: "some value" };
|
var model = { someKey: "some value" };
|
||||||
mockHttp.and.returnValue(mockPromise({
|
mockHttp.and.returnValue(mockPromise({
|
||||||
data: { "_id": "abc", "_version": 1, "_source": model }
|
data: { "_id": "abc", "_seq_no": 1, "_primary_term": 1, "_source": model }
|
||||||
}));
|
}));
|
||||||
provider.readObject("testSpace", "abc").then(capture);
|
provider.readObject("testSpace", "abc").then(capture);
|
||||||
expect(mockHttp).toHaveBeenCalledWith({
|
expect(mockHttp).toHaveBeenCalledWith({
|
||||||
@ -117,19 +117,19 @@ define(
|
|||||||
|
|
||||||
// First do a read to populate rev tags...
|
// First do a read to populate rev tags...
|
||||||
mockHttp.and.returnValue(mockPromise({
|
mockHttp.and.returnValue(mockPromise({
|
||||||
data: { "_id": "abc", "_version": 42, "_source": {} }
|
data: { "_id": "abc", "_source": {} }
|
||||||
}));
|
}));
|
||||||
provider.readObject("testSpace", "abc");
|
provider.readObject("testSpace", "abc");
|
||||||
|
|
||||||
// Now perform an update
|
// Now perform an update
|
||||||
mockHttp.and.returnValue(mockPromise({
|
mockHttp.and.returnValue(mockPromise({
|
||||||
data: { "_id": "abc", "_version": 43, "_source": {} }
|
data: { "_id": "abc", "_seq_no": 1, "_source": {} }
|
||||||
}));
|
}));
|
||||||
provider.updateObject("testSpace", "abc", model).then(capture);
|
provider.updateObject("testSpace", "abc", model).then(capture);
|
||||||
expect(mockHttp).toHaveBeenCalledWith({
|
expect(mockHttp).toHaveBeenCalledWith({
|
||||||
url: "/test/db/abc",
|
url: "/test/db/abc",
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
params: { version: 42 },
|
params: undefined,
|
||||||
data: model
|
data: model
|
||||||
});
|
});
|
||||||
expect(capture.calls.mostRecent().args[0]).toBeTruthy();
|
expect(capture.calls.mostRecent().args[0]).toBeTruthy();
|
||||||
@ -138,13 +138,13 @@ define(
|
|||||||
it("allows object deletion", function () {
|
it("allows object deletion", function () {
|
||||||
// First do a read to populate rev tags...
|
// First do a read to populate rev tags...
|
||||||
mockHttp.and.returnValue(mockPromise({
|
mockHttp.and.returnValue(mockPromise({
|
||||||
data: { "_id": "abc", "_version": 42, "_source": {} }
|
data: { "_id": "abc", "_source": {} }
|
||||||
}));
|
}));
|
||||||
provider.readObject("testSpace", "abc");
|
provider.readObject("testSpace", "abc");
|
||||||
|
|
||||||
// Now perform an update
|
// Now perform an update
|
||||||
mockHttp.and.returnValue(mockPromise({
|
mockHttp.and.returnValue(mockPromise({
|
||||||
data: { "_id": "abc", "_version": 42, "_source": {} }
|
data: { "_id": "abc", "_source": {} }
|
||||||
}));
|
}));
|
||||||
provider.deleteObject("testSpace", "abc", {}).then(capture);
|
provider.deleteObject("testSpace", "abc", {}).then(capture);
|
||||||
expect(mockHttp).toHaveBeenCalledWith({
|
expect(mockHttp).toHaveBeenCalledWith({
|
||||||
@ -167,13 +167,13 @@ define(
|
|||||||
expect(capture).toHaveBeenCalledWith(undefined);
|
expect(capture).toHaveBeenCalledWith(undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("handles rejection due to version", function () {
|
it("handles rejection due to _seq_no", function () {
|
||||||
var model = { someKey: "some value" },
|
var model = { someKey: "some value" },
|
||||||
mockErrorCallback = jasmine.createSpy('error');
|
mockErrorCallback = jasmine.createSpy('error');
|
||||||
|
|
||||||
// First do a read to populate rev tags...
|
// First do a read to populate rev tags...
|
||||||
mockHttp.and.returnValue(mockPromise({
|
mockHttp.and.returnValue(mockPromise({
|
||||||
data: { "_id": "abc", "_version": 42, "_source": {} }
|
data: { "_id": "abc", "_seq_no": 1, "_source": {} }
|
||||||
}));
|
}));
|
||||||
provider.readObject("testSpace", "abc");
|
provider.readObject("testSpace", "abc");
|
||||||
|
|
||||||
@ -196,7 +196,7 @@ define(
|
|||||||
|
|
||||||
// First do a read to populate rev tags...
|
// First do a read to populate rev tags...
|
||||||
mockHttp.and.returnValue(mockPromise({
|
mockHttp.and.returnValue(mockPromise({
|
||||||
data: { "_id": "abc", "_version": 42, "_source": {} }
|
data: { "_id": "abc", "_seq_no": 1, "_source": {} }
|
||||||
}));
|
}));
|
||||||
provider.readObject("testSpace", "abc");
|
provider.readObject("testSpace", "abc");
|
||||||
|
|
||||||
|
2
scripts/test-coverage.sh
Executable file
2
scripts/test-coverage.sh
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
export NODE_OPTIONS=--max_old_space_size=4096
|
||||||
|
cross-env COVERAGE=true karma start --single-run
|
@ -252,6 +252,7 @@ define([
|
|||||||
// Plugin's that are installed by default
|
// Plugin's that are installed by default
|
||||||
|
|
||||||
this.install(this.plugins.Plot());
|
this.install(this.plugins.Plot());
|
||||||
|
this.install(this.plugins.PlotlyPlot());
|
||||||
this.install(this.plugins.TelemetryTable());
|
this.install(this.plugins.TelemetryTable());
|
||||||
this.install(PreviewPlugin.default());
|
this.install(PreviewPlugin.default());
|
||||||
this.install(LegacyIndicatorsPlugin());
|
this.install(LegacyIndicatorsPlugin());
|
||||||
|
@ -30,7 +30,7 @@ define(
|
|||||||
// DISCONNECTED: HTTP failed; maybe misconfigured, disconnected.
|
// DISCONNECTED: HTTP failed; maybe misconfigured, disconnected.
|
||||||
// PENDING: Still trying to connect, and haven't failed yet.
|
// PENDING: Still trying to connect, and haven't failed yet.
|
||||||
var CONNECTED = {
|
var CONNECTED = {
|
||||||
statusClass: "s-status-ok"
|
statusClass: "s-status-on"
|
||||||
},
|
},
|
||||||
PENDING = {
|
PENDING = {
|
||||||
statusClass: "s-status-warning-lo"
|
statusClass: "s-status-warning-lo"
|
||||||
|
@ -122,7 +122,7 @@ define(
|
|||||||
it("indicates success if connection is nominal", function () {
|
it("indicates success if connection is nominal", function () {
|
||||||
jasmine.clock().tick(pluginOptions.interval + 1);
|
jasmine.clock().tick(pluginOptions.interval + 1);
|
||||||
ajaxOptions.success();
|
ajaxOptions.success();
|
||||||
expect(indicatorElement.classList.contains('s-status-ok')).toBe(true);
|
expect(indicatorElement.classList.contains('s-status-on')).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("indicates an error when the server cannot be reached", function () {
|
it("indicates an error when the server cannot be reached", function () {
|
||||||
|
@ -23,8 +23,8 @@
|
|||||||
import EventEmitter from 'EventEmitter';
|
import EventEmitter from 'EventEmitter';
|
||||||
import uuid from 'uuid';
|
import uuid from 'uuid';
|
||||||
import TelemetryCriterion from "./criterion/TelemetryCriterion";
|
import TelemetryCriterion from "./criterion/TelemetryCriterion";
|
||||||
import { TRIGGER } from "./utils/constants";
|
import { evaluateResults } from './utils/evaluator';
|
||||||
import {computeCondition, computeConditionByLimit} from "./utils/evaluator";
|
import { getLatestTimestamp } from './utils/time';
|
||||||
import AllTelemetryCriterion from "./criterion/AllTelemetryCriterion";
|
import AllTelemetryCriterion from "./criterion/AllTelemetryCriterion";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -56,29 +56,38 @@ export default class ConditionClass extends EventEmitter {
|
|||||||
this.conditionManager = conditionManager;
|
this.conditionManager = conditionManager;
|
||||||
this.id = conditionConfiguration.id;
|
this.id = conditionConfiguration.id;
|
||||||
this.criteria = [];
|
this.criteria = [];
|
||||||
this.criteriaResults = {};
|
|
||||||
this.result = undefined;
|
this.result = undefined;
|
||||||
this.latestTimestamp = {};
|
this.timeSystems = this.openmct.time.getAllTimeSystems();
|
||||||
|
|
||||||
if (conditionConfiguration.configuration.criteria) {
|
if (conditionConfiguration.configuration.criteria) {
|
||||||
this.createCriteria(conditionConfiguration.configuration.criteria);
|
this.createCriteria(conditionConfiguration.configuration.criteria);
|
||||||
}
|
}
|
||||||
this.trigger = conditionConfiguration.configuration.trigger;
|
this.trigger = conditionConfiguration.configuration.trigger;
|
||||||
this.conditionManager.on('broadcastTelemetry', this.handleBroadcastTelemetry, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBroadcastTelemetry(datum) {
|
getResult(datum) {
|
||||||
if (!datum || !datum.id) {
|
if (!datum || !datum.id) {
|
||||||
console.log('no data received');
|
console.log('no data received');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.criteria.forEach(criterion => {
|
this.criteria.forEach(criterion => {
|
||||||
if (criterion.telemetry && (criterion.telemetry === 'all' || criterion.telemetry === 'any')) {
|
if (this.isAnyOrAllTelemetry(criterion)) {
|
||||||
criterion.handleSubscription(datum, this.conditionManager.telemetryObjects);
|
criterion.getResult(datum, this.conditionManager.telemetryObjects);
|
||||||
} else {
|
} else {
|
||||||
criterion.emit(`subscription:${datum.id}`, datum);
|
criterion.getResult(datum);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.result = evaluateResults(this.criteria.map(criterion => criterion.result), this.trigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
isAnyOrAllTelemetry(criterion) {
|
||||||
|
return (criterion.telemetry && (criterion.telemetry === 'all' || criterion.telemetry === 'any'));
|
||||||
|
}
|
||||||
|
|
||||||
|
isTelemetryUsed(id) {
|
||||||
|
return this.criteria.some(criterion => {
|
||||||
|
return this.isAnyOrAllTelemetry(criterion) || criterion.telemetryObjectIdAsString === id;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
update(conditionConfiguration) {
|
update(conditionConfiguration) {
|
||||||
@ -89,13 +98,12 @@ export default class ConditionClass extends EventEmitter {
|
|||||||
updateTrigger(trigger) {
|
updateTrigger(trigger) {
|
||||||
if (this.trigger !== trigger) {
|
if (this.trigger !== trigger) {
|
||||||
this.trigger = trigger;
|
this.trigger = trigger;
|
||||||
this.handleConditionUpdated();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
generateCriterion(criterionConfiguration) {
|
generateCriterion(criterionConfiguration) {
|
||||||
return {
|
return {
|
||||||
id: uuid(),
|
id: criterionConfiguration.id || uuid(),
|
||||||
telemetry: criterionConfiguration.telemetry || '',
|
telemetry: criterionConfiguration.telemetry || '',
|
||||||
telemetryObject: this.conditionManager.telemetryObjects[this.openmct.objects.makeKeyString(criterionConfiguration.telemetry)],
|
telemetryObject: this.conditionManager.telemetryObjects[this.openmct.objects.makeKeyString(criterionConfiguration.telemetry)],
|
||||||
operation: criterionConfiguration.operation || '',
|
operation: criterionConfiguration.operation || '',
|
||||||
@ -133,7 +141,6 @@ export default class ConditionClass extends EventEmitter {
|
|||||||
criterion = new TelemetryCriterion(criterionConfigurationWithId, this.openmct);
|
criterion = new TelemetryCriterion(criterionConfigurationWithId, this.openmct);
|
||||||
}
|
}
|
||||||
criterion.on('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
|
criterion.on('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
|
||||||
criterion.on('criterionResultUpdated', (obj) => this.handleCriterionResult(obj));
|
|
||||||
if (!this.criteria) {
|
if (!this.criteria) {
|
||||||
this.criteria = [];
|
this.criteria = [];
|
||||||
}
|
}
|
||||||
@ -162,22 +169,11 @@ export default class ConditionClass extends EventEmitter {
|
|||||||
const newCriterionConfiguration = this.generateCriterion(criterionConfiguration);
|
const newCriterionConfiguration = this.generateCriterion(criterionConfiguration);
|
||||||
let newCriterion = new TelemetryCriterion(newCriterionConfiguration, this.openmct);
|
let newCriterion = new TelemetryCriterion(newCriterionConfiguration, this.openmct);
|
||||||
newCriterion.on('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
|
newCriterion.on('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
|
||||||
newCriterion.on('criterionResultUpdated', (obj) => this.handleCriterionResult(obj));
|
|
||||||
|
|
||||||
let criterion = found.item;
|
let criterion = found.item;
|
||||||
criterion.unsubscribe();
|
criterion.unsubscribe();
|
||||||
criterion.off('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
|
criterion.off('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
|
||||||
criterion.off('criterionResultUpdated', (obj) => this.handleCriterionResult(obj));
|
|
||||||
this.criteria.splice(found.index, 1, newCriterion);
|
this.criteria.splice(found.index, 1, newCriterion);
|
||||||
if (this.criteriaResults[criterion.id] !== undefined) {
|
|
||||||
delete this.criteriaResults[criterion.id];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
removeCriterion(id) {
|
|
||||||
if (this.destroyCriterion(id)) {
|
|
||||||
this.handleConditionUpdated();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,15 +181,12 @@ export default class ConditionClass extends EventEmitter {
|
|||||||
let found = this.findCriterion(id);
|
let found = this.findCriterion(id);
|
||||||
if (found) {
|
if (found) {
|
||||||
let criterion = found.item;
|
let criterion = found.item;
|
||||||
criterion.destroy();
|
criterion.off('criterionUpdated', (obj) => {
|
||||||
// TODO this is passing the wrong args
|
this.handleCriterionUpdated(obj);
|
||||||
criterion.off('criterionUpdated', (result) => {
|
|
||||||
this.handleCriterionUpdated(id, result);
|
|
||||||
});
|
});
|
||||||
|
criterion.destroy();
|
||||||
this.criteria.splice(found.index, 1);
|
this.criteria.splice(found.index, 1);
|
||||||
if (this.criteriaResults[criterion.id] !== undefined) {
|
|
||||||
delete this.criteriaResults[criterion.id];
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -203,59 +196,40 @@ export default class ConditionClass extends EventEmitter {
|
|||||||
let found = this.findCriterion(criterion.id);
|
let found = this.findCriterion(criterion.id);
|
||||||
if (found) {
|
if (found) {
|
||||||
this.criteria[found.index] = criterion.data;
|
this.criteria[found.index] = criterion.data;
|
||||||
// TODO nothing is listening to this
|
|
||||||
this.emitEvent('conditionUpdated', {
|
|
||||||
trigger: this.trigger,
|
|
||||||
criteria: this.criteria
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCriteriaResults(eventData) {
|
|
||||||
const id = eventData.id;
|
|
||||||
|
|
||||||
if (this.findCriterion(id)) {
|
|
||||||
// The !! here is important to convert undefined to false otherwise the criteriaResults won't get deleted when the criteria is destroyed
|
|
||||||
this.criteriaResults[id] = !!eventData.data.result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleCriterionResult(eventData) {
|
|
||||||
this.updateCriteriaResults(eventData);
|
|
||||||
this.handleConditionUpdated(eventData.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
requestLADConditionResult() {
|
requestLADConditionResult() {
|
||||||
const criteriaResults = this.criteria
|
let latestTimestamp;
|
||||||
.map(criterion => criterion.requestLAD({telemetryObjects: this.conditionManager.telemetryObjects}));
|
let criteriaResults = {};
|
||||||
|
const criteriaRequests = this.criteria
|
||||||
|
.map(criterion => criterion.requestLAD(this.conditionManager.telemetryObjects));
|
||||||
|
|
||||||
return Promise.all(criteriaResults)
|
return Promise.all(criteriaRequests)
|
||||||
.then(results => {
|
.then(results => {
|
||||||
results.forEach(result => {
|
results.forEach(resultObj => {
|
||||||
this.updateCriteriaResults(result);
|
const { id, data, data: { result } } = resultObj;
|
||||||
this.latestTimestamp = this.getLatestTimestamp(this.latestTimestamp, result.data)
|
if (this.findCriterion(id)) {
|
||||||
|
criteriaResults[id] = !!result;
|
||||||
|
}
|
||||||
|
latestTimestamp = getLatestTimestamp(
|
||||||
|
latestTimestamp,
|
||||||
|
data,
|
||||||
|
this.timeSystems,
|
||||||
|
this.openmct.time.timeSystem()
|
||||||
|
);
|
||||||
});
|
});
|
||||||
this.evaluate();
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
data: Object.assign({}, this.latestTimestamp, { result: this.result })
|
data: Object.assign(
|
||||||
}
|
{},
|
||||||
|
latestTimestamp,
|
||||||
|
{ result: evaluateResults(Object.values(criteriaResults), this.trigger) }
|
||||||
|
)
|
||||||
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getTelemetrySubscriptions() {
|
|
||||||
return this.criteria.map(criterion => criterion.telemetryObjectIdAsString);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleConditionUpdated(datum) {
|
|
||||||
// trigger an updated event so that consumers can react accordingly
|
|
||||||
this.evaluate();
|
|
||||||
this.emitEvent('conditionResultUpdated',
|
|
||||||
Object.assign({}, datum, { result: this.result })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getCriteria() {
|
getCriteria() {
|
||||||
return this.criteria;
|
return this.criteria;
|
||||||
}
|
}
|
||||||
@ -269,41 +243,7 @@ export default class ConditionClass extends EventEmitter {
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
evaluate() {
|
|
||||||
if (this.trigger && this.trigger === TRIGGER.XOR) {
|
|
||||||
this.result = computeConditionByLimit(this.criteriaResults, 1);
|
|
||||||
} else if (this.trigger && this.trigger === TRIGGER.NOT) {
|
|
||||||
this.result = computeConditionByLimit(this.criteriaResults, 0);
|
|
||||||
} else {
|
|
||||||
this.result = computeCondition(this.criteriaResults, this.trigger === TRIGGER.ALL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getLatestTimestamp(current, compare) {
|
|
||||||
const timestamp = Object.assign({}, current);
|
|
||||||
|
|
||||||
this.openmct.time.getAllTimeSystems().forEach(timeSystem => {
|
|
||||||
if (!timestamp[timeSystem.key]
|
|
||||||
|| compare[timeSystem.key] > timestamp[timeSystem.key]
|
|
||||||
) {
|
|
||||||
timestamp[timeSystem.key] = compare[timeSystem.key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
emitEvent(eventName, data) {
|
|
||||||
this.emit(eventName, {
|
|
||||||
id: this.id,
|
|
||||||
data: data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
this.conditionManager.off('broadcastTelemetry', this.handleBroadcastTelemetry, this);
|
|
||||||
if (typeof this.stopObservingForChanges === 'function') {
|
|
||||||
this.stopObservingForChanges();
|
|
||||||
}
|
|
||||||
this.destroyCriteria();
|
this.destroyCriteria();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import Condition from "./Condition";
|
import Condition from "./Condition";
|
||||||
|
import { getLatestTimestamp } from './utils/time';
|
||||||
import uuid from "uuid";
|
import uuid from "uuid";
|
||||||
import EventEmitter from 'EventEmitter';
|
import EventEmitter from 'EventEmitter';
|
||||||
|
|
||||||
@ -29,8 +30,7 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
super();
|
super();
|
||||||
this.openmct = openmct;
|
this.openmct = openmct;
|
||||||
this.conditionSetDomainObject = conditionSetDomainObject;
|
this.conditionSetDomainObject = conditionSetDomainObject;
|
||||||
this.timeAPI = this.openmct.time;
|
this.timeSystems = this.openmct.time.getAllTimeSystems();
|
||||||
this.latestTimestamp = {};
|
|
||||||
this.composition = this.openmct.composition.get(conditionSetDomainObject);
|
this.composition = this.openmct.composition.get(conditionSetDomainObject);
|
||||||
this.composition.on('add', this.subscribeToTelemetry, this);
|
this.composition.on('add', this.subscribeToTelemetry, this);
|
||||||
this.composition.on('remove', this.unsubscribeFromTelemetry, this);
|
this.composition.on('remove', this.unsubscribeFromTelemetry, this);
|
||||||
@ -55,8 +55,9 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
this.telemetryObjects[id] = Object.assign({}, endpoint, {telemetryMetaData: this.openmct.telemetry.getMetadata(endpoint).valueMetadatas});
|
this.telemetryObjects[id] = Object.assign({}, endpoint, {telemetryMetaData: this.openmct.telemetry.getMetadata(endpoint).valueMetadatas});
|
||||||
this.subscriptions[id] = this.openmct.telemetry.subscribe(
|
this.subscriptions[id] = this.openmct.telemetry.subscribe(
|
||||||
endpoint,
|
endpoint,
|
||||||
this.broadcastTelemetry.bind(this, id)
|
this.telemetryReceived.bind(this, endpoint)
|
||||||
);
|
);
|
||||||
|
// TODO check if this is needed
|
||||||
this.updateConditionTelemetry();
|
this.updateConditionTelemetry();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,10 +71,10 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
this.subscriptions[id]();
|
this.subscriptions[id]();
|
||||||
delete this.subscriptions[id];
|
delete this.subscriptions[id];
|
||||||
delete this.telemetryObjects[id];
|
delete this.telemetryObjects[id];
|
||||||
|
this.removeConditionTelemetry();
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
this.conditionResults = {};
|
|
||||||
this.conditionClassCollection = [];
|
this.conditionClassCollection = [];
|
||||||
if (this.conditionSetDomainObject.configuration.conditionCollection.length) {
|
if (this.conditionSetDomainObject.configuration.conditionCollection.length) {
|
||||||
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
|
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
|
||||||
@ -86,6 +87,30 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
this.conditionClassCollection.forEach((condition) => condition.updateTelemetry());
|
this.conditionClassCollection.forEach((condition) => condition.updateTelemetry());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeConditionTelemetry() {
|
||||||
|
let conditionsChanged = false;
|
||||||
|
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration) => {
|
||||||
|
conditionConfiguration.configuration.criteria.forEach((criterion, index) => {
|
||||||
|
const isAnyAllTelemetry = criterion.telemetry && (criterion.telemetry === 'any' || criterion.telemetry === 'all');
|
||||||
|
if (!isAnyAllTelemetry) {
|
||||||
|
const found = Object.values(this.telemetryObjects).find((telemetryObject) => {
|
||||||
|
return this.openmct.objects.areIdsEqual(telemetryObject.identifier, criterion.telemetry);
|
||||||
|
});
|
||||||
|
if (!found) {
|
||||||
|
criterion.telemetry = '';
|
||||||
|
criterion.metadata = '';
|
||||||
|
criterion.input = [];
|
||||||
|
criterion.operation = '';
|
||||||
|
conditionsChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (conditionsChanged) {
|
||||||
|
this.persistConditions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
updateCondition(conditionConfiguration, index) {
|
updateCondition(conditionConfiguration, index) {
|
||||||
let condition = this.conditionClassCollection[index];
|
let condition = this.conditionClassCollection[index];
|
||||||
condition.update(conditionConfiguration);
|
condition.update(conditionConfiguration);
|
||||||
@ -95,7 +120,6 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
|
|
||||||
initCondition(conditionConfiguration, index) {
|
initCondition(conditionConfiguration, index) {
|
||||||
let condition = new Condition(conditionConfiguration, this.openmct, this);
|
let condition = new Condition(conditionConfiguration, this.openmct, this);
|
||||||
condition.on('conditionResultUpdated', this.handleConditionResult.bind(this));
|
|
||||||
if (index !== undefined) {
|
if (index !== undefined) {
|
||||||
this.conditionClassCollection.splice(index + 1, 0, condition);
|
this.conditionClassCollection.splice(index + 1, 0, condition);
|
||||||
} else {
|
} else {
|
||||||
@ -122,6 +146,7 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
output: 'false',
|
output: 'false',
|
||||||
trigger: 'all',
|
trigger: 'all',
|
||||||
criteria: [{
|
criteria: [{
|
||||||
|
id: uuid(),
|
||||||
telemetry: '',
|
telemetry: '',
|
||||||
operation: '',
|
operation: '',
|
||||||
input: [],
|
input: [],
|
||||||
@ -140,7 +165,9 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cloneCondition(conditionConfiguration, index) {
|
cloneCondition(conditionConfiguration, index) {
|
||||||
this.createAndSaveCondition(index, JSON.parse(JSON.stringify(conditionConfiguration)));
|
let clonedConfig = JSON.parse(JSON.stringify(conditionConfiguration));
|
||||||
|
clonedConfig.configuration.criteria.forEach((criterion) => criterion.id = uuid());
|
||||||
|
this.createAndSaveCondition(index, clonedConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
createAndSaveCondition(index, conditionConfiguration) {
|
createAndSaveCondition(index, conditionConfiguration) {
|
||||||
@ -156,22 +183,16 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
|
|
||||||
removeCondition(index) {
|
removeCondition(index) {
|
||||||
let condition = this.conditionClassCollection[index];
|
let condition = this.conditionClassCollection[index];
|
||||||
condition.destroyCriteria();
|
condition.destroy();
|
||||||
condition.off('conditionResultUpdated', this.handleConditionResult.bind(this));
|
|
||||||
this.conditionClassCollection.splice(index, 1);
|
this.conditionClassCollection.splice(index, 1);
|
||||||
this.conditionSetDomainObject.configuration.conditionCollection.splice(index, 1);
|
this.conditionSetDomainObject.configuration.conditionCollection.splice(index, 1);
|
||||||
if (this.conditionResults[condition.id] !== undefined) {
|
|
||||||
delete this.conditionResults[condition.id];
|
|
||||||
}
|
|
||||||
this.persistConditions();
|
this.persistConditions();
|
||||||
this.handleConditionResult();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
findConditionById(id) {
|
findConditionById(id) {
|
||||||
return this.conditionClassCollection.find(conditionClass => conditionClass.id === id);
|
return this.conditionClassCollection.find(conditionClass => conditionClass.id === id);
|
||||||
}
|
}
|
||||||
|
|
||||||
//this.$set(this.conditionClassCollection, reorderEvent.newIndex, oldConditions[reorderEvent.oldIndex]);
|
|
||||||
reorderConditions(reorderPlan) {
|
reorderConditions(reorderPlan) {
|
||||||
let oldConditions = Array.from(this.conditionSetDomainObject.configuration.conditionCollection);
|
let oldConditions = Array.from(this.conditionSetDomainObject.configuration.conditionCollection);
|
||||||
let newCollection = [];
|
let newCollection = [];
|
||||||
@ -188,7 +209,23 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
let currentCondition = conditionCollection[conditionCollection.length-1];
|
let currentCondition = conditionCollection[conditionCollection.length-1];
|
||||||
|
|
||||||
for (let i = 0; i < conditionCollection.length - 1; i++) {
|
for (let i = 0; i < conditionCollection.length - 1; i++) {
|
||||||
if (this.conditionResults[conditionCollection[i].id]) {
|
const condition = this.findConditionById(conditionCollection[i].id)
|
||||||
|
if (condition.result) {
|
||||||
|
//first condition to be true wins
|
||||||
|
currentCondition = conditionCollection[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentCondition;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentConditionLAD(conditionResults) {
|
||||||
|
const conditionCollection = this.conditionSetDomainObject.configuration.conditionCollection;
|
||||||
|
let currentCondition = conditionCollection[conditionCollection.length-1];
|
||||||
|
|
||||||
|
for (let i = 0; i < conditionCollection.length - 1; i++) {
|
||||||
|
if (conditionResults[conditionCollection[i].id]) {
|
||||||
//first condition to be true wins
|
//first condition to be true wins
|
||||||
currentCondition = conditionCollection[i];
|
currentCondition = conditionCollection[i];
|
||||||
break;
|
break;
|
||||||
@ -197,24 +234,79 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
return currentCondition;
|
return currentCondition;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateConditionResults(resultObj) {
|
requestLADConditionSetOutput() {
|
||||||
if (!resultObj) {
|
if (!this.conditionClassCollection.length) {
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.compositionLoad.then(() => {
|
||||||
|
let latestTimestamp;
|
||||||
|
let conditionResults = {};
|
||||||
|
const conditionRequests = this.conditionClassCollection
|
||||||
|
.map(condition => condition.requestLADConditionResult());
|
||||||
|
|
||||||
|
return Promise.all(conditionRequests)
|
||||||
|
.then((results) => {
|
||||||
|
results.forEach(resultObj => {
|
||||||
|
const { id, data, data: { result } } = resultObj;
|
||||||
|
if (this.findConditionById(id)) {
|
||||||
|
conditionResults[id] = !!result;
|
||||||
|
}
|
||||||
|
latestTimestamp = getLatestTimestamp(
|
||||||
|
latestTimestamp,
|
||||||
|
data,
|
||||||
|
this.timeSystems,
|
||||||
|
this.openmct.time.timeSystem()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!Object.values(latestTimestamp).some(timeSystem => timeSystem)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentCondition = this.getCurrentConditionLAD(conditionResults);
|
||||||
|
const currentOutput = Object.assign(
|
||||||
|
{
|
||||||
|
output: currentCondition.configuration.output,
|
||||||
|
id: this.conditionSetDomainObject.identifier,
|
||||||
|
conditionId: currentCondition.id
|
||||||
|
},
|
||||||
|
latestTimestamp
|
||||||
|
);
|
||||||
|
|
||||||
|
return [currentOutput];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
isTelemetryUsed(endpoint) {
|
||||||
|
const id = this.openmct.objects.makeKeyString(endpoint.identifier);
|
||||||
|
|
||||||
|
for(const condition of this.conditionClassCollection) {
|
||||||
|
if (condition.isTelemetryUsed(id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
telemetryReceived(endpoint, datum) {
|
||||||
|
if (!this.isTelemetryUsed(endpoint)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = resultObj.id;
|
const normalizedDatum = this.createNormalizedDatum(datum, endpoint);
|
||||||
|
const timeSystemKey = this.openmct.time.timeSystem().key;
|
||||||
|
let timestamp = {};
|
||||||
|
timestamp[timeSystemKey] = normalizedDatum[timeSystemKey];
|
||||||
|
|
||||||
if (this.findConditionById(id)) {
|
this.conditionClassCollection.forEach(condition => {
|
||||||
this.conditionResults[id] = resultObj.data.result;
|
condition.getResult(normalizedDatum);
|
||||||
}
|
});
|
||||||
|
|
||||||
this.updateTimestamp(resultObj.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleConditionResult(resultObj) {
|
|
||||||
// update conditions results and then calculate the current condition
|
|
||||||
this.updateConditionResults(resultObj);
|
|
||||||
const currentCondition = this.getCurrentCondition();
|
const currentCondition = this.getCurrentCondition();
|
||||||
|
|
||||||
this.emit('conditionSetResultUpdated',
|
this.emit('conditionSetResultUpdated',
|
||||||
Object.assign(
|
Object.assign(
|
||||||
{
|
{
|
||||||
@ -222,51 +314,11 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
id: this.conditionSetDomainObject.identifier,
|
id: this.conditionSetDomainObject.identifier,
|
||||||
conditionId: currentCondition.id
|
conditionId: currentCondition.id
|
||||||
},
|
},
|
||||||
this.latestTimestamp
|
timestamp
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTimestamp(timestamp) {
|
|
||||||
this.timeAPI.getAllTimeSystems().forEach(timeSystem => {
|
|
||||||
if (!this.latestTimestamp[timeSystem.key]
|
|
||||||
|| timestamp[timeSystem.key] > this.latestTimestamp[timeSystem.key]
|
|
||||||
) {
|
|
||||||
this.latestTimestamp[timeSystem.key] = timestamp[timeSystem.key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
requestLADConditionSetOutput() {
|
|
||||||
if (!this.conditionClassCollection.length || this.conditionClassCollection.length === 1) {
|
|
||||||
return Promise.resolve([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.compositionLoad.then(() => {
|
|
||||||
const ladConditionResults = this.conditionClassCollection
|
|
||||||
.map(condition => condition.requestLADConditionResult());
|
|
||||||
|
|
||||||
return Promise.all(ladConditionResults)
|
|
||||||
.then((results) => {
|
|
||||||
results.forEach(resultObj => { this.updateConditionResults(resultObj); });
|
|
||||||
const currentCondition = this.getCurrentCondition();
|
|
||||||
|
|
||||||
return Object.assign(
|
|
||||||
{
|
|
||||||
output: currentCondition.configuration.output,
|
|
||||||
id: this.conditionSetDomainObject.identifier,
|
|
||||||
conditionId: currentCondition.id
|
|
||||||
},
|
|
||||||
this.latestTimestamp
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
broadcastTelemetry(id, datum) {
|
|
||||||
this.emit(`broadcastTelemetry`, Object.assign({}, this.createNormalizedDatum(datum, id), {id: id}));
|
|
||||||
}
|
|
||||||
|
|
||||||
getTestData(metadatum) {
|
getTestData(metadatum) {
|
||||||
let data = undefined;
|
let data = undefined;
|
||||||
if (this.testData.applied) {
|
if (this.testData.applied) {
|
||||||
@ -278,13 +330,20 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
createNormalizedDatum(telemetryDatum, id) {
|
createNormalizedDatum(telemetryDatum, endpoint) {
|
||||||
return Object.values(this.telemetryObjects[id].telemetryMetaData).reduce((normalizedDatum, metadatum) => {
|
const id = this.openmct.objects.makeKeyString(endpoint.identifier);
|
||||||
|
const metadata = this.openmct.telemetry.getMetadata(endpoint).valueMetadatas;
|
||||||
|
|
||||||
|
const normalizedDatum = Object.values(metadata).reduce((datum, metadatum) => {
|
||||||
const testValue = this.getTestData(metadatum);
|
const testValue = this.getTestData(metadatum);
|
||||||
const formatter = this.openmct.telemetry.getValueFormatter(metadatum);
|
const formatter = this.openmct.telemetry.getValueFormatter(metadatum);
|
||||||
normalizedDatum[metadatum.key] = testValue !== undefined ? formatter.parse(testValue) : formatter.parse(telemetryDatum[metadatum.source]);
|
datum[metadatum.key] = testValue !== undefined ? formatter.parse(testValue) : formatter.parse(telemetryDatum[metadatum.source]);
|
||||||
return normalizedDatum;
|
return datum;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
|
normalizedDatum.id = id;
|
||||||
|
|
||||||
|
return normalizedDatum;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTestData(testData) {
|
updateTestData(testData) {
|
||||||
@ -300,14 +359,13 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
this.composition.off('add', this.subscribeToTelemetry, this);
|
this.composition.off('add', this.subscribeToTelemetry, this);
|
||||||
this.composition.off('remove', this.unsubscribeFromTelemetry, this);
|
this.composition.off('remove', this.unsubscribeFromTelemetry, this);
|
||||||
Object.values(this.subscriptions).forEach(unsubscribe => unsubscribe());
|
Object.values(this.subscriptions).forEach(unsubscribe => unsubscribe());
|
||||||
this.subscriptions = undefined;
|
delete this.subscriptions;
|
||||||
|
|
||||||
if(this.stopObservingForChanges) {
|
if(this.stopObservingForChanges) {
|
||||||
this.stopObservingForChanges();
|
this.stopObservingForChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.conditionClassCollection.forEach((condition) => {
|
this.conditionClassCollection.forEach((condition) => {
|
||||||
condition.off('conditionResultUpdated', this.handleConditionResult);
|
|
||||||
condition.destroy();
|
condition.destroy();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,7 @@ describe('ConditionManager', () => {
|
|||||||
};
|
};
|
||||||
let mockComposition;
|
let mockComposition;
|
||||||
let loader;
|
let loader;
|
||||||
|
let mockTimeSystems;
|
||||||
|
|
||||||
function mockAngularComponents() {
|
function mockAngularComponents() {
|
||||||
let mockInjector = jasmine.createSpyObj('$injector', ['get']);
|
let mockInjector = jasmine.createSpyObj('$injector', ['get']);
|
||||||
@ -111,10 +112,16 @@ describe('ConditionManager', () => {
|
|||||||
openmct.objects.observe.and.returnValue(function () {});
|
openmct.objects.observe.and.returnValue(function () {});
|
||||||
openmct.objects.mutate.and.returnValue(function () {});
|
openmct.objects.mutate.and.returnValue(function () {});
|
||||||
|
|
||||||
|
mockTimeSystems = {
|
||||||
|
key: 'utc'
|
||||||
|
};
|
||||||
|
openmct.time = jasmine.createSpyObj('time', ['getAllTimeSystems']);
|
||||||
|
openmct.time.getAllTimeSystems.and.returnValue([mockTimeSystems]);
|
||||||
|
|
||||||
conditionMgr = new ConditionManager(conditionSetDomainObject, openmct);
|
conditionMgr = new ConditionManager(conditionSetDomainObject, openmct);
|
||||||
|
|
||||||
conditionMgr.on('conditionSetResultUpdated', mockListener);
|
conditionMgr.on('conditionSetResultUpdated', mockListener);
|
||||||
conditionMgr.on('broadcastTelemetry', mockListener);
|
conditionMgr.on('telemetryReceived', mockListener);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('creates a conditionCollection with a default condition', function () {
|
it('creates a conditionCollection with a default condition', function () {
|
||||||
|
@ -54,13 +54,22 @@ export default class ConditionSetMetadataProvider {
|
|||||||
return {
|
return {
|
||||||
values: this.getDomains().concat([
|
values: this.getDomains().concat([
|
||||||
{
|
{
|
||||||
name: 'Output',
|
key: "state",
|
||||||
key: 'output',
|
source: "output",
|
||||||
format: 'enum',
|
name: "State",
|
||||||
|
format: "enum",
|
||||||
enumerations: enumerations,
|
enumerations: enumerations,
|
||||||
hints: {
|
hints: {
|
||||||
range: 1
|
range: 1
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "output",
|
||||||
|
name: "Value",
|
||||||
|
format: "string",
|
||||||
|
hints: {
|
||||||
|
range: 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
};
|
};
|
||||||
|
@ -45,7 +45,7 @@ export default class ConditionSetTelemetryProvider {
|
|||||||
|
|
||||||
return conditionManager.requestLADConditionSetOutput()
|
return conditionManager.requestLADConditionSetOutput()
|
||||||
.then(latestOutput => {
|
.then(latestOutput => {
|
||||||
return latestOutput ? [latestOutput] : [];
|
return latestOutput;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,12 +25,12 @@ import {TRIGGER} from "./utils/constants";
|
|||||||
import TelemetryCriterion from "./criterion/TelemetryCriterion";
|
import TelemetryCriterion from "./criterion/TelemetryCriterion";
|
||||||
|
|
||||||
let openmct = {},
|
let openmct = {},
|
||||||
mockListener,
|
|
||||||
testConditionDefinition,
|
testConditionDefinition,
|
||||||
testTelemetryObject,
|
testTelemetryObject,
|
||||||
conditionObj,
|
conditionObj,
|
||||||
conditionManager,
|
conditionManager,
|
||||||
mockBroadcastTelemetry;
|
mockTelemetryReceived,
|
||||||
|
mockTimeSystems;
|
||||||
|
|
||||||
describe("The condition", function () {
|
describe("The condition", function () {
|
||||||
|
|
||||||
@ -38,10 +38,9 @@ describe("The condition", function () {
|
|||||||
conditionManager = jasmine.createSpyObj('conditionManager',
|
conditionManager = jasmine.createSpyObj('conditionManager',
|
||||||
['on']
|
['on']
|
||||||
);
|
);
|
||||||
mockBroadcastTelemetry = jasmine.createSpy('listener');
|
mockTelemetryReceived = jasmine.createSpy('listener');
|
||||||
conditionManager.on('broadcastTelemetry', mockBroadcastTelemetry);
|
conditionManager.on('telemetryReceived', mockTelemetryReceived);
|
||||||
|
|
||||||
mockListener = jasmine.createSpy('listener');
|
|
||||||
testTelemetryObject = {
|
testTelemetryObject = {
|
||||||
identifier:{ namespace: "", key: "test-object"},
|
identifier:{ namespace: "", key: "test-object"},
|
||||||
type: "test-object",
|
type: "test-object",
|
||||||
@ -74,6 +73,12 @@ describe("The condition", function () {
|
|||||||
openmct.telemetry.subscribe.and.returnValue(function () {});
|
openmct.telemetry.subscribe.and.returnValue(function () {});
|
||||||
openmct.telemetry.getMetadata.and.returnValue(testTelemetryObject.telemetry.values);
|
openmct.telemetry.getMetadata.and.returnValue(testTelemetryObject.telemetry.values);
|
||||||
|
|
||||||
|
mockTimeSystems = {
|
||||||
|
key: 'utc'
|
||||||
|
};
|
||||||
|
openmct.time = jasmine.createSpyObj('time', ['getAllTimeSystems']);
|
||||||
|
openmct.time.getAllTimeSystems.and.returnValue([mockTimeSystems]);
|
||||||
|
|
||||||
testConditionDefinition = {
|
testConditionDefinition = {
|
||||||
id: '123-456',
|
id: '123-456',
|
||||||
configuration: {
|
configuration: {
|
||||||
@ -97,8 +102,6 @@ describe("The condition", function () {
|
|||||||
openmct,
|
openmct,
|
||||||
conditionManager
|
conditionManager
|
||||||
);
|
);
|
||||||
|
|
||||||
conditionObj.on('conditionUpdated', mockListener);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("generates criteria with the correct properties", function () {
|
it("generates criteria with the correct properties", function () {
|
||||||
|
@ -23,10 +23,13 @@
|
|||||||
import EventEmitter from 'EventEmitter';
|
import EventEmitter from 'EventEmitter';
|
||||||
|
|
||||||
export default class StyleRuleManager extends EventEmitter {
|
export default class StyleRuleManager extends EventEmitter {
|
||||||
constructor(styleConfiguration, openmct, callback) {
|
constructor(styleConfiguration, openmct, callback, suppressSubscriptionOnEdit) {
|
||||||
super();
|
super();
|
||||||
this.openmct = openmct;
|
this.openmct = openmct;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
|
if (suppressSubscriptionOnEdit) {
|
||||||
|
this.openmct.editor.on('isEditing', this.toggleSubscription.bind(this));
|
||||||
|
}
|
||||||
if (styleConfiguration) {
|
if (styleConfiguration) {
|
||||||
this.initialize(styleConfiguration);
|
this.initialize(styleConfiguration);
|
||||||
if (styleConfiguration.conditionSetIdentifier) {
|
if (styleConfiguration.conditionSetIdentifier) {
|
||||||
@ -37,9 +40,25 @@ export default class StyleRuleManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleSubscription(isEditing) {
|
||||||
|
this.isEditing = isEditing;
|
||||||
|
if (this.isEditing) {
|
||||||
|
if (this.stopProvidingTelemetry) {
|
||||||
|
this.stopProvidingTelemetry();
|
||||||
|
}
|
||||||
|
if (this.conditionSetIdentifier) {
|
||||||
|
this.applySelectedConditionStyle();
|
||||||
|
}
|
||||||
|
} else if (this.conditionSetIdentifier) {
|
||||||
|
this.subscribeToConditionSet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
initialize(styleConfiguration) {
|
initialize(styleConfiguration) {
|
||||||
this.conditionSetIdentifier = styleConfiguration.conditionSetIdentifier;
|
this.conditionSetIdentifier = styleConfiguration.conditionSetIdentifier;
|
||||||
this.staticStyle = styleConfiguration.staticStyle;
|
this.staticStyle = styleConfiguration.staticStyle;
|
||||||
|
this.selectedConditionId = styleConfiguration.selectedConditionId;
|
||||||
|
this.defaultConditionId = styleConfiguration.defaultConditionId;
|
||||||
this.updateConditionStylesMap(styleConfiguration.styles || []);
|
this.updateConditionStylesMap(styleConfiguration.styles || []);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,7 +73,7 @@ export default class StyleRuleManager extends EventEmitter {
|
|||||||
this.handleConditionSetResultUpdated(output[0]);
|
this.handleConditionSetResultUpdated(output[0]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(conditionSetDomainObject, output => this.handleConditionSetResultUpdated(output));
|
this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(conditionSetDomainObject, this.handleConditionSetResultUpdated.bind(this));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,12 +85,16 @@ export default class StyleRuleManager extends EventEmitter {
|
|||||||
let isNewConditionSet = !this.conditionSetIdentifier ||
|
let isNewConditionSet = !this.conditionSetIdentifier ||
|
||||||
!this.openmct.objects.areIdsEqual(this.conditionSetIdentifier, styleConfiguration.conditionSetIdentifier);
|
!this.openmct.objects.areIdsEqual(this.conditionSetIdentifier, styleConfiguration.conditionSetIdentifier);
|
||||||
this.initialize(styleConfiguration);
|
this.initialize(styleConfiguration);
|
||||||
|
if (this.isEditing) {
|
||||||
|
this.applySelectedConditionStyle();
|
||||||
|
} else {
|
||||||
//Only resubscribe if the conditionSet has changed.
|
//Only resubscribe if the conditionSet has changed.
|
||||||
if (isNewConditionSet) {
|
if (isNewConditionSet) {
|
||||||
this.subscribeToConditionSet();
|
this.subscribeToConditionSet();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
updateConditionStylesMap(conditionStyles) {
|
updateConditionStylesMap(conditionStyles) {
|
||||||
let conditionStyleMap = {};
|
let conditionStyleMap = {};
|
||||||
@ -103,13 +126,23 @@ export default class StyleRuleManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
applySelectedConditionStyle() {
|
||||||
|
const conditionId = this.selectedConditionId || this.defaultConditionId;
|
||||||
|
if (!conditionId) {
|
||||||
|
this.applyStaticStyle();
|
||||||
|
} else if (this.conditionalStyleMap[conditionId]) {
|
||||||
|
this.currentStyle = this.conditionalStyleMap[conditionId];
|
||||||
|
this.updateDomainObjectStyle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
applyStaticStyle() {
|
applyStaticStyle() {
|
||||||
if (this.staticStyle) {
|
if (this.staticStyle) {
|
||||||
this.currentStyle = this.staticStyle.style;
|
this.currentStyle = this.staticStyle.style;
|
||||||
} else {
|
} else {
|
||||||
if (this.currentStyle) {
|
if (this.currentStyle) {
|
||||||
Object.keys(this.currentStyle).forEach(key => {
|
Object.keys(this.currentStyle).forEach(key => {
|
||||||
this.currentStyle[key] = 'transparent';
|
this.currentStyle[key] = '__no_value';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,6 +156,8 @@ export default class StyleRuleManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
delete this.stopProvidingTelemetry;
|
delete this.stopProvidingTelemetry;
|
||||||
this.conditionSetIdentifier = undefined;
|
this.conditionSetIdentifier = undefined;
|
||||||
|
this.isEditing = undefined;
|
||||||
|
this.callback = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,16 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<div class="c-condition-h"
|
||||||
|
:class="{ 'is-drag-target': draggingOver }"
|
||||||
|
@dragover.prevent
|
||||||
|
@drop.prevent="dropCondition($event, conditionIndex)"
|
||||||
|
@dragenter="dragEnter($event, conditionIndex)"
|
||||||
|
@dragleave="dragLeave($event, conditionIndex)"
|
||||||
|
>
|
||||||
|
<div class="c-condition-h__drop-target"></div>
|
||||||
<div v-if="isEditing"
|
<div v-if="isEditing"
|
||||||
class="c-condition c-condition--edit js-condition-drag-wrapper"
|
class="c-condition c-condition--edit"
|
||||||
>
|
>
|
||||||
<!-- Edit view -->
|
<!-- Edit view -->
|
||||||
<div class="c-condition__header">
|
<div class="c-condition__header">
|
||||||
@ -31,8 +39,7 @@
|
|||||||
:class="[{ 'is-enabled': !condition.isDefault }, { 'hide-nice': condition.isDefault }]"
|
:class="[{ 'is-enabled': !condition.isDefault }, { 'hide-nice': condition.isDefault }]"
|
||||||
:draggable="!condition.isDefault"
|
:draggable="!condition.isDefault"
|
||||||
@dragstart="dragStart"
|
@dragstart="dragStart"
|
||||||
@dragstop="dragStop"
|
@dragend="dragEnd"
|
||||||
@dragover.stop
|
|
||||||
></span>
|
></span>
|
||||||
|
|
||||||
<span class="c-condition__disclosure c-disclosure-triangle c-tree__item__view-control is-enabled"
|
<span class="c-condition__disclosure c-disclosure-triangle c-tree__item__view-control is-enabled"
|
||||||
@ -41,7 +48,6 @@
|
|||||||
></span>
|
></span>
|
||||||
|
|
||||||
<span class="c-condition__name">{{ condition.configuration.name }}</span>
|
<span class="c-condition__name">{{ condition.configuration.name }}</span>
|
||||||
<!-- TODO: description should be derived from criteria -->
|
|
||||||
<span class="c-condition__summary">
|
<span class="c-condition__summary">
|
||||||
<template v-if="!canEvaluateCriteria">
|
<template v-if="!canEvaluateCriteria">
|
||||||
Define criteria
|
Define criteria
|
||||||
@ -76,7 +82,7 @@
|
|||||||
<input v-model="condition.configuration.name"
|
<input v-model="condition.configuration.name"
|
||||||
class="t-condition-input__name"
|
class="t-condition-input__name"
|
||||||
type="text"
|
type="text"
|
||||||
@blur="persist"
|
@change="persist"
|
||||||
>
|
>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@ -99,7 +105,7 @@
|
|||||||
v-model="condition.configuration.output"
|
v-model="condition.configuration.output"
|
||||||
class="t-condition-name-input"
|
class="t-condition-name-input"
|
||||||
type="text"
|
type="text"
|
||||||
@blur="persist"
|
@change="persist"
|
||||||
>
|
>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@ -122,7 +128,7 @@
|
|||||||
|
|
||||||
<template v-if="telemetry.length || condition.configuration.criteria.length">
|
<template v-if="telemetry.length || condition.configuration.criteria.length">
|
||||||
<div v-for="(criterion, index) in condition.configuration.criteria"
|
<div v-for="(criterion, index) in condition.configuration.criteria"
|
||||||
:key="index"
|
:key="criterion.id"
|
||||||
class="c-cdef__criteria"
|
class="c-cdef__criteria"
|
||||||
>
|
>
|
||||||
<Criterion :telemetry="telemetry"
|
<Criterion :telemetry="telemetry"
|
||||||
@ -177,12 +183,14 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Criterion from './Criterion.vue';
|
import Criterion from './Criterion.vue';
|
||||||
import ConditionDescription from "./ConditionDescription.vue";
|
import ConditionDescription from "./ConditionDescription.vue";
|
||||||
import { TRIGGER, TRIGGER_LABEL } from "@/plugins/condition/utils/constants";
|
import { TRIGGER, TRIGGER_LABEL } from "@/plugins/condition/utils/constants";
|
||||||
|
import uuid from 'uuid';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
@ -207,6 +215,14 @@ export default {
|
|||||||
type: Array,
|
type: Array,
|
||||||
required: true,
|
required: true,
|
||||||
default: () => []
|
default: () => []
|
||||||
|
},
|
||||||
|
isDragging: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
moveIndex: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@ -217,8 +233,8 @@ export default {
|
|||||||
selectedOutputSelection: '',
|
selectedOutputSelection: '',
|
||||||
outputOptions: ['false', 'true', 'string'],
|
outputOptions: ['false', 'true', 'string'],
|
||||||
criterionIndex: 0,
|
criterionIndex: 0,
|
||||||
selectedTelemetryName: '',
|
draggingOver: false,
|
||||||
selectedFieldName: ''
|
isDefault: this.condition.isDefault
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -275,6 +291,7 @@ export default {
|
|||||||
},
|
},
|
||||||
addCriteria() {
|
addCriteria() {
|
||||||
const criteriaObject = {
|
const criteriaObject = {
|
||||||
|
id: uuid(),
|
||||||
telemetry: '',
|
telemetry: '',
|
||||||
operation: '',
|
operation: '',
|
||||||
input: '',
|
input: '',
|
||||||
@ -285,11 +302,39 @@ export default {
|
|||||||
dragStart(e) {
|
dragStart(e) {
|
||||||
e.dataTransfer.setData('dragging', e.target); // required for FF to initiate drag
|
e.dataTransfer.setData('dragging', e.target); // required for FF to initiate drag
|
||||||
e.dataTransfer.effectAllowed = "copyMove";
|
e.dataTransfer.effectAllowed = "copyMove";
|
||||||
e.dataTransfer.setDragImage(e.target.closest('.js-condition-drag-wrapper'), 0, 0);
|
e.dataTransfer.setDragImage(e.target.closest('.c-condition-h'), 0, 0);
|
||||||
this.$emit('setMoveIndex', this.conditionIndex);
|
this.$emit('setMoveIndex', this.conditionIndex);
|
||||||
},
|
},
|
||||||
dragStop(e) {
|
dragEnd(event) {
|
||||||
e.dataTransfer.clearData();
|
this.dragStarted = false;
|
||||||
|
event.dataTransfer.clearData();
|
||||||
|
this.$emit('dragComplete');
|
||||||
|
},
|
||||||
|
dropCondition(event, targetIndex) {
|
||||||
|
if (!this.isDragging) { return }
|
||||||
|
if (targetIndex > this.moveIndex) { targetIndex-- } // for 'downward' move
|
||||||
|
if (this.isValidTarget(targetIndex)) {
|
||||||
|
this.dragElement = undefined;
|
||||||
|
this.draggingOver = false;
|
||||||
|
this.$emit('dropCondition', targetIndex);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dragEnter(event, targetIndex) {
|
||||||
|
if (!this.isDragging) { return }
|
||||||
|
if (targetIndex > this.moveIndex) { targetIndex-- } // for 'downward' move
|
||||||
|
if (this.isValidTarget(targetIndex)) {
|
||||||
|
this.dragElement = event.target.parentElement;
|
||||||
|
this.draggingOver = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dragLeave(event) {
|
||||||
|
if (event.target.parentElement === this.dragElement) {
|
||||||
|
this.draggingOver = false;
|
||||||
|
this.dragElement = undefined;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isValidTarget(targetIndex) {
|
||||||
|
return this.moveIndex !== targetIndex;
|
||||||
},
|
},
|
||||||
destroy() {
|
destroy() {
|
||||||
},
|
},
|
||||||
@ -308,6 +353,7 @@ export default {
|
|||||||
},
|
},
|
||||||
cloneCriterion(index) {
|
cloneCriterion(index) {
|
||||||
const clonedCriterion = JSON.parse(JSON.stringify(this.condition.configuration.criteria[index]));
|
const clonedCriterion = JSON.parse(JSON.stringify(this.condition.configuration.criteria[index]));
|
||||||
|
clonedCriterion.id = uuid();
|
||||||
this.condition.configuration.criteria.splice(index + 1, 0, clonedCriterion);
|
this.condition.configuration.criteria.splice(index + 1, 0, clonedCriterion);
|
||||||
this.persist();
|
this.persist();
|
||||||
},
|
},
|
||||||
@ -317,7 +363,7 @@ export default {
|
|||||||
index: this.conditionIndex
|
index: this.conditionIndex
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
initCap: function (str) {
|
initCap(str) {
|
||||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section id="conditionCollection"
|
<section id="conditionCollection"
|
||||||
class="c-cs__conditions"
|
|
||||||
:class="{ 'is-expanded': expanded }"
|
:class="{ 'is-expanded': expanded }"
|
||||||
>
|
>
|
||||||
<div class="c-cs__header c-section__header">
|
<div class="c-cs__header c-section__header">
|
||||||
@ -53,30 +52,26 @@
|
|||||||
<span class="c-cs-button__label">Add Condition</span>
|
<span class="c-cs-button__label">Add Condition</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="c-cs__conditions-h">
|
<div class="c-cs__conditions-h"
|
||||||
<div v-for="(condition, index) in conditionCollection"
|
:class="{ 'is-active-dragging': isDragging }"
|
||||||
:key="condition.id"
|
|
||||||
class="c-condition-h"
|
|
||||||
>
|
>
|
||||||
<div v-if="isEditing"
|
<Condition v-for="(condition, index) in conditionCollection"
|
||||||
class="c-c__drag-ghost"
|
:key="condition.id"
|
||||||
@drop.prevent="dropCondition"
|
:condition="condition"
|
||||||
@dragenter="dragEnter"
|
|
||||||
@dragleave="dragLeave"
|
|
||||||
@dragover.prevent
|
|
||||||
></div>
|
|
||||||
<Condition :condition="condition"
|
|
||||||
:condition-index="index"
|
:condition-index="index"
|
||||||
:telemetry="telemetryObjs"
|
:telemetry="telemetryObjs"
|
||||||
:is-editing="isEditing"
|
:is-editing="isEditing"
|
||||||
|
:move-index="moveIndex"
|
||||||
|
:is-dragging="isDragging"
|
||||||
@updateCondition="updateCondition"
|
@updateCondition="updateCondition"
|
||||||
@removeCondition="removeCondition"
|
@removeCondition="removeCondition"
|
||||||
@cloneCondition="cloneCondition"
|
@cloneCondition="cloneCondition"
|
||||||
@setMoveIndex="setMoveIndex"
|
@setMoveIndex="setMoveIndex"
|
||||||
|
@dragComplete="dragComplete"
|
||||||
|
@dropCondition="dropCondition"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -109,9 +104,10 @@ export default {
|
|||||||
conditionResults: {},
|
conditionResults: {},
|
||||||
conditions: [],
|
conditions: [],
|
||||||
telemetryObjs: [],
|
telemetryObjs: [],
|
||||||
moveIndex: Number,
|
moveIndex: undefined,
|
||||||
isDragging: false,
|
isDragging: false,
|
||||||
defaultOutput: undefined
|
defaultOutput: undefined,
|
||||||
|
dragCounter: 0
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@ -166,9 +162,7 @@ export default {
|
|||||||
this.moveIndex = index;
|
this.moveIndex = index;
|
||||||
this.isDragging = true;
|
this.isDragging = true;
|
||||||
},
|
},
|
||||||
dropCondition(e) {
|
dropCondition(targetIndex) {
|
||||||
let targetIndex = Array.from(document.querySelectorAll('.c-c__drag-ghost')).indexOf(e.target);
|
|
||||||
if (targetIndex > this.moveIndex) { targetIndex-- } // for 'downward' move
|
|
||||||
const oldIndexArr = Object.keys(this.conditionCollection);
|
const oldIndexArr = Object.keys(this.conditionCollection);
|
||||||
const move = function (arr, old_index, new_index) {
|
const move = function (arr, old_index, new_index) {
|
||||||
while (old_index < 0) {
|
while (old_index < 0) {
|
||||||
@ -194,20 +188,10 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.reorder(reorderPlan);
|
this.reorder(reorderPlan);
|
||||||
|
},
|
||||||
e.target.classList.remove("dragging");
|
dragComplete() {
|
||||||
this.isDragging = false;
|
this.isDragging = false;
|
||||||
},
|
},
|
||||||
dragEnter(e) {
|
|
||||||
if (!this.isDragging) { return }
|
|
||||||
let targetIndex = Array.from(document.querySelectorAll('.c-c__drag-ghost')).indexOf(e.target);
|
|
||||||
if (targetIndex > this.moveIndex) { targetIndex-- } // for 'downward' move
|
|
||||||
if (this.moveIndex === targetIndex) { return }
|
|
||||||
e.target.classList.add("dragging");
|
|
||||||
},
|
|
||||||
dragLeave(e) {
|
|
||||||
e.target.classList.remove("dragging");
|
|
||||||
},
|
|
||||||
addTelemetryObject(domainObject) {
|
addTelemetryObject(domainObject) {
|
||||||
this.telemetryObjs.push(domainObject);
|
this.telemetryObjs.push(domainObject);
|
||||||
this.$emit('telemetryUpdated', this.telemetryObjs);
|
this.$emit('telemetryUpdated', this.telemetryObjs);
|
||||||
|
@ -105,6 +105,14 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
getCriterionDescription(criterion, index) {
|
getCriterionDescription(criterion, index) {
|
||||||
|
if (!criterion.telemetry) {
|
||||||
|
let description = `Unknown ${criterion.metadata} ${this.getOperatorText(criterion.operation, criterion.input)}`;
|
||||||
|
this.criterionDescriptions.splice(index, 0, description);
|
||||||
|
} else if (criterion.telemetry === 'all' || criterion.telemetry === 'any') {
|
||||||
|
const telemetryDescription = criterion.telemetry === 'all' ? 'All telemetry' : 'Any telemetry';
|
||||||
|
let description = `${telemetryDescription} ${criterion.metadata} ${this.getOperatorText(criterion.operation, criterion.input)}`;
|
||||||
|
this.criterionDescriptions.splice(index, 0, description);
|
||||||
|
} else {
|
||||||
this.openmct.objects.get(criterion.telemetry).then((telemetryObject) => {
|
this.openmct.objects.get(criterion.telemetry).then((telemetryObject) => {
|
||||||
if (telemetryObject.type === 'unknown') {
|
if (telemetryObject.type === 'unknown') {
|
||||||
let description = `Unknown ${criterion.metadata} ${this.getOperatorText(criterion.operation, criterion.input)}`;
|
let description = `Unknown ${criterion.metadata} ${this.getOperatorText(criterion.operation, criterion.input)}`;
|
||||||
@ -135,6 +143,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
getOperatorText(operationName, values) {
|
getOperatorText(operationName, values) {
|
||||||
const found = OPERATIONS.find((operation) => operation.name === operationName);
|
const found = OPERATIONS.find((operation) => operation.name === operationName);
|
||||||
|
@ -66,6 +66,13 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
getCriterionErrors(criterion, index) {
|
getCriterionErrors(criterion, index) {
|
||||||
|
if (!criterion.telemetry) {
|
||||||
|
this.conditionErrors.push({
|
||||||
|
message: ERROR.TELEMETRY_NOT_FOUND,
|
||||||
|
additionalInfo: ''
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (criterion.telemetry !== 'all' && criterion.telemetry !== 'any') {
|
||||||
this.openmct.objects.get(criterion.telemetry).then((telemetryObject) => {
|
this.openmct.objects.get(criterion.telemetry).then((telemetryObject) => {
|
||||||
if (telemetryObject.type === 'unknown') {
|
if (telemetryObject.type === 'unknown') {
|
||||||
this.conditionErrors.push({
|
this.conditionErrors.push({
|
||||||
@ -77,4 +84,6 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -23,24 +23,26 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="c-cs">
|
<div class="c-cs">
|
||||||
<section class="c-cs__current-output c-section">
|
<section class="c-cs__current-output c-section">
|
||||||
<div class="c-cs__header c-section__header">
|
|
||||||
<span class="c-cs__header-label c-section__label">Current Output</span>
|
|
||||||
</div>
|
|
||||||
<div class="c-cs__content c-cs__current-output-value">
|
<div class="c-cs__content c-cs__current-output-value">
|
||||||
|
<span class="c-cs__current-output-value__label">Current Output</span>
|
||||||
|
<span class="c-cs__current-output-value__value">
|
||||||
<template v-if="currentConditionOutput">
|
<template v-if="currentConditionOutput">
|
||||||
{{ currentConditionOutput }}
|
{{ currentConditionOutput }}
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
{{ defaultConditionOutput }}
|
{{ defaultConditionOutput }}
|
||||||
</template>
|
</template>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<TestData :is-editing="isEditing"
|
<div class="c-cs__test-data-and-conditions-w">
|
||||||
|
<TestData class="c-cs__test-data"
|
||||||
|
:is-editing="isEditing"
|
||||||
:test-data="testData"
|
:test-data="testData"
|
||||||
:telemetry="telemetryObjs"
|
:telemetry="telemetryObjs"
|
||||||
@updateTestData="updateTestData"
|
@updateTestData="updateTestData"
|
||||||
/>
|
/>
|
||||||
<ConditionCollection
|
<ConditionCollection class="c-cs__conditions"
|
||||||
:is-editing="isEditing"
|
:is-editing="isEditing"
|
||||||
:test-data="testData"
|
:test-data="testData"
|
||||||
@conditionSetResultUpdated="updateCurrentOutput"
|
@conditionSetResultUpdated="updateCurrentOutput"
|
||||||
@ -48,6 +50,7 @@
|
|||||||
@telemetryUpdated="updateTelemetry"
|
@telemetryUpdated="updateTelemetry"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
0
src/plugins/condition/components/CurrentOutput.vue
Normal file
0
src/plugins/condition/components/CurrentOutput.vue
Normal file
@ -23,7 +23,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<section v-show="isEditing"
|
<section v-show="isEditing"
|
||||||
id="test-data"
|
id="test-data"
|
||||||
class="c-cs__test-data"
|
|
||||||
:class="{ 'is-expanded': expanded }"
|
:class="{ 'is-expanded': expanded }"
|
||||||
>
|
>
|
||||||
<div class="c-cs__header c-section__header">
|
<div class="c-cs__header c-section__header">
|
||||||
@ -37,7 +36,7 @@
|
|||||||
<div v-if="expanded"
|
<div v-if="expanded"
|
||||||
class="c-cs__content"
|
class="c-cs__content"
|
||||||
>
|
>
|
||||||
<div class="c-cdef__controls"
|
<div class="c-cs__test-data__controls c-cdef__controls"
|
||||||
:disabled="!telemetry.length"
|
:disabled="!telemetry.length"
|
||||||
>
|
>
|
||||||
<label class="c-toggle-switch">
|
<label class="c-toggle-switch">
|
||||||
@ -96,7 +95,7 @@
|
|||||||
>
|
>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<div class="c-test-datum__buttons">
|
<div class="c-cs-test__buttons">
|
||||||
<button class="c-click-icon c-test-data__duplicate-button icon-duplicate"
|
<button class="c-click-icon c-test-data__duplicate-button icon-duplicate"
|
||||||
title="Duplicate this test datum"
|
title="Duplicate this test datum"
|
||||||
@click="addTestInput(testInput)"
|
@click="addTestInput(testInput)"
|
||||||
|
@ -1,116 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT, Copyright (c) 2014-2020, 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.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
.c-cs {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
&__content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
flex: 0 1 auto;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
> * {
|
|
||||||
flex: 0 0 auto;
|
|
||||||
overflow: hidden;
|
|
||||||
+ * {
|
|
||||||
margin-top: $interiorMarginSm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.c-button {
|
|
||||||
align-self: start;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-editing & {
|
|
||||||
// Add some space to kick away from blue editing border indication
|
|
||||||
padding: $interiorMargin;
|
|
||||||
}
|
|
||||||
|
|
||||||
section {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__conditions-h {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
flex: 1 1 auto;
|
|
||||||
overflow: auto;
|
|
||||||
padding-right: $interiorMarginSm;
|
|
||||||
|
|
||||||
> * + * {
|
|
||||||
margin-top: $interiorMarginSm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__conditions {
|
|
||||||
> * + * {
|
|
||||||
margin-top: $interiorMarginSm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.hint {
|
|
||||||
padding: $interiorMarginSm;
|
|
||||||
}
|
|
||||||
|
|
||||||
/************************** SPECIFIC ITEMS */
|
|
||||||
&__current-output-value {
|
|
||||||
font-size: 1.25em;
|
|
||||||
padding: $interiorMargin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************** TEST DATA */
|
|
||||||
.c-cs-tests {
|
|
||||||
flex: 0 1 auto;
|
|
||||||
overflow: auto;
|
|
||||||
padding-right: $interiorMarginSm;
|
|
||||||
|
|
||||||
> * + * {
|
|
||||||
margin-top: $interiorMarginSm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.c-cs-test {
|
|
||||||
> * {
|
|
||||||
flex: 0 0 auto;
|
|
||||||
+ * {
|
|
||||||
margin-left: $interiorMargin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__controls {
|
|
||||||
display: flex;
|
|
||||||
flex: 1 1 auto;
|
|
||||||
|
|
||||||
> * + * {
|
|
||||||
margin-left: $interiorMargin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT, Copyright (c) 2014-2020, 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.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
.c-condition,
|
|
||||||
.c-test-datum {
|
|
||||||
@include discreteItem();
|
|
||||||
display: flex;
|
|
||||||
padding: $interiorMargin;
|
|
||||||
|
|
||||||
&--edit {
|
|
||||||
line-height: 160%; // For layout when inputs wrap, like in criteria
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.c-condition {
|
|
||||||
flex-direction: column;
|
|
||||||
min-width: 400px;
|
|
||||||
|
|
||||||
> * + * {
|
|
||||||
margin-top: $interiorMarginSm;
|
|
||||||
}
|
|
||||||
&--browse {
|
|
||||||
.c-condition__summary {
|
|
||||||
border-top: 1px solid $colorInteriorBorder;
|
|
||||||
padding-top: $interiorMargin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************** HEADER */
|
|
||||||
&__header {
|
|
||||||
$h: 22px;
|
|
||||||
display: flex;
|
|
||||||
align-items: start;
|
|
||||||
align-content: stretch;
|
|
||||||
overflow: hidden;
|
|
||||||
min-height: $h;
|
|
||||||
line-height: $h;
|
|
||||||
|
|
||||||
> * {
|
|
||||||
flex: 0 0 auto;
|
|
||||||
+ * {
|
|
||||||
margin-left: $interiorMarginSm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__drag-grippy {
|
|
||||||
transform: translateY(50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&__name {
|
|
||||||
font-weight: bold;
|
|
||||||
align-self: baseline; // Fixes bold line-height offset problem
|
|
||||||
}
|
|
||||||
|
|
||||||
&__output,
|
|
||||||
&__summary {
|
|
||||||
flex: 1 1 auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************** CONDITION DEFINITION, EDITING */
|
|
||||||
.c-cdef {
|
|
||||||
display: grid;
|
|
||||||
grid-row-gap: $interiorMarginSm;
|
|
||||||
grid-column-gap: $interiorMargin;
|
|
||||||
grid-auto-columns: min-content 1fr max-content;
|
|
||||||
align-items: start;
|
|
||||||
min-width: 150px;
|
|
||||||
margin-left: 29px;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
&__criteria,
|
|
||||||
&__match-and-criteria {
|
|
||||||
display: contents;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__label {
|
|
||||||
grid-column: 1;
|
|
||||||
text-align: right;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__separator {
|
|
||||||
grid-column: 1 / span 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__controls {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
align-items: flex-start;
|
|
||||||
grid-column: 2;
|
|
||||||
|
|
||||||
> * > * {
|
|
||||||
margin-right: $interiorMarginSm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__buttons {
|
|
||||||
grid-column: 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.c-c__drag-ghost {
|
|
||||||
width: 100%;
|
|
||||||
min-height: $interiorMarginSm;
|
|
||||||
|
|
||||||
&.dragging {
|
|
||||||
min-height: 5em;
|
|
||||||
background-color: lightblue;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
311
src/plugins/condition/components/conditionals.scss
Normal file
311
src/plugins/condition/components/conditionals.scss
Normal file
@ -0,0 +1,311 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
/***************************** DRAGGING */
|
||||||
|
.is-active-dragging {
|
||||||
|
.c-condition-h__drop-target {
|
||||||
|
height: 3px;
|
||||||
|
margin-bottom: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-condition-h {
|
||||||
|
&__drop-target {
|
||||||
|
border-radius: $controlCr;
|
||||||
|
height: 0;
|
||||||
|
min-height: 0;
|
||||||
|
transition: background-color, height;
|
||||||
|
transition-duration: 150ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-drag-target {
|
||||||
|
.c-condition > * {
|
||||||
|
pointer-events: none; // Keeps the JS drop handler from being intercepted by internal elements
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-condition-h__drop-target {
|
||||||
|
background-color: rgba($colorKey, 0.7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-cs {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
/************************** CONDITION SET LAYOUT */
|
||||||
|
&__current-output {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__test-data-and-conditions-w {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__test-data,
|
||||||
|
&__conditions {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__test-data {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
max-height: 50%;
|
||||||
|
|
||||||
|
&.is-expanded {
|
||||||
|
margin-bottom: $interiorMargin * 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__conditions {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
|
||||||
|
> * + * {
|
||||||
|
margin-top: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 0 1 auto;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
overflow: hidden;
|
||||||
|
+ * {
|
||||||
|
margin-top: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-button {
|
||||||
|
align-self: start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-editing & {
|
||||||
|
// Add some space to kick away from blue editing border indication
|
||||||
|
padding: $interiorMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__conditions-h {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
overflow: auto;
|
||||||
|
padding-right: $interiorMarginSm;
|
||||||
|
|
||||||
|
> * + * {
|
||||||
|
margin-top: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint {
|
||||||
|
padding: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************** SPECIFIC ITEMS */
|
||||||
|
&__current-output-value {
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: baseline;
|
||||||
|
padding: 0 $interiorMargin $interiorMarginLg $interiorMargin;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
padding: $interiorMargin 0; // Must do this to align label and value
|
||||||
|
}
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
color: $colorInspectorSectionHeaderFg;
|
||||||
|
opacity: 0.9;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__value {
|
||||||
|
$p: $interiorMargin * 3;
|
||||||
|
font-size: 1.25em;
|
||||||
|
margin-left: $interiorMargin;
|
||||||
|
padding-left: $p;
|
||||||
|
padding-right: $p;
|
||||||
|
background: rgba(black, 0.2);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************** CONDITIONS AND TEST DATUM ELEMENTS */
|
||||||
|
.c-condition,
|
||||||
|
.c-test-datum {
|
||||||
|
@include discreteItem();
|
||||||
|
display: flex;
|
||||||
|
padding: $interiorMargin;
|
||||||
|
line-height: 170%; // Aligns text with controls like selects
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-cdef,
|
||||||
|
.c-cs-test {
|
||||||
|
&__controls {
|
||||||
|
display: flex;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
> * > * {
|
||||||
|
margin-right: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__buttons {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-condition {
|
||||||
|
flex-direction: column;
|
||||||
|
min-width: 400px;
|
||||||
|
|
||||||
|
> * + * {
|
||||||
|
margin-top: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
&--browse {
|
||||||
|
.c-condition__summary {
|
||||||
|
border-top: 1px solid $colorInteriorBorder;
|
||||||
|
padding-top: $interiorMargin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************** HEADER */
|
||||||
|
&__header {
|
||||||
|
$h: 22px;
|
||||||
|
display: flex;
|
||||||
|
align-items: start;
|
||||||
|
align-content: stretch;
|
||||||
|
overflow: hidden;
|
||||||
|
min-height: $h;
|
||||||
|
line-height: $h;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
+ * {
|
||||||
|
margin-left: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__drag-grippy {
|
||||||
|
transform: translateY(50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__name {
|
||||||
|
font-weight: bold;
|
||||||
|
align-self: baseline; // Fixes bold line-height offset problem
|
||||||
|
}
|
||||||
|
|
||||||
|
&__output,
|
||||||
|
&__summary {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************** CONDITION DEFINITION, EDITING */
|
||||||
|
.c-cdef {
|
||||||
|
display: grid;
|
||||||
|
grid-row-gap: $interiorMarginSm;
|
||||||
|
grid-column-gap: $interiorMargin;
|
||||||
|
grid-auto-columns: min-content 1fr max-content;
|
||||||
|
align-items: start;
|
||||||
|
min-width: 150px;
|
||||||
|
margin-left: 29px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&__criteria,
|
||||||
|
&__match-and-criteria {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
grid-column: 1;
|
||||||
|
text-align: right;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__separator {
|
||||||
|
grid-column: 1 / span 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__controls {
|
||||||
|
align-items: flex-start;
|
||||||
|
grid-column: 2;
|
||||||
|
|
||||||
|
> * > * {
|
||||||
|
margin-right: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__buttons {
|
||||||
|
grid-column: 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-c__drag-ghost {
|
||||||
|
width: 100%;
|
||||||
|
min-height: $interiorMarginSm;
|
||||||
|
|
||||||
|
&.dragging {
|
||||||
|
min-height: 5em;
|
||||||
|
background-color: lightblue;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************** TEST DATA */
|
||||||
|
.c-cs__test-data {
|
||||||
|
&__controls {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-cs-tests {
|
||||||
|
flex: 0 1 auto;
|
||||||
|
overflow: auto;
|
||||||
|
padding-right: $interiorMarginSm;
|
||||||
|
|
||||||
|
> * + * {
|
||||||
|
margin-top: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-cs-test {
|
||||||
|
> * + * {
|
||||||
|
margin-left: $interiorMargin;
|
||||||
|
}
|
||||||
|
}
|
@ -79,6 +79,8 @@
|
|||||||
<div v-for="(conditionStyle, index) in conditionalStyles"
|
<div v-for="(conditionStyle, index) in conditionalStyles"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="c-inspect-styles__condition"
|
class="c-inspect-styles__condition"
|
||||||
|
:class="{'is-current': conditionStyle.conditionId === selectedConditionId}"
|
||||||
|
@click="applySelectedConditionStyle(conditionStyle.conditionId)"
|
||||||
>
|
>
|
||||||
<condition-error :show-label="true"
|
<condition-error :show-label="true"
|
||||||
:condition="getCondition(conditionStyle.conditionId)"
|
:condition="getCondition(conditionStyle.conditionId)"
|
||||||
@ -105,6 +107,7 @@ import ConditionDescription from "@/plugins/condition/components/ConditionDescri
|
|||||||
import ConditionError from "@/plugins/condition/components/ConditionError.vue";
|
import ConditionError from "@/plugins/condition/components/ConditionError.vue";
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import PreviewAction from "@/ui/preview/PreviewAction.js";
|
import PreviewAction from "@/ui/preview/PreviewAction.js";
|
||||||
|
import {getApplicableStylesForItem} from "@/plugins/condition/utils/styleUtils";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ConditionalStylesView',
|
name: 'ConditionalStylesView',
|
||||||
@ -115,24 +118,8 @@ export default {
|
|||||||
},
|
},
|
||||||
inject: [
|
inject: [
|
||||||
'openmct',
|
'openmct',
|
||||||
'domainObject'
|
'selection'
|
||||||
],
|
],
|
||||||
props: {
|
|
||||||
itemId: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
initialStyles: {
|
|
||||||
type: Object,
|
|
||||||
default() {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
canHide: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
conditionalStyles: [],
|
conditionalStyles: [],
|
||||||
@ -141,13 +128,16 @@ export default {
|
|||||||
isEditing: this.openmct.editor.isEditing(),
|
isEditing: this.openmct.editor.isEditing(),
|
||||||
conditions: undefined,
|
conditions: undefined,
|
||||||
conditionsLoaded: false,
|
conditionsLoaded: false,
|
||||||
navigateToPath: ''
|
navigateToPath: '',
|
||||||
|
selectedConditionId: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
this.openmct.editor.off('isEditing', this.setEditState);
|
this.removeListeners();
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.itemId = '';
|
||||||
|
this.getDomainObjectFromSelection();
|
||||||
this.previewAction = new PreviewAction(this.openmct);
|
this.previewAction = new PreviewAction(this.openmct);
|
||||||
if (this.domainObject.configuration && this.domainObject.configuration.objectStyles) {
|
if (this.domainObject.configuration && this.domainObject.configuration.objectStyles) {
|
||||||
let objectStyles = this.itemId ? this.domainObject.configuration.objectStyles[this.itemId] : this.domainObject.configuration.objectStyles;
|
let objectStyles = this.itemId ? this.domainObject.configuration.objectStyles[this.itemId] : this.domainObject.configuration.objectStyles;
|
||||||
@ -162,6 +152,52 @@ export default {
|
|||||||
this.openmct.editor.on('isEditing', this.setEditState);
|
this.openmct.editor.on('isEditing', this.setEditState);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
isItemType(type, item) {
|
||||||
|
return item && (item.type === type);
|
||||||
|
},
|
||||||
|
getDomainObjectFromSelection() {
|
||||||
|
let layoutItem;
|
||||||
|
let domainObject;
|
||||||
|
|
||||||
|
if (this.selection[0].length > 1) {
|
||||||
|
//If there are more than 1 items in the this.selection[0] list, the first one could either be a sub domain object OR a layout drawing control.
|
||||||
|
//The second item in the this.selection[0] list is the container object (usually a layout)
|
||||||
|
layoutItem = this.selection[0][0].context.layoutItem;
|
||||||
|
const item = this.selection[0][0].context.item;
|
||||||
|
this.canHide = true;
|
||||||
|
if (item &&
|
||||||
|
(!layoutItem || (this.isItemType('subobject-view', layoutItem)))) {
|
||||||
|
domainObject = item;
|
||||||
|
} else {
|
||||||
|
domainObject = this.selection[0][1].context.item;
|
||||||
|
if (layoutItem) {
|
||||||
|
this.itemId = layoutItem.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
domainObject = this.selection[0][0].context.item;
|
||||||
|
}
|
||||||
|
this.domainObject = domainObject;
|
||||||
|
this.initialStyles = getApplicableStylesForItem(domainObject, layoutItem);
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.removeListeners();
|
||||||
|
if (this.domainObject) {
|
||||||
|
this.stopObserving = this.openmct.objects.observe(this.domainObject, '*', newDomainObject => this.domainObject = newDomainObject);
|
||||||
|
this.stopObservingItems = this.openmct.objects.observe(this.domainObject, 'configuration.items', this.updateDomainObjectItemStyles);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
removeListeners() {
|
||||||
|
if (this.stopObserving) {
|
||||||
|
this.stopObserving();
|
||||||
|
}
|
||||||
|
if (this.stopObservingItems) {
|
||||||
|
this.stopObservingItems();
|
||||||
|
}
|
||||||
|
if (this.stopProvidingTelemetry) {
|
||||||
|
this.stopProvidingTelemetry();
|
||||||
|
}
|
||||||
|
},
|
||||||
initialize(conditionSetDomainObject) {
|
initialize(conditionSetDomainObject) {
|
||||||
//If there are new conditions in the conditionSet we need to set those styles to default
|
//If there are new conditions in the conditionSet we need to set those styles to default
|
||||||
this.conditionSetDomainObject = conditionSetDomainObject;
|
this.conditionSetDomainObject = conditionSetDomainObject;
|
||||||
@ -170,6 +206,13 @@ export default {
|
|||||||
},
|
},
|
||||||
setEditState(isEditing) {
|
setEditState(isEditing) {
|
||||||
this.isEditing = isEditing;
|
this.isEditing = isEditing;
|
||||||
|
if (this.isEditing) {
|
||||||
|
if (this.stopProvidingTelemetry) {
|
||||||
|
this.stopProvidingTelemetry();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.subscribeToConditionSet();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
addConditionSet() {
|
addConditionSet() {
|
||||||
let conditionSetDomainObject;
|
let conditionSetDomainObject;
|
||||||
@ -240,6 +283,8 @@ export default {
|
|||||||
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
|
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
|
||||||
if (this.itemId) {
|
if (this.itemId) {
|
||||||
domainObjectStyles[this.itemId].conditionSetIdentifier = undefined;
|
domainObjectStyles[this.itemId].conditionSetIdentifier = undefined;
|
||||||
|
domainObjectStyles[this.itemId].selectedConditionId = undefined;
|
||||||
|
domainObjectStyles[this.itemId].defaultConditionId = undefined;
|
||||||
delete domainObjectStyles[this.itemId].conditionSetIdentifier;
|
delete domainObjectStyles[this.itemId].conditionSetIdentifier;
|
||||||
domainObjectStyles[this.itemId].styles = undefined;
|
domainObjectStyles[this.itemId].styles = undefined;
|
||||||
delete domainObjectStyles[this.itemId].styles;
|
delete domainObjectStyles[this.itemId].styles;
|
||||||
@ -248,6 +293,8 @@ export default {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
domainObjectStyles.conditionSetIdentifier = undefined;
|
domainObjectStyles.conditionSetIdentifier = undefined;
|
||||||
|
domainObjectStyles.selectedConditionId = undefined;
|
||||||
|
domainObjectStyles.defaultConditionId = undefined;
|
||||||
delete domainObjectStyles.conditionSetIdentifier;
|
delete domainObjectStyles.conditionSetIdentifier;
|
||||||
domainObjectStyles.styles = undefined;
|
domainObjectStyles.styles = undefined;
|
||||||
delete domainObjectStyles.styles;
|
delete domainObjectStyles.styles;
|
||||||
@ -257,6 +304,43 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.persist(domainObjectStyles);
|
this.persist(domainObjectStyles);
|
||||||
|
if (this.stopProvidingTelemetry) {
|
||||||
|
this.stopProvidingTelemetry();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateDomainObjectItemStyles(newItems) {
|
||||||
|
//check that all items that have been styles still exist. Otherwise delete those styles
|
||||||
|
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
|
||||||
|
let itemsToRemove = [];
|
||||||
|
let keys = Object.keys(domainObjectStyles);
|
||||||
|
//TODO: Need an easier way to find which properties are itemIds
|
||||||
|
keys.forEach((key) => {
|
||||||
|
const keyIsItemId = (key !== 'styles') &&
|
||||||
|
(key !== 'staticStyle') &&
|
||||||
|
(key !== 'defaultConditionId') &&
|
||||||
|
(key !== 'selectedConditionId') &&
|
||||||
|
(key !== 'conditionSetIdentifier');
|
||||||
|
if (keyIsItemId) {
|
||||||
|
if (!(newItems.find(item => item.id === key))) {
|
||||||
|
itemsToRemove.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (itemsToRemove.length) {
|
||||||
|
this.removeItemStyles(itemsToRemove, domainObjectStyles);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeItemStyles(itemIds, domainObjectStyles) {
|
||||||
|
itemIds.forEach(itemId => {
|
||||||
|
if (domainObjectStyles[itemId]) {
|
||||||
|
domainObjectStyles[itemId] = undefined;
|
||||||
|
delete domainObjectStyles[this.itemId];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (_.isEmpty(domainObjectStyles)) {
|
||||||
|
domainObjectStyles = undefined;
|
||||||
|
}
|
||||||
|
this.persist(domainObjectStyles);
|
||||||
},
|
},
|
||||||
initializeConditionalStyles() {
|
initializeConditionalStyles() {
|
||||||
if (!this.conditions) {
|
if (!this.conditions) {
|
||||||
@ -264,6 +348,9 @@ export default {
|
|||||||
}
|
}
|
||||||
let conditionalStyles = [];
|
let conditionalStyles = [];
|
||||||
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
|
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
|
||||||
|
if (conditionConfiguration.isDefault) {
|
||||||
|
this.selectedConditionId = conditionConfiguration.id;
|
||||||
|
}
|
||||||
this.conditions[conditionConfiguration.id] = conditionConfiguration;
|
this.conditions[conditionConfiguration.id] = conditionConfiguration;
|
||||||
let foundStyle = this.findStyleByConditionId(conditionConfiguration.id);
|
let foundStyle = this.findStyleByConditionId(conditionConfiguration.id);
|
||||||
if (foundStyle) {
|
if (foundStyle) {
|
||||||
@ -279,13 +366,39 @@ export default {
|
|||||||
//we're doing this so that we remove styles for any conditions that have been removed from the condition set
|
//we're doing this so that we remove styles for any conditions that have been removed from the condition set
|
||||||
this.conditionalStyles = conditionalStyles;
|
this.conditionalStyles = conditionalStyles;
|
||||||
this.conditionsLoaded = true;
|
this.conditionsLoaded = true;
|
||||||
this.persist(this.getDomainObjectConditionalStyle());
|
this.persist(this.getDomainObjectConditionalStyle(this.selectedConditionId));
|
||||||
|
if (!this.isEditing) {
|
||||||
|
this.subscribeToConditionSet();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
subscribeToConditionSet() {
|
||||||
|
if (this.stopProvidingTelemetry) {
|
||||||
|
this.stopProvidingTelemetry();
|
||||||
|
}
|
||||||
|
if (this.conditionSetDomainObject) {
|
||||||
|
this.openmct.telemetry.request(this.conditionSetDomainObject)
|
||||||
|
.then(output => {
|
||||||
|
if (output && output.length) {
|
||||||
|
this.handleConditionSetResultUpdated(output[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(this.conditionSetDomainObject, this.handleConditionSetResultUpdated.bind(this));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleConditionSetResultUpdated(resultData) {
|
||||||
|
this.selectedConditionId = resultData ? resultData.conditionId : '';
|
||||||
},
|
},
|
||||||
initializeStaticStyle(objectStyles) {
|
initializeStaticStyle(objectStyles) {
|
||||||
let staticStyle = objectStyles && objectStyles.staticStyle;
|
let staticStyle = objectStyles && objectStyles.staticStyle;
|
||||||
this.staticStyle = staticStyle || {
|
if (staticStyle) {
|
||||||
|
this.staticStyle = {
|
||||||
|
style: Object.assign({}, this.initialStyles, staticStyle.style)
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
this.staticStyle = {
|
||||||
style: Object.assign({}, this.initialStyles)
|
style: Object.assign({}, this.initialStyles)
|
||||||
};
|
};
|
||||||
|
}
|
||||||
},
|
},
|
||||||
findStyleByConditionId(id) {
|
findStyleByConditionId(id) {
|
||||||
return this.conditionalStyles.find(conditionalStyle => conditionalStyle.conditionId === id);
|
return this.conditionalStyles.find(conditionalStyle => conditionalStyle.conditionId === id);
|
||||||
@ -298,14 +411,19 @@ export default {
|
|||||||
let found = this.findStyleByConditionId(conditionStyle.conditionId);
|
let found = this.findStyleByConditionId(conditionStyle.conditionId);
|
||||||
if (found) {
|
if (found) {
|
||||||
found.style = conditionStyle.style;
|
found.style = conditionStyle.style;
|
||||||
|
this.selectedConditionId = found.conditionId;
|
||||||
this.persist(this.getDomainObjectConditionalStyle());
|
this.persist(this.getDomainObjectConditionalStyle());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getDomainObjectConditionalStyle() {
|
getDomainObjectConditionalStyle(defaultConditionId) {
|
||||||
let objectStyle = {
|
let objectStyle = {
|
||||||
styles: this.conditionalStyles,
|
styles: this.conditionalStyles,
|
||||||
staticStyle: this.staticStyle
|
staticStyle: this.staticStyle,
|
||||||
|
selectedConditionId: this.selectedConditionId
|
||||||
};
|
};
|
||||||
|
if (defaultConditionId) {
|
||||||
|
objectStyle.defaultConditionId = defaultConditionId;
|
||||||
|
}
|
||||||
if (this.conditionSetDomainObject) {
|
if (this.conditionSetDomainObject) {
|
||||||
objectStyle.conditionSetIdentifier = this.conditionSetDomainObject.identifier;
|
objectStyle.conditionSetIdentifier = this.conditionSetDomainObject.identifier;
|
||||||
}
|
}
|
||||||
@ -327,6 +445,10 @@ export default {
|
|||||||
getCondition(id) {
|
getCondition(id) {
|
||||||
return this.conditions ? this.conditions[id] : {};
|
return this.conditions ? this.conditions[id] : {};
|
||||||
},
|
},
|
||||||
|
applySelectedConditionStyle(conditionId) {
|
||||||
|
this.selectedConditionId = conditionId;
|
||||||
|
this.persist(this.getDomainObjectConditionalStyle());
|
||||||
|
},
|
||||||
persist(style) {
|
persist(style) {
|
||||||
this.openmct.objects.mutate(this.domainObject, 'configuration.objectStyles', style);
|
this.openmct.objects.mutate(this.domainObject, 'configuration.objectStyles', style);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,269 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, 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="c-inspector__styles c-inspect-styles">
|
||||||
|
<div class="c-inspect-styles__header">
|
||||||
|
Object Style
|
||||||
|
</div>
|
||||||
|
<div class="c-inspect-styles__content">
|
||||||
|
<div v-if="isStaticAndConditionalStyles"
|
||||||
|
class="c-inspect-styles__mixed-static-and-conditional u-alert u-alert--block u-alert--with-icon"
|
||||||
|
>
|
||||||
|
Your selection includes one or more items that use Conditional Styling. Applying a static style below will replace any Conditional Styling with the new choice.
|
||||||
|
</div>
|
||||||
|
<div v-if="staticStyle"
|
||||||
|
class="c-inspect-styles__style"
|
||||||
|
>
|
||||||
|
<style-editor class="c-inspect-styles__editor"
|
||||||
|
:style-item="staticStyle"
|
||||||
|
:is-editing="isEditing"
|
||||||
|
:mixed-styles="mixedStyles"
|
||||||
|
@persist="updateStaticStyle"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import StyleEditor from "./StyleEditor.vue";
|
||||||
|
import PreviewAction from "@/ui/preview/PreviewAction.js";
|
||||||
|
import { getApplicableStylesForItem, getConsolidatedStyleValues, getConditionalStyleForItem } from "@/plugins/condition/utils/styleUtils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'MultiSelectStylesView',
|
||||||
|
components: {
|
||||||
|
StyleEditor
|
||||||
|
},
|
||||||
|
inject: [
|
||||||
|
'openmct',
|
||||||
|
'selection'
|
||||||
|
],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
staticStyle: undefined,
|
||||||
|
isEditing: this.openmct.editor.isEditing(),
|
||||||
|
mixedStyles: [],
|
||||||
|
isStaticAndConditionalStyles: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
this.removeListeners();
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.items = [];
|
||||||
|
this.previewAction = new PreviewAction(this.openmct);
|
||||||
|
this.getObjectsAndItemsFromSelection();
|
||||||
|
this.initializeStaticStyle();
|
||||||
|
this.openmct.editor.on('isEditing', this.setEditState);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isItemType(type, item) {
|
||||||
|
return item && (item.type === type);
|
||||||
|
},
|
||||||
|
hasConditionalStyles(domainObject, id) {
|
||||||
|
return getConditionalStyleForItem(domainObject, id) !== undefined;
|
||||||
|
},
|
||||||
|
getObjectsAndItemsFromSelection() {
|
||||||
|
let domainObject;
|
||||||
|
let subObjects = [];
|
||||||
|
|
||||||
|
//multiple selection
|
||||||
|
let itemInitialStyles = [];
|
||||||
|
let itemStyle;
|
||||||
|
this.selection.forEach((selectionItem) => {
|
||||||
|
const item = selectionItem[0].context.item;
|
||||||
|
const layoutItem = selectionItem[0].context.layoutItem;
|
||||||
|
if (item && this.isItemType('subobject-view', layoutItem)) {
|
||||||
|
subObjects.push(item);
|
||||||
|
itemStyle = getApplicableStylesForItem(item);
|
||||||
|
if (!this.isStaticAndConditionalStyles) {
|
||||||
|
this.isStaticAndConditionalStyles = this.hasConditionalStyles(item);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
domainObject = selectionItem[1].context.item;
|
||||||
|
itemStyle = getApplicableStylesForItem(domainObject, layoutItem || item);
|
||||||
|
this.items.push({
|
||||||
|
id: layoutItem.id,
|
||||||
|
applicableStyles: itemStyle
|
||||||
|
});
|
||||||
|
if (!this.isStaticAndConditionalStyles) {
|
||||||
|
this.isStaticAndConditionalStyles = this.hasConditionalStyles(domainObject, layoutItem.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
itemInitialStyles.push(itemStyle);
|
||||||
|
});
|
||||||
|
const {styles, mixedStyles} = getConsolidatedStyleValues(itemInitialStyles);
|
||||||
|
this.initialStyles = styles;
|
||||||
|
this.mixedStyles = mixedStyles;
|
||||||
|
|
||||||
|
this.domainObject = domainObject;
|
||||||
|
this.removeListeners();
|
||||||
|
if (this.domainObject) {
|
||||||
|
this.stopObserving = this.openmct.objects.observe(this.domainObject, '*', newDomainObject => this.domainObject = newDomainObject);
|
||||||
|
this.stopObservingItems = this.openmct.objects.observe(this.domainObject, 'configuration.items', this.updateDomainObjectItemStyles);
|
||||||
|
}
|
||||||
|
|
||||||
|
subObjects.forEach(this.registerListener);
|
||||||
|
},
|
||||||
|
updateDomainObjectItemStyles(newItems) {
|
||||||
|
//check that all items that have been styles still exist. Otherwise delete those styles
|
||||||
|
let keys = Object.keys(this.domainObject.configuration.objectStyles || {});
|
||||||
|
keys.forEach((key) => {
|
||||||
|
if ((key !== 'styles') &&
|
||||||
|
(key !== 'staticStyle') &&
|
||||||
|
(key !== 'conditionSetIdentifier')) {
|
||||||
|
if (!(newItems.find(item => item.id === key))) {
|
||||||
|
this.removeItemStyles(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
registerListener(domainObject) {
|
||||||
|
let id = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||||
|
|
||||||
|
if (!this.domainObjectsById) {
|
||||||
|
this.domainObjectsById = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.domainObjectsById[id]) {
|
||||||
|
this.domainObjectsById[id] = domainObject;
|
||||||
|
this.observeObject(domainObject, id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
observeObject(domainObject, id) {
|
||||||
|
let unobserveObject = this.openmct.objects.observe(domainObject, '*', function (newObject) {
|
||||||
|
this.domainObjectsById[id] = JSON.parse(JSON.stringify(newObject));
|
||||||
|
}.bind(this));
|
||||||
|
this.unObserveObjects.push(unobserveObject);
|
||||||
|
},
|
||||||
|
removeListeners() {
|
||||||
|
if (this.stopObserving) {
|
||||||
|
this.stopObserving();
|
||||||
|
}
|
||||||
|
if (this.stopObservingItems) {
|
||||||
|
this.stopObservingItems();
|
||||||
|
}
|
||||||
|
if (this.unObserveObjects) {
|
||||||
|
this.unObserveObjects.forEach((unObserveObject) => {
|
||||||
|
unObserveObject();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.unObserveObjects = [];
|
||||||
|
},
|
||||||
|
removeItemStyles(itemId) {
|
||||||
|
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
|
||||||
|
if (itemId && domainObjectStyles[itemId]) {
|
||||||
|
domainObjectStyles[itemId] = undefined;
|
||||||
|
delete domainObjectStyles[this.itemId];
|
||||||
|
|
||||||
|
if (_.isEmpty(domainObjectStyles)) {
|
||||||
|
domainObjectStyles = undefined;
|
||||||
|
}
|
||||||
|
this.persist(this.domainObject, domainObjectStyles);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeConditionalStyles(domainObjectStyles, itemId) {
|
||||||
|
if (itemId) {
|
||||||
|
domainObjectStyles[itemId].conditionSetIdentifier = undefined;
|
||||||
|
delete domainObjectStyles[itemId].conditionSetIdentifier;
|
||||||
|
domainObjectStyles[itemId].styles = undefined;
|
||||||
|
delete domainObjectStyles[itemId].styles;
|
||||||
|
} else {
|
||||||
|
domainObjectStyles.conditionSetIdentifier = undefined;
|
||||||
|
delete domainObjectStyles.conditionSetIdentifier;
|
||||||
|
domainObjectStyles.styles = undefined;
|
||||||
|
delete domainObjectStyles.styles;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setEditState(isEditing) {
|
||||||
|
this.isEditing = isEditing;
|
||||||
|
},
|
||||||
|
initializeStaticStyle() {
|
||||||
|
this.staticStyle = {
|
||||||
|
style: Object.assign({}, this.initialStyles)
|
||||||
|
};
|
||||||
|
},
|
||||||
|
updateStaticStyle(staticStyle, property) {
|
||||||
|
//update the static style for each of the layoutItems as well as each sub object item
|
||||||
|
this.staticStyle = staticStyle;
|
||||||
|
this.persist(this.domainObject, this.getDomainObjectStyle(this.domainObject, property, this.items));
|
||||||
|
if (this.domainObjectsById) {
|
||||||
|
const keys = Object.keys(this.domainObjectsById);
|
||||||
|
keys.forEach(key => {
|
||||||
|
let domainObject = this.domainObjectsById[key];
|
||||||
|
this.persist(domainObject, this.getDomainObjectStyle(domainObject, property));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.isStaticAndConditionalStyles = false;
|
||||||
|
let foundIndex = this.mixedStyles.indexOf(property);
|
||||||
|
if (foundIndex > -1) {
|
||||||
|
this.mixedStyles.splice(foundIndex, 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getDomainObjectStyle(domainObject, property, items) {
|
||||||
|
let domainObjectStyles = (domainObject.configuration && domainObject.configuration.objectStyles) || {};
|
||||||
|
|
||||||
|
if (items) {
|
||||||
|
items.forEach(item => {
|
||||||
|
let itemStaticStyle = {};
|
||||||
|
if (domainObjectStyles[item.id] && domainObjectStyles[item.id].staticStyle) {
|
||||||
|
itemStaticStyle = domainObjectStyles[item.id].staticStyle.style;
|
||||||
|
}
|
||||||
|
Object.keys(item.applicableStyles).forEach(key => {
|
||||||
|
if (property === key) {
|
||||||
|
itemStaticStyle[key] = this.staticStyle.style[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (this.isStaticAndConditionalStyles) {
|
||||||
|
this.removeConditionalStyles(domainObjectStyles, item.id);
|
||||||
|
}
|
||||||
|
if (_.isEmpty(itemStaticStyle)) {
|
||||||
|
itemStaticStyle = undefined;
|
||||||
|
domainObjectStyles[item.id] = undefined;
|
||||||
|
} else {
|
||||||
|
domainObjectStyles[item.id] = Object.assign({}, { staticStyle: { style: itemStaticStyle } });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (!domainObjectStyles.staticStyle) {
|
||||||
|
domainObjectStyles.staticStyle = {
|
||||||
|
style: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.isStaticAndConditionalStyles) {
|
||||||
|
this.removeConditionalStyles(domainObjectStyles);
|
||||||
|
}
|
||||||
|
domainObjectStyles.staticStyle.style[property] = this.staticStyle.style[property];
|
||||||
|
}
|
||||||
|
|
||||||
|
return domainObjectStyles;
|
||||||
|
},
|
||||||
|
|
||||||
|
persist(domainObject, style) {
|
||||||
|
this.openmct.objects.mutate(domainObject, 'configuration.objectStyles', style);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -22,38 +22,41 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="c-style">
|
<div class="c-style">
|
||||||
<span class="c-style-thumb"
|
<span :class="[
|
||||||
:class="{ 'is-style-invisible': styleItem.style.isStyleInvisible }"
|
{ 'is-style-invisible': styleItem.style.isStyleInvisible },
|
||||||
:style="[styleItem.style.imageUrl ? { backgroundImage:'url(' + styleItem.style.imageUrl + ')'} : styleItem.style ]"
|
{ 'c-style-thumb--mixed': mixedStyles.indexOf('backgroundColor') > -1 }
|
||||||
|
]"
|
||||||
|
:style="[styleItem.style.imageUrl ? { backgroundImage:'url(' + styleItem.style.imageUrl + ')'} : itemStyle ]"
|
||||||
|
class="c-style-thumb"
|
||||||
>
|
>
|
||||||
<span class="c-style-thumb__text"
|
<span class="c-style-thumb__text"
|
||||||
:class="{ 'hide-nice': !styleItem.style.color }"
|
:class="{ 'hide-nice': !hasProperty(styleItem.style.color) }"
|
||||||
>
|
>
|
||||||
ABC
|
ABC
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="c-toolbar">
|
<span class="c-toolbar">
|
||||||
<toolbar-color-picker v-if="styleItem.style.border"
|
<toolbar-color-picker v-if="hasProperty(styleItem.style.border)"
|
||||||
class="c-style__toolbar-button--border-color u-menu-to--center"
|
class="c-style__toolbar-button--border-color u-menu-to--center"
|
||||||
:options="borderColorOption"
|
:options="borderColorOption"
|
||||||
@change="updateStyleValue"
|
@change="updateStyleValue"
|
||||||
/>
|
/>
|
||||||
<toolbar-color-picker v-if="styleItem.style.backgroundColor"
|
<toolbar-color-picker v-if="hasProperty(styleItem.style.backgroundColor)"
|
||||||
class="c-style__toolbar-button--background-color u-menu-to--center"
|
class="c-style__toolbar-button--background-color u-menu-to--center"
|
||||||
:options="backgroundColorOption"
|
:options="backgroundColorOption"
|
||||||
@change="updateStyleValue"
|
@change="updateStyleValue"
|
||||||
/>
|
/>
|
||||||
<toolbar-color-picker v-if="styleItem.style.color"
|
<toolbar-color-picker v-if="hasProperty(styleItem.style.color)"
|
||||||
class="c-style__toolbar-button--color u-menu-to--center"
|
class="c-style__toolbar-button--color u-menu-to--center"
|
||||||
:options="colorOption"
|
:options="colorOption"
|
||||||
@change="updateStyleValue"
|
@change="updateStyleValue"
|
||||||
/>
|
/>
|
||||||
<toolbar-button v-if="styleItem.style.imageUrl !== undefined"
|
<toolbar-button v-if="hasProperty(styleItem.style.imageUrl)"
|
||||||
class="c-style__toolbar-button--image-url"
|
class="c-style__toolbar-button--image-url"
|
||||||
:options="imageUrlOption"
|
:options="imageUrlOption"
|
||||||
@change="updateStyleValue"
|
@change="updateStyleValue"
|
||||||
/>
|
/>
|
||||||
<toolbar-toggle-button v-if="styleItem.style.isStyleInvisible !== undefined"
|
<toolbar-toggle-button v-if="hasProperty(styleItem.style.isStyleInvisible)"
|
||||||
class="c-style__toolbar-button--toggle-visible"
|
class="c-style__toolbar-button--toggle-visible"
|
||||||
:options="isStyleInvisibleOption"
|
:options="isStyleInvisibleOption"
|
||||||
@change="updateStyleValue"
|
@change="updateStyleValue"
|
||||||
@ -68,6 +71,7 @@ import ToolbarColorPicker from "@/ui/toolbar/components/toolbar-color-picker.vue
|
|||||||
import ToolbarButton from "@/ui/toolbar/components/toolbar-button.vue";
|
import ToolbarButton from "@/ui/toolbar/components/toolbar-button.vue";
|
||||||
import ToolbarToggleButton from "@/ui/toolbar/components/toolbar-toggle-button.vue";
|
import ToolbarToggleButton from "@/ui/toolbar/components/toolbar-toggle-button.vue";
|
||||||
import {STYLE_CONSTANTS} from "@/plugins/condition/utils/constants";
|
import {STYLE_CONSTANTS} from "@/plugins/condition/utils/constants";
|
||||||
|
import {getStylesWithoutNoneValue} from "@/plugins/condition/utils/styleUtils";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'StyleEditor',
|
name: 'StyleEditor',
|
||||||
@ -83,37 +87,52 @@ export default {
|
|||||||
isEditing: {
|
isEditing: {
|
||||||
type: Boolean
|
type: Boolean
|
||||||
},
|
},
|
||||||
|
mixedStyles: {
|
||||||
|
type: Array,
|
||||||
|
default() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
styleItem: {
|
styleItem: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
itemStyle() {
|
||||||
|
return getStylesWithoutNoneValue(this.styleItem.style);
|
||||||
|
},
|
||||||
borderColorOption() {
|
borderColorOption() {
|
||||||
|
let value = this.styleItem.style.border.replace('1px solid ', '');
|
||||||
return {
|
return {
|
||||||
icon: 'icon-line-horz',
|
icon: 'icon-line-horz',
|
||||||
title: STYLE_CONSTANTS.borderColorTitle,
|
title: STYLE_CONSTANTS.borderColorTitle,
|
||||||
value: this.styleItem.style.border.replace('1px solid ', ''),
|
value: this.normalizeValueForSwatch(value),
|
||||||
property: 'border',
|
property: 'border',
|
||||||
isEditing: this.isEditing
|
isEditing: this.isEditing,
|
||||||
|
nonSpecific: this.mixedStyles.indexOf('border') > -1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
backgroundColorOption() {
|
backgroundColorOption() {
|
||||||
|
let value = this.styleItem.style.backgroundColor;
|
||||||
return {
|
return {
|
||||||
icon: 'icon-paint-bucket',
|
icon: 'icon-paint-bucket',
|
||||||
title: STYLE_CONSTANTS.backgroundColorTitle,
|
title: STYLE_CONSTANTS.backgroundColorTitle,
|
||||||
value: this.styleItem.style.backgroundColor,
|
value: this.normalizeValueForSwatch(value),
|
||||||
property: 'backgroundColor',
|
property: 'backgroundColor',
|
||||||
isEditing: this.isEditing
|
isEditing: this.isEditing,
|
||||||
|
nonSpecific: this.mixedStyles.indexOf('backgroundColor') > -1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
colorOption() {
|
colorOption() {
|
||||||
|
let value = this.styleItem.style.color;
|
||||||
return {
|
return {
|
||||||
icon: 'icon-font',
|
icon: 'icon-font',
|
||||||
title: STYLE_CONSTANTS.textColorTitle,
|
title: STYLE_CONSTANTS.textColorTitle,
|
||||||
value: this.styleItem.style.color,
|
value: this.normalizeValueForSwatch(value),
|
||||||
property: 'color',
|
property: 'color',
|
||||||
isEditing: this.isEditing
|
isEditing: this.isEditing,
|
||||||
|
nonSpecific: this.mixedStyles.indexOf('color') > -1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
imageUrlOption() {
|
imageUrlOption() {
|
||||||
@ -138,7 +157,8 @@ export default {
|
|||||||
property: 'imageUrl',
|
property: 'imageUrl',
|
||||||
formKeys: ['url'],
|
formKeys: ['url'],
|
||||||
value: {url: this.styleItem.style.imageUrl},
|
value: {url: this.styleItem.style.imageUrl},
|
||||||
isEditing: this.isEditing
|
isEditing: this.isEditing,
|
||||||
|
nonSpecific: this.mixedStyles.indexOf('imageUrl') > -1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isStyleInvisibleOption() {
|
isStyleInvisibleOption() {
|
||||||
@ -163,7 +183,23 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
hasProperty(property) {
|
||||||
|
return property !== undefined;
|
||||||
|
},
|
||||||
|
normalizeValueForSwatch(value) {
|
||||||
|
if (value && value.indexOf('__no_value') > -1) {
|
||||||
|
return value.replace('__no_value', 'transparent');
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
normalizeValueForStyle(value) {
|
||||||
|
if (value && value === 'transparent') {
|
||||||
|
return '__no_value';
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
},
|
||||||
updateStyleValue(value, item) {
|
updateStyleValue(value, item) {
|
||||||
|
value = this.normalizeValueForStyle(value);
|
||||||
if (item.property === 'border') {
|
if (item.property === 'border') {
|
||||||
value = '1px solid ' + value;
|
value = '1px solid ' + value;
|
||||||
}
|
}
|
||||||
@ -172,7 +208,7 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.styleItem.style[item.property] = value;
|
this.styleItem.style[item.property] = value;
|
||||||
}
|
}
|
||||||
this.$emit('persist', this.styleItem);
|
this.$emit('persist', this.styleItem, item.property);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,31 @@
|
|||||||
|
|
||||||
&__condition {
|
&__condition {
|
||||||
@include discreteItem();
|
@include discreteItem();
|
||||||
|
border: 1px solid transparent;
|
||||||
|
pointer-events: none; // Prevent selecting when the object isn't being edited
|
||||||
|
|
||||||
|
&.is-current {
|
||||||
|
$c: $colorBodyFg;
|
||||||
|
border-color: rgba($c, 0.5);
|
||||||
|
background: rgba($c, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-editing & {
|
||||||
|
cursor: pointer;
|
||||||
|
pointer-events: initial;
|
||||||
|
transition: $transOut;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba($colorBodyFg, 0.1);
|
||||||
|
transition: $transIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-current {
|
||||||
|
$c: $editUIColorBg;
|
||||||
|
border-color: $c;
|
||||||
|
background: rgba($c, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.c-style {
|
.c-style {
|
||||||
|
@ -20,11 +20,11 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import EventEmitter from 'EventEmitter';
|
import TelemetryCriterion from './TelemetryCriterion';
|
||||||
import {OPERATIONS} from '../utils/operations';
|
import { evaluateResults } from "../utils/evaluator";
|
||||||
import {computeCondition} from "@/plugins/condition/utils/evaluator";
|
import { getLatestTimestamp } from '../utils/time';
|
||||||
|
|
||||||
export default class TelemetryCriterion extends EventEmitter {
|
export default class AllTelemetryCriterion extends TelemetryCriterion {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribes/Unsubscribes to telemetry and emits the result
|
* Subscribes/Unsubscribes to telemetry and emits the result
|
||||||
@ -34,23 +34,35 @@ export default class TelemetryCriterion extends EventEmitter {
|
|||||||
* @param openmct
|
* @param openmct
|
||||||
*/
|
*/
|
||||||
constructor(telemetryDomainObjectDefinition, openmct) {
|
constructor(telemetryDomainObjectDefinition, openmct) {
|
||||||
super();
|
super(telemetryDomainObjectDefinition, openmct);
|
||||||
|
}
|
||||||
|
|
||||||
this.openmct = openmct;
|
initialize() {
|
||||||
this.objectAPI = this.openmct.objects;
|
this.telemetryObjects = { ...this.telemetryDomainObjectDefinition.telemetryObjects };
|
||||||
this.telemetryAPI = this.openmct.telemetry;
|
|
||||||
this.timeAPI = this.openmct.time;
|
|
||||||
this.id = telemetryDomainObjectDefinition.id;
|
|
||||||
this.telemetry = telemetryDomainObjectDefinition.telemetry;
|
|
||||||
this.operation = telemetryDomainObjectDefinition.operation;
|
|
||||||
this.telemetryObjects = Object.assign({}, telemetryDomainObjectDefinition.telemetryObjects);
|
|
||||||
this.input = telemetryDomainObjectDefinition.input;
|
|
||||||
this.metadata = telemetryDomainObjectDefinition.metadata;
|
|
||||||
this.telemetryDataCache = {};
|
this.telemetryDataCache = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isValid() {
|
||||||
|
return (this.telemetry === 'any' || this.telemetry === 'all') && this.metadata && this.operation;
|
||||||
|
}
|
||||||
|
|
||||||
updateTelemetry(telemetryObjects) {
|
updateTelemetry(telemetryObjects) {
|
||||||
this.telemetryObjects = Object.assign({}, telemetryObjects);
|
this.telemetryObjects = { ...telemetryObjects };
|
||||||
|
this.removeTelemetryDataCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
removeTelemetryDataCache() {
|
||||||
|
const telemetryCacheIds = Object.keys(this.telemetryDataCache);
|
||||||
|
Object.values(this.telemetryObjects).forEach(telemetryObject => {
|
||||||
|
const id = this.openmct.objects.makeKeyString(telemetryObject.identifier);
|
||||||
|
const foundIndex = telemetryCacheIds.indexOf(id);
|
||||||
|
if (foundIndex > -1) {
|
||||||
|
telemetryCacheIds.splice(foundIndex, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
telemetryCacheIds.forEach(id => {
|
||||||
|
delete (this.telemetryDataCache[id]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
formatData(data, telemetryObjects) {
|
formatData(data, telemetryObjects) {
|
||||||
@ -68,105 +80,87 @@ export default class TelemetryCriterion extends EventEmitter {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const datum = {
|
const datum = {
|
||||||
result: computeCondition(this.telemetryDataCache, this.telemetry === 'all')
|
result: evaluateResults(Object.values(this.telemetryDataCache), this.telemetry)
|
||||||
};
|
};
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
// TODO check back to see if we should format times here
|
this.openmct.time.getAllTimeSystems().forEach(timeSystem => {
|
||||||
this.timeAPI.getAllTimeSystems().forEach(timeSystem => {
|
|
||||||
datum[timeSystem.key] = data[timeSystem.key]
|
datum[timeSystem.key] = data[timeSystem.key]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return datum;
|
return datum;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubscription(data, telemetryObjects) {
|
getResult(data, telemetryObjects) {
|
||||||
if(this.isValid()) {
|
const validatedData = this.isValid() ? data : {};
|
||||||
this.emitEvent('criterionResultUpdated', this.formatData(data, telemetryObjects));
|
|
||||||
} else {
|
if (validatedData) {
|
||||||
this.emitEvent('criterionResultUpdated', this.formatData({}, telemetryObjects));
|
this.telemetryDataCache[validatedData.id] = this.computeResult(validatedData);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
findOperation(operation) {
|
Object.values(telemetryObjects).forEach(telemetryObject => {
|
||||||
for (let i=0; i < OPERATIONS.length; i++) {
|
const id = this.openmct.objects.makeKeyString(telemetryObject.identifier);
|
||||||
if (operation === OPERATIONS[i].name) {
|
if (this.telemetryDataCache[id] === undefined) {
|
||||||
return OPERATIONS[i].operation;
|
this.telemetryDataCache[id] = false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
computeResult(data) {
|
|
||||||
let result = false;
|
|
||||||
if (data) {
|
|
||||||
let comparator = this.findOperation(this.operation);
|
|
||||||
let params = [];
|
|
||||||
params.push(data[this.metadata]);
|
|
||||||
if (this.input instanceof Array && this.input.length) {
|
|
||||||
this.input.forEach(input => params.push(input));
|
|
||||||
}
|
|
||||||
if (typeof comparator === 'function') {
|
|
||||||
result = comparator(params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
emitEvent(eventName, data) {
|
|
||||||
this.emit(eventName, {
|
|
||||||
id: this.id,
|
|
||||||
data: data
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.result = evaluateResults(Object.values(this.telemetryDataCache), this.telemetry);
|
||||||
}
|
}
|
||||||
|
|
||||||
isValid() {
|
requestLAD(telemetryObjects) {
|
||||||
return (this.telemetry === 'any' || this.telemetry === 'all') && this.metadata && this.operation;
|
const options = {
|
||||||
}
|
|
||||||
|
|
||||||
requestLAD(options) {
|
|
||||||
options = Object.assign({},
|
|
||||||
options,
|
|
||||||
{
|
|
||||||
strategy: 'latest',
|
strategy: 'latest',
|
||||||
size: 1
|
size: 1
|
||||||
}
|
};
|
||||||
);
|
|
||||||
|
|
||||||
if (!this.isValid()) {
|
if (!this.isValid()) {
|
||||||
return this.formatData({}, options.telemetryObjects);
|
return this.formatData({}, telemetryObjects);
|
||||||
}
|
}
|
||||||
|
|
||||||
const telemetryRequests = options.telemetryObjects
|
let keys = Object.keys(Object.assign({}, telemetryObjects));
|
||||||
.map(telemetryObject => this.telemetryAPI.request(
|
const telemetryRequests = keys
|
||||||
telemetryObject,
|
.map(key => this.openmct.telemetry.request(
|
||||||
|
telemetryObjects[key],
|
||||||
options
|
options
|
||||||
));
|
));
|
||||||
|
|
||||||
|
let telemetryDataCache = {};
|
||||||
return Promise.all(telemetryRequests)
|
return Promise.all(telemetryRequests)
|
||||||
.then(telemetryRequestsResults => {
|
.then(telemetryRequestsResults => {
|
||||||
|
let latestTimestamp;
|
||||||
|
const timeSystems = this.openmct.time.getAllTimeSystems();
|
||||||
|
const timeSystem = this.openmct.time.timeSystem();
|
||||||
|
|
||||||
telemetryRequestsResults.forEach((results, index) => {
|
telemetryRequestsResults.forEach((results, index) => {
|
||||||
const latestDatum = results.length ? results[results.length - 1] : {};
|
const latestDatum = results.length ? results[results.length - 1] : {};
|
||||||
if (index === telemetryRequestsResults.length-1) {
|
const datumId = keys[index];
|
||||||
//when the last result is computed, we return the result
|
const normalizedDatum = this.createNormalizedDatum(latestDatum, telemetryObjects[datumId]);
|
||||||
|
|
||||||
|
telemetryDataCache[datumId] = this.computeResult(normalizedDatum);
|
||||||
|
|
||||||
|
latestTimestamp = getLatestTimestamp(
|
||||||
|
latestTimestamp,
|
||||||
|
normalizedDatum,
|
||||||
|
timeSystems,
|
||||||
|
timeSystem
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const datum = {
|
||||||
|
result: evaluateResults(Object.values(telemetryDataCache), this.telemetry),
|
||||||
|
...latestTimestamp
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
data: this.formatData(latestDatum, options.telemetryObjects)
|
data: datum
|
||||||
};
|
};
|
||||||
} else {
|
|
||||||
if (latestDatum) {
|
|
||||||
this.telemetryDataCache[latestDatum.id] = this.computeResult(latestDatum);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
this.emitEvent('criterionRemoved');
|
|
||||||
delete this.telemetryObjects;
|
delete this.telemetryObjects;
|
||||||
delete this.telemetryDataCache;
|
delete this.telemetryDataCache;
|
||||||
delete this.telemetryObjectIdAsString;
|
|
||||||
delete this.telemetryObject;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,44 +36,89 @@ export default class TelemetryCriterion extends EventEmitter {
|
|||||||
super();
|
super();
|
||||||
|
|
||||||
this.openmct = openmct;
|
this.openmct = openmct;
|
||||||
this.objectAPI = this.openmct.objects;
|
this.telemetryDomainObjectDefinition = telemetryDomainObjectDefinition;
|
||||||
this.telemetryAPI = this.openmct.telemetry;
|
|
||||||
this.timeAPI = this.openmct.time;
|
|
||||||
this.id = telemetryDomainObjectDefinition.id;
|
this.id = telemetryDomainObjectDefinition.id;
|
||||||
this.telemetry = telemetryDomainObjectDefinition.telemetry;
|
this.telemetry = telemetryDomainObjectDefinition.telemetry;
|
||||||
this.operation = telemetryDomainObjectDefinition.operation;
|
this.operation = telemetryDomainObjectDefinition.operation;
|
||||||
this.input = telemetryDomainObjectDefinition.input;
|
this.input = telemetryDomainObjectDefinition.input;
|
||||||
this.metadata = telemetryDomainObjectDefinition.metadata;
|
this.metadata = telemetryDomainObjectDefinition.metadata;
|
||||||
this.telemetryObject = telemetryDomainObjectDefinition.telemetryObject;
|
this.result = undefined;
|
||||||
this.telemetryObjectIdAsString = this.objectAPI.makeKeyString(telemetryDomainObjectDefinition.telemetry);
|
|
||||||
this.on(`subscription:${this.telemetryObjectIdAsString}`, this.handleSubscription);
|
this.initialize();
|
||||||
this.emitEvent('criterionUpdated', this);
|
this.emitEvent('criterionUpdated', this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
this.telemetryObject = this.telemetryDomainObjectDefinition.telemetryObject;
|
||||||
|
this.telemetryObjectIdAsString = this.openmct.objects.makeKeyString(this.telemetryDomainObjectDefinition.telemetry);
|
||||||
|
}
|
||||||
|
|
||||||
|
isValid() {
|
||||||
|
return this.telemetryObject && this.metadata && this.operation;
|
||||||
|
}
|
||||||
|
|
||||||
updateTelemetry(telemetryObjects) {
|
updateTelemetry(telemetryObjects) {
|
||||||
this.telemetryObject = telemetryObjects[this.telemetryObjectIdAsString];
|
this.telemetryObject = telemetryObjects[this.telemetryObjectIdAsString];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createNormalizedDatum(telemetryDatum, endpoint) {
|
||||||
|
const id = this.openmct.objects.makeKeyString(endpoint.identifier);
|
||||||
|
const metadata = this.openmct.telemetry.getMetadata(endpoint).valueMetadatas;
|
||||||
|
|
||||||
|
const normalizedDatum = Object.values(metadata).reduce((datum, metadatum) => {
|
||||||
|
const formatter = this.openmct.telemetry.getValueFormatter(metadatum);
|
||||||
|
datum[metadatum.key] = formatter.parse(telemetryDatum[metadatum.source]);
|
||||||
|
return datum;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
normalizedDatum.id = id;
|
||||||
|
|
||||||
|
return normalizedDatum;
|
||||||
|
}
|
||||||
|
|
||||||
formatData(data) {
|
formatData(data) {
|
||||||
const datum = {
|
const datum = {
|
||||||
result: this.computeResult(data)
|
result: this.computeResult(data)
|
||||||
};
|
};
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
// TODO check back to see if we should format times here
|
this.openmct.time.getAllTimeSystems().forEach(timeSystem => {
|
||||||
this.timeAPI.getAllTimeSystems().forEach(timeSystem => {
|
|
||||||
datum[timeSystem.key] = data[timeSystem.key]
|
datum[timeSystem.key] = data[timeSystem.key]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return datum;
|
return datum;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubscription(data) {
|
getResult(data) {
|
||||||
if(this.isValid()) {
|
const validatedData = this.isValid() ? data : {};
|
||||||
this.emitEvent('criterionResultUpdated', this.formatData(data));
|
this.result = this.computeResult(validatedData);
|
||||||
} else {
|
|
||||||
this.emitEvent('criterionResultUpdated', this.formatData({}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestLAD() {
|
||||||
|
const options = {
|
||||||
|
strategy: 'latest',
|
||||||
|
size: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!this.isValid()) {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
data: this.formatData({})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.openmct.telemetry.request(
|
||||||
|
this.telemetryObject,
|
||||||
|
options
|
||||||
|
).then(results => {
|
||||||
|
const latestDatum = results.length ? results[results.length - 1] : {};
|
||||||
|
const normalizedDatum = this.createNormalizedDatum(latestDatum, this.telemetryObject);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
data: this.formatData(normalizedDatum)
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
findOperation(operation) {
|
findOperation(operation) {
|
||||||
@ -95,7 +140,7 @@ export default class TelemetryCriterion extends EventEmitter {
|
|||||||
this.input.forEach(input => params.push(input));
|
this.input.forEach(input => params.push(input));
|
||||||
}
|
}
|
||||||
if (typeof comparator === 'function') {
|
if (typeof comparator === 'function') {
|
||||||
result = comparator(params);
|
result = !!comparator(params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@ -108,42 +153,9 @@ export default class TelemetryCriterion extends EventEmitter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isValid() {
|
|
||||||
return this.telemetryObject && this.metadata && this.operation;
|
|
||||||
}
|
|
||||||
|
|
||||||
requestLAD(options) {
|
|
||||||
options = Object.assign({},
|
|
||||||
options,
|
|
||||||
{
|
|
||||||
strategy: 'latest',
|
|
||||||
size: 1
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!this.isValid()) {
|
|
||||||
return {
|
|
||||||
id: this.id,
|
|
||||||
data: this.formatData({})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.telemetryAPI.request(
|
|
||||||
this.telemetryObject,
|
|
||||||
options
|
|
||||||
).then(results => {
|
|
||||||
const latestDatum = results.length ? results[results.length - 1] : {};
|
|
||||||
return {
|
|
||||||
id: this.id,
|
|
||||||
data: this.formatData(latestDatum)
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
this.off(`subscription:${this.telemetryObjectIdAsString}`, this.handleSubscription);
|
|
||||||
this.emitEvent('criterionRemoved');
|
|
||||||
delete this.telemetryObjectIdAsString;
|
|
||||||
delete this.telemetryObject;
|
delete this.telemetryObject;
|
||||||
|
delete this.telemetryObjectIdAsString;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ describe("The telemetry criterion", function () {
|
|||||||
key: "testSource",
|
key: "testSource",
|
||||||
source: "value",
|
source: "value",
|
||||||
name: "Test",
|
name: "Test",
|
||||||
format: "enum"
|
format: "string"
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -80,8 +80,9 @@ describe("The telemetry criterion", function () {
|
|||||||
testCriterionDefinition = {
|
testCriterionDefinition = {
|
||||||
id: 'test-criterion-id',
|
id: 'test-criterion-id',
|
||||||
telemetry: openmct.objects.makeKeyString(testTelemetryObject.identifier),
|
telemetry: openmct.objects.makeKeyString(testTelemetryObject.identifier),
|
||||||
operation: 'lessThan',
|
operation: 'textContains',
|
||||||
metadata: 'sin',
|
metadata: 'value',
|
||||||
|
input: ['Hell'],
|
||||||
telemetryObject: testTelemetryObject
|
telemetryObject: testTelemetryObject
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -100,12 +101,21 @@ describe("The telemetry criterion", function () {
|
|||||||
expect(telemetryCriterion.telemetryObjectIdAsString).toEqual(testTelemetryObject.identifier.key);
|
expect(telemetryCriterion.telemetryObjectIdAsString).toEqual(testTelemetryObject.identifier.key);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("updates and emits event on new data from telemetry providers", function () {
|
it("returns a result on new data from relevant telemetry providers", function () {
|
||||||
spyOn(telemetryCriterion, 'emitEvent').and.callThrough();
|
telemetryCriterion.getResult({
|
||||||
telemetryCriterion.handleSubscription({
|
|
||||||
value: 'Hello',
|
value: 'Hello',
|
||||||
utc: 'Hi'
|
utc: 'Hi',
|
||||||
|
id: testTelemetryObject.identifier.key
|
||||||
});
|
});
|
||||||
expect(telemetryCriterion.emitEvent).toHaveBeenCalled();
|
expect(telemetryCriterion.result).toBeTrue();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// it("does not return a result on new data from irrelavant telemetry providers", function () {
|
||||||
|
// telemetryCriterion.getResult({
|
||||||
|
// value: 'Hello',
|
||||||
|
// utc: 'Hi',
|
||||||
|
// id: '1234'
|
||||||
|
// });
|
||||||
|
// expect(telemetryCriterion.result).toBeFalse();
|
||||||
|
// });
|
||||||
});
|
});
|
||||||
|
@ -19,36 +19,50 @@
|
|||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
import { TRIGGER } from "./constants";
|
||||||
|
|
||||||
export const computeCondition = (resultMap, allMustBeTrue) => {
|
export const evaluateResults = (results, trigger) => {
|
||||||
let result = false;
|
if (trigger && trigger === TRIGGER.XOR) {
|
||||||
for (let key in resultMap) {
|
return matchExact(results, 1);
|
||||||
if (resultMap.hasOwnProperty(key)) {
|
} else if (trigger && trigger === TRIGGER.NOT) {
|
||||||
result = resultMap[key];
|
return matchExact(results, 0);
|
||||||
if (allMustBeTrue && !result) {
|
} else if (trigger && trigger === TRIGGER.ALL) {
|
||||||
//If we want all conditions to be true, then even one negative result should break.
|
return matchAll(results);
|
||||||
break;
|
} else {
|
||||||
} else if (!allMustBeTrue && result) {
|
return matchAny(results);
|
||||||
//If we want at least one condition to be true, then even one positive result should break.
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
//Returns true only if limit number of results are satisfied
|
function matchAll(results) {
|
||||||
export const computeConditionByLimit = (resultMap, limit) => {
|
for (const result of results) {
|
||||||
let trueCount = 0;
|
if (!result) {
|
||||||
for (let key in resultMap) {
|
return false;
|
||||||
if (resultMap.hasOwnProperty(key)) {
|
|
||||||
if (resultMap[key]) {
|
|
||||||
trueCount++;
|
|
||||||
}
|
|
||||||
if (trueCount > limit) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function matchAny(results) {
|
||||||
|
for (const result of results) {
|
||||||
|
if (result) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function matchExact(results, target) {
|
||||||
|
let matches = 0;
|
||||||
|
for (const result of results) {
|
||||||
|
if (result) {
|
||||||
|
matches++;
|
||||||
|
}
|
||||||
|
if (matches > target) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matches === target;
|
||||||
}
|
}
|
||||||
return trueCount === limit;
|
|
||||||
};
|
|
||||||
|
@ -20,47 +20,185 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import { computeConditionByLimit } from "./evaluator";
|
import { evaluateResults } from './evaluator';
|
||||||
|
import { TRIGGER } from './constants';
|
||||||
|
|
||||||
describe('evaluate results based on trigger', function () {
|
describe('evaluate results', () => {
|
||||||
|
// const allTrue = [true, true, true, true, true];
|
||||||
|
// const oneTrue = [false, false, false, false, true];
|
||||||
|
// const multipleTrue = [false, true, false, true, false];
|
||||||
|
// const noneTrue = [false, false, false, false, false];
|
||||||
|
// const allTrueWithUndefined = [true, true, true, undefined, true];
|
||||||
|
// const oneTrueWithUndefined = [undefined, undefined, undefined, undefined, true];
|
||||||
|
// const multipleTrueWithUndefined = [true, undefined, true, undefined, true];
|
||||||
|
// const allUndefined = [undefined, undefined, undefined, undefined, undefined];
|
||||||
|
// const singleTrue = [true];
|
||||||
|
// const singleFalse = [false];
|
||||||
|
// const singleUndefined = [undefined];
|
||||||
|
// const empty = [];
|
||||||
|
|
||||||
it('should evaluate to true if trigger is NOT', () => {
|
const tests = [
|
||||||
const results = {
|
{
|
||||||
result: false,
|
name: 'allTrue',
|
||||||
result1: false,
|
values: [true, true, true, true, true],
|
||||||
result2: false
|
any: true,
|
||||||
};
|
all: true,
|
||||||
const result = computeConditionByLimit(results, 0);
|
not: false,
|
||||||
expect(result).toBeTrue();
|
xor: false
|
||||||
});
|
}, {
|
||||||
|
name: 'oneTrue',
|
||||||
|
values: [false, false, false, false, true],
|
||||||
|
any: true,
|
||||||
|
all: false,
|
||||||
|
not: false,
|
||||||
|
xor: true
|
||||||
|
}, {
|
||||||
|
name: 'multipleTrue',
|
||||||
|
values: [false, true, false, true, false],
|
||||||
|
any: true,
|
||||||
|
all: false,
|
||||||
|
not: false,
|
||||||
|
xor: false
|
||||||
|
}, {
|
||||||
|
name: 'noneTrue',
|
||||||
|
values: [false, false, false, false, false],
|
||||||
|
any: false,
|
||||||
|
all: false,
|
||||||
|
not: true,
|
||||||
|
xor: false
|
||||||
|
}, {
|
||||||
|
name: 'allTrueWithUndefined',
|
||||||
|
values: [true, true, true, undefined, true],
|
||||||
|
any: true,
|
||||||
|
all: false,
|
||||||
|
not: false,
|
||||||
|
xor: false
|
||||||
|
}, {
|
||||||
|
name: 'oneTrueWithUndefined',
|
||||||
|
values: [undefined, undefined, undefined, undefined, true],
|
||||||
|
any: true,
|
||||||
|
all: false,
|
||||||
|
not: false,
|
||||||
|
xor: true
|
||||||
|
}, {
|
||||||
|
name: 'multipleTrueWithUndefined',
|
||||||
|
values: [true, undefined, true, undefined, true],
|
||||||
|
any: true,
|
||||||
|
all: false,
|
||||||
|
not: false,
|
||||||
|
xor: false
|
||||||
|
}, {
|
||||||
|
name: 'allUndefined',
|
||||||
|
values: [undefined, undefined, undefined, undefined, undefined],
|
||||||
|
any: false,
|
||||||
|
all: false,
|
||||||
|
not: true,
|
||||||
|
xor: false
|
||||||
|
}, {
|
||||||
|
name: 'singleTrue',
|
||||||
|
values: [true],
|
||||||
|
any: true,
|
||||||
|
all: true,
|
||||||
|
not: false,
|
||||||
|
xor: true
|
||||||
|
}, {
|
||||||
|
name: 'singleFalse',
|
||||||
|
values: [false],
|
||||||
|
any: false,
|
||||||
|
all: false,
|
||||||
|
not: true,
|
||||||
|
xor: false
|
||||||
|
}, {
|
||||||
|
name: 'singleUndefined',
|
||||||
|
values: [undefined],
|
||||||
|
any: false,
|
||||||
|
all: false,
|
||||||
|
not: true,
|
||||||
|
xor: false
|
||||||
|
}
|
||||||
|
// , {
|
||||||
|
// name: 'empty',
|
||||||
|
// values: [],
|
||||||
|
// any: false,
|
||||||
|
// all: false,
|
||||||
|
// not: true,
|
||||||
|
// xor: false
|
||||||
|
// }
|
||||||
|
];
|
||||||
|
|
||||||
it('should evaluate to false if trigger is NOT', () => {
|
describe(`based on trigger ${TRIGGER.ANY}`, () => {
|
||||||
const results = {
|
it('should evaluate to expected result', () => {
|
||||||
result: true,
|
tests.forEach(test => {
|
||||||
result1: false,
|
const result = evaluateResults(test.values, TRIGGER.ANY);
|
||||||
result2: false
|
expect(result).toEqual(test[TRIGGER.ANY])
|
||||||
};
|
|
||||||
const result = computeConditionByLimit(results, 0);
|
|
||||||
expect(result).toBeFalse();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should evaluate to true if trigger is XOR', () => {
|
|
||||||
const results = {
|
|
||||||
result: false,
|
|
||||||
result1: true,
|
|
||||||
result2: false
|
|
||||||
};
|
|
||||||
const result = computeConditionByLimit(results, 1);
|
|
||||||
expect(result).toBeTrue();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should evaluate to false if trigger is XOR', () => {
|
|
||||||
const results = {
|
|
||||||
result: false,
|
|
||||||
result1: true,
|
|
||||||
result2: true
|
|
||||||
};
|
|
||||||
const result = computeConditionByLimit(results, 1);
|
|
||||||
expect(result).toBeFalse();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(`based on trigger ${TRIGGER.ALL}`, () => {
|
||||||
|
it('should evaluate to expected result', () => {
|
||||||
|
tests.forEach(test => {
|
||||||
|
const result = evaluateResults(test.values, TRIGGER.ALL);
|
||||||
|
expect(result).toEqual(test[TRIGGER.ALL])
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(`based on trigger ${TRIGGER.NOT}`, () => {
|
||||||
|
it('should evaluate to expected result', () => {
|
||||||
|
tests.forEach(test => {
|
||||||
|
const result = evaluateResults(test.values, TRIGGER.NOT);
|
||||||
|
expect(result).toEqual(test[TRIGGER.NOT])
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(`based on trigger ${TRIGGER.XOR}`, () => {
|
||||||
|
it('should evaluate to expected result', () => {
|
||||||
|
tests.forEach(test => {
|
||||||
|
const result = evaluateResults(test.values, TRIGGER.XOR);
|
||||||
|
expect(result).toEqual(test[TRIGGER.XOR])
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// it('should evaluate to true if trigger is NOT', () => {
|
||||||
|
// const results = {
|
||||||
|
// result: false,
|
||||||
|
// result1: false,
|
||||||
|
// result2: false
|
||||||
|
// };
|
||||||
|
// const result = computeConditionByLimit(results, 0);
|
||||||
|
// expect(result).toBeTrue();
|
||||||
|
// });
|
||||||
|
|
||||||
|
// it('should evaluate to false if trigger is NOT', () => {
|
||||||
|
// const results = {
|
||||||
|
// result: true,
|
||||||
|
// result1: false,
|
||||||
|
// result2: false
|
||||||
|
// };
|
||||||
|
// const result = computeConditionByLimit(results, 0);
|
||||||
|
// expect(result).toBeFalse();
|
||||||
|
// });
|
||||||
|
|
||||||
|
// it('should evaluate to true if trigger is XOR', () => {
|
||||||
|
// const results = {
|
||||||
|
// result: false,
|
||||||
|
// result1: true,
|
||||||
|
// result2: false
|
||||||
|
// };
|
||||||
|
// const result = computeConditionByLimit(results, 1);
|
||||||
|
// expect(result).toBeTrue();
|
||||||
|
// });
|
||||||
|
|
||||||
|
// it('should evaluate to false if trigger is XOR', () => {
|
||||||
|
// const results = {
|
||||||
|
// result: false,
|
||||||
|
// result1: true,
|
||||||
|
// result2: true
|
||||||
|
// };
|
||||||
|
// const result = computeConditionByLimit(results, 1);
|
||||||
|
// expect(result).toBeFalse();
|
||||||
|
// });
|
||||||
|
});
|
||||||
|
@ -22,6 +22,22 @@
|
|||||||
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
const convertToNumbers = (input) => {
|
||||||
|
let numberInputs = [];
|
||||||
|
input.forEach(inputValue => numberInputs.push(Number(inputValue)));
|
||||||
|
return numberInputs;
|
||||||
|
};
|
||||||
|
|
||||||
|
const convertToStrings = (input) => {
|
||||||
|
let stringInputs = [];
|
||||||
|
input.forEach(inputValue => stringInputs.push(inputValue !== undefined ? inputValue.toString() : ''));
|
||||||
|
return stringInputs;
|
||||||
|
};
|
||||||
|
|
||||||
|
const joinValues = (values, length) => {
|
||||||
|
return values.slice(0, length).join(', ');
|
||||||
|
};
|
||||||
|
|
||||||
export const OPERATIONS = [
|
export const OPERATIONS = [
|
||||||
{
|
{
|
||||||
name: 'equalTo',
|
name: 'equalTo',
|
||||||
@ -32,7 +48,7 @@ export const OPERATIONS = [
|
|||||||
appliesTo: ['number'],
|
appliesTo: ['number'],
|
||||||
inputCount: 1,
|
inputCount: 1,
|
||||||
getDescription: function (values) {
|
getDescription: function (values) {
|
||||||
return ' is ' + values.join(', ');
|
return ' is ' + joinValues(values, 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -44,7 +60,7 @@ export const OPERATIONS = [
|
|||||||
appliesTo: ['number'],
|
appliesTo: ['number'],
|
||||||
inputCount: 1,
|
inputCount: 1,
|
||||||
getDescription: function (values) {
|
getDescription: function (values) {
|
||||||
return ' is not ' + values.join(', ');
|
return ' is not ' + joinValues(values, 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -56,7 +72,7 @@ export const OPERATIONS = [
|
|||||||
appliesTo: ['number'],
|
appliesTo: ['number'],
|
||||||
inputCount: 1,
|
inputCount: 1,
|
||||||
getDescription: function (values) {
|
getDescription: function (values) {
|
||||||
return ' > ' + values.join(', ');
|
return ' > ' + joinValues(values, 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -68,7 +84,7 @@ export const OPERATIONS = [
|
|||||||
appliesTo: ['number'],
|
appliesTo: ['number'],
|
||||||
inputCount: 1,
|
inputCount: 1,
|
||||||
getDescription: function (values) {
|
getDescription: function (values) {
|
||||||
return ' < ' + values.join(', ');
|
return ' < ' + joinValues(values, 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -80,7 +96,7 @@ export const OPERATIONS = [
|
|||||||
appliesTo: ['number'],
|
appliesTo: ['number'],
|
||||||
inputCount: 1,
|
inputCount: 1,
|
||||||
getDescription: function (values) {
|
getDescription: function (values) {
|
||||||
return ' >= ' + values.join(', ');
|
return ' >= ' + joinValues(values, 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -92,14 +108,13 @@ export const OPERATIONS = [
|
|||||||
appliesTo: ['number'],
|
appliesTo: ['number'],
|
||||||
inputCount: 1,
|
inputCount: 1,
|
||||||
getDescription: function (values) {
|
getDescription: function (values) {
|
||||||
return ' <= ' + values.join(', ');
|
return ' <= ' + joinValues(values, 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'between',
|
name: 'between',
|
||||||
operation: function (input) {
|
operation: function (input) {
|
||||||
let numberInputs = [];
|
let numberInputs = convertToNumbers(input);
|
||||||
input.forEach(inputValue => numberInputs.push(Number(inputValue)));
|
|
||||||
let larger = Math.max(...numberInputs.slice(1,3));
|
let larger = Math.max(...numberInputs.slice(1,3));
|
||||||
let smaller = Math.min(...numberInputs.slice(1,3));
|
let smaller = Math.min(...numberInputs.slice(1,3));
|
||||||
return (numberInputs[0] > smaller) && (numberInputs[0] < larger);
|
return (numberInputs[0] > smaller) && (numberInputs[0] < larger);
|
||||||
@ -114,8 +129,7 @@ export const OPERATIONS = [
|
|||||||
{
|
{
|
||||||
name: 'notBetween',
|
name: 'notBetween',
|
||||||
operation: function (input) {
|
operation: function (input) {
|
||||||
let numberInputs = [];
|
let numberInputs = convertToNumbers(input);
|
||||||
input.forEach(inputValue => numberInputs.push(Number(inputValue)));
|
|
||||||
let larger = Math.max(...numberInputs.slice(1,3));
|
let larger = Math.max(...numberInputs.slice(1,3));
|
||||||
let smaller = Math.min(...numberInputs.slice(1,3));
|
let smaller = Math.min(...numberInputs.slice(1,3));
|
||||||
return (numberInputs[0] < smaller) || (numberInputs[0] > larger);
|
return (numberInputs[0] < smaller) || (numberInputs[0] > larger);
|
||||||
@ -136,7 +150,7 @@ export const OPERATIONS = [
|
|||||||
appliesTo: ['string'],
|
appliesTo: ['string'],
|
||||||
inputCount: 1,
|
inputCount: 1,
|
||||||
getDescription: function (values) {
|
getDescription: function (values) {
|
||||||
return ' contains ' + values.join(', ');
|
return ' contains ' + joinValues(values, 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -148,7 +162,7 @@ export const OPERATIONS = [
|
|||||||
appliesTo: ['string'],
|
appliesTo: ['string'],
|
||||||
inputCount: 1,
|
inputCount: 1,
|
||||||
getDescription: function (values) {
|
getDescription: function (values) {
|
||||||
return ' does not contain ' + values.join(', ');
|
return ' does not contain ' + joinValues(values, 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -160,7 +174,7 @@ export const OPERATIONS = [
|
|||||||
appliesTo: ['string'],
|
appliesTo: ['string'],
|
||||||
inputCount: 1,
|
inputCount: 1,
|
||||||
getDescription: function (values) {
|
getDescription: function (values) {
|
||||||
return ' starts with ' + values.join(', ');
|
return ' starts with ' + joinValues(values, 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -172,7 +186,7 @@ export const OPERATIONS = [
|
|||||||
appliesTo: ['string'],
|
appliesTo: ['string'],
|
||||||
inputCount: 1,
|
inputCount: 1,
|
||||||
getDescription: function (values) {
|
getDescription: function (values) {
|
||||||
return ' ends with ' + values.join(', ');
|
return ' ends with ' + joinValues(values, 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -184,7 +198,7 @@ export const OPERATIONS = [
|
|||||||
appliesTo: ['string'],
|
appliesTo: ['string'],
|
||||||
inputCount: 1,
|
inputCount: 1,
|
||||||
getDescription: function (values) {
|
getDescription: function (values) {
|
||||||
return ' is exactly ' + values.join(', ');
|
return ' is exactly ' + joinValues(values, 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -214,33 +228,36 @@ export const OPERATIONS = [
|
|||||||
{
|
{
|
||||||
name: 'enumValueIs',
|
name: 'enumValueIs',
|
||||||
operation: function (input) {
|
operation: function (input) {
|
||||||
return input[0] === input[1];
|
let stringInputs = convertToStrings(input);
|
||||||
|
return stringInputs[0] === stringInputs[1];
|
||||||
},
|
},
|
||||||
text: 'is',
|
text: 'is',
|
||||||
appliesTo: ['enum'],
|
appliesTo: ['enum'],
|
||||||
inputCount: 1,
|
inputCount: 1,
|
||||||
getDescription: function (values) {
|
getDescription: function (values) {
|
||||||
return ' is ' + values.join(', ');
|
return ' is ' + joinValues(values, 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'enumValueIsNot',
|
name: 'enumValueIsNot',
|
||||||
operation: function (input) {
|
operation: function (input) {
|
||||||
return input[0] !== input[1];
|
let stringInputs = convertToStrings(input);
|
||||||
|
return stringInputs[0] !== stringInputs[1];
|
||||||
},
|
},
|
||||||
text: 'is not',
|
text: 'is not',
|
||||||
appliesTo: ['enum'],
|
appliesTo: ['enum'],
|
||||||
inputCount: 1,
|
inputCount: 1,
|
||||||
getDescription: function (values) {
|
getDescription: function (values) {
|
||||||
return ' is not ' + values.join(', ');
|
return ' is not ' + joinValues(values, 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'valueIs',
|
name: 'valueIs',
|
||||||
operation: function (input) {
|
operation: function (input) {
|
||||||
|
const lhsValue = input[0] !== undefined ? input[0].toString() : '';
|
||||||
if (input[1]) {
|
if (input[1]) {
|
||||||
const values = input[1].split(',');
|
const values = input[1].split(',');
|
||||||
return values.find((value) => input[0].toString() === _.trim(value.toString()));
|
return values.find((value) => lhsValue === _.trim(value.toString()));
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
@ -254,9 +271,10 @@ export const OPERATIONS = [
|
|||||||
{
|
{
|
||||||
name: 'valueIsNot',
|
name: 'valueIsNot',
|
||||||
operation: function (input) {
|
operation: function (input) {
|
||||||
|
const lhsValue = input[0] !== undefined ? input[0].toString() : '';
|
||||||
if (input[1]) {
|
if (input[1]) {
|
||||||
const values = input[1].split(',');
|
const values = input[1].split(',');
|
||||||
const found = values.find((value) => input[0].toString() === _.trim(value.toString()));
|
const found = values.find((value) => lhsValue === _.trim(value.toString()));
|
||||||
return !found;
|
return !found;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -25,8 +25,10 @@ let isOneOfOperation = OPERATIONS.find((operation) => operation.name === 'valueI
|
|||||||
let isNotOneOfOperation = OPERATIONS.find((operation) => operation.name === 'valueIsNot');
|
let isNotOneOfOperation = OPERATIONS.find((operation) => operation.name === 'valueIsNot');
|
||||||
let isBetween = OPERATIONS.find((operation) => operation.name === 'between');
|
let isBetween = OPERATIONS.find((operation) => operation.name === 'between');
|
||||||
let isNotBetween = OPERATIONS.find((operation) => operation.name === 'notBetween');
|
let isNotBetween = OPERATIONS.find((operation) => operation.name === 'notBetween');
|
||||||
|
let enumIsOperation = OPERATIONS.find((operation) => operation.name === 'enumValueIs');
|
||||||
|
let enumIsNotOperation = OPERATIONS.find((operation) => operation.name === 'enumValueIsNot');
|
||||||
|
|
||||||
describe('Is one of and is not one of operations', function () {
|
describe('operations', function () {
|
||||||
|
|
||||||
it('should evaluate isOneOf to true for number inputs', () => {
|
it('should evaluate isOneOf to true for number inputs', () => {
|
||||||
const inputs = [45, "5,6,45,8"];
|
const inputs = [45, "5,6,45,8"];
|
||||||
@ -87,4 +89,54 @@ describe('Is one of and is not one of operations', function () {
|
|||||||
const inputs = ["45", "30", "50"];
|
const inputs = ["45", "30", "50"];
|
||||||
expect(!!isNotBetween.operation(inputs)).toBeFalse();
|
expect(!!isNotBetween.operation(inputs)).toBeFalse();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should evaluate enumValueIs to true for number inputs', () => {
|
||||||
|
const inputs = [1, "1"];
|
||||||
|
expect(!!enumIsOperation.operation(inputs)).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should evaluate enumValueIs to true for string inputs', () => {
|
||||||
|
const inputs = ["45", "45"];
|
||||||
|
expect(!!enumIsOperation.operation(inputs)).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should evaluate enumValueIsNot to true for number inputs', () => {
|
||||||
|
const inputs = [45, "46"];
|
||||||
|
expect(!!enumIsNotOperation.operation(inputs)).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should evaluate enumValueIsNot to true for string inputs', () => {
|
||||||
|
const inputs = ["45", "46"];
|
||||||
|
expect(!!enumIsNotOperation.operation(inputs)).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should evaluate enumValueIs to false for number inputs', () => {
|
||||||
|
const inputs = [1, "2"];
|
||||||
|
expect(!!enumIsOperation.operation(inputs)).toBeFalse();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should evaluate enumValueIs to false for string inputs', () => {
|
||||||
|
const inputs = ["45", "46"];
|
||||||
|
expect(!!enumIsOperation.operation(inputs)).toBeFalse();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should evaluate enumValueIsNot to false for number inputs', () => {
|
||||||
|
const inputs = [45, "45"];
|
||||||
|
expect(!!enumIsNotOperation.operation(inputs)).toBeFalse();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should evaluate enumValueIsNot to false for string inputs', () => {
|
||||||
|
const inputs = ["45", "45"];
|
||||||
|
expect(!!enumIsNotOperation.operation(inputs)).toBeFalse();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should evaluate enumValueIs to false for undefined input', () => {
|
||||||
|
const inputs = [undefined, "45"];
|
||||||
|
expect(!!enumIsOperation.operation(inputs)).toBeFalse();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should evaluate enumValueIsNot to true for undefined input', () => {
|
||||||
|
const inputs = [undefined, "45"];
|
||||||
|
expect(!!enumIsNotOperation.operation(inputs)).toBeTrue();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -19,27 +19,154 @@
|
|||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
const NONE_VALUE = '__no_value';
|
||||||
|
|
||||||
export const getStyleProp = (key, defaultValue) => {
|
const styleProps = {
|
||||||
let styleProp = undefined;
|
backgroundColor: {
|
||||||
switch(key) {
|
svgProperty: 'fill',
|
||||||
case 'fill': styleProp = {
|
noneValue: NONE_VALUE,
|
||||||
backgroundColor: defaultValue || 'transparent'
|
applicableForType: type => {
|
||||||
};
|
return !type ? true : (type === 'text-view' ||
|
||||||
break;
|
type === 'telemetry-view' ||
|
||||||
case 'stroke': styleProp = {
|
type === 'box-view' ||
|
||||||
border: '1px solid ' + (defaultValue || 'transparent')
|
type === 'subobject-view');
|
||||||
};
|
}
|
||||||
break;
|
},
|
||||||
case 'color': styleProp = {
|
border: {
|
||||||
color: defaultValue || 'transparent'
|
svgProperty: 'stroke',
|
||||||
};
|
noneValue: NONE_VALUE,
|
||||||
break;
|
applicableForType: type => {
|
||||||
case 'url': styleProp = {
|
return !type ? true : (type === 'text-view' ||
|
||||||
imageUrl: defaultValue || 'transparent'
|
type === 'telemetry-view' ||
|
||||||
};
|
type === 'box-view' ||
|
||||||
break;
|
type === 'image-view' ||
|
||||||
|
type === 'line-view'||
|
||||||
|
type === 'subobject-view');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
svgProperty: 'color',
|
||||||
|
noneValue: NONE_VALUE,
|
||||||
|
applicableForType: type => {
|
||||||
|
return !type ? true : (type === 'text-view' ||
|
||||||
|
type === 'telemetry-view'||
|
||||||
|
type === 'subobject-view');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
imageUrl: {
|
||||||
|
svgProperty: 'url',
|
||||||
|
noneValue: '',
|
||||||
|
applicableForType: type => {
|
||||||
|
return !type ? false : type === 'image-view';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
return styleProp;
|
|
||||||
|
const aggregateStyleValues = (accumulator, currentStyle) => {
|
||||||
|
const styleKeys = Object.keys(currentStyle);
|
||||||
|
const properties = Object.keys(styleProps);
|
||||||
|
properties.forEach((property) => {
|
||||||
|
if (!accumulator[property]) {
|
||||||
|
accumulator[property] = [];
|
||||||
|
}
|
||||||
|
const found = styleKeys.find(key => key === property);
|
||||||
|
if (found) {
|
||||||
|
accumulator[property].push(currentStyle[found]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return accumulator;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns a union of styles used by multiple items.
|
||||||
|
// Styles that are common to all items but don't have the same value are added to the mixedStyles list
|
||||||
|
export const getConsolidatedStyleValues = (multipleItemStyles) => {
|
||||||
|
let aggregatedStyleValues = multipleItemStyles.reduce(aggregateStyleValues, {});
|
||||||
|
|
||||||
|
let styleValues = {};
|
||||||
|
let mixedStyles = [];
|
||||||
|
const properties = Object.keys(styleProps);
|
||||||
|
properties.forEach((property) => {
|
||||||
|
const values = aggregatedStyleValues[property];
|
||||||
|
if (values.length) {
|
||||||
|
if (values.every(value => value === values[0])) {
|
||||||
|
styleValues[property] = values[0];
|
||||||
|
} else {
|
||||||
|
styleValues[property] = '';
|
||||||
|
mixedStyles.push(property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
styles: styleValues,
|
||||||
|
mixedStyles
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStaticStyleForItem = (domainObject, id) => {
|
||||||
|
let domainObjectStyles = domainObject && domainObject.configuration && domainObject.configuration.objectStyles;
|
||||||
|
if (domainObjectStyles) {
|
||||||
|
if (id) {
|
||||||
|
if(domainObjectStyles[id] && domainObjectStyles[id].staticStyle) {
|
||||||
|
return domainObjectStyles[id].staticStyle.style;
|
||||||
|
}
|
||||||
|
} else if (domainObjectStyles.staticStyle) {
|
||||||
|
return domainObjectStyles.staticStyle.style;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getConditionalStyleForItem = (domainObject, id) => {
|
||||||
|
let domainObjectStyles = domainObject && domainObject.configuration && domainObject.configuration.objectStyles;
|
||||||
|
if (domainObjectStyles) {
|
||||||
|
if (id) {
|
||||||
|
if (domainObjectStyles[id] && domainObjectStyles[id].conditionSetIdentifier) {
|
||||||
|
return domainObjectStyles[id].styles;
|
||||||
|
}
|
||||||
|
} else if (domainObjectStyles.staticStyle) {
|
||||||
|
return domainObjectStyles.styles;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//Returns either existing static styles or uses SVG defaults if available
|
||||||
|
export const getApplicableStylesForItem = (domainObject, item) => {
|
||||||
|
const type = item && item.type;
|
||||||
|
const id = item && item.id;
|
||||||
|
let style = {};
|
||||||
|
|
||||||
|
let staticStyle = getStaticStyleForItem(domainObject, id);
|
||||||
|
|
||||||
|
const properties = Object.keys(styleProps);
|
||||||
|
properties.forEach(property => {
|
||||||
|
const styleProp = styleProps[property];
|
||||||
|
if (styleProp.applicableForType(type)) {
|
||||||
|
let defaultValue;
|
||||||
|
if (staticStyle) {
|
||||||
|
defaultValue = staticStyle[property];
|
||||||
|
} else if (item) {
|
||||||
|
defaultValue = item[styleProp.svgProperty];
|
||||||
|
}
|
||||||
|
style[property] = defaultValue === undefined ? styleProp.noneValue : defaultValue;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return style;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getStylesWithoutNoneValue = (style) => {
|
||||||
|
if (_.isEmpty(style) || !style) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let styleObj = {};
|
||||||
|
const keys = Object.keys(style);
|
||||||
|
keys.forEach(key => {
|
||||||
|
if ((typeof style[key] === 'string')) {
|
||||||
|
if (style[key].indexOf('__no_value') > -1) {
|
||||||
|
style[key] = '';
|
||||||
|
} else {
|
||||||
|
styleObj[key] = style[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return styleObj;
|
||||||
};
|
};
|
||||||
|
52
src/plugins/condition/utils/time.js
Normal file
52
src/plugins/condition/utils/time.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
export const getLatestTimestamp = (
|
||||||
|
currentTimestamp,
|
||||||
|
compareTimestamp,
|
||||||
|
timeSystems,
|
||||||
|
currentTimeSystem
|
||||||
|
) => {
|
||||||
|
let latest = { ...currentTimestamp };
|
||||||
|
const compare = { ...compareTimestamp };
|
||||||
|
const key = currentTimeSystem.key;
|
||||||
|
|
||||||
|
if (!latest || !latest[key]) {
|
||||||
|
latest = updateLatestTimeStamp(compare, timeSystems)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compare[key] > latest[key]) {
|
||||||
|
latest = updateLatestTimeStamp(compare, timeSystems)
|
||||||
|
}
|
||||||
|
|
||||||
|
return latest;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLatestTimeStamp(timestamp, timeSystems) {
|
||||||
|
let latest = {};
|
||||||
|
|
||||||
|
timeSystems.forEach(timeSystem => {
|
||||||
|
latest[timeSystem.key] = timestamp[timeSystem.key];
|
||||||
|
});
|
||||||
|
|
||||||
|
return latest;
|
||||||
|
}
|
@ -21,13 +21,14 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<a class="c-condition-widget"
|
<component :is="urlDefined ? 'a' : 'span'"
|
||||||
:href="internalDomainObject.url"
|
class="c-condition-widget"
|
||||||
|
:href="urlDefined ? internalDomainObject.url : null"
|
||||||
>
|
>
|
||||||
<div class="c-condition-widget__label">
|
<div class="c-condition-widget__label">
|
||||||
{{ internalDomainObject.label }}
|
{{ internalDomainObject.label }}
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</component>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -38,6 +39,11 @@ export default {
|
|||||||
internalDomainObject: this.domainObject
|
internalDomainObject: this.domainObject
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
urlDefined() {
|
||||||
|
return this.internalDomainObject.url && this.internalDomainObject.url.length > 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.unlisten = this.openmct.objects.observe(this.internalDomainObject, '*', this.updateInternalDomainObject);
|
this.unlisten = this.openmct.objects.observe(this.internalDomainObject, '*', this.updateInternalDomainObject);
|
||||||
},
|
},
|
||||||
|
@ -28,10 +28,12 @@
|
|||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: $interiorMarginLg $interiorMarginLg * 2;
|
padding: $interiorMarginLg $interiorMarginLg * 2;
|
||||||
cursor: inherit !important;
|
|
||||||
&[href] {
|
|
||||||
cursor: pointer !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.c-condition-widget {
|
||||||
|
// Widget is conditionally made into a <a> when URL property has been defined
|
||||||
|
cursor: pointer !important;
|
||||||
|
pointer-events: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make Condition Widget expand when in a hidden frame Layout context
|
// Make Condition Widget expand when in a hidden frame Layout context
|
||||||
|
@ -43,7 +43,7 @@ export default {
|
|||||||
makeDefinition() {
|
makeDefinition() {
|
||||||
return {
|
return {
|
||||||
fill: '#717171',
|
fill: '#717171',
|
||||||
stroke: 'transparent',
|
stroke: '',
|
||||||
x: 1,
|
x: 1,
|
||||||
y: 1,
|
y: 1,
|
||||||
width: 10,
|
width: 10,
|
||||||
@ -74,13 +74,14 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
style() {
|
style() {
|
||||||
return Object.assign({
|
if (this.itemStyle) {
|
||||||
|
return this.itemStyle;
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
backgroundColor: this.item.fill,
|
backgroundColor: this.item.fill,
|
||||||
border: '1px solid ' + this.item.stroke
|
border: this.item.stroke ? '1px solid ' + this.item.stroke : ''
|
||||||
}, this.itemStyle);
|
};
|
||||||
},
|
}
|
||||||
styleClass() {
|
|
||||||
return this.itemStyle && this.itemStyle.isStyleInvisible;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -74,13 +74,18 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
style() {
|
style() {
|
||||||
|
let backgroundImage = 'url(' + this.item.url + ')';
|
||||||
|
let border = '1px solid ' + this.item.stroke;
|
||||||
|
if (this.itemStyle) {
|
||||||
|
if (this.itemStyle.imageUrl !== undefined) {
|
||||||
|
backgroundImage = 'url(' + this.itemStyle.imageUrl + ')';
|
||||||
|
}
|
||||||
|
border = this.itemStyle.border;
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
backgroundImage: this.itemStyle ? ('url(' + this.itemStyle.imageUrl + ')') : 'url(' + this.item.url + ')',
|
backgroundImage,
|
||||||
border: (this.itemStyle && this.itemStyle.border) ? this.itemStyle.border : ('1px solid ' + this.item.stroke)
|
border
|
||||||
};
|
};
|
||||||
},
|
|
||||||
styleClass() {
|
|
||||||
return this.itemStyle && this.itemStyle.isStyleInvisible;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -127,8 +127,11 @@ export default {
|
|||||||
return {x, y, x2, y2};
|
return {x, y, x2, y2};
|
||||||
},
|
},
|
||||||
stroke() {
|
stroke() {
|
||||||
if (this.itemStyle && this.itemStyle.border) {
|
if (this.itemStyle) {
|
||||||
|
if (this.itemStyle.border) {
|
||||||
return this.itemStyle.border.replace('1px solid ', '');
|
return this.itemStyle.border.replace('1px solid ', '');
|
||||||
|
}
|
||||||
|
return '';
|
||||||
} else {
|
} else {
|
||||||
return this.item.stroke;
|
return this.item.stroke;
|
||||||
}
|
}
|
||||||
@ -146,9 +149,6 @@ export default {
|
|||||||
height: `${height}px`
|
height: `${height}px`
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
styleClass() {
|
|
||||||
return this.itemStyle && this.itemStyle.isStyleInvisible;
|
|
||||||
},
|
|
||||||
startHandleClass() {
|
startHandleClass() {
|
||||||
return START_HANDLE_QUADRANTS[this.vectorQuadrant];
|
return START_HANDLE_QUADRANTS[this.vectorQuadrant];
|
||||||
},
|
},
|
||||||
|
@ -30,14 +30,13 @@
|
|||||||
<div
|
<div
|
||||||
v-if="domainObject"
|
v-if="domainObject"
|
||||||
class="c-telemetry-view"
|
class="c-telemetry-view"
|
||||||
|
:class="styleClass"
|
||||||
:style="styleObject"
|
:style="styleObject"
|
||||||
@contextmenu.prevent="showContextMenu"
|
@contextmenu.prevent="showContextMenu"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="showLabel"
|
v-if="showLabel"
|
||||||
class="c-telemetry-view__label"
|
class="c-telemetry-view__label"
|
||||||
:class="[styleClass]"
|
|
||||||
:style="objectStyle"
|
|
||||||
>
|
>
|
||||||
<div class="c-telemetry-view__label-text">
|
<div class="c-telemetry-view__label-text">
|
||||||
{{ domainObject.name }}
|
{{ domainObject.name }}
|
||||||
@ -48,8 +47,7 @@
|
|||||||
v-if="showValue"
|
v-if="showValue"
|
||||||
:title="fieldName"
|
:title="fieldName"
|
||||||
class="c-telemetry-view__value"
|
class="c-telemetry-view__value"
|
||||||
:class="[telemetryClass, !telemetryClass && styleClass]"
|
:class="[telemetryClass]"
|
||||||
:style="!telemetryClass && objectStyle"
|
|
||||||
>
|
>
|
||||||
<div class="c-telemetry-view__value-text">
|
<div class="c-telemetry-view__value-text">
|
||||||
{{ telemetryValue }}
|
{{ telemetryValue }}
|
||||||
@ -62,7 +60,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import LayoutFrame from './LayoutFrame.vue'
|
import LayoutFrame from './LayoutFrame.vue'
|
||||||
import printj from 'printj'
|
import printj from 'printj'
|
||||||
import StyleRuleManager from "../../condition/StyleRuleManager";
|
import conditionalStylesMixin from "../mixins/objectStyles-mixin";
|
||||||
|
|
||||||
const DEFAULT_TELEMETRY_DIMENSIONS = [10, 5],
|
const DEFAULT_TELEMETRY_DIMENSIONS = [10, 5],
|
||||||
DEFAULT_POSITION = [1, 1],
|
DEFAULT_POSITION = [1, 1],
|
||||||
@ -81,8 +79,8 @@ export default {
|
|||||||
height: DEFAULT_TELEMETRY_DIMENSIONS[1],
|
height: DEFAULT_TELEMETRY_DIMENSIONS[1],
|
||||||
displayMode: 'all',
|
displayMode: 'all',
|
||||||
value: metadata.getDefaultDisplayValue(),
|
value: metadata.getDefaultDisplayValue(),
|
||||||
stroke: "transparent",
|
stroke: "",
|
||||||
fill: "transparent",
|
fill: "",
|
||||||
color: "",
|
color: "",
|
||||||
size: "13px"
|
size: "13px"
|
||||||
};
|
};
|
||||||
@ -91,6 +89,7 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
LayoutFrame
|
LayoutFrame
|
||||||
},
|
},
|
||||||
|
mixins: [conditionalStylesMixin],
|
||||||
props: {
|
props: {
|
||||||
item: {
|
item: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@ -113,8 +112,7 @@ export default {
|
|||||||
datum: undefined,
|
datum: undefined,
|
||||||
formats: undefined,
|
formats: undefined,
|
||||||
domainObject: undefined,
|
domainObject: undefined,
|
||||||
currentObjectPath: undefined,
|
currentObjectPath: undefined
|
||||||
objectStyle: ''
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -127,15 +125,10 @@ export default {
|
|||||||
return displayMode === 'all' || displayMode === 'value';
|
return displayMode === 'all' || displayMode === 'value';
|
||||||
},
|
},
|
||||||
styleObject() {
|
styleObject() {
|
||||||
return {
|
return Object.assign({}, {
|
||||||
backgroundColor: this.item.fill,
|
|
||||||
borderColor: this.item.stroke,
|
|
||||||
color: this.item.color,
|
|
||||||
fontSize: this.item.size
|
fontSize: this.item.size
|
||||||
}
|
}, this.itemStyle);
|
||||||
},
|
|
||||||
styleClass() {
|
|
||||||
return this.objectStyle && this.objectStyle.isStyleInvisible;
|
|
||||||
},
|
},
|
||||||
fieldName() {
|
fieldName() {
|
||||||
return this.valueMetadata && this.valueMetadata.name;
|
return this.valueMetadata && this.valueMetadata.name;
|
||||||
@ -190,15 +183,6 @@ export default {
|
|||||||
this.removeSelectable();
|
this.removeSelectable();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.unlistenStyles) {
|
|
||||||
this.unlistenStyles();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.styleRuleManager) {
|
|
||||||
this.styleRuleManager.destroy();
|
|
||||||
delete this.styleRuleManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.openmct.time.off("bounds", this.refreshData);
|
this.openmct.time.off("bounds", this.refreshData);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -241,7 +225,6 @@ export default {
|
|||||||
},
|
},
|
||||||
setObject(domainObject) {
|
setObject(domainObject) {
|
||||||
this.domainObject = domainObject;
|
this.domainObject = domainObject;
|
||||||
this.initObjectStyles();
|
|
||||||
this.keyString = this.openmct.objects.makeKeyString(domainObject.identifier);
|
this.keyString = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||||
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
||||||
this.limitEvaluator = this.openmct.telemetry.limitEvaluator(this.domainObject);
|
this.limitEvaluator = this.openmct.telemetry.limitEvaluator(this.domainObject);
|
||||||
@ -266,30 +249,6 @@ export default {
|
|||||||
},
|
},
|
||||||
showContextMenu(event) {
|
showContextMenu(event) {
|
||||||
this.openmct.contextMenu._showContextMenuForObjectPath(this.currentObjectPath, event.x, event.y, CONTEXT_MENU_ACTIONS);
|
this.openmct.contextMenu._showContextMenuForObjectPath(this.currentObjectPath, event.x, event.y, CONTEXT_MENU_ACTIONS);
|
||||||
},
|
|
||||||
initObjectStyles() {
|
|
||||||
if (this.domainObject.configuration) {
|
|
||||||
this.styleRuleManager = new StyleRuleManager(this.domainObject.configuration.objectStyles, this.openmct, this.updateStyle.bind(this));
|
|
||||||
|
|
||||||
if (this.unlistenStyles) {
|
|
||||||
this.unlistenStyles();
|
|
||||||
}
|
|
||||||
this.unlistenStyles = this.openmct.objects.observe(this.domainObject, 'configuration.objectStyles', (newObjectStyle) => {
|
|
||||||
//Updating object styles in the inspector view will trigger this so that the changes are reflected immediately
|
|
||||||
this.styleRuleManager.updateObjectStyleConfig(newObjectStyle);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
updateStyle(styleObj) {
|
|
||||||
let keys = Object.keys(styleObj);
|
|
||||||
keys.forEach(key => {
|
|
||||||
if ((typeof styleObj[key] === 'string') && (styleObj[key].indexOf('transparent') > -1)) {
|
|
||||||
if (styleObj[key]) {
|
|
||||||
styleObj[key] = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.objectStyle = styleObj;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
:class="[styleClass]"
|
:class="[styleClass]"
|
||||||
:style="style"
|
:style="style"
|
||||||
>
|
>
|
||||||
{{ item.text }}
|
<div class="c-text-view__text">{{ item.text }}</div>
|
||||||
</div>
|
</div>
|
||||||
</layout-frame>
|
</layout-frame>
|
||||||
</template>
|
</template>
|
||||||
@ -44,8 +44,8 @@ import conditionalStylesMixin from "../mixins/objectStyles-mixin";
|
|||||||
export default {
|
export default {
|
||||||
makeDefinition(openmct, gridSize, element) {
|
makeDefinition(openmct, gridSize, element) {
|
||||||
return {
|
return {
|
||||||
fill: 'transparent',
|
fill: '',
|
||||||
stroke: 'transparent',
|
stroke: '',
|
||||||
size: '13px',
|
size: '13px',
|
||||||
color: '',
|
color: '',
|
||||||
x: 1,
|
x: 1,
|
||||||
@ -80,14 +80,8 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
style() {
|
style() {
|
||||||
return Object.assign({
|
return Object.assign({
|
||||||
backgroundColor: this.item.fill,
|
|
||||||
border: '1px solid ' + this.item.stroke,
|
|
||||||
color: this.item.color,
|
|
||||||
fontSize: this.item.size
|
fontSize: this.item.size
|
||||||
}, this.itemStyle);
|
}, this.itemStyle);
|
||||||
},
|
|
||||||
styleClass() {
|
|
||||||
return this.itemStyle && this.itemStyle.isStyleInvisible;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
.is-editing {
|
.is-editing {
|
||||||
/******************* STYLES FOR C-FRAME WHILE EDITING */
|
/******************* STYLES FOR C-FRAME WHILE EDITING */
|
||||||
.c-frame {
|
.c-frame {
|
||||||
|
border: 1px solid rgba($editFrameColorHov, 0.3);
|
||||||
|
|
||||||
&:not([s-selected]) {
|
&:not([s-selected]) {
|
||||||
&:hover {
|
&:hover {
|
||||||
border: $editFrameBorderHov;
|
border: $editFrameBorderHov;
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
.c-text-view {
|
.c-text-view {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: stretch;
|
align-items: center; // Vertically center text
|
||||||
|
overflow: hidden;
|
||||||
|
padding: $interiorMargin;
|
||||||
|
|
||||||
.c-frame & {
|
.c-frame & {
|
||||||
@include abs();
|
@include abs();
|
||||||
|
@ -21,18 +21,20 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import StyleRuleManager from "@/plugins/condition/StyleRuleManager";
|
import StyleRuleManager from "@/plugins/condition/StyleRuleManager";
|
||||||
|
import {getStylesWithoutNoneValue} from "@/plugins/condition/utils/styleUtils";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
itemStyle: this.itemStyle
|
itemStyle: undefined,
|
||||||
|
styleClass: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.domainObject = this.$parent.domainObject;
|
this.parentDomainObject = this.$parent.domainObject;
|
||||||
this.itemId = this.item.id;
|
this.itemId = this.item.id;
|
||||||
this.objectStyle = this.getObjectStyleForItem(this.domainObject.configuration.objectStyles);
|
this.objectStyle = this.getObjectStyleForItem(this.parentDomainObject.configuration.objectStyles);
|
||||||
this.initObjectStyles();
|
this.initObjectStyles();
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
@ -50,7 +52,7 @@ export default {
|
|||||||
},
|
},
|
||||||
initObjectStyles() {
|
initObjectStyles() {
|
||||||
if (!this.styleRuleManager) {
|
if (!this.styleRuleManager) {
|
||||||
this.styleRuleManager = new StyleRuleManager(this.objectStyle, this.openmct, this.updateStyle.bind(this));
|
this.styleRuleManager = new StyleRuleManager(this.objectStyle, this.openmct, this.updateStyle.bind(this), true);
|
||||||
} else {
|
} else {
|
||||||
this.styleRuleManager.updateObjectStyleConfig(this.objectStyle);
|
this.styleRuleManager.updateObjectStyleConfig(this.objectStyle);
|
||||||
}
|
}
|
||||||
@ -59,7 +61,7 @@ export default {
|
|||||||
this.stopListeningObjectStyles();
|
this.stopListeningObjectStyles();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.stopListeningObjectStyles = this.openmct.objects.observe(this.domainObject, 'configuration.objectStyles', (newObjectStyle) => {
|
this.stopListeningObjectStyles = this.openmct.objects.observe(this.parentDomainObject, 'configuration.objectStyles', (newObjectStyle) => {
|
||||||
//Updating object styles in the inspector view will trigger this so that the changes are reflected immediately
|
//Updating object styles in the inspector view will trigger this so that the changes are reflected immediately
|
||||||
let newItemObjectStyle = this.getObjectStyleForItem(newObjectStyle);
|
let newItemObjectStyle = this.getObjectStyleForItem(newObjectStyle);
|
||||||
if (this.objectStyle !== newItemObjectStyle) {
|
if (this.objectStyle !== newItemObjectStyle) {
|
||||||
@ -69,13 +71,8 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
updateStyle(style) {
|
updateStyle(style) {
|
||||||
this.itemStyle = style;
|
this.itemStyle = getStylesWithoutNoneValue(style);
|
||||||
let keys = Object.keys(this.itemStyle);
|
this.styleClass = this.itemStyle && this.itemStyle.isStyleInvisible;
|
||||||
keys.forEach((key) => {
|
|
||||||
if ((typeof this.itemStyle[key] === 'string') && (this.itemStyle[key].indexOf('transparent') > -1)) {
|
|
||||||
delete this.itemStyle[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
21
src/plugins/notebook/components/menu-items.vue
Normal file
21
src/plugins/notebook/components/menu-items.vue
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<template>
|
||||||
|
<div class="c-menu">
|
||||||
|
<ul>
|
||||||
|
<li
|
||||||
|
v-for="(item, index) in popupMenuItems"
|
||||||
|
:key="index"
|
||||||
|
:class="item.cssClass"
|
||||||
|
:title="item.name"
|
||||||
|
@click="item.callback"
|
||||||
|
>
|
||||||
|
{{ item.name }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
inject: ['popupMenuItems']
|
||||||
|
}
|
||||||
|
</script>
|
@ -12,24 +12,7 @@
|
|||||||
:class="embed.cssClass"
|
:class="embed.cssClass"
|
||||||
@click="changeLocation"
|
@click="changeLocation"
|
||||||
>{{ embed.name }}</a>
|
>{{ embed.name }}</a>
|
||||||
<a class="c-ne__embed__context-available icon-arrow-down"
|
<PopupMenu :popup-menu-items="popupMenuItems" />
|
||||||
@click="toggleActionMenu"
|
|
||||||
></a>
|
|
||||||
</div>
|
|
||||||
<div class="hide-menu hidden">
|
|
||||||
<div class="menu-element context-menu-wrapper mobile-disable-select">
|
|
||||||
<div class="c-menu">
|
|
||||||
<ul>
|
|
||||||
<li v-for="action in actions"
|
|
||||||
:key="action.name"
|
|
||||||
:class="action.cssClass"
|
|
||||||
@click="action.perform(embed)"
|
|
||||||
>
|
|
||||||
{{ action.name }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-if="embed.snapshot"
|
<div v-if="embed.snapshot"
|
||||||
class="c-ne__embed__time"
|
class="c-ne__embed__time"
|
||||||
@ -42,15 +25,17 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Moment from 'moment';
|
import Moment from 'moment';
|
||||||
|
import PopupMenu from './popup-menu.vue';
|
||||||
import PreviewAction from '../../../ui/preview/PreviewAction';
|
import PreviewAction from '../../../ui/preview/PreviewAction';
|
||||||
import Painterro from 'painterro';
|
import Painterro from 'painterro';
|
||||||
|
import RemoveDialog from '../utils/removeDialog';
|
||||||
import SnapshotTemplate from './snapshot-template.html';
|
import SnapshotTemplate from './snapshot-template.html';
|
||||||
import { togglePopupMenu } from '../utils/popup-menu';
|
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
components: {
|
components: {
|
||||||
|
PopupMenu
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
embed: {
|
embed: {
|
||||||
@ -62,23 +47,35 @@ export default {
|
|||||||
removeActionString: {
|
removeActionString: {
|
||||||
type: String,
|
type: String,
|
||||||
default() {
|
default() {
|
||||||
return 'Remove Embed';
|
return 'Remove This Embed';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
actions: [this.removeEmbedAction()],
|
popupMenuItems: []
|
||||||
agentService: this.openmct.$injector.get('agentService'),
|
|
||||||
popupService: this.openmct.$injector.get('popupService')
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
},
|
},
|
||||||
beforeMount() {
|
mounted() {
|
||||||
this.populateActionMenu();
|
this.addPopupMenuItems();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
addPopupMenuItems() {
|
||||||
|
const removeEmbed = {
|
||||||
|
cssClass: 'icon-trash',
|
||||||
|
name: this.removeActionString,
|
||||||
|
callback: this.getRemoveDialog.bind(this)
|
||||||
|
}
|
||||||
|
const preview = {
|
||||||
|
cssClass: 'icon-eye-open',
|
||||||
|
name: 'Preview',
|
||||||
|
callback: this.previewEmbed.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.popupMenuItems = [removeEmbed, preview];
|
||||||
|
},
|
||||||
annotateSnapshot() {
|
annotateSnapshot() {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
@ -165,24 +162,44 @@ export default {
|
|||||||
}).show(this.embed.snapshot.src);
|
}).show(this.embed.snapshot.src);
|
||||||
},
|
},
|
||||||
changeLocation() {
|
changeLocation() {
|
||||||
this.openmct.time.stopClock();
|
|
||||||
this.openmct.time.bounds({
|
|
||||||
start: this.embed.bounds.start,
|
|
||||||
end: this.embed.bounds.end
|
|
||||||
});
|
|
||||||
|
|
||||||
const link = this.embed.historicLink;
|
const link = this.embed.historicLink;
|
||||||
if (!link) {
|
if (!link) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bounds = this.openmct.time.bounds();
|
||||||
|
const isTimeBoundChanged = this.embed.bounds.start !== bounds.start
|
||||||
|
&& this.embed.bounds.end !== bounds.end;
|
||||||
|
const isFixedTimespanMode = !this.openmct.time.clock();
|
||||||
|
|
||||||
window.location.href = link;
|
window.location.href = link;
|
||||||
const message = 'Time bounds changed to fixed timespan mode';
|
|
||||||
|
let message = '';
|
||||||
|
if (isTimeBoundChanged) {
|
||||||
|
this.openmct.time.bounds({
|
||||||
|
start: this.embed.bounds.start,
|
||||||
|
end: this.embed.bounds.end
|
||||||
|
});
|
||||||
|
message = 'Time bound values changed';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isFixedTimespanMode) {
|
||||||
|
message = 'Time bound values changed to fixed timespan mode';
|
||||||
|
}
|
||||||
|
|
||||||
this.openmct.notifications.alert(message);
|
this.openmct.notifications.alert(message);
|
||||||
},
|
},
|
||||||
formatTime(unixTime, timeFormat) {
|
formatTime(unixTime, timeFormat) {
|
||||||
return Moment.utc(unixTime).format(timeFormat);
|
return Moment.utc(unixTime).format(timeFormat);
|
||||||
},
|
},
|
||||||
|
getRemoveDialog() {
|
||||||
|
const options = {
|
||||||
|
name: this.removeActionString,
|
||||||
|
callback: this.removeEmbed.bind(this)
|
||||||
|
}
|
||||||
|
const removeDialog = new RemoveDialog(this.openmct, options);
|
||||||
|
removeDialog.show();
|
||||||
|
},
|
||||||
openSnapshot() {
|
openSnapshot() {
|
||||||
const self = this;
|
const self = this;
|
||||||
const snapshot = new Vue({
|
const snapshot = new Vue({
|
||||||
@ -214,53 +231,17 @@ export default {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
populateActionMenu() {
|
previewEmbed() {
|
||||||
const self = this;
|
const self = this;
|
||||||
const actions = [new PreviewAction(self.openmct)];
|
const previewAction = new PreviewAction(self.openmct);
|
||||||
|
previewAction.invoke(JSON.parse(self.embed.objectPath));
|
||||||
|
},
|
||||||
|
removeEmbed(success) {
|
||||||
|
if (!success) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
actions.forEach((action) => {
|
this.$emit('removeEmbed', this.embed.id);
|
||||||
self.actions.push({
|
|
||||||
cssClass: action.cssClass,
|
|
||||||
name: action.name,
|
|
||||||
perform: () => {
|
|
||||||
action.invoke(JSON.parse(self.embed.objectPath));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
removeEmbed(id) {
|
|
||||||
this.$emit('removeEmbed', id);
|
|
||||||
},
|
|
||||||
removeEmbedAction() {
|
|
||||||
const self = this;
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: self.removeActionString,
|
|
||||||
cssClass: 'icon-trash',
|
|
||||||
perform: function (embed) {
|
|
||||||
const dialog = self.openmct.overlays.dialog({
|
|
||||||
iconClass: "error",
|
|
||||||
message: `This action will permanently ${self.removeActionString.toLowerCase()}. Do you wish to continue?`,
|
|
||||||
buttons: [{
|
|
||||||
label: "No",
|
|
||||||
callback: function () {
|
|
||||||
dialog.dismiss();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Yes",
|
|
||||||
emphasis: true,
|
|
||||||
callback: function () {
|
|
||||||
dialog.dismiss();
|
|
||||||
self.removeEmbed(embed.id);
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
toggleActionMenu(event) {
|
|
||||||
togglePopupMenu(event, this.openmct);
|
|
||||||
},
|
},
|
||||||
updateEmbed(embed) {
|
updateEmbed(embed) {
|
||||||
this.$emit('updateEmbed', embed);
|
this.$emit('updateEmbed', embed);
|
||||||
|
@ -221,7 +221,7 @@ export default {
|
|||||||
return position;
|
return position;
|
||||||
},
|
},
|
||||||
formatTime(unixTime, timeFormat) {
|
formatTime(unixTime, timeFormat) {
|
||||||
return Moment(unixTime).format(timeFormat);
|
return Moment.utc(unixTime).format(timeFormat);
|
||||||
},
|
},
|
||||||
moveSnapshot(snapshotId) {
|
moveSnapshot(snapshotId) {
|
||||||
const snapshot = this.snapshotContainer.getSnapshot(snapshotId);
|
const snapshot = this.snapshotContainer.getSnapshot(snapshotId);
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="l-browse-bar__view-switcher c-ctrl-wrapper c-ctrl-wrapper--menus-left">
|
<div class="c-menu-button c-ctrl-wrapper c-ctrl-wrapper--menus-left">
|
||||||
<button
|
<button
|
||||||
class="c-button--menu icon-notebook"
|
class="c-button--menu icon-notebook"
|
||||||
title="Switch view type"
|
title="Take a Notebook Snapshot"
|
||||||
@click="setNotebookTypes"
|
@click="setNotebookTypes"
|
||||||
@click.stop="toggleMenu"
|
@click.stop="toggleMenu"
|
||||||
>
|
>
|
||||||
@ -40,6 +40,18 @@ export default {
|
|||||||
default() {
|
default() {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
ignoreLink: {
|
||||||
|
type: Boolean,
|
||||||
|
default() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
objectPath: {
|
||||||
|
type: Array,
|
||||||
|
default() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@ -97,17 +109,27 @@ export default {
|
|||||||
this.showMenu = false;
|
this.showMenu = false;
|
||||||
},
|
},
|
||||||
snapshot(notebook) {
|
snapshot(notebook) {
|
||||||
let element = document.getElementsByClassName("l-shell__main-container")[0];
|
this.hideMenu();
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const element = document.querySelector('.c-overlay__contents')
|
||||||
|
|| document.getElementsByClassName('l-shell__main-container')[0];
|
||||||
|
|
||||||
const bounds = this.openmct.time.bounds();
|
const bounds = this.openmct.time.bounds();
|
||||||
const objectPath = this.openmct.router.path;
|
const link = !this.ignoreLink
|
||||||
|
? window.location.href
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const objectPath = this.objectPath || this.openmct.router.path;
|
||||||
const snapshotMeta = {
|
const snapshotMeta = {
|
||||||
bounds,
|
bounds,
|
||||||
link: window.location.href,
|
link,
|
||||||
objectPath,
|
objectPath,
|
||||||
openmct: this.openmct
|
openmct: this.openmct
|
||||||
};
|
};
|
||||||
|
|
||||||
this.notebookSnapshot.capture(snapshotMeta, notebook.type, element);
|
this.notebookSnapshot.capture(snapshotMeta, notebook.type, element);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,24 +10,9 @@
|
|||||||
> {{ snapshots.length }} of {{ getNotebookSnapshotMaxCount() }}
|
> {{ snapshots.length }} of {{ getNotebookSnapshotMaxCount() }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<a class="l-browse-bar__context-actions c-disclosure-button"
|
<PopupMenu v-if="snapshots.length > 0"
|
||||||
@click="toggleActionMenu"
|
:popup-menu-items="popupMenuItems"
|
||||||
></a>
|
/>
|
||||||
<div class="hide-menu hidden">
|
|
||||||
<div class="menu-element context-menu-wrapper mobile-disable-select">
|
|
||||||
<div class="c-menu">
|
|
||||||
<ul>
|
|
||||||
<li v-for="action in actions"
|
|
||||||
:key="action.name"
|
|
||||||
:class="action.cssClass"
|
|
||||||
@click="action.perform()"
|
|
||||||
>
|
|
||||||
{{ action.name }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@ -62,14 +47,16 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import NotebookEmbed from './notebook-embed.vue';
|
import NotebookEmbed from './notebook-embed.vue';
|
||||||
|
import PopupMenu from './popup-menu.vue';
|
||||||
|
import RemoveDialog from '../utils/removeDialog';
|
||||||
import { NOTEBOOK_SNAPSHOT_MAX_COUNT } from '../snapshot-container';
|
import { NOTEBOOK_SNAPSHOT_MAX_COUNT } from '../snapshot-container';
|
||||||
import { EVENT_SNAPSHOTS_UPDATED } from '../notebook-constants';
|
import { EVENT_SNAPSHOTS_UPDATED } from '../notebook-constants';
|
||||||
import { togglePopupMenu } from '../utils/popup-menu';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct', 'snapshotContainer'],
|
inject: ['openmct', 'snapshotContainer'],
|
||||||
components: {
|
components: {
|
||||||
NotebookEmbed
|
NotebookEmbed,
|
||||||
|
PopupMenu
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
toggleSnapshot: {
|
toggleSnapshot: {
|
||||||
@ -81,54 +68,47 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
actions: [this.removeAllSnapshotAction()],
|
popupMenuItems: [],
|
||||||
|
removeActionString: 'Delete all snapshots',
|
||||||
snapshots: []
|
snapshots: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.addPopupMenuItems();
|
||||||
this.snapshotContainer.on(EVENT_SNAPSHOTS_UPDATED, this.snapshotsUpdated);
|
this.snapshotContainer.on(EVENT_SNAPSHOTS_UPDATED, this.snapshotsUpdated);
|
||||||
this.snapshots = this.snapshotContainer.getSnapshots();
|
this.snapshots = this.snapshotContainer.getSnapshots();
|
||||||
},
|
},
|
||||||
beforeDestory() {
|
beforeDestory() {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
addPopupMenuItems() {
|
||||||
|
const removeSnapshot = {
|
||||||
|
cssClass: 'icon-trash',
|
||||||
|
name: this.removeActionString,
|
||||||
|
callback: this.getRemoveDialog.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.popupMenuItems = [removeSnapshot];
|
||||||
|
},
|
||||||
close() {
|
close() {
|
||||||
this.toggleSnapshot();
|
this.toggleSnapshot();
|
||||||
},
|
},
|
||||||
getNotebookSnapshotMaxCount() {
|
getNotebookSnapshotMaxCount() {
|
||||||
return NOTEBOOK_SNAPSHOT_MAX_COUNT;
|
return NOTEBOOK_SNAPSHOT_MAX_COUNT;
|
||||||
},
|
},
|
||||||
removeAllSnapshotAction() {
|
getRemoveDialog() {
|
||||||
const self = this;
|
const options = {
|
||||||
|
name: this.removeActionString,
|
||||||
|
callback: this.removeAllSnapshots.bind(this)
|
||||||
|
}
|
||||||
|
const removeDialog = new RemoveDialog(this.openmct, options);
|
||||||
|
removeDialog.show();
|
||||||
|
},
|
||||||
|
removeAllSnapshots(success) {
|
||||||
|
if (!success) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
|
||||||
name: 'Delete All Snapshots',
|
|
||||||
cssClass: 'icon-trash',
|
|
||||||
perform: function (embed) {
|
|
||||||
const dialog = self.openmct.overlays.dialog({
|
|
||||||
iconClass: "error",
|
|
||||||
message: 'This action will delete all notebook snapshots. Do you want to continue?',
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
label: "No",
|
|
||||||
callback: () => {
|
|
||||||
dialog.dismiss();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Yes",
|
|
||||||
emphasis: true,
|
|
||||||
callback: () => {
|
|
||||||
self.removeAllSnapshots();
|
|
||||||
dialog.dismiss();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
removeAllSnapshots() {
|
|
||||||
this.snapshotContainer.removeAllSnapshots();
|
this.snapshotContainer.removeAllSnapshots();
|
||||||
},
|
},
|
||||||
removeSnapshot(id) {
|
removeSnapshot(id) {
|
||||||
@ -141,9 +121,6 @@ export default {
|
|||||||
event.dataTransfer.setData('text/plain', snapshot.id);
|
event.dataTransfer.setData('text/plain', snapshot.id);
|
||||||
event.dataTransfer.setData('snapshot/id', snapshot.id);
|
event.dataTransfer.setData('snapshot/id', snapshot.id);
|
||||||
},
|
},
|
||||||
toggleActionMenu(event) {
|
|
||||||
togglePopupMenu(event, this.openmct);
|
|
||||||
},
|
|
||||||
updateSnapshot(snapshot) {
|
updateSnapshot(snapshot) {
|
||||||
this.snapshotContainer.updateSnapshot(snapshot);
|
this.snapshotContainer.updateSnapshot(snapshot);
|
||||||
}
|
}
|
||||||
|
@ -49,13 +49,19 @@
|
|||||||
class="c-notebook__controls__time"
|
class="c-notebook__controls__time"
|
||||||
>
|
>
|
||||||
<option value="0"
|
<option value="0"
|
||||||
selected="selected"
|
:selected="showTime === 0"
|
||||||
>
|
>
|
||||||
Show all
|
Show all
|
||||||
</option>
|
</option>
|
||||||
<option value="1">Last hour</option>
|
<option value="1"
|
||||||
<option value="8">Last 8 hours</option>
|
:selected="showTime === 1"
|
||||||
<option value="24">Last 24 hours</option>
|
>Last hour</option>
|
||||||
|
<option value="8"
|
||||||
|
:selected="showTime === 8"
|
||||||
|
>Last 8 hours</option>
|
||||||
|
<option value="24"
|
||||||
|
:selected="showTime === 24"
|
||||||
|
>Last 24 hours</option>
|
||||||
</select>
|
</select>
|
||||||
<select v-model="defaultSort"
|
<select v-model="defaultSort"
|
||||||
class="c-notebook__controls__time"
|
class="c-notebook__controls__time"
|
||||||
@ -80,10 +86,10 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="selectedSection && selectedPage"
|
<div v-if="selectedSection && selectedPage"
|
||||||
|
ref="notebookEntries"
|
||||||
class="c-notebook__entries"
|
class="c-notebook__entries"
|
||||||
>
|
>
|
||||||
<NotebookEntry v-for="entry in filteredAndSortedEntries"
|
<NotebookEntry v-for="entry in filteredAndSortedEntries"
|
||||||
ref="notebookEntry"
|
|
||||||
:key="entry.id"
|
:key="entry.id"
|
||||||
:entry="entry"
|
:entry="entry"
|
||||||
:domain-object="internalDomainObject"
|
:domain-object="internalDomainObject"
|
||||||
@ -122,6 +128,7 @@ export default {
|
|||||||
defaultPageId: getDefaultNotebook() ? getDefaultNotebook().page.id : '',
|
defaultPageId: getDefaultNotebook() ? getDefaultNotebook().page.id : '',
|
||||||
defaultSectionId: getDefaultNotebook() ? getDefaultNotebook().section.id : '',
|
defaultSectionId: getDefaultNotebook() ? getDefaultNotebook().section.id : '',
|
||||||
defaultSort: this.domainObject.configuration.defaultSort,
|
defaultSort: this.domainObject.configuration.defaultSort,
|
||||||
|
focusEntryId: null,
|
||||||
internalDomainObject: this.domainObject,
|
internalDomainObject: this.domainObject,
|
||||||
search: '',
|
search: '',
|
||||||
showTime: 0,
|
showTime: 0,
|
||||||
@ -131,9 +138,17 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
filteredAndSortedEntries() {
|
filteredAndSortedEntries() {
|
||||||
|
const filterTime = Date.now();
|
||||||
const pageEntries = getNotebookEntries(this.internalDomainObject, this.selectedSection, this.selectedPage) || [];
|
const pageEntries = getNotebookEntries(this.internalDomainObject, this.selectedSection, this.selectedPage) || [];
|
||||||
|
|
||||||
return pageEntries.sort(this.sortEntries);
|
const hours = parseInt(this.showTime, 10);
|
||||||
|
const filteredPageEntriesByTime = hours
|
||||||
|
? pageEntries.filter(entry => (filterTime - entry.createdOn) <= hours * 60 * 60 * 1000)
|
||||||
|
: pageEntries;
|
||||||
|
|
||||||
|
return this.defaultSort === 'oldest'
|
||||||
|
? filteredPageEntriesByTime
|
||||||
|
: [...filteredPageEntriesByTime].reverse();
|
||||||
},
|
},
|
||||||
pages() {
|
pages() {
|
||||||
return this.getPages() || [];
|
return this.getPages() || [];
|
||||||
@ -174,6 +189,11 @@ export default {
|
|||||||
this.unlisten();
|
this.unlisten();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
updated: function () {
|
||||||
|
this.$nextTick(function () {
|
||||||
|
this.focusOnEntryId();
|
||||||
|
})
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
addDefaultClass() {
|
addDefaultClass() {
|
||||||
const classList = this.internalDomainObject.classList || [];
|
const classList = this.internalDomainObject.classList || [];
|
||||||
@ -210,6 +230,20 @@ export default {
|
|||||||
this.updateSection({ sections });
|
this.updateSection({ sections });
|
||||||
this.throttledSearchItem('');
|
this.throttledSearchItem('');
|
||||||
},
|
},
|
||||||
|
createNotebookStorageObject() {
|
||||||
|
const notebookMeta = {
|
||||||
|
name: this.internalDomainObject.name,
|
||||||
|
identifier: this.internalDomainObject.identifier
|
||||||
|
};
|
||||||
|
const page = this.getSelectedPage();
|
||||||
|
const section = this.getSelectedSection();
|
||||||
|
|
||||||
|
return {
|
||||||
|
notebookMeta,
|
||||||
|
section,
|
||||||
|
page
|
||||||
|
}
|
||||||
|
},
|
||||||
dragOver(event) {
|
dragOver(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.dataTransfer.dropEffect = "copy";
|
event.dataTransfer.dropEffect = "copy";
|
||||||
@ -245,6 +279,20 @@ export default {
|
|||||||
const embed = createNewEmbed(snapshotMeta);
|
const embed = createNewEmbed(snapshotMeta);
|
||||||
this.newEntry(embed);
|
this.newEntry(embed);
|
||||||
},
|
},
|
||||||
|
focusOnEntryId() {
|
||||||
|
if (!this.focusEntryId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const element = this.$refs.notebookEntries.querySelector(`#${this.focusEntryId}`);
|
||||||
|
|
||||||
|
if (!element) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
element.focus();
|
||||||
|
this.focusEntryId = null;
|
||||||
|
},
|
||||||
formatSidebar() {
|
formatSidebar() {
|
||||||
/*
|
/*
|
||||||
Determine if the sidebar should slide over content, or compress it
|
Determine if the sidebar should slide over content, or compress it
|
||||||
@ -359,20 +407,12 @@ export default {
|
|||||||
this.updateSection({ sections });
|
this.updateSection({ sections });
|
||||||
},
|
},
|
||||||
newEntry(embed = null) {
|
newEntry(embed = null) {
|
||||||
const selectedSection = this.getSelectedSection();
|
|
||||||
const selectedPage = this.getSelectedPage();
|
|
||||||
this.search = '';
|
this.search = '';
|
||||||
|
const notebookStorage = this.createNotebookStorageObject();
|
||||||
this.updateDefaultNotebook(selectedSection, selectedPage);
|
this.updateDefaultNotebook(notebookStorage);
|
||||||
const notebookStorage = getDefaultNotebook();
|
|
||||||
const id = addNotebookEntry(this.openmct, this.internalDomainObject, notebookStorage, embed);
|
const id = addNotebookEntry(this.openmct, this.internalDomainObject, notebookStorage, embed);
|
||||||
|
this.focusEntryId = id;
|
||||||
this.$nextTick(() => {
|
this.search = '';
|
||||||
const element = this.$el.querySelector(`#${id}`);
|
|
||||||
element.focus();
|
|
||||||
});
|
|
||||||
|
|
||||||
return id;
|
|
||||||
},
|
},
|
||||||
orientationChange() {
|
orientationChange() {
|
||||||
this.formatSidebar();
|
this.formatSidebar();
|
||||||
@ -394,21 +434,16 @@ export default {
|
|||||||
searchItem(input) {
|
searchItem(input) {
|
||||||
this.search = input;
|
this.search = input;
|
||||||
},
|
},
|
||||||
sortEntries(right, left) {
|
|
||||||
return this.defaultSort === 'newest'
|
|
||||||
? left.createdOn - right.createdOn
|
|
||||||
: right.createdOn - left.createdOn;
|
|
||||||
},
|
|
||||||
toggleNav() {
|
toggleNav() {
|
||||||
this.showNav = !this.showNav;
|
this.showNav = !this.showNav;
|
||||||
},
|
},
|
||||||
async updateDefaultNotebook(selectedSection, selectedPage) {
|
async updateDefaultNotebook(notebookStorage) {
|
||||||
const defaultNotebookObject = await this.getDefaultNotebookObject();
|
const defaultNotebookObject = await this.getDefaultNotebookObject();
|
||||||
this.removeDefaultClass(defaultNotebookObject);
|
this.removeDefaultClass(defaultNotebookObject);
|
||||||
setDefaultNotebook(this.internalDomainObject, selectedSection, selectedPage);
|
setDefaultNotebook(notebookStorage);
|
||||||
this.addDefaultClass();
|
this.addDefaultClass();
|
||||||
this.defaultSectionId = selectedSection.id;
|
this.defaultSectionId = notebookStorage.section.id;
|
||||||
this.defaultPageId = selectedPage.id;
|
this.defaultPageId = notebookStorage.page.id;
|
||||||
},
|
},
|
||||||
updateDefaultNotebookPage(pages, id) {
|
updateDefaultNotebookPage(pages, id) {
|
||||||
if (!id) {
|
if (!id) {
|
||||||
@ -460,7 +495,7 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (section.id !== defaultNotebookSection.id) {
|
if (id !== defaultNotebookSection.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,32 +9,19 @@
|
|||||||
@keydown.enter="updateName"
|
@keydown.enter="updateName"
|
||||||
@blur="updateName"
|
@blur="updateName"
|
||||||
>{{ page.name.length ? page.name : `Unnamed ${pageTitle}` }}</span>
|
>{{ page.name.length ? page.name : `Unnamed ${pageTitle}` }}</span>
|
||||||
<a class="c-list__item__menu-indicator icon-arrow-down"
|
<PopupMenu :popup-menu-items="popupMenuItems" />
|
||||||
@click="toggleActionMenu"
|
|
||||||
></a>
|
|
||||||
<div class="hide-menu hidden">
|
|
||||||
<div class="menu-element context-menu-wrapper mobile-disable-select">
|
|
||||||
<div class="c-menu">
|
|
||||||
<ul>
|
|
||||||
<li v-for="action in actions"
|
|
||||||
:key="action.name"
|
|
||||||
:class="action.cssClass"
|
|
||||||
@click="action.perform(page.id)"
|
|
||||||
>
|
|
||||||
{{ action.name }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { togglePopupMenu } from '../utils/popup-menu';
|
import PopupMenu from './popup-menu.vue';
|
||||||
|
import RemoveDialog from '../utils/removeDialog';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
|
components: {
|
||||||
|
PopupMenu
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
defaultPageId: {
|
defaultPageId: {
|
||||||
type: String,
|
type: String,
|
||||||
@ -55,7 +42,8 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
actions: [this.deletePage()]
|
popupMenuItems: [],
|
||||||
|
removeActionString: `Delete ${this.pageTitle}`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@ -64,40 +52,37 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.addPopupMenuItems();
|
||||||
this.toggleContentEditable();
|
this.toggleContentEditable();
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
deletePage() {
|
addPopupMenuItems() {
|
||||||
const self = this;
|
const removePage = {
|
||||||
|
|
||||||
return {
|
|
||||||
name: `Delete ${this.pageTitle}`,
|
|
||||||
cssClass: 'icon-trash',
|
cssClass: 'icon-trash',
|
||||||
perform: function (id) {
|
name: this.removeActionString,
|
||||||
const dialog = self.openmct.overlays.dialog({
|
callback: this.getRemoveDialog.bind(this)
|
||||||
iconClass: "error",
|
|
||||||
message: 'This action will delete this page and all of its entries. Do you want to continue?',
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
label: "No",
|
|
||||||
callback: () => {
|
|
||||||
dialog.dismiss();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.popupMenuItems = [removePage];
|
||||||
},
|
},
|
||||||
{
|
deletePage(success) {
|
||||||
label: "Yes",
|
if (!success) {
|
||||||
emphasis: true,
|
return;
|
||||||
callback: () => {
|
|
||||||
self.$emit('deletePage', id);
|
|
||||||
dialog.dismiss();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.$emit('deletePage', this.page.id);
|
||||||
|
},
|
||||||
|
getRemoveDialog() {
|
||||||
|
const message = 'This action will delete this page and all of its entries. Do you want to continue?';
|
||||||
|
const options = {
|
||||||
|
name: this.removeActionString,
|
||||||
|
callback: this.deletePage.bind(this),
|
||||||
|
message
|
||||||
}
|
}
|
||||||
]
|
const removeDialog = new RemoveDialog(this.openmct, options);
|
||||||
});
|
removeDialog.show();
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
selectPage(event) {
|
selectPage(event) {
|
||||||
const target = event.target;
|
const target = event.target;
|
||||||
@ -117,10 +102,6 @@ export default {
|
|||||||
|
|
||||||
this.$emit('selectPage', id);
|
this.$emit('selectPage', id);
|
||||||
},
|
},
|
||||||
toggleActionMenu(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
togglePopupMenu(event, this.openmct);
|
|
||||||
},
|
|
||||||
toggleContentEditable(page = this.page) {
|
toggleContentEditable(page = this.page) {
|
||||||
const pageTitle = this.$el.querySelector('span');
|
const pageTitle = this.$el.querySelector('span');
|
||||||
pageTitle.contentEditable = page.isSelected;
|
pageTitle.contentEditable = page.isSelected;
|
||||||
|
93
src/plugins/notebook/components/popup-menu.vue
Normal file
93
src/plugins/notebook/components/popup-menu.vue
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
<template>
|
||||||
|
<button
|
||||||
|
class="c-popup-menu-button c-disclosure-button"
|
||||||
|
title="popup menu"
|
||||||
|
@click="showMenuItems"
|
||||||
|
>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MenuItems from './menu-items.vue';
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
inject: ['openmct'],
|
||||||
|
props: {
|
||||||
|
domainObject: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
popupMenuItems: {
|
||||||
|
type: Array,
|
||||||
|
default() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
menuItems: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
calculateMenuPosition(event, element) {
|
||||||
|
let eventPosX = event.clientX;
|
||||||
|
let eventPosY = event.clientY;
|
||||||
|
|
||||||
|
let menuDimensions = element.getBoundingClientRect();
|
||||||
|
let overflowX = (eventPosX + menuDimensions.width) - document.body.clientWidth;
|
||||||
|
let overflowY = (eventPosY + menuDimensions.height) - document.body.clientHeight;
|
||||||
|
|
||||||
|
if (overflowX > 0) {
|
||||||
|
eventPosX = eventPosX - overflowX;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (overflowY > 0) {
|
||||||
|
eventPosY = eventPosY - overflowY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: eventPosX,
|
||||||
|
y: eventPosY
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hideMenuItems() {
|
||||||
|
document.body.removeChild(this.menuItems.$el);
|
||||||
|
this.menuItems.$destroy();
|
||||||
|
this.menuItems = null;
|
||||||
|
document.removeEventListener('click', this.hideMenuItems);
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
showMenuItems($event) {
|
||||||
|
const menuItems = new Vue({
|
||||||
|
components: {
|
||||||
|
MenuItems
|
||||||
|
},
|
||||||
|
provide: {
|
||||||
|
popupMenuItems: this.popupMenuItems
|
||||||
|
},
|
||||||
|
template: '<MenuItems />'
|
||||||
|
});
|
||||||
|
this.menuItems = menuItems;
|
||||||
|
|
||||||
|
menuItems.$mount();
|
||||||
|
const element = this.menuItems.$el;
|
||||||
|
document.body.appendChild(element);
|
||||||
|
|
||||||
|
const position = this.calculateMenuPosition($event, element);
|
||||||
|
element.style.left = `${position.x}px`;
|
||||||
|
element.style.top = `${position.y}px`;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
document.addEventListener('click', this.hideMenuItems);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -9,24 +9,7 @@
|
|||||||
@keydown.enter="updateName"
|
@keydown.enter="updateName"
|
||||||
@blur="updateName"
|
@blur="updateName"
|
||||||
>{{ section.name.length ? section.name : `Unnamed ${sectionTitle}` }}</span>
|
>{{ section.name.length ? section.name : `Unnamed ${sectionTitle}` }}</span>
|
||||||
<a class="c-list__item__menu-indicator icon-arrow-down"
|
<PopupMenu :popup-menu-items="popupMenuItems" />
|
||||||
@click="toggleActionMenu"
|
|
||||||
></a>
|
|
||||||
<div class="hide-menu hidden">
|
|
||||||
<div class="menu-element context-menu-wrapper mobile-disable-select">
|
|
||||||
<div class="c-menu">
|
|
||||||
<ul>
|
|
||||||
<li v-for="action in actions"
|
|
||||||
:key="action.name"
|
|
||||||
:class="action.cssClass"
|
|
||||||
@click="action.perform(section.id)"
|
|
||||||
>
|
|
||||||
{{ action.name }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -34,10 +17,14 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { togglePopupMenu } from '../utils/popup-menu';
|
import PopupMenu from './popup-menu.vue';
|
||||||
|
import RemoveDialog from '../utils/removeDialog';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
|
components: {
|
||||||
|
PopupMenu
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
defaultSectionId: {
|
defaultSectionId: {
|
||||||
type: String,
|
type: String,
|
||||||
@ -58,7 +45,8 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
actions: [this.deleteSectionAction()]
|
popupMenuItems: [],
|
||||||
|
removeActionString: `Delete ${this.sectionTitle}`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@ -67,40 +55,38 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.addPopupMenuItems();
|
||||||
this.toggleContentEditable();
|
this.toggleContentEditable();
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
deleteSectionAction() {
|
addPopupMenuItems() {
|
||||||
const self = this;
|
const removeSection = {
|
||||||
|
|
||||||
return {
|
|
||||||
name: `Delete ${this.sectionTitle}`,
|
|
||||||
cssClass: 'icon-trash',
|
cssClass: 'icon-trash',
|
||||||
perform: function (id) {
|
name: this.removeActionString,
|
||||||
const dialog = self.openmct.overlays.dialog({
|
callback: this.getRemoveDialog.bind(this)
|
||||||
iconClass: "error",
|
|
||||||
message: 'This action will delete this section and all of its pages and entries. Do you want to continue?',
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
label: "No",
|
|
||||||
callback: () => {
|
|
||||||
dialog.dismiss();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.popupMenuItems = [removeSection];
|
||||||
},
|
},
|
||||||
{
|
deleteSection(success) {
|
||||||
label: "Yes",
|
if (!success) {
|
||||||
emphasis: true,
|
return;
|
||||||
callback: () => {
|
|
||||||
self.$emit('deleteSection', id);
|
|
||||||
dialog.dismiss();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.$emit('deleteSection', this.section.id);
|
||||||
|
},
|
||||||
|
getRemoveDialog() {
|
||||||
|
const message = 'This action will delete this section and all of its pages and entries. Do you want to continue?';
|
||||||
|
const options = {
|
||||||
|
name: this.removeActionString,
|
||||||
|
callback: this.deleteSection.bind(this),
|
||||||
|
message
|
||||||
}
|
}
|
||||||
]
|
|
||||||
});
|
const removeDialog = new RemoveDialog(this.openmct, options);
|
||||||
}
|
removeDialog.show();
|
||||||
};
|
|
||||||
},
|
},
|
||||||
selectSection(event) {
|
selectSection(event) {
|
||||||
const target = event.target;
|
const target = event.target;
|
||||||
@ -121,9 +107,6 @@ export default {
|
|||||||
|
|
||||||
this.$emit('selectSection', id);
|
this.$emit('selectSection', id);
|
||||||
},
|
},
|
||||||
toggleActionMenu(event) {
|
|
||||||
togglePopupMenu(event, this.openmct);
|
|
||||||
},
|
|
||||||
toggleContentEditable(section = this.section) {
|
toggleContentEditable(section = this.section) {
|
||||||
const sectionTitle = this.$el.querySelector('span');
|
const sectionTitle = this.$el.querySelector('span');
|
||||||
sectionTitle.contentEditable = section.isSelected;
|
sectionTitle.contentEditable = section.isSelected;
|
||||||
|
@ -10,18 +10,7 @@ export function getDefaultNotebook() {
|
|||||||
return JSON.parse(notebookStorage);
|
return JSON.parse(notebookStorage);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setDefaultNotebook(domainObject, section, page) {
|
export function setDefaultNotebook(notebookStorage) {
|
||||||
const notebookMeta = {
|
|
||||||
name: domainObject.name,
|
|
||||||
identifier: domainObject.identifier
|
|
||||||
};
|
|
||||||
|
|
||||||
const notebookStorage = {
|
|
||||||
notebookMeta,
|
|
||||||
section,
|
|
||||||
page
|
|
||||||
}
|
|
||||||
|
|
||||||
window.localStorage.setItem(NOTEBOOK_LOCAL_STORAGE, JSON.stringify(notebookStorage));
|
window.localStorage.setItem(NOTEBOOK_LOCAL_STORAGE, JSON.stringify(notebookStorage));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
import $ from 'zepto';
|
|
||||||
|
|
||||||
export const togglePopupMenu = (event, openmct) => {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
const body = $(document.body);
|
|
||||||
const container = $(event.target.parentElement.parentElement);
|
|
||||||
const classList = document.querySelector('body').classList;
|
|
||||||
const isPhone = Array.from(classList).includes('phone');
|
|
||||||
const isTablet = Array.from(classList).includes('tablet');
|
|
||||||
|
|
||||||
const initiatingEvent = isPhone || isTablet
|
|
||||||
? 'touchstart'
|
|
||||||
: 'mousedown';
|
|
||||||
const menu = container.find('.menu-element');
|
|
||||||
let dismissExistingMenu;
|
|
||||||
|
|
||||||
function dismiss() {
|
|
||||||
container.find('.hide-menu').append(menu);
|
|
||||||
body.off(initiatingEvent, menuClickHandler);
|
|
||||||
dismissExistingMenu = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function menuClickHandler(e) {
|
|
||||||
window.setTimeout(() => {
|
|
||||||
dismiss();
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dismiss any menu which was already showing
|
|
||||||
if (dismissExistingMenu) {
|
|
||||||
dismissExistingMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...and record the presence of this menu.
|
|
||||||
dismissExistingMenu = dismiss;
|
|
||||||
|
|
||||||
const popupService = openmct.$injector.get('popupService');
|
|
||||||
popupService.display(menu, [event.pageX,event.pageY], {
|
|
||||||
marginX: 0,
|
|
||||||
marginY: -50
|
|
||||||
});
|
|
||||||
|
|
||||||
body.on(initiatingEvent, menuClickHandler);
|
|
||||||
}
|
|
36
src/plugins/notebook/utils/removeDialog.js
Normal file
36
src/plugins/notebook/utils/removeDialog.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
export default class RemoveDialog {
|
||||||
|
constructor(openmct, options) {
|
||||||
|
this.name = options.name;
|
||||||
|
this.openmct = openmct;
|
||||||
|
|
||||||
|
this.callback = options.callback;
|
||||||
|
this.cssClass = options.cssClass || 'icon-trash';
|
||||||
|
this.description = options.description || 'Remove action dialog';
|
||||||
|
this.iconClass = "error";
|
||||||
|
this.key = 'remove';
|
||||||
|
this.message = options.message || `This action will permanently ${this.name.toLowerCase()}. Do you wish to continue?`;
|
||||||
|
}
|
||||||
|
|
||||||
|
show() {
|
||||||
|
const dialog = this.openmct.overlays.dialog({
|
||||||
|
iconClass: this.iconClass,
|
||||||
|
message: this.message,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
label: "Ok",
|
||||||
|
callback: () => {
|
||||||
|
this.callback(true);
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Cancel",
|
||||||
|
callback: () => {
|
||||||
|
this.callback(false);
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -21,7 +21,7 @@
|
|||||||
-->
|
-->
|
||||||
<div ng-controller="StackedPlotController as stackedPlot"
|
<div ng-controller="StackedPlotController as stackedPlot"
|
||||||
class="c-plot c-plot--stacked holder holder-plot has-control-bar">
|
class="c-plot c-plot--stacked holder holder-plot has-control-bar">
|
||||||
<div class="l-control-bar" ng-show="!stackedPlot.hideExportButtons">
|
<div class="c-control-bar" ng-show="!stackedPlot.hideExportButtons">
|
||||||
<span class="c-button-set c-button-set--strip-h">
|
<span class="c-button-set c-button-set--strip-h">
|
||||||
<button class="c-button icon-download"
|
<button class="c-button icon-download"
|
||||||
ng-click="stackedPlot.exportPNG()"
|
ng-click="stackedPlot.exportPNG()"
|
||||||
|
@ -54,6 +54,7 @@ function (
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function MCTChartController($scope) {
|
function MCTChartController($scope) {
|
||||||
|
this.$onInit = () => {
|
||||||
this.$scope = $scope;
|
this.$scope = $scope;
|
||||||
this.isDestroyed = false;
|
this.isDestroyed = false;
|
||||||
this.lines = [];
|
this.lines = [];
|
||||||
@ -76,6 +77,7 @@ function (
|
|||||||
this.$scope.$watch('rectangles', this.scheduleDraw);
|
this.$scope.$watch('rectangles', this.scheduleDraw);
|
||||||
this.config.series.forEach(this.onSeriesAdd, this);
|
this.config.series.forEach(this.onSeriesAdd, this);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
eventHelpers.extend(MCTChartController.prototype);
|
eventHelpers.extend(MCTChartController.prototype);
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ define([
|
|||||||
* values near the cursor.
|
* values near the cursor.
|
||||||
*/
|
*/
|
||||||
function MCTPlotController($scope, $element, $window) {
|
function MCTPlotController($scope, $element, $window) {
|
||||||
|
this.$onInit = () => {
|
||||||
this.$scope = $scope;
|
this.$scope = $scope;
|
||||||
this.$scope.config = this.config;
|
this.$scope.config = this.config;
|
||||||
this.$scope.plot = this;
|
this.$scope.plot = this;
|
||||||
@ -54,6 +55,7 @@ define([
|
|||||||
|
|
||||||
this.initialize();
|
this.initialize();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MCTPlotController.$inject = ['$scope', '$element', '$window'];
|
MCTPlotController.$inject = ['$scope', '$element', '$window'];
|
||||||
|
|
||||||
|
@ -114,6 +114,7 @@ define([
|
|||||||
}
|
}
|
||||||
|
|
||||||
function MCTTicksController($scope, $element) {
|
function MCTTicksController($scope, $element) {
|
||||||
|
this.$onInit = () => {
|
||||||
this.$scope = $scope;
|
this.$scope = $scope;
|
||||||
this.$element = $element;
|
this.$element = $element;
|
||||||
|
|
||||||
@ -124,6 +125,7 @@ define([
|
|||||||
this.listenTo(this.$scope, '$destroy', this.stopListening, this);
|
this.listenTo(this.$scope, '$destroy', this.stopListening, this);
|
||||||
this.updateTicks();
|
this.updateTicks();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MCTTicksController.$inject = ['$scope', '$element'];
|
MCTTicksController.$inject = ['$scope', '$element'];
|
||||||
|
|
||||||
|
@ -81,7 +81,8 @@ define(
|
|||||||
clonedElement.classList.add(className);
|
clonedElement.classList.add(className);
|
||||||
}
|
}
|
||||||
element.id = oldId;
|
element.id = oldId;
|
||||||
}
|
},
|
||||||
|
removeContainer: true // Set to false to debug what html2canvas renders
|
||||||
}).then(function (canvas) {
|
}).then(function (canvas) {
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
|
35
src/plugins/plotlyPlot/PlotlyTelemetryProvider.js
Normal file
35
src/plugins/plotlyPlot/PlotlyTelemetryProvider.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
export default class PlotlyTelemetryProvider {
|
||||||
|
constructor(openmct) {
|
||||||
|
this.openmct = openmct;
|
||||||
|
}
|
||||||
|
|
||||||
|
isTelemetryObject(domainObject) {
|
||||||
|
return domainObject.type === 'plotlyPlot';
|
||||||
|
}
|
||||||
|
|
||||||
|
supportsRequest(domainObject) {
|
||||||
|
return domainObject.type === 'plotlyPlot';
|
||||||
|
}
|
||||||
|
|
||||||
|
supportsSubscribe(domainObject) {
|
||||||
|
return domainObject.type === 'plotlyPlot';
|
||||||
|
}
|
||||||
|
|
||||||
|
request(domainObject) {
|
||||||
|
// let conditionManager = this.getConditionManager(domainObject);
|
||||||
|
|
||||||
|
// return conditionManager.requestLADConditionSetOutput()
|
||||||
|
// .then(latestOutput => {
|
||||||
|
// return latestOutput;
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribe(domainObject, callback) {
|
||||||
|
// let conditionManager = this.getConditionManager(domainObject);
|
||||||
|
|
||||||
|
// conditionManager.on('conditionSetResultUpdated', callback);
|
||||||
|
|
||||||
|
// return this.destroyConditionManager.bind(this, this.openmct.objects.makeKeyString(domainObject.identifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
73
src/plugins/plotlyPlot/PlotlyViewProvider.js
Normal file
73
src/plugins/plotlyPlot/PlotlyViewProvider.js
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2019, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
import PlotlyViewLayout from './components/PlotlyViewLayout.vue';
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
export default function PlotlyViewProvider(openmct) {
|
||||||
|
return {
|
||||||
|
key: 'plotlyPlot',
|
||||||
|
name: 'Plotly Plot',
|
||||||
|
cssClass: 'icon-plot-overlay',
|
||||||
|
canView: function (domainObject) {
|
||||||
|
return domainObject.type === 'plotlyPlot';
|
||||||
|
},
|
||||||
|
canEdit: function (domainObject) {
|
||||||
|
return domainObject.type === 'plotlyPlot';
|
||||||
|
},
|
||||||
|
view: function (domainObject, objectPath) {
|
||||||
|
let component;
|
||||||
|
|
||||||
|
return {
|
||||||
|
show: function (element, isEditing) {
|
||||||
|
component = new Vue({
|
||||||
|
provide: {
|
||||||
|
openmct,
|
||||||
|
domainObject,
|
||||||
|
objectPath
|
||||||
|
},
|
||||||
|
el: element,
|
||||||
|
components: {
|
||||||
|
PlotlyViewLayout
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isEditing
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: '<plotly-view-layout :isEditing="isEditing"></plotly-view-layout>'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onEditModeChange: function (isEditing) {
|
||||||
|
component.isEditing = isEditing;
|
||||||
|
},
|
||||||
|
destroy: function (element) {
|
||||||
|
component.$destroy();
|
||||||
|
component = undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
priority: function () {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
138
src/plugins/plotlyPlot/components/PlotlyViewLayout.vue
Normal file
138
src/plugins/plotlyPlot/components/PlotlyViewLayout.vue
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
<template>
|
||||||
|
<div class="l-view-section"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Plotly from 'plotly.js-dist';
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
inject: ['openmct', 'domainObject', 'objectPath'],
|
||||||
|
data: function () {
|
||||||
|
|
||||||
|
return {
|
||||||
|
telemetryObjects: []
|
||||||
|
// currentDomainObject: this.domainObject
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.composition = this.openmct.composition.get(this.domainObject);
|
||||||
|
this.composition.on('add', this.addTelemetry);
|
||||||
|
this.composition.load();
|
||||||
|
|
||||||
|
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
||||||
|
|
||||||
|
console.log('this.metadata', this.metadata);
|
||||||
|
|
||||||
|
// this.keystring = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||||
|
// this.subscribe(this.domainObject);
|
||||||
|
this.plotElement = document.querySelector('.l-view-section');
|
||||||
|
// Plotly.newPlot(this.plotElement, [{
|
||||||
|
// x: [1, 2, 3, 4, 5],
|
||||||
|
// y: [1, 2, 4, 8, 16]
|
||||||
|
// }], this.getLayout(), {displayModeBar: false});
|
||||||
|
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getLayout() {
|
||||||
|
return {
|
||||||
|
hovermode: 'compare',
|
||||||
|
hoverdistance: -1,
|
||||||
|
autosize: "true",
|
||||||
|
showlegend: false,
|
||||||
|
font: {
|
||||||
|
family: "'Helvetica Neue', Helvetica, Arial, sans-serif",
|
||||||
|
size: "12px",
|
||||||
|
color: "#666"
|
||||||
|
},
|
||||||
|
xaxis: {
|
||||||
|
// title: this.plotAxisTitle.xAxisTitle,
|
||||||
|
zeroline: false
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
// title: this.plotAxisTitle.yAxisTitle,
|
||||||
|
zeroline: false
|
||||||
|
},
|
||||||
|
margin: {
|
||||||
|
l: 20,
|
||||||
|
r: 10,
|
||||||
|
b: 20,
|
||||||
|
t: 10
|
||||||
|
},
|
||||||
|
paper_bgcolor: 'transparent',
|
||||||
|
plot_bgcolor: 'transparent'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
addTelemetry(telemetryObject) {
|
||||||
|
return this.openmct.telemetry.request(telemetryObject)
|
||||||
|
.then(telemetryData => {
|
||||||
|
this.createPlot(telemetryData, telemetryObject);
|
||||||
|
}, () => {console.log(error)});
|
||||||
|
},
|
||||||
|
formatDatumX(datum) {
|
||||||
|
let timestamp = moment.utc(datum.utc).format('YYYY-MM-DD hh:mm:ss.ms');
|
||||||
|
return timestamp;
|
||||||
|
},
|
||||||
|
formatDatumY(datum) {
|
||||||
|
return datum.sin;
|
||||||
|
},
|
||||||
|
createPlot(telemetryData, telemetryObject) {
|
||||||
|
let x = [],
|
||||||
|
y = [];
|
||||||
|
|
||||||
|
telemetryData.forEach((datum, index) => {
|
||||||
|
x.push(this.formatDatumX(datum));
|
||||||
|
y.push(this.formatDatumY(datum));
|
||||||
|
})
|
||||||
|
|
||||||
|
let data = [{
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
mode: 'line'
|
||||||
|
}];
|
||||||
|
var layout = {
|
||||||
|
title:'Line and Scatter Plot'
|
||||||
|
};
|
||||||
|
Plotly.newPlot(
|
||||||
|
this.plotElement,
|
||||||
|
data,
|
||||||
|
this.getLayout()
|
||||||
|
)
|
||||||
|
|
||||||
|
this.subscribe(telemetryObject);
|
||||||
|
},
|
||||||
|
subscribe(domainObject) {
|
||||||
|
// this.date = ''
|
||||||
|
// this.openmct.objects.get(this.keystring)
|
||||||
|
// .then((object) => {
|
||||||
|
// const metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
||||||
|
// console.log('metadata', metadata);
|
||||||
|
// // this.timeKey = this.openmct.time.timeSystem().key;
|
||||||
|
// // this.timeFormat = this.openmct.telemetry.getValueFormatter(metadata.value(this.timeKey));
|
||||||
|
// // // this.imageFormat = this.openmct.telemetry.getValueFormatter(metadata.valuesForHints(['image'])[0]);
|
||||||
|
// // this.unsubscribe = this.openmct.telemetry
|
||||||
|
// // .subscribe(this.domainObject, (datum) => {
|
||||||
|
// // this.updateHistory(datum);
|
||||||
|
// // this.updateValues(datum);
|
||||||
|
// // });
|
||||||
|
|
||||||
|
// // this.requestHistory(this.openmct.time.bounds());
|
||||||
|
// });
|
||||||
|
|
||||||
|
this.openmct.telemetry.subscribe(domainObject, (datum) => {
|
||||||
|
this.updateData(datum)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
updateData(datum) {
|
||||||
|
Plotly.extendTraces(
|
||||||
|
this.plotElement,
|
||||||
|
{
|
||||||
|
x: [[this.formatDatumX(datum)]],
|
||||||
|
y: [[this.formatDatumY(datum)]]
|
||||||
|
},
|
||||||
|
[0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
2
src/plugins/plotlyPlot/components/plotly.scss
Normal file
2
src/plugins/plotlyPlot/components/plotly.scss
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
.plot svg {
|
||||||
|
}
|
20
src/plugins/plotlyPlot/plugin.js
Normal file
20
src/plugins/plotlyPlot/plugin.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import PlotlyViewProvider from './PlotlyViewProvider.js';
|
||||||
|
import PlotlyTelemetryProvider from './PlotlyTelemetryProvider.js';
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
return function install(openmct) {
|
||||||
|
openmct.objectViews.addProvider(new PlotlyViewProvider(openmct));
|
||||||
|
openmct.telemetry.addProvider(new PlotlyTelemetryProvider(openmct));
|
||||||
|
|
||||||
|
openmct.types.addType('plotlyPlot', {
|
||||||
|
name: "Plotly Plot",
|
||||||
|
description: "Simple plot rendered by plotly.js",
|
||||||
|
creatable: true,
|
||||||
|
cssClass: 'icon-plot-overlay',
|
||||||
|
initialize: function (domainObject) {
|
||||||
|
domainObject.composition = [];
|
||||||
|
domainObject.telemetry = {};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
@ -34,6 +34,7 @@ define([
|
|||||||
'./URLIndicatorPlugin/URLIndicatorPlugin',
|
'./URLIndicatorPlugin/URLIndicatorPlugin',
|
||||||
'./telemetryMean/plugin',
|
'./telemetryMean/plugin',
|
||||||
'./plot/plugin',
|
'./plot/plugin',
|
||||||
|
'./plotlyPlot/plugin',
|
||||||
'./telemetryTable/plugin',
|
'./telemetryTable/plugin',
|
||||||
'./staticRootPlugin/plugin',
|
'./staticRootPlugin/plugin',
|
||||||
'./notebook/plugin',
|
'./notebook/plugin',
|
||||||
@ -66,6 +67,7 @@ define([
|
|||||||
URLIndicatorPlugin,
|
URLIndicatorPlugin,
|
||||||
TelemetryMean,
|
TelemetryMean,
|
||||||
PlotPlugin,
|
PlotPlugin,
|
||||||
|
PlotlyPlotPlugin,
|
||||||
TelemetryTablePlugin,
|
TelemetryTablePlugin,
|
||||||
StaticRootPlugin,
|
StaticRootPlugin,
|
||||||
Notebook,
|
Notebook,
|
||||||
@ -88,7 +90,8 @@ define([
|
|||||||
var bundleMap = {
|
var bundleMap = {
|
||||||
LocalStorage: 'platform/persistence/local',
|
LocalStorage: 'platform/persistence/local',
|
||||||
MyItems: 'platform/features/my-items',
|
MyItems: 'platform/features/my-items',
|
||||||
CouchDB: 'platform/persistence/couch'
|
CouchDB: 'platform/persistence/couch',
|
||||||
|
Elasticsearch: 'platform/persistence/elastic'
|
||||||
};
|
};
|
||||||
|
|
||||||
var plugins = _.mapValues(bundleMap, function (bundleName, pluginName) {
|
var plugins = _.mapValues(bundleMap, function (bundleName, pluginName) {
|
||||||
@ -170,8 +173,8 @@ define([
|
|||||||
plugins.ExampleImagery = ExampleImagery;
|
plugins.ExampleImagery = ExampleImagery;
|
||||||
plugins.ImageryPlugin = ImageryPlugin;
|
plugins.ImageryPlugin = ImageryPlugin;
|
||||||
plugins.Plot = PlotPlugin;
|
plugins.Plot = PlotPlugin;
|
||||||
|
plugins.PlotlyPlot = PlotlyPlotPlugin.default;
|
||||||
plugins.TelemetryTable = TelemetryTablePlugin;
|
plugins.TelemetryTable = TelemetryTablePlugin;
|
||||||
|
|
||||||
plugins.SummaryWidget = SummaryWidget;
|
plugins.SummaryWidget = SummaryWidget;
|
||||||
plugins.TelemetryMean = TelemetryMean;
|
plugins.TelemetryMean = TelemetryMean;
|
||||||
plugins.URLIndicator = URLIndicatorPlugin;
|
plugins.URLIndicator = URLIndicatorPlugin;
|
||||||
|
@ -165,7 +165,16 @@
|
|||||||
/******************************* LEGACY */
|
/******************************* LEGACY */
|
||||||
.s-status-taking-snapshot,
|
.s-status-taking-snapshot,
|
||||||
.overlay.snapshot {
|
.overlay.snapshot {
|
||||||
// Handle overflow-y issues with tables and html2canvas
|
.c-table {
|
||||||
// Replaces .l-sticky-headers .l-tabular-body { overflow: auto; }
|
&__body-w {
|
||||||
.c-table__body-w { overflow: auto; }
|
overflow: auto; // Handle overflow-y issues with tables and html2canvas
|
||||||
|
}
|
||||||
|
|
||||||
|
&-control-bar {
|
||||||
|
display: none;
|
||||||
|
+ * {
|
||||||
|
margin-top: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
@import "~styles/controls";
|
@import "~styles/controls";
|
||||||
@import "~styles/forms";
|
@import "~styles/forms";
|
||||||
@import "~styles/table";
|
@import "~styles/table";
|
||||||
@import "~styles/layout";
|
|
||||||
@import "~styles/legacy";
|
@import "~styles/legacy";
|
||||||
@import "~styles/legacy-plots";
|
@import "~styles/legacy-plots";
|
||||||
@import "~styles/plotly";
|
@import "~styles/plotly";
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
@import "~styles/controls";
|
@import "~styles/controls";
|
||||||
@import "~styles/forms";
|
@import "~styles/forms";
|
||||||
@import "~styles/table";
|
@import "~styles/table";
|
||||||
@import "~styles/layout";
|
|
||||||
@import "~styles/legacy";
|
@import "~styles/legacy";
|
||||||
@import "~styles/legacy-plots";
|
@import "~styles/legacy-plots";
|
||||||
@import "~styles/plotly";
|
@import "~styles/plotly";
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
@import "~styles/controls";
|
@import "~styles/controls";
|
||||||
@import "~styles/forms";
|
@import "~styles/forms";
|
||||||
@import "~styles/table";
|
@import "~styles/table";
|
||||||
@import "~styles/layout";
|
|
||||||
@import "~styles/legacy";
|
@import "~styles/legacy";
|
||||||
@import "~styles/legacy-plots";
|
@import "~styles/legacy-plots";
|
||||||
@import "~styles/plotly";
|
@import "~styles/plotly";
|
||||||
|
@ -122,13 +122,8 @@ button {
|
|||||||
margin-left: $interiorMargin;
|
margin-left: $interiorMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
$c1: nth($mixedSettingBg, 1);
|
|
||||||
$c2: nth($mixedSettingBg, 2);
|
|
||||||
$mixedBgD: $mixedSettingBgSize $mixedSettingBgSize;
|
|
||||||
|
|
||||||
&--mixed {
|
&--mixed {
|
||||||
// E.g. click-icons in toolbar that apply to multiple selected items with different settings
|
@include mixedBg();
|
||||||
@include bgStripes2Color($c1, $c2, $bgSize: $mixedBgD);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&--swatched {
|
&--swatched {
|
||||||
@ -151,13 +146,6 @@ button {
|
|||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
&--mixed {
|
|
||||||
// Styling for swatched buttons when settings are mixed
|
|
||||||
> [class*='swatch'] {
|
|
||||||
@include bgStripes2Color($c1, $c2, $bgSize: $mixedBgD);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,18 +232,10 @@ button {
|
|||||||
|
|
||||||
/******************************************************** SECTION */
|
/******************************************************** SECTION */
|
||||||
section {
|
section {
|
||||||
flex: 0 0 auto;
|
flex: 0 1 auto;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
+ section {
|
+ section {
|
||||||
margin-top: $interiorMargin;
|
margin-top: $interiorMargin;
|
||||||
|
|
||||||
&.is-expanded {
|
|
||||||
margin-bottom: $interiorMargin * 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-expanded {
|
|
||||||
flex: 0 1 auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.c-section__header {
|
.c-section__header {
|
||||||
@ -585,6 +565,20 @@ select {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/******************************************************** CONTROL BARS */
|
||||||
|
.c-control-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
> * + * {
|
||||||
|
margin-left: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/******************************************************** PALETTES */
|
/******************************************************** PALETTES */
|
||||||
.c-palette {
|
.c-palette {
|
||||||
@ -829,6 +823,10 @@ select {
|
|||||||
box-shadow: rgba($colorBodyFg, 0.4) 0 0 3px;
|
box-shadow: rgba($colorBodyFg, 0.4) 0 0 3px;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
padding: $interiorMargin $interiorMarginLg;
|
padding: $interiorMargin $interiorMarginLg;
|
||||||
|
|
||||||
|
&--mixed {
|
||||||
|
@include mixedBg();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************** SLIDERS */
|
/******************************************************** SLIDERS */
|
||||||
|
@ -88,6 +88,12 @@ body.desktop {
|
|||||||
background: $scrollbarThumbColorMenuHov;
|
background: $scrollbarThumbColorMenuHov;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div, span {
|
||||||
|
// Firefox
|
||||||
|
scrollbar-color: $scrollbarThumbColor $scrollbarTrackColorBg;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************** HTML ENTITIES */
|
/******************************************************** HTML ENTITIES */
|
||||||
|
@ -1,87 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT, Copyright (c) 2014-2018, 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.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
/************************** BROWSE BAR */
|
|
||||||
.l-browse-bar {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
[class*="__"] {
|
|
||||||
// Removes extraneous horizontal white space
|
|
||||||
display: inline-flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__start {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex: 1 1 auto;
|
|
||||||
margin-right: $interiorMargin;
|
|
||||||
min-width: 0; // Forces interior to compress when pushed on
|
|
||||||
}
|
|
||||||
|
|
||||||
&__end {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex: 0 0 auto;
|
|
||||||
|
|
||||||
[class*="__"] + [class*="__"] {
|
|
||||||
margin-left: $interiorMarginSm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__nav-to-parent-button,
|
|
||||||
&__disclosure-button {
|
|
||||||
flex: 0 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__nav-to-parent-button {
|
|
||||||
// This is an icon-button
|
|
||||||
$p: $interiorMargin;
|
|
||||||
margin-right: $interiorMargin;
|
|
||||||
padding-left: $p;
|
|
||||||
padding-right: $p;
|
|
||||||
|
|
||||||
.is-editing & {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__object-name--w {
|
|
||||||
flex: 0 1 auto;
|
|
||||||
@include headerFont(1.4em);
|
|
||||||
min-width: 0;
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
// Icon
|
|
||||||
margin-right: $interiorMargin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__object-name {
|
|
||||||
flex: 0 1 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__object-details {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
}
|
|
@ -40,17 +40,51 @@ mct-plot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.c-plot,
|
||||||
|
.gl-plot {
|
||||||
|
.s-status-taking-snapshot & {
|
||||||
|
.c-control-bar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.gl-plot-y-label__select {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.c-plot {
|
.c-plot {
|
||||||
$p: $mainViewPad;
|
//$p: $mainViewPad;
|
||||||
position: absolute;
|
@include abs($mainViewPad);
|
||||||
top: $p; right: $p; bottom: $p; left: $p;
|
//position: absolute;
|
||||||
|
//top: $p; right: $p; bottom: $p; left: $p;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
> * + * {
|
||||||
|
margin-top: $interiorMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.l-control-bar {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.l-view-section {
|
||||||
|
display: flex;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
&--stacked {
|
&--stacked {
|
||||||
.l-view-section {
|
.child-frame {
|
||||||
// Make this a flex container
|
.has-control-bar {
|
||||||
display: flex;
|
.c-control-bar {
|
||||||
flex-flow: column nowrap;
|
// Hides buttons per plot element in a stacked plot
|
||||||
.gl-plot.child-frame {
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mct-plot {
|
mct-plot {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
@ -58,10 +92,6 @@ mct-plot {
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
&:not(:first-child) {
|
|
||||||
margin-top: $interiorMargin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.s-status-timeconductor-unsynced .holder-plot {
|
.s-status-timeconductor-unsynced .holder-plot {
|
||||||
@ -70,7 +100,6 @@ mct-plot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -186,7 +215,7 @@ mct-plot {
|
|||||||
left: 0; top: 0; right: auto; bottom: 0;
|
left: 0; top: 0; right: auto; bottom: 0;
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
text-orientation: mixed;
|
text-orientation: mixed;
|
||||||
overflow: hidden;
|
//overflow: hidden;
|
||||||
writing-mode: vertical-lr;
|
writing-mode: vertical-lr;
|
||||||
&:before {
|
&:before {
|
||||||
// Icon denoting configurability
|
// Icon denoting configurability
|
||||||
@ -339,11 +368,11 @@ mct-plot {
|
|||||||
z-index: -10;
|
z-index: -10;
|
||||||
|
|
||||||
.l-view-section {
|
.l-view-section {
|
||||||
$m: $interiorMargin;
|
//$m: $interiorMargin;
|
||||||
top: $m !important;
|
//top: $m !important;
|
||||||
right: $m;
|
//right: $m;
|
||||||
bottom: $m;
|
//bottom: $m;
|
||||||
left: $m;
|
//left: $m;
|
||||||
|
|
||||||
.s-status-timeconductor-unsynced .holder-plot {
|
.s-status-timeconductor-unsynced .holder-plot {
|
||||||
.t-object-alert.t-alert-unsynced {
|
.t-object-alert.t-alert-unsynced {
|
||||||
|
@ -19,59 +19,13 @@
|
|||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
/******************************************************************* VIEWS */
|
|
||||||
// From _views.scss
|
|
||||||
// Legacy overlay and stacked plots depend on this for now
|
|
||||||
// Styles for sub-dividing views generically
|
|
||||||
.l-control-bar {
|
|
||||||
// Element that can be placed above l-view-section, holds controls, buttons, etc.
|
|
||||||
height: $controlBarH;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c-control-bar {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
> * + * {
|
|
||||||
margin-left: $interiorMarginSm;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__label {
|
|
||||||
display: inline-block;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.l-view-section {
|
|
||||||
@include abs();
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.has-control-bar {
|
|
||||||
.l-view-section {
|
|
||||||
top: $controlBarH + $interiorMargin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.child-frame {
|
|
||||||
.has-control-bar {
|
|
||||||
.l-control-bar,
|
|
||||||
.c-control-bar {
|
|
||||||
// Hides buttons per plot element in a stacked plot
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.l-view-section {
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*********************************************************************** CLOCKS AND TIMERS */
|
/*********************************************************************** CLOCKS AND TIMERS */
|
||||||
.c-clock,
|
.c-clock,
|
||||||
.c-timer {
|
.c-timer {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 1.25em;
|
font-size: 1.25em;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
@ -82,6 +36,12 @@
|
|||||||
&__value {
|
&__value {
|
||||||
color: $colorBodyFgEm;
|
color: $colorBodyFgEm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.c-frame & {
|
||||||
|
// When in a Display or Flexible Layout
|
||||||
|
@include abs();
|
||||||
|
padding: $interiorMargin;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.c-clock {
|
.c-clock {
|
||||||
|
@ -50,6 +50,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/************************** EFFECTS */
|
/************************** EFFECTS */
|
||||||
|
@mixin mixedBg() {
|
||||||
|
$c1: nth($mixedSettingBg, 1);
|
||||||
|
$c2: nth($mixedSettingBg, 2);
|
||||||
|
$mixedBgD: $mixedSettingBgSize $mixedSettingBgSize;
|
||||||
|
@include bgStripes2Color($c1, $c2, $bgSize: $mixedBgD);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@mixin pulse($animName: pulse, $dur: 500ms, $iteration: infinite, $opacity0: 0.5, $opacity100: 1) {
|
@mixin pulse($animName: pulse, $dur: 500ms, $iteration: infinite, $opacity0: 0.5, $opacity100: 1) {
|
||||||
@keyframes #{$animName} {
|
@keyframes #{$animName} {
|
||||||
0% { opacity: $opacity0; }
|
0% { opacity: $opacity0; }
|
||||||
|
@ -54,7 +54,6 @@
|
|||||||
|
|
||||||
@mixin elementStatusColors($c) {
|
@mixin elementStatusColors($c) {
|
||||||
// Sets bg and icon colors for elements
|
// Sets bg and icon colors for elements
|
||||||
background: rgba($c, 0.5) !important;
|
|
||||||
&:before {
|
&:before {
|
||||||
color: $c !important;
|
color: $c !important;
|
||||||
}
|
}
|
||||||
|
@ -388,7 +388,21 @@
|
|||||||
.s-status-taking-snapshot,
|
.s-status-taking-snapshot,
|
||||||
.overlay.snapshot {
|
.overlay.snapshot {
|
||||||
// Handle overflow-y issues with tables and html2canvas
|
// Handle overflow-y issues with tables and html2canvas
|
||||||
|
background: $colorBodyBg; // Prevent html2canvas from using white background
|
||||||
|
color: $colorBodyFg;
|
||||||
|
padding: $interiorMarginSm !important; // Prevents items from going right to the edge of the image
|
||||||
|
|
||||||
.l-sticky-headers .l-tabular-body { overflow: auto; }
|
.l-sticky-headers .l-tabular-body { overflow: auto; }
|
||||||
|
.l-browse-bar {
|
||||||
|
display: none; // Suppress browse-bar when snapshotting from view-large overlay
|
||||||
|
+ * {
|
||||||
|
margin-top: 0 !important; // Remove margin from any following elements
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-shadow: none !important; // Prevent html2canvas problems with box-shadow
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.c-notebook-snapshot {
|
.c-notebook-snapshot {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
@import "../api/overlays/components/dialog-component.scss";
|
@import "../api/overlays/components/dialog-component.scss";
|
||||||
@import "../api/overlays/components/overlay-component.scss";
|
@import "../api/overlays/components/overlay-component.scss";
|
||||||
@import "../plugins/condition/components/condition.scss";
|
@import "../plugins/condition/components/conditionals.scss";
|
||||||
@import "../plugins/condition/components/condition-set.scss";
|
|
||||||
@import "../plugins/conditionWidget/components/condition-widget.scss";
|
@import "../plugins/conditionWidget/components/condition-widget.scss";
|
||||||
@import "../plugins/condition/components/inspector/conditional-styles.scss";
|
@import "../plugins/condition/components/inspector/conditional-styles.scss";
|
||||||
@import "../plugins/displayLayout/components/box-view.scss";
|
@import "../plugins/displayLayout/components/box-view.scss";
|
||||||
@ -19,6 +18,7 @@
|
|||||||
@import "../plugins/folderView/components/list-item.scss";
|
@import "../plugins/folderView/components/list-item.scss";
|
||||||
@import "../plugins/folderView/components/list-view.scss";
|
@import "../plugins/folderView/components/list-view.scss";
|
||||||
@import "../plugins/imagery/components/imagery-view-layout.scss";
|
@import "../plugins/imagery/components/imagery-view-layout.scss";
|
||||||
|
@import "../plugins/plotlyPlot/components/plotly.scss";
|
||||||
@import "../plugins/telemetryTable/components/table-row.scss";
|
@import "../plugins/telemetryTable/components/table-row.scss";
|
||||||
@import "../plugins/telemetryTable/components/telemetry-filter-indicator.scss";
|
@import "../plugins/telemetryTable/components/telemetry-filter-indicator.scss";
|
||||||
@import "../plugins/tabs/components/tabs.scss";
|
@import "../plugins/tabs/components/tabs.scss";
|
||||||
|
@ -59,6 +59,8 @@
|
|||||||
<script>
|
<script>
|
||||||
import ObjectView from './ObjectView.vue'
|
import ObjectView from './ObjectView.vue'
|
||||||
import ContextMenuDropDown from './contextMenuDropDown.vue';
|
import ContextMenuDropDown from './contextMenuDropDown.vue';
|
||||||
|
import PreviewHeader from '@/ui/preview/preview-header.vue';
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
const SIMPLE_CONTENT_TYPES = [
|
const SIMPLE_CONTENT_TYPES = [
|
||||||
'clock',
|
'clock',
|
||||||
@ -116,13 +118,41 @@ export default {
|
|||||||
childElement = parentElement.children[0];
|
childElement = parentElement.children[0];
|
||||||
|
|
||||||
this.openmct.overlays.overlay({
|
this.openmct.overlays.overlay({
|
||||||
element: childElement,
|
element: this.getOverlayElement(childElement),
|
||||||
size: 'large',
|
size: 'large',
|
||||||
onDestroy() {
|
onDestroy() {
|
||||||
parentElement.append(childElement);
|
parentElement.append(childElement);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
getOverlayElement(childElement) {
|
||||||
|
const fragment = new DocumentFragment();
|
||||||
|
const header = this.getPreviewHeader();
|
||||||
|
fragment.append(header);
|
||||||
|
fragment.append(childElement);
|
||||||
|
|
||||||
|
return fragment;
|
||||||
|
},
|
||||||
|
getPreviewHeader() {
|
||||||
|
const domainObject = this.objectPath[0];
|
||||||
|
const preview = new Vue({
|
||||||
|
components: {
|
||||||
|
PreviewHeader
|
||||||
|
},
|
||||||
|
provide: {
|
||||||
|
openmct: this.openmct,
|
||||||
|
objectPath: this.objectPath
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
domainObject
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: '<PreviewHeader :domainObject="domainObject" :hideViewSwitcher="true" :showNotebookMenuSwitcher="true"></PreviewHeader>'
|
||||||
|
});
|
||||||
|
|
||||||
|
return preview.$mount().$el;
|
||||||
|
},
|
||||||
getSelectionContext() {
|
getSelectionContext() {
|
||||||
return this.$refs.objectView.getSelectionContext();
|
return this.$refs.objectView.getSelectionContext();
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@ export default {
|
|||||||
keys.forEach(key => {
|
keys.forEach(key => {
|
||||||
let firstChild = this.$el.querySelector(':first-child');
|
let firstChild = this.$el.querySelector(':first-child');
|
||||||
if (firstChild) {
|
if (firstChild) {
|
||||||
if ((typeof styleObj[key] === 'string') && (styleObj[key].indexOf('transparent') > -1)) {
|
if ((typeof styleObj[key] === 'string') && (styleObj[key].indexOf('__no_value') > -1)) {
|
||||||
if (firstChild.style[key]) {
|
if (firstChild.style[key]) {
|
||||||
firstChild.style[key] = '';
|
firstChild.style[key] = '';
|
||||||
}
|
}
|
||||||
@ -201,7 +201,7 @@ export default {
|
|||||||
},
|
},
|
||||||
initObjectStyles() {
|
initObjectStyles() {
|
||||||
if (!this.styleRuleManager) {
|
if (!this.styleRuleManager) {
|
||||||
this.styleRuleManager = new StyleRuleManager((this.currentObject.configuration && this.currentObject.configuration.objectStyles), this.openmct, this.updateStyle.bind(this));
|
this.styleRuleManager = new StyleRuleManager((this.currentObject.configuration && this.currentObject.configuration.objectStyles), this.openmct, this.updateStyle.bind(this), true);
|
||||||
} else {
|
} else {
|
||||||
this.styleRuleManager.updateObjectStyleConfig(this.currentObject.configuration && this.currentObject.configuration.objectStyles);
|
this.styleRuleManager.updateObjectStyleConfig(this.currentObject.configuration && this.currentObject.configuration.objectStyles);
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:not(.c-so-view--no-frame) {
|
&:not(.c-so-view--no-frame) {
|
||||||
background: $colorBodyBg;
|
|
||||||
border: $browseFrameBorder;
|
border: $browseFrameBorder;
|
||||||
padding: $interiorMargin;
|
padding: $interiorMargin;
|
||||||
|
|
||||||
|
.is-editing & {
|
||||||
|
background: rgba($colorBodyBg, 0.8);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&--no-frame {
|
&--no-frame {
|
||||||
|
@ -81,7 +81,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.excludeObjectTypes = ['folder', 'webPage', 'conditionSet'];
|
this.excludeObjectTypes = ['folder', 'webPage', 'conditionSet', 'summary-widget', 'hyperlink'];
|
||||||
this.openmct.selection.on('change', this.updateInspectorViews);
|
this.openmct.selection.on('change', this.updateInspectorViews);
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
@ -108,7 +108,7 @@ export default {
|
|||||||
let object = selection[0][0].context.item;
|
let object = selection[0][0].context.item;
|
||||||
if (object) {
|
if (object) {
|
||||||
let type = this.openmct.types.get(object.type);
|
let type = this.openmct.types.get(object.type);
|
||||||
this.showStyles = (this.excludeObjectTypes.indexOf(object.type) < 0) && type.definition.creatable;
|
this.showStyles = this.isLayoutObject(selection[0], object.type) || this.isCreatableObject(object, type);
|
||||||
}
|
}
|
||||||
if (!this.currentTabbedView.key || (!this.showStyles && this.currentTabbedView.key === this.tabbedViews[1].key))
|
if (!this.currentTabbedView.key || (!this.showStyles && this.currentTabbedView.key === this.tabbedViews[1].key))
|
||||||
{
|
{
|
||||||
@ -116,6 +116,14 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
isLayoutObject(selection, objectType) {
|
||||||
|
//we allow conditionSets to be styled if they're part of a layout
|
||||||
|
return selection.length > 1 &&
|
||||||
|
((objectType === 'conditionSet') || (this.excludeObjectTypes.indexOf(objectType) < 0));
|
||||||
|
},
|
||||||
|
isCreatableObject(object, type) {
|
||||||
|
return (this.excludeObjectTypes.indexOf(object.type) < 0) && type.definition.creatable;
|
||||||
|
},
|
||||||
updateCurrentTab(view) {
|
updateCurrentTab(view) {
|
||||||
this.currentTabbedView = view;
|
this.currentTabbedView = view;
|
||||||
},
|
},
|
||||||
|
@ -26,8 +26,8 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ConditionalStylesView from '../../plugins/condition/components/inspector/ConditionalStylesView.vue';
|
import ConditionalStylesView from '../../plugins/condition/components/inspector/ConditionalStylesView.vue';
|
||||||
|
import MultiSelectStylesView from '../../plugins/condition/components/inspector/MultiSelectStylesView.vue';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { getStyleProp } from "../../plugins/condition/utils/styleUtils";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
@ -44,35 +44,9 @@ export default {
|
|||||||
this.openmct.selection.off('change', this.updateSelection);
|
this.openmct.selection.off('change', this.updateSelection);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getStyleProperties(item) {
|
|
||||||
let styleProps = {};
|
|
||||||
Object.keys(item).forEach((key) => {
|
|
||||||
Object.assign(styleProps, getStyleProp(key, item[key]));
|
|
||||||
});
|
|
||||||
return styleProps;
|
|
||||||
},
|
|
||||||
updateSelection(selection) {
|
updateSelection(selection) {
|
||||||
if (selection.length > 0 && selection[0].length > 0) {
|
if (selection.length > 0 && selection[0].length > 0) {
|
||||||
let isChildItem = false;
|
let template = selection.length > 1 ? '<multi-select-styles-view></multi-select-styles-view>' : '<conditional-styles-view></conditional-styles-view>';
|
||||||
let domainObject = selection[0][0].context.item;
|
|
||||||
let layoutItem = {};
|
|
||||||
let styleProps = this.getStyleProperties({
|
|
||||||
fill: 'transparent',
|
|
||||||
stroke: 'transparent',
|
|
||||||
color: 'transparent'
|
|
||||||
});
|
|
||||||
if (selection[0].length > 1) {
|
|
||||||
isChildItem = true;
|
|
||||||
//If there are more than 1 items in the selection[0] list, the first one could either be a sub domain object OR a layout drawing control.
|
|
||||||
//The second item in the selection[0] list is the container object (usually a layout)
|
|
||||||
if (!domainObject) {
|
|
||||||
styleProps = {};
|
|
||||||
layoutItem = selection[0][0].context.layoutItem;
|
|
||||||
styleProps = this.getStyleProperties(layoutItem);
|
|
||||||
domainObject = selection[0][1].context.item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.component) {
|
if (this.component) {
|
||||||
this.component.$destroy();
|
this.component.$destroy();
|
||||||
this.component = undefined;
|
this.component = undefined;
|
||||||
@ -83,20 +57,14 @@ export default {
|
|||||||
this.component = new Vue({
|
this.component = new Vue({
|
||||||
provide: {
|
provide: {
|
||||||
openmct: this.openmct,
|
openmct: this.openmct,
|
||||||
domainObject: domainObject
|
selection: selection
|
||||||
},
|
},
|
||||||
el: viewContainer,
|
el: viewContainer,
|
||||||
components: {
|
components: {
|
||||||
ConditionalStylesView
|
ConditionalStylesView,
|
||||||
|
MultiSelectStylesView
|
||||||
},
|
},
|
||||||
data() {
|
template: template
|
||||||
return {
|
|
||||||
layoutItem,
|
|
||||||
styleProps,
|
|
||||||
isChildItem
|
|
||||||
}
|
|
||||||
},
|
|
||||||
template: '<conditional-styles-view :can-hide="isChildItem" :item-id="layoutItem.id" :initial-styles="styleProps"></conditional-styles-view>'
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
<div class="l-browse-bar__end">
|
<div class="l-browse-bar__end">
|
||||||
<view-switcher
|
<view-switcher
|
||||||
|
v-if="!isEditing"
|
||||||
:current-view="currentView"
|
:current-view="currentView"
|
||||||
:views="views"
|
:views="views"
|
||||||
@setView="setView"
|
@setView="setView"
|
||||||
@ -35,6 +36,7 @@
|
|||||||
<!-- Action buttons -->
|
<!-- Action buttons -->
|
||||||
<NotebookMenuSwitcher v-if="notebookEnabled"
|
<NotebookMenuSwitcher v-if="notebookEnabled"
|
||||||
:domain-object="domainObject"
|
:domain-object="domainObject"
|
||||||
|
:object-path="openmct.router.path"
|
||||||
class="c-notebook-snapshot-menubutton"
|
class="c-notebook-snapshot-menubutton"
|
||||||
/>
|
/>
|
||||||
<div class="l-browse-bar__actions">
|
<div class="l-browse-bar__actions">
|
||||||
@ -197,8 +199,6 @@ export default {
|
|||||||
updateName(event) {
|
updateName(event) {
|
||||||
if (event.target.innerText !== this.domainObject.name && event.target.innerText.match(/\S/)) {
|
if (event.target.innerText !== this.domainObject.name && event.target.innerText.match(/\S/)) {
|
||||||
this.openmct.objects.mutate(this.domainObject, 'name', event.target.innerText);
|
this.openmct.objects.mutate(this.domainObject, 'name', event.target.innerText);
|
||||||
} else {
|
|
||||||
event.target.innerText = this.domainObject.name;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateNameOnEnterKeyPress(event) {
|
updateNameOnEnterKeyPress(event) {
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<button
|
<button
|
||||||
class="c-button--menu"
|
class="c-button--menu"
|
||||||
:class="currentView.cssClass"
|
:class="currentView.cssClass"
|
||||||
title="Switch view type"
|
title="Change the current view"
|
||||||
@click.stop="toggleViewMenu"
|
@click.stop="toggleViewMenu"
|
||||||
>
|
>
|
||||||
<span class="c-button__label">
|
<span class="c-button__label">
|
||||||
|
@ -1,3 +1,24 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, 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.
|
||||||
|
*****************************************************************************/
|
||||||
/******************************* SHELL */
|
/******************************* SHELL */
|
||||||
.l-shell {
|
.l-shell {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -126,6 +147,9 @@
|
|||||||
|
|
||||||
body.mobile & .l-shell__main-view-browse-bar {
|
body.mobile & .l-shell__main-view-browse-bar {
|
||||||
margin-left: $mobileMenuIconD; // Make room for the hamburger!
|
margin-left: $mobileMenuIconD; // Make room for the hamburger!
|
||||||
|
.c-button[class*='__actions__edit'] {
|
||||||
|
display: none; // Hide the main view edit button when in mobile context
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__head {
|
&__head {
|
||||||
@ -269,6 +293,79 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/************************** BROWSE BAR */
|
||||||
|
.l-browse-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
[class*="__"] {
|
||||||
|
// Removes extraneous horizontal white space
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__start,
|
||||||
|
&__end,
|
||||||
|
&__actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__actions,
|
||||||
|
&__end {
|
||||||
|
> * + * {
|
||||||
|
margin-left: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__start {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
margin-right: $interiorMargin;
|
||||||
|
min-width: 0; // Forces interior to compress when pushed on
|
||||||
|
}
|
||||||
|
|
||||||
|
&__end {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__nav-to-parent-button,
|
||||||
|
&__disclosure-button {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__nav-to-parent-button {
|
||||||
|
// This is an icon-button
|
||||||
|
$p: $interiorMargin;
|
||||||
|
margin-right: $interiorMargin;
|
||||||
|
padding-left: $p;
|
||||||
|
padding-right: $p;
|
||||||
|
|
||||||
|
.is-editing & {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__object-name--w,
|
||||||
|
&__object-name {
|
||||||
|
flex: 0 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__object-name--w {
|
||||||
|
@include headerFont(1.4em);
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
// Icon
|
||||||
|
margin-right: $interiorMargin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__object-details {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************** DRAWER */
|
||||||
.c-drawer {
|
.c-drawer {
|
||||||
/* New sliding overlay or push element to contain things
|
/* New sliding overlay or push element to contain things
|
||||||
* Designed for mobile and compact desktop scenarios
|
* Designed for mobile and compact desktop scenarios
|
||||||
@ -332,4 +429,3 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,28 +21,12 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
<template>
|
<template>
|
||||||
<div class="l-preview-window">
|
<div class="l-preview-window">
|
||||||
<div class="l-browse-bar">
|
<PreviewHeader
|
||||||
<div class="l-browse-bar__start">
|
|
||||||
<div
|
|
||||||
class="l-browse-bar__object-name--w"
|
|
||||||
:class="type.cssClass"
|
|
||||||
>
|
|
||||||
<span class="l-browse-bar__object-name">
|
|
||||||
{{ domainObject.name }}
|
|
||||||
</span>
|
|
||||||
<context-menu-drop-down :object-path="objectPath" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="l-browse-bar__end">
|
|
||||||
<div class="l-browse-bar__actions">
|
|
||||||
<view-switcher
|
|
||||||
:views="views"
|
|
||||||
:current-view="currentView"
|
:current-view="currentView"
|
||||||
|
:domain-object="domainObject"
|
||||||
|
:views="views"
|
||||||
@setView="setView"
|
@setView="setView"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="l-preview-window__object-view">
|
<div class="l-preview-window__object-view">
|
||||||
<div ref="objectView"></div>
|
<div ref="objectView"></div>
|
||||||
</div>
|
</div>
|
||||||
@ -50,13 +34,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ContextMenuDropDown from '../../ui/components/contextMenuDropDown.vue';
|
import PreviewHeader from './preview-header.vue';
|
||||||
import ViewSwitcher from '../../ui/layout/ViewSwitcher.vue';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
ContextMenuDropDown,
|
PreviewHeader
|
||||||
ViewSwitcher
|
|
||||||
},
|
},
|
||||||
inject: [
|
inject: [
|
||||||
'openmct',
|
'openmct',
|
||||||
@ -64,12 +46,9 @@ export default {
|
|||||||
],
|
],
|
||||||
data() {
|
data() {
|
||||||
let domainObject = this.objectPath[0];
|
let domainObject = this.objectPath[0];
|
||||||
let type = this.openmct.types.get(domainObject.type);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
domainObject: domainObject,
|
domainObject: domainObject,
|
||||||
type: type,
|
|
||||||
notebookEnabled: false,
|
|
||||||
viewKey: undefined
|
viewKey: undefined
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -97,6 +76,7 @@ export default {
|
|||||||
this.view.destroy();
|
this.view.destroy();
|
||||||
this.$refs.objectView.innerHTML = '';
|
this.$refs.objectView.innerHTML = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
delete this.view;
|
delete this.view;
|
||||||
delete this.viewContainer;
|
delete this.viewContainer;
|
||||||
},
|
},
|
||||||
|
@ -36,7 +36,12 @@ export default class PreviewAction {
|
|||||||
* Dependencies
|
* Dependencies
|
||||||
*/
|
*/
|
||||||
this._openmct = openmct;
|
this._openmct = openmct;
|
||||||
|
|
||||||
|
if (PreviewAction.isVisible === undefined) {
|
||||||
|
PreviewAction.isVisible = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
invoke(objectPath) {
|
invoke(objectPath) {
|
||||||
let preview = new Vue({
|
let preview = new Vue({
|
||||||
components: {
|
components: {
|
||||||
@ -59,16 +64,27 @@ export default class PreviewAction {
|
|||||||
callback: () => overlay.dismiss()
|
callback: () => overlay.dismiss()
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
onDestroy: () => preview.$destroy()
|
onDestroy: () => {
|
||||||
|
PreviewAction.isVisible = false;
|
||||||
|
preview.$destroy()
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
PreviewAction.isVisible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
appliesTo(objectPath) {
|
appliesTo(objectPath) {
|
||||||
return !this._isNavigatedObject(objectPath)
|
return !PreviewAction.isVisible && !this._isNavigatedObject(objectPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
_isNavigatedObject(objectPath) {
|
_isNavigatedObject(objectPath) {
|
||||||
let targetObject = objectPath[0];
|
let targetObject = objectPath[0];
|
||||||
let navigatedObject = this._openmct.router.path[0];
|
let navigatedObject = this._openmct.router.path[0];
|
||||||
return targetObject.identifier.namespace === navigatedObject.identifier.namespace &&
|
return targetObject.identifier.namespace === navigatedObject.identifier.namespace &&
|
||||||
targetObject.identifier.key === navigatedObject.identifier.key;
|
targetObject.identifier.key === navigatedObject.identifier.key;
|
||||||
}
|
}
|
||||||
|
_preventPreview(objectPath) {
|
||||||
|
const noPreviewTypes = ['folder'];
|
||||||
|
return noPreviewTypes.includes(objectPath[0].type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user