mirror of
https://github.com/nasa/openmct.git
synced 2025-06-29 04:03:03 +00:00
Compare commits
48 Commits
plotly-tes
...
couchdb-pe
Author | SHA1 | Date | |
---|---|---|---|
8258f21f7b | |||
44bfcf33ef | |||
669415d362 | |||
8601ec441f | |||
9a57a20404 | |||
1a3bff9813 | |||
baa5f21640 | |||
af9dceee3c | |||
41138a1731 | |||
a54a2f8f84 | |||
5bbe710552 | |||
f2d34d7c33 | |||
8fa1770885 | |||
7221dc1ac6 | |||
25bb9939d6 | |||
e7e12504f2 | |||
63bf856d89 | |||
e3dcd51f8d | |||
cb63f4eca1 | |||
3f60c3c0f1 | |||
16bb22e834 | |||
b1467548da | |||
baa7c0bc58 | |||
73b81e38e7 | |||
8b088b7a2c | |||
894da25461 | |||
87d63806b9 | |||
f0e7f8cfc0 | |||
db597e1e93 | |||
98db273f5d | |||
8a6f944655 | |||
bacad24811 | |||
8cc58946cf | |||
3338bc1000 | |||
80c20b3d05 | |||
0d9558b891 | |||
c29c3c386f | |||
9ceb3c5b1e | |||
bee3a9eedf | |||
e515d19acd | |||
dd13efe065 | |||
b5dfbe268c | |||
a9b9107cc3 | |||
cfda4e4214 | |||
0a657de4b2 | |||
8153edb9cb | |||
dadb6120c2 | |||
d9a94db59d |
3
.gitignore
vendored
3
.gitignore
vendored
@ -37,4 +37,7 @@ protractor/logs
|
|||||||
# npm-debug log
|
# npm-debug log
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
|
|
||||||
|
# karma reports
|
||||||
|
report.*.json
|
||||||
|
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
@ -43,7 +43,9 @@
|
|||||||
openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
|
openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
|
||||||
);
|
);
|
||||||
|
|
||||||
openmct.install(openmct.plugins.LocalStorage());
|
//openmct.install(openmct.plugins.LocalStorage());
|
||||||
|
//Change 'vipersim' to whatever your local db is named
|
||||||
|
openmct.install(openmct.plugins.CouchDB('http://localhost:5984/vipersim'));
|
||||||
openmct.install(openmct.plugins.Espresso());
|
openmct.install(openmct.plugins.Espresso());
|
||||||
openmct.install(openmct.plugins.MyItems());
|
openmct.install(openmct.plugins.MyItems());
|
||||||
openmct.install(openmct.plugins.Generator());
|
openmct.install(openmct.plugins.Generator());
|
||||||
@ -113,7 +115,10 @@
|
|||||||
openmct.install(openmct.plugins.LADTable());
|
openmct.install(openmct.plugins.LADTable());
|
||||||
openmct.install(openmct.plugins.Filters(['table', 'telemetry.plot.overlay']));
|
openmct.install(openmct.plugins.Filters(['table', 'telemetry.plot.overlay']));
|
||||||
openmct.install(openmct.plugins.ObjectMigration());
|
openmct.install(openmct.plugins.ObjectMigration());
|
||||||
openmct.install(openmct.plugins.ClearData(['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked']));
|
openmct.install(openmct.plugins.ClearData(
|
||||||
|
['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked'],
|
||||||
|
{indicator: true}
|
||||||
|
));
|
||||||
openmct.start();
|
openmct.start();
|
||||||
</script>
|
</script>
|
||||||
</html>
|
</html>
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
/*global module,process*/
|
/*global module,process*/
|
||||||
|
|
||||||
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' : 'FirefoxHeadless'];
|
||||||
const coverageEnabled = process.env.COVERAGE === 'true';
|
const coverageEnabled = process.env.COVERAGE === 'true';
|
||||||
const reporters = ['progress', 'html'];
|
const reporters = ['progress', 'html'];
|
||||||
|
|
||||||
@ -95,6 +95,7 @@ module.exports = (config) => {
|
|||||||
stats: 'errors-only',
|
stats: 'errors-only',
|
||||||
logLevel: 'warn'
|
logLevel: 'warn'
|
||||||
},
|
},
|
||||||
singleRun: true
|
singleRun: true,
|
||||||
|
browserNoActivityTimeout: 90000
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
"jsdoc": "^3.3.2",
|
"jsdoc": "^3.3.2",
|
||||||
"karma": "^2.0.3",
|
"karma": "^2.0.3",
|
||||||
"karma-chrome-launcher": "^2.2.0",
|
"karma-chrome-launcher": "^2.2.0",
|
||||||
|
"karma-firefox-launcher": "^1.3.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-coverage-istanbul-reporter": "^2.1.1",
|
||||||
@ -59,7 +60,7 @@
|
|||||||
"moment-timezone": "0.5.28",
|
"moment-timezone": "0.5.28",
|
||||||
"node-bourbon": "^4.2.3",
|
"node-bourbon": "^4.2.3",
|
||||||
"node-sass": "^4.9.2",
|
"node-sass": "^4.9.2",
|
||||||
"painterro": "^0.2.65",
|
"painterro": "^1.0.35",
|
||||||
"printj": "^1.2.1",
|
"printj": "^1.2.1",
|
||||||
"raw-loader": "^0.5.1",
|
"raw-loader": "^0.5.1",
|
||||||
"request": "^2.69.0",
|
"request": "^2.69.0",
|
||||||
|
@ -36,8 +36,6 @@ define(
|
|||||||
}
|
}
|
||||||
|
|
||||||
EditPersistableObjectsPolicy.prototype.allow = function (action, context) {
|
EditPersistableObjectsPolicy.prototype.allow = function (action, context) {
|
||||||
var identifier;
|
|
||||||
var provider;
|
|
||||||
var domainObject = context.domainObject;
|
var domainObject = context.domainObject;
|
||||||
var key = action.getMetadata().key;
|
var key = action.getMetadata().key;
|
||||||
var category = (context || {}).category;
|
var category = (context || {}).category;
|
||||||
@ -46,9 +44,8 @@ define(
|
|||||||
// is also invoked during the create process which should be allowed,
|
// is also invoked during the create process which should be allowed,
|
||||||
// because it may be saved elsewhere
|
// because it may be saved elsewhere
|
||||||
if ((key === 'edit' && category === 'view-control') || key === 'properties') {
|
if ((key === 'edit' && category === 'view-control') || key === 'properties') {
|
||||||
identifier = objectUtils.parseKeyString(domainObject.getId());
|
let newStyleObject = objectUtils.toNewFormat(domainObject, domainObject.getId());
|
||||||
provider = this.openmct.objects.getProvider(identifier);
|
return this.openmct.objects.isPersistable(newStyleObject);
|
||||||
return provider.save !== undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -43,7 +43,7 @@ define(
|
|||||||
);
|
);
|
||||||
|
|
||||||
mockObjectAPI = jasmine.createSpyObj('objectAPI', [
|
mockObjectAPI = jasmine.createSpyObj('objectAPI', [
|
||||||
'getProvider'
|
'isPersistable'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
mockAPI = {
|
mockAPI = {
|
||||||
@ -69,34 +69,31 @@ define(
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("Applies to edit action", function () {
|
it("Applies to edit action", function () {
|
||||||
mockObjectAPI.getProvider.and.returnValue({});
|
expect(mockObjectAPI.isPersistable).not.toHaveBeenCalled();
|
||||||
expect(mockObjectAPI.getProvider).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
policy.allow(mockEditAction, testContext);
|
policy.allow(mockEditAction, testContext);
|
||||||
expect(mockObjectAPI.getProvider).toHaveBeenCalled();
|
expect(mockObjectAPI.isPersistable).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Applies to properties action", function () {
|
it("Applies to properties action", function () {
|
||||||
mockObjectAPI.getProvider.and.returnValue({});
|
expect(mockObjectAPI.isPersistable).not.toHaveBeenCalled();
|
||||||
expect(mockObjectAPI.getProvider).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
policy.allow(mockPropertiesAction, testContext);
|
policy.allow(mockPropertiesAction, testContext);
|
||||||
expect(mockObjectAPI.getProvider).toHaveBeenCalled();
|
expect(mockObjectAPI.isPersistable).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not apply to other actions", function () {
|
it("does not apply to other actions", function () {
|
||||||
mockObjectAPI.getProvider.and.returnValue({});
|
expect(mockObjectAPI.isPersistable).not.toHaveBeenCalled();
|
||||||
expect(mockObjectAPI.getProvider).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
policy.allow(mockOtherAction, testContext);
|
policy.allow(mockOtherAction, testContext);
|
||||||
expect(mockObjectAPI.getProvider).not.toHaveBeenCalled();
|
expect(mockObjectAPI.isPersistable).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Tests object provider for editability", function () {
|
it("Tests object provider for editability", function () {
|
||||||
mockObjectAPI.getProvider.and.returnValue({});
|
mockObjectAPI.isPersistable.and.returnValue(false);
|
||||||
expect(policy.allow(mockEditAction, testContext)).toBe(false);
|
expect(policy.allow(mockEditAction, testContext)).toBe(false);
|
||||||
expect(mockObjectAPI.getProvider).toHaveBeenCalled();
|
expect(mockObjectAPI.isPersistable).toHaveBeenCalled();
|
||||||
mockObjectAPI.getProvider.and.returnValue({save: function () {}});
|
mockObjectAPI.isPersistable.and.returnValue(true);
|
||||||
expect(policy.allow(mockEditAction, testContext)).toBe(true);
|
expect(policy.allow(mockEditAction, testContext)).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -19,7 +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.
|
||||||
-->
|
-->
|
||||||
<div class="c-object-label">
|
<div class="c-object-label"
|
||||||
<div class="c-object-label__type-icon {{type.getCssClass()}}" ng-class="{ 'l-icon-link':location.isLink() }"></div>
|
ng-class="{ 'is-missing': model.status === 'missing' }"
|
||||||
|
>
|
||||||
|
<div class="c-object-label__type-icon {{type.getCssClass()}}"
|
||||||
|
ng-class="{ 'l-icon-link':location.isLink() }"
|
||||||
|
>
|
||||||
|
<span class="is-missing__indicator" title="This item is missing"></span>
|
||||||
|
</div>
|
||||||
<div class='c-object-label__name'>{{model.name}}</div>
|
<div class='c-object-label__name'>{{model.name}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -48,9 +48,8 @@ define(
|
|||||||
// prevents editing of objects that cannot be persisted, so we can assume that this
|
// prevents editing of objects that cannot be persisted, so we can assume that this
|
||||||
// is a new object.
|
// is a new object.
|
||||||
if (!(parent.hasCapability('editor') && parent.getCapability('editor').isEditContextRoot())) {
|
if (!(parent.hasCapability('editor') && parent.getCapability('editor').isEditContextRoot())) {
|
||||||
var identifier = objectUtils.parseKeyString(parent.getId());
|
let newStyleObject = objectUtils.toNewFormat(parent, parent.getId());
|
||||||
var provider = this.openmct.objects.getProvider(identifier);
|
return this.openmct.objects.isPersistable(newStyleObject);
|
||||||
return provider.save !== undefined;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
@ -33,7 +33,7 @@ define(
|
|||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
objectAPI = jasmine.createSpyObj('objectsAPI', [
|
objectAPI = jasmine.createSpyObj('objectsAPI', [
|
||||||
'getProvider'
|
'isPersistable'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
mockOpenMCT = {
|
mockOpenMCT = {
|
||||||
@ -51,10 +51,6 @@ define(
|
|||||||
'isEditContextRoot'
|
'isEditContextRoot'
|
||||||
]);
|
]);
|
||||||
mockParent.getCapability.and.returnValue(mockEditorCapability);
|
mockParent.getCapability.and.returnValue(mockEditorCapability);
|
||||||
|
|
||||||
objectAPI.getProvider.and.returnValue({
|
|
||||||
save: function () {}
|
|
||||||
});
|
|
||||||
persistableCompositionPolicy = new PersistableCompositionPolicy(mockOpenMCT);
|
persistableCompositionPolicy = new PersistableCompositionPolicy(mockOpenMCT);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -65,19 +61,22 @@ define(
|
|||||||
|
|
||||||
it("Does not allow composition for objects that are not persistable", function () {
|
it("Does not allow composition for objects that are not persistable", function () {
|
||||||
mockEditorCapability.isEditContextRoot.and.returnValue(false);
|
mockEditorCapability.isEditContextRoot.and.returnValue(false);
|
||||||
|
objectAPI.isPersistable.and.returnValue(true);
|
||||||
expect(persistableCompositionPolicy.allow(mockParent, mockChild)).toBe(true);
|
expect(persistableCompositionPolicy.allow(mockParent, mockChild)).toBe(true);
|
||||||
objectAPI.getProvider.and.returnValue({});
|
objectAPI.isPersistable.and.returnValue(false);
|
||||||
expect(persistableCompositionPolicy.allow(mockParent, mockChild)).toBe(false);
|
expect(persistableCompositionPolicy.allow(mockParent, mockChild)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Always allows composition of objects in edit mode to support object creation", function () {
|
it("Always allows composition of objects in edit mode to support object creation", function () {
|
||||||
mockEditorCapability.isEditContextRoot.and.returnValue(true);
|
mockEditorCapability.isEditContextRoot.and.returnValue(true);
|
||||||
|
objectAPI.isPersistable.and.returnValue(true);
|
||||||
expect(persistableCompositionPolicy.allow(mockParent, mockChild)).toBe(true);
|
expect(persistableCompositionPolicy.allow(mockParent, mockChild)).toBe(true);
|
||||||
expect(objectAPI.getProvider).not.toHaveBeenCalled();
|
expect(objectAPI.isPersistable).not.toHaveBeenCalled();
|
||||||
|
|
||||||
mockEditorCapability.isEditContextRoot.and.returnValue(false);
|
mockEditorCapability.isEditContextRoot.and.returnValue(false);
|
||||||
|
objectAPI.isPersistable.and.returnValue(true);
|
||||||
expect(persistableCompositionPolicy.allow(mockParent, mockChild)).toBe(true);
|
expect(persistableCompositionPolicy.allow(mockParent, mockChild)).toBe(true);
|
||||||
expect(objectAPI.getProvider).toHaveBeenCalled();
|
expect(objectAPI.isPersistable).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -297,7 +297,8 @@ define([
|
|||||||
"persistenceService",
|
"persistenceService",
|
||||||
"identifierService",
|
"identifierService",
|
||||||
"notificationService",
|
"notificationService",
|
||||||
"$q"
|
"$q",
|
||||||
|
"openmct"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -20,8 +20,8 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(
|
define(["objectUtils"],
|
||||||
function () {
|
function (objectUtils) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the `persistence` capability, used to trigger the
|
* Defines the `persistence` capability, used to trigger the
|
||||||
@ -47,6 +47,7 @@ define(
|
|||||||
identifierService,
|
identifierService,
|
||||||
notificationService,
|
notificationService,
|
||||||
$q,
|
$q,
|
||||||
|
openmct,
|
||||||
domainObject
|
domainObject
|
||||||
) {
|
) {
|
||||||
// Cache modified timestamp
|
// Cache modified timestamp
|
||||||
@ -58,6 +59,7 @@ define(
|
|||||||
this.persistenceService = persistenceService;
|
this.persistenceService = persistenceService;
|
||||||
this.notificationService = notificationService;
|
this.notificationService = notificationService;
|
||||||
this.$q = $q;
|
this.$q = $q;
|
||||||
|
this.openmct = openmct;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,7 +68,7 @@ define(
|
|||||||
*/
|
*/
|
||||||
function rejectIfFalsey(value, $q) {
|
function rejectIfFalsey(value, $q) {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return $q.reject("Error persisting object");
|
return Promise.reject("Error persisting object");
|
||||||
} else {
|
} else {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@ -98,7 +100,7 @@ define(
|
|||||||
dismissable: true
|
dismissable: true
|
||||||
});
|
});
|
||||||
|
|
||||||
return $q.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -110,30 +112,12 @@ define(
|
|||||||
*/
|
*/
|
||||||
PersistenceCapability.prototype.persist = function () {
|
PersistenceCapability.prototype.persist = function () {
|
||||||
var self = this,
|
var self = this,
|
||||||
domainObject = this.domainObject,
|
domainObject = this.domainObject;
|
||||||
model = domainObject.getModel(),
|
|
||||||
modified = model.modified,
|
|
||||||
persisted = model.persisted,
|
|
||||||
persistenceService = this.persistenceService,
|
|
||||||
persistenceFn = persisted !== undefined ?
|
|
||||||
this.persistenceService.updateObject :
|
|
||||||
this.persistenceService.createObject;
|
|
||||||
|
|
||||||
if (persisted !== undefined && persisted === modified) {
|
let newStyleObject = objectUtils.toNewFormat(domainObject.getModel(), domainObject.getId());
|
||||||
return this.$q.when(true);
|
return this.openmct.objects
|
||||||
}
|
.save(newStyleObject)
|
||||||
|
.then(function (result) {
|
||||||
// Update persistence timestamp...
|
|
||||||
domainObject.useCapability("mutation", function (m) {
|
|
||||||
m.persisted = modified;
|
|
||||||
}, modified);
|
|
||||||
|
|
||||||
// ...and persist
|
|
||||||
return persistenceFn.apply(persistenceService, [
|
|
||||||
this.getSpace(),
|
|
||||||
this.getKey(),
|
|
||||||
domainObject.getModel()
|
|
||||||
]).then(function (result) {
|
|
||||||
return rejectIfFalsey(result, self.$q);
|
return rejectIfFalsey(result, self.$q);
|
||||||
}).catch(function (error) {
|
}).catch(function (error) {
|
||||||
return notifyOnError(error, domainObject, self.notificationService, self.$q);
|
return notifyOnError(error, domainObject, self.notificationService, self.$q);
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
* 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.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PersistenceCapabilitySpec. Created by vwoeltje on 11/6/14.
|
* PersistenceCapabilitySpec. Created by vwoeltje on 11/6/14.
|
||||||
*/
|
*/
|
||||||
@ -40,7 +39,8 @@ define(
|
|||||||
model,
|
model,
|
||||||
SPACE = "some space",
|
SPACE = "some space",
|
||||||
persistence,
|
persistence,
|
||||||
happyPromise;
|
mockOpenMCT,
|
||||||
|
mockNewStyleDomainObject;
|
||||||
|
|
||||||
function asPromise(value, doCatch) {
|
function asPromise(value, doCatch) {
|
||||||
return (value || {}).then ? value : {
|
return (value || {}).then ? value : {
|
||||||
@ -56,7 +56,6 @@ define(
|
|||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
happyPromise = asPromise(true);
|
|
||||||
model = { someKey: "some value", name: "domain object"};
|
model = { someKey: "some value", name: "domain object"};
|
||||||
|
|
||||||
mockPersistenceService = jasmine.createSpyObj(
|
mockPersistenceService = jasmine.createSpyObj(
|
||||||
@ -94,12 +93,23 @@ define(
|
|||||||
},
|
},
|
||||||
useCapability: jasmine.createSpy()
|
useCapability: jasmine.createSpy()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mockNewStyleDomainObject = Object.assign({}, model);
|
||||||
|
mockNewStyleDomainObject.identifier = {
|
||||||
|
namespace: "",
|
||||||
|
key: id
|
||||||
|
}
|
||||||
|
|
||||||
// Simulate mutation capability
|
// Simulate mutation capability
|
||||||
mockDomainObject.useCapability.and.callFake(function (capability, mutator) {
|
mockDomainObject.useCapability.and.callFake(function (capability, mutator) {
|
||||||
if (capability === 'mutation') {
|
if (capability === 'mutation') {
|
||||||
model = mutator(model) || model;
|
model = mutator(model) || model;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
mockOpenMCT = {};
|
||||||
|
mockOpenMCT.objects = jasmine.createSpyObj('Object API', ['save']);
|
||||||
|
|
||||||
mockIdentifierService.parse.and.returnValue(mockIdentifier);
|
mockIdentifierService.parse.and.returnValue(mockIdentifier);
|
||||||
mockIdentifier.getSpace.and.returnValue(SPACE);
|
mockIdentifier.getSpace.and.returnValue(SPACE);
|
||||||
mockIdentifier.getKey.and.returnValue(key);
|
mockIdentifier.getKey.and.returnValue(key);
|
||||||
@ -110,51 +120,28 @@ define(
|
|||||||
mockIdentifierService,
|
mockIdentifierService,
|
||||||
mockNofificationService,
|
mockNofificationService,
|
||||||
mockQ,
|
mockQ,
|
||||||
|
mockOpenMCT,
|
||||||
mockDomainObject
|
mockDomainObject
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("successful persistence", function () {
|
describe("successful persistence", function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockPersistenceService.updateObject.and.returnValue(happyPromise);
|
mockOpenMCT.objects.save.and.returnValue(Promise.resolve(true));
|
||||||
mockPersistenceService.createObject.and.returnValue(happyPromise);
|
|
||||||
});
|
});
|
||||||
it("creates unpersisted objects with the persistence service", function () {
|
it("creates unpersisted objects with the persistence service", function () {
|
||||||
// Verify precondition; no call made during constructor
|
// Verify precondition; no call made during constructor
|
||||||
expect(mockPersistenceService.createObject).not.toHaveBeenCalled();
|
expect(mockOpenMCT.objects.save).not.toHaveBeenCalled();
|
||||||
|
|
||||||
persistence.persist();
|
persistence.persist();
|
||||||
|
|
||||||
expect(mockPersistenceService.createObject).toHaveBeenCalledWith(
|
expect(mockOpenMCT.objects.save).toHaveBeenCalledWith(mockNewStyleDomainObject);
|
||||||
SPACE,
|
|
||||||
key,
|
|
||||||
model
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("updates previously persisted objects with the persistence service", function () {
|
|
||||||
// Verify precondition; no call made during constructor
|
|
||||||
expect(mockPersistenceService.updateObject).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
model.persisted = 12321;
|
|
||||||
persistence.persist();
|
|
||||||
|
|
||||||
expect(mockPersistenceService.updateObject).toHaveBeenCalledWith(
|
|
||||||
SPACE,
|
|
||||||
key,
|
|
||||||
model
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("reports which persistence space an object belongs to", function () {
|
it("reports which persistence space an object belongs to", function () {
|
||||||
expect(persistence.getSpace()).toEqual(SPACE);
|
expect(persistence.getSpace()).toEqual(SPACE);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("updates persisted timestamp on persistence", function () {
|
|
||||||
model.modified = 12321;
|
|
||||||
persistence.persist();
|
|
||||||
expect(model.persisted).toEqual(12321);
|
|
||||||
});
|
|
||||||
it("refreshes the domain object model from persistence", function () {
|
it("refreshes the domain object model from persistence", function () {
|
||||||
var refreshModel = {someOtherKey: "some other value"};
|
var refreshModel = {someOtherKey: "some other value"};
|
||||||
model.persisted = 1;
|
model.persisted = 1;
|
||||||
@ -165,32 +152,39 @@ define(
|
|||||||
|
|
||||||
it("does not trigger error notification on successful" +
|
it("does not trigger error notification on successful" +
|
||||||
" persistence", function () {
|
" persistence", function () {
|
||||||
persistence.persist();
|
let rejected = false;
|
||||||
expect(mockQ.reject).not.toHaveBeenCalled();
|
return persistence.persist()
|
||||||
|
.catch(() => rejected = true)
|
||||||
|
.then(() => {
|
||||||
|
expect(rejected).toBe(false);
|
||||||
expect(mockNofificationService.error).not.toHaveBeenCalled();
|
expect(mockNofificationService.error).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("unsuccessful persistence", function () {
|
describe("unsuccessful persistence", function () {
|
||||||
var sadPromise = {
|
|
||||||
then: function (callback) {
|
|
||||||
return asPromise(callback(0), true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockPersistenceService.createObject.and.returnValue(sadPromise);
|
mockOpenMCT.objects.save.and.returnValue(Promise.resolve(false));
|
||||||
});
|
});
|
||||||
it("rejects on falsey persistence result", function () {
|
it("rejects on falsey persistence result", function () {
|
||||||
persistence.persist();
|
let rejected = false;
|
||||||
expect(mockQ.reject).toHaveBeenCalled();
|
return persistence.persist()
|
||||||
|
.catch(() => rejected = true)
|
||||||
|
.then(() => {
|
||||||
|
expect(rejected).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("notifies user on persistence failure", function () {
|
it("notifies user on persistence failure", function () {
|
||||||
persistence.persist();
|
let rejected = false;
|
||||||
expect(mockQ.reject).toHaveBeenCalled();
|
return persistence.persist()
|
||||||
|
.catch(() => rejected = true)
|
||||||
|
.then(() => {
|
||||||
|
expect(rejected).toBe(true);
|
||||||
expect(mockNofificationService.error).toHaveBeenCalled();
|
expect(mockNofificationService.error).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -35,7 +35,8 @@ define([
|
|||||||
'./services/LegacyObjectAPIInterceptor',
|
'./services/LegacyObjectAPIInterceptor',
|
||||||
'./views/installLegacyViews',
|
'./views/installLegacyViews',
|
||||||
'./policies/LegacyCompositionPolicyAdapter',
|
'./policies/LegacyCompositionPolicyAdapter',
|
||||||
'./actions/LegacyActionAdapter'
|
'./actions/LegacyActionAdapter',
|
||||||
|
'./services/LegacyPersistenceAdapter'
|
||||||
], function (
|
], function (
|
||||||
ActionDialogDecorator,
|
ActionDialogDecorator,
|
||||||
AdapterCapability,
|
AdapterCapability,
|
||||||
@ -51,7 +52,8 @@ define([
|
|||||||
LegacyObjectAPIInterceptor,
|
LegacyObjectAPIInterceptor,
|
||||||
installLegacyViews,
|
installLegacyViews,
|
||||||
legacyCompositionPolicyAdapter,
|
legacyCompositionPolicyAdapter,
|
||||||
LegacyActionAdapter
|
LegacyActionAdapter,
|
||||||
|
LegacyPersistenceAdapter
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
name: 'src/adapter',
|
name: 'src/adapter',
|
||||||
@ -114,6 +116,13 @@ define([
|
|||||||
"instantiate",
|
"instantiate",
|
||||||
"topic"
|
"topic"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provides: "persistenceService",
|
||||||
|
type: "provider",
|
||||||
|
priority: "fallback",
|
||||||
|
implementation: LegacyPersistenceAdapter.default,
|
||||||
|
depends: ["openmct"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
policies: [
|
policies: [
|
||||||
|
@ -25,10 +25,11 @@ define([
|
|||||||
], function (
|
], function (
|
||||||
utils
|
utils
|
||||||
) {
|
) {
|
||||||
function ObjectServiceProvider(eventEmitter, objectService, instantiate, topic) {
|
function ObjectServiceProvider(eventEmitter, objectService, instantiate, topic, $injector) {
|
||||||
this.eventEmitter = eventEmitter;
|
this.eventEmitter = eventEmitter;
|
||||||
this.objectService = objectService;
|
this.objectService = objectService;
|
||||||
this.instantiate = instantiate;
|
this.instantiate = instantiate;
|
||||||
|
this.$injector = $injector;
|
||||||
|
|
||||||
this.generalTopic = topic('mutation');
|
this.generalTopic = topic('mutation');
|
||||||
this.bridgeEventBuses();
|
this.bridgeEventBuses();
|
||||||
@ -68,16 +69,53 @@ define([
|
|||||||
removeGeneralTopicListener = this.generalTopic.listen(handleLegacyMutation);
|
removeGeneralTopicListener = this.generalTopic.listen(handleLegacyMutation);
|
||||||
};
|
};
|
||||||
|
|
||||||
ObjectServiceProvider.prototype.save = function (object) {
|
ObjectServiceProvider.prototype.create = async function (object) {
|
||||||
var key = object.key;
|
let model = utils.toOldFormat(object);
|
||||||
|
|
||||||
return object.getCapability('persistence')
|
return this.getPersistenceService().createObject(
|
||||||
.persist()
|
this.getSpace(utils.makeKeyString(object.identifier)),
|
||||||
.then(function () {
|
object.identifier.key,
|
||||||
return utils.toNewFormat(object, key);
|
model
|
||||||
});
|
);
|
||||||
|
}
|
||||||
|
ObjectServiceProvider.prototype.update = async function (object) {
|
||||||
|
let model = utils.toOldFormat(object);
|
||||||
|
|
||||||
|
return this.getPersistenceService().updateObject(
|
||||||
|
this.getSpace(utils.makeKeyString(object.identifier)),
|
||||||
|
object.identifier.key,
|
||||||
|
model
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the space in which this domain object is persisted;
|
||||||
|
* this is useful when, for example, decided which space a
|
||||||
|
* newly-created domain object should be persisted to (by
|
||||||
|
* default, this should be the space of its containing
|
||||||
|
* object.)
|
||||||
|
*
|
||||||
|
* @returns {string} the name of the space which should
|
||||||
|
* be used to persist this object
|
||||||
|
*/
|
||||||
|
ObjectServiceProvider.prototype.getSpace = function (keystring) {
|
||||||
|
return this.getIdentifierService().parse(keystring).getSpace();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ObjectServiceProvider.prototype.getIdentifierService = function () {
|
||||||
|
if (this.identifierService === undefined) {
|
||||||
|
this.identifierService = this.$injector.get('identifierService');
|
||||||
|
}
|
||||||
|
return this.identifierService;
|
||||||
|
};
|
||||||
|
|
||||||
|
ObjectServiceProvider.prototype.getPersistenceService = function () {
|
||||||
|
if (this.persistenceService === undefined) {
|
||||||
|
this.persistenceService = this.$injector.get('persistenceService');
|
||||||
|
}
|
||||||
|
return this.persistenceService;
|
||||||
|
}
|
||||||
|
|
||||||
ObjectServiceProvider.prototype.delete = function (object) {
|
ObjectServiceProvider.prototype.delete = function (object) {
|
||||||
// TODO!
|
// TODO!
|
||||||
};
|
};
|
||||||
@ -118,7 +156,8 @@ define([
|
|||||||
eventEmitter,
|
eventEmitter,
|
||||||
objectService,
|
objectService,
|
||||||
instantiate,
|
instantiate,
|
||||||
topic
|
topic,
|
||||||
|
openmct.$injector
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
28
src/adapter/services/LegacyPersistenceAdapter.js
Normal file
28
src/adapter/services/LegacyPersistenceAdapter.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import objectUtils from 'objectUtils';
|
||||||
|
|
||||||
|
function LegacyPersistenceProvider(openmct) {
|
||||||
|
this.openmct = openmct;
|
||||||
|
}
|
||||||
|
|
||||||
|
LegacyPersistenceProvider.prototype.listObjects = function () {
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
LegacyPersistenceProvider.prototype.listSpaces = function () {
|
||||||
|
return Promise.resolve(Object.keys(this.openmct.objects.providers));
|
||||||
|
}
|
||||||
|
|
||||||
|
LegacyPersistenceProvider.prototype.updateObject = function (legacyDomainObject) {
|
||||||
|
return this.openmct.objects.save(legacyDomainObject.useCapability('adapter'));
|
||||||
|
}
|
||||||
|
|
||||||
|
LegacyPersistenceProvider.prototype.updateObject = function (legacyDomainObject) {
|
||||||
|
return this.openmct.objects.save(legacyDomainObject.useCapability('adapter'));
|
||||||
|
}
|
||||||
|
|
||||||
|
LegacyPersistenceProvider.prototype.readObject = function (keystring) {
|
||||||
|
let identifier = objectUtils.parseKeyString(keystring);
|
||||||
|
return this.openmct.legacyObject(this.openmct.objects.get(identifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LegacyPersistenceProvider;
|
@ -101,14 +101,25 @@ define([
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save this domain object in its current state.
|
* Create the given domain object in the corresponding persistence store
|
||||||
*
|
*
|
||||||
* @method save
|
* @method create
|
||||||
* @memberof module:openmct.ObjectProvider#
|
* @memberof module:openmct.ObjectProvider#
|
||||||
* @param {module:openmct.DomainObject} domainObject the domain object to
|
* @param {module:openmct.DomainObject} domainObject the domain object to
|
||||||
* save
|
* create
|
||||||
* @returns {Promise} a promise which will resolve when the domain object
|
* @returns {Promise} a promise which will resolve when the domain object
|
||||||
* has been saved, or be rejected if it cannot be saved
|
* has been created, or be rejected if it cannot be saved
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update this domain object in its persistence store
|
||||||
|
*
|
||||||
|
* @method update
|
||||||
|
* @memberof module:openmct.ObjectProvider#
|
||||||
|
* @param {module:openmct.DomainObject} domainObject the domain object to
|
||||||
|
* update
|
||||||
|
* @returns {Promise} a promise which will resolve when the domain object
|
||||||
|
* has been updated, or be rejected if it cannot be saved
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -161,8 +172,41 @@ define([
|
|||||||
throw new Error('Delete not implemented');
|
throw new Error('Delete not implemented');
|
||||||
};
|
};
|
||||||
|
|
||||||
ObjectAPI.prototype.save = function () {
|
ObjectAPI.prototype.isPersistable = function (domainObject) {
|
||||||
throw new Error('Save not implemented');
|
let provider = this.getProvider(domainObject.identifier);
|
||||||
|
return provider !== undefined &&
|
||||||
|
provider.create !== undefined &&
|
||||||
|
provider.update !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save this domain object in its current state. EXPERIMENTAL
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @memberof module:openmct.ObjectAPI#
|
||||||
|
* @param {module:openmct.DomainObject} domainObject the domain object to
|
||||||
|
* save
|
||||||
|
* @returns {Promise} a promise which will resolve when the domain object
|
||||||
|
* has been saved, or be rejected if it cannot be saved
|
||||||
|
*/
|
||||||
|
ObjectAPI.prototype.save = function (domainObject) {
|
||||||
|
let provider = this.getProvider(domainObject.identifier);
|
||||||
|
let result;
|
||||||
|
|
||||||
|
if (!this.isPersistable(domainObject)) {
|
||||||
|
result = Promise.reject('Object provider does not support saving');
|
||||||
|
} else if (hasAlreadyBeenPersisted(domainObject)) {
|
||||||
|
result = Promise.resolve(true);
|
||||||
|
} else {
|
||||||
|
if (domainObject.persisted === undefined) {
|
||||||
|
this.mutate(domainObject, 'persisted', domainObject.modified);
|
||||||
|
result = provider.create(domainObject);
|
||||||
|
} else {
|
||||||
|
this.mutate(domainObject, 'persisted', domainObject.modified);
|
||||||
|
result = provider.update(domainObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -276,5 +320,9 @@ define([
|
|||||||
* @memberof module:openmct
|
* @memberof module:openmct
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
function hasAlreadyBeenPersisted(domainObject) {
|
||||||
|
return domainObject.persisted !== undefined &&
|
||||||
|
domainObject.persisted === domainObject.modified;
|
||||||
|
}
|
||||||
return ObjectAPI;
|
return ObjectAPI;
|
||||||
});
|
});
|
||||||
|
60
src/api/objects/ObjectAPISpec.js
Normal file
60
src/api/objects/ObjectAPISpec.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import ObjectAPI from './ObjectAPI.js';
|
||||||
|
|
||||||
|
describe("The Object API", () => {
|
||||||
|
let objectAPI;
|
||||||
|
let mockDomainObject;
|
||||||
|
const TEST_NAMESPACE = "test-namespace";
|
||||||
|
const FIFTEEN_MINUTES = 15 * 60 * 1000;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
objectAPI = new ObjectAPI();
|
||||||
|
mockDomainObject = {
|
||||||
|
identifier: {
|
||||||
|
namespace: TEST_NAMESPACE,
|
||||||
|
key: "test-key"
|
||||||
|
},
|
||||||
|
name: "test object",
|
||||||
|
type: "test-type"
|
||||||
|
};
|
||||||
|
})
|
||||||
|
describe("The save function", () => {
|
||||||
|
it("Rejects if no provider available", () => {
|
||||||
|
let rejected = false;
|
||||||
|
return objectAPI.save(mockDomainObject)
|
||||||
|
.catch(() => rejected = true)
|
||||||
|
.then(() => expect(rejected).toBe(true));
|
||||||
|
});
|
||||||
|
describe("when a provider is available", () => {
|
||||||
|
let mockProvider;
|
||||||
|
beforeEach(() => {
|
||||||
|
mockProvider = jasmine.createSpyObj("mock provider", [
|
||||||
|
"create",
|
||||||
|
"update"
|
||||||
|
]);
|
||||||
|
objectAPI.addProvider(TEST_NAMESPACE, mockProvider);
|
||||||
|
})
|
||||||
|
it("Calls 'create' on provider if object is new", () => {
|
||||||
|
objectAPI.save(mockDomainObject);
|
||||||
|
expect(mockProvider.create).toHaveBeenCalled();
|
||||||
|
expect(mockProvider.update).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
it("Calls 'update' on provider if object is not new", () => {
|
||||||
|
mockDomainObject.persisted = Date.now() - FIFTEEN_MINUTES;
|
||||||
|
mockDomainObject.modified = Date.now();
|
||||||
|
|
||||||
|
objectAPI.save(mockDomainObject);
|
||||||
|
expect(mockProvider.create).not.toHaveBeenCalled();
|
||||||
|
expect(mockProvider.update).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Does not persist if the object is unchanged", () => {
|
||||||
|
mockDomainObject.persisted =
|
||||||
|
mockDomainObject.modified = Date.now();
|
||||||
|
|
||||||
|
objectAPI.save(mockDomainObject);
|
||||||
|
expect(mockProvider.create).not.toHaveBeenCalled();
|
||||||
|
expect(mockProvider.update).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
});
|
@ -334,8 +334,8 @@ define([
|
|||||||
});
|
});
|
||||||
if (subscriber.callbacks.length === 0) {
|
if (subscriber.callbacks.length === 0) {
|
||||||
subscriber.unsubscribe();
|
subscriber.unsubscribe();
|
||||||
}
|
|
||||||
delete this.subscribeCache[keyString];
|
delete this.subscribeCache[keyString];
|
||||||
|
}
|
||||||
}.bind(this);
|
}.bind(this);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -156,6 +156,29 @@ define([
|
|||||||
expect(callbacktwo).not.toHaveBeenCalledWith('anotherValue');
|
expect(callbacktwo).not.toHaveBeenCalledWith('anotherValue');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('only deletes subscription cache when there are no more subscribers', function () {
|
||||||
|
var unsubFunc = jasmine.createSpy('unsubscribe');
|
||||||
|
telemetryProvider.subscribe.and.returnValue(unsubFunc);
|
||||||
|
telemetryProvider.supportsSubscribe.and.returnValue(true);
|
||||||
|
telemetryAPI.addProvider(telemetryProvider);
|
||||||
|
|
||||||
|
var callback = jasmine.createSpy('callback');
|
||||||
|
var callbacktwo = jasmine.createSpy('callback two');
|
||||||
|
var callbackThree = jasmine.createSpy('callback three');
|
||||||
|
var unsubscribe = telemetryAPI.subscribe(domainObject, callback);
|
||||||
|
var unsubscribeTwo = telemetryAPI.subscribe(domainObject, callbacktwo);
|
||||||
|
|
||||||
|
expect(telemetryProvider.subscribe.calls.count()).toBe(1);
|
||||||
|
unsubscribe();
|
||||||
|
var unsubscribeThree = telemetryAPI.subscribe(domainObject, callbackThree);
|
||||||
|
// Regression test for where subscription cache was deleted on each unsubscribe, resulting in
|
||||||
|
// superfluous additional subscriptions. If the subscription cache is being deleted on each unsubscribe,
|
||||||
|
// then a subsequent subscribe will result in a new subscription at the provider.
|
||||||
|
expect(telemetryProvider.subscribe.calls.count()).toBe(1);
|
||||||
|
unsubscribeTwo();
|
||||||
|
unsubscribeThree();
|
||||||
|
});
|
||||||
|
|
||||||
it('does subscribe/unsubscribe', function () {
|
it('does subscribe/unsubscribe', function () {
|
||||||
var unsubFunc = jasmine.createSpy('unsubscribe');
|
var unsubFunc = jasmine.createSpy('unsubscribe');
|
||||||
telemetryProvider.subscribe.and.returnValue(unsubFunc);
|
telemetryProvider.subscribe.and.returnValue(unsubFunc);
|
||||||
|
@ -29,10 +29,13 @@ define([
|
|||||||
ClearDataAction,
|
ClearDataAction,
|
||||||
Vue
|
Vue
|
||||||
) {
|
) {
|
||||||
return function plugin(appliesToObjects) {
|
return function plugin(appliesToObjects, options = {indicator: true}) {
|
||||||
|
let installIndicator = options.indicator;
|
||||||
|
|
||||||
appliesToObjects = appliesToObjects || [];
|
appliesToObjects = appliesToObjects || [];
|
||||||
|
|
||||||
return function install(openmct) {
|
return function install(openmct) {
|
||||||
|
if (installIndicator) {
|
||||||
let component = new Vue ({
|
let component = new Vue ({
|
||||||
provide: {
|
provide: {
|
||||||
openmct
|
openmct
|
||||||
@ -47,6 +50,7 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
openmct.indicators.add(indicator);
|
openmct.indicators.add(indicator);
|
||||||
|
}
|
||||||
|
|
||||||
openmct.contextMenu.registerAction(new ClearDataAction.default(openmct, appliesToObjects));
|
openmct.contextMenu.registerAction(new ClearDataAction.default(openmct, appliesToObjects));
|
||||||
};
|
};
|
||||||
|
@ -103,6 +103,8 @@ export default class ConditionManager extends EventEmitter {
|
|||||||
criterion.operation = '';
|
criterion.operation = '';
|
||||||
conditionChanged = true;
|
conditionChanged = true;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
conditionChanged = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (conditionChanged) {
|
if (conditionChanged) {
|
||||||
|
@ -46,6 +46,7 @@ export default class StyleRuleManager extends EventEmitter {
|
|||||||
if (this.isEditing) {
|
if (this.isEditing) {
|
||||||
if (this.stopProvidingTelemetry) {
|
if (this.stopProvidingTelemetry) {
|
||||||
this.stopProvidingTelemetry();
|
this.stopProvidingTelemetry();
|
||||||
|
delete this.stopProvidingTelemetry;
|
||||||
}
|
}
|
||||||
if (this.conditionSetIdentifier) {
|
if (this.conditionSetIdentifier) {
|
||||||
this.applySelectedConditionStyle();
|
this.applySelectedConditionStyle();
|
||||||
@ -66,6 +67,7 @@ export default class StyleRuleManager extends EventEmitter {
|
|||||||
subscribeToConditionSet() {
|
subscribeToConditionSet() {
|
||||||
if (this.stopProvidingTelemetry) {
|
if (this.stopProvidingTelemetry) {
|
||||||
this.stopProvidingTelemetry();
|
this.stopProvidingTelemetry();
|
||||||
|
delete this.stopProvidingTelemetry;
|
||||||
}
|
}
|
||||||
this.openmct.objects.get(this.conditionSetIdentifier).then((conditionSetDomainObject) => {
|
this.openmct.objects.get(this.conditionSetIdentifier).then((conditionSetDomainObject) => {
|
||||||
this.openmct.telemetry.request(conditionSetDomainObject)
|
this.openmct.telemetry.request(conditionSetDomainObject)
|
||||||
@ -154,8 +156,8 @@ export default class StyleRuleManager extends EventEmitter {
|
|||||||
this.applyStaticStyle();
|
this.applyStaticStyle();
|
||||||
if (this.stopProvidingTelemetry) {
|
if (this.stopProvidingTelemetry) {
|
||||||
this.stopProvidingTelemetry();
|
this.stopProvidingTelemetry();
|
||||||
}
|
|
||||||
delete this.stopProvidingTelemetry;
|
delete this.stopProvidingTelemetry;
|
||||||
|
}
|
||||||
this.conditionSetIdentifier = undefined;
|
this.conditionSetIdentifier = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,6 +197,7 @@ export default {
|
|||||||
}
|
}
|
||||||
if (this.stopProvidingTelemetry) {
|
if (this.stopProvidingTelemetry) {
|
||||||
this.stopProvidingTelemetry();
|
this.stopProvidingTelemetry();
|
||||||
|
delete this.stopProvidingTelemetry;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
initialize(conditionSetDomainObject) {
|
initialize(conditionSetDomainObject) {
|
||||||
@ -210,6 +211,7 @@ export default {
|
|||||||
if (this.isEditing) {
|
if (this.isEditing) {
|
||||||
if (this.stopProvidingTelemetry) {
|
if (this.stopProvidingTelemetry) {
|
||||||
this.stopProvidingTelemetry();
|
this.stopProvidingTelemetry();
|
||||||
|
delete this.stopProvidingTelemetry;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.subscribeToConditionSet();
|
this.subscribeToConditionSet();
|
||||||
@ -307,6 +309,7 @@ export default {
|
|||||||
this.persist(domainObjectStyles);
|
this.persist(domainObjectStyles);
|
||||||
if (this.stopProvidingTelemetry) {
|
if (this.stopProvidingTelemetry) {
|
||||||
this.stopProvidingTelemetry();
|
this.stopProvidingTelemetry();
|
||||||
|
delete this.stopProvidingTelemetry;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateDomainObjectItemStyles(newItems) {
|
updateDomainObjectItemStyles(newItems) {
|
||||||
@ -375,6 +378,7 @@ export default {
|
|||||||
subscribeToConditionSet() {
|
subscribeToConditionSet() {
|
||||||
if (this.stopProvidingTelemetry) {
|
if (this.stopProvidingTelemetry) {
|
||||||
this.stopProvidingTelemetry();
|
this.stopProvidingTelemetry();
|
||||||
|
delete this.stopProvidingTelemetry;
|
||||||
}
|
}
|
||||||
if (this.conditionSetDomainObject) {
|
if (this.conditionSetDomainObject) {
|
||||||
this.openmct.telemetry.request(this.conditionSetDomainObject)
|
this.openmct.telemetry.request(this.conditionSetDomainObject)
|
||||||
|
@ -190,6 +190,7 @@ export default {
|
|||||||
if (this.isEditing) {
|
if (this.isEditing) {
|
||||||
if (this.stopProvidingTelemetry) {
|
if (this.stopProvidingTelemetry) {
|
||||||
this.stopProvidingTelemetry();
|
this.stopProvidingTelemetry();
|
||||||
|
delete this.stopProvidingTelemetry;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.subscribeToConditionSet();
|
this.subscribeToConditionSet();
|
||||||
@ -325,6 +326,7 @@ export default {
|
|||||||
|
|
||||||
if (this.stopProvidingTelemetry) {
|
if (this.stopProvidingTelemetry) {
|
||||||
this.stopProvidingTelemetry();
|
this.stopProvidingTelemetry();
|
||||||
|
delete this.stopProvidingTelemetry;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.unObserveObjects) {
|
if (this.unObserveObjects) {
|
||||||
@ -337,6 +339,7 @@ export default {
|
|||||||
subscribeToConditionSet() {
|
subscribeToConditionSet() {
|
||||||
if (this.stopProvidingTelemetry) {
|
if (this.stopProvidingTelemetry) {
|
||||||
this.stopProvidingTelemetry();
|
this.stopProvidingTelemetry();
|
||||||
|
delete this.stopProvidingTelemetry;
|
||||||
}
|
}
|
||||||
if (this.conditionSetDomainObject) {
|
if (this.conditionSetDomainObject) {
|
||||||
this.openmct.telemetry.request(this.conditionSetDomainObject)
|
this.openmct.telemetry.request(this.conditionSetDomainObject)
|
||||||
@ -494,6 +497,7 @@ export default {
|
|||||||
|
|
||||||
if (this.stopProvidingTelemetry) {
|
if (this.stopProvidingTelemetry) {
|
||||||
this.stopProvidingTelemetry();
|
this.stopProvidingTelemetry();
|
||||||
|
delete this.stopProvidingTelemetry;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
removeConditionalStyles(domainObjectStyles, itemId) {
|
removeConditionalStyles(domainObjectStyles, itemId) {
|
||||||
|
@ -457,15 +457,43 @@ export default {
|
|||||||
this.objectViewMap = {};
|
this.objectViewMap = {};
|
||||||
this.layoutItems.forEach(this.trackItem);
|
this.layoutItems.forEach(this.trackItem);
|
||||||
},
|
},
|
||||||
|
isItemAlreadyTracked(child) {
|
||||||
|
let found = false,
|
||||||
|
keyString = this.openmct.objects.makeKeyString(child.identifier);
|
||||||
|
|
||||||
|
this.layoutItems.forEach(item => {
|
||||||
|
if (item.identifier) {
|
||||||
|
let itemKeyString = this.openmct.objects.makeKeyString(item.identifier);
|
||||||
|
|
||||||
|
if (itemKeyString === keyString) {
|
||||||
|
found = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
return true;
|
||||||
|
} else if (this.isTelemetry(child)) {
|
||||||
|
return this.telemetryViewMap[keyString] && this.objectViewMap[keyString];
|
||||||
|
} else {
|
||||||
|
return this.objectViewMap[keyString];
|
||||||
|
}
|
||||||
|
},
|
||||||
addChild(child) {
|
addChild(child) {
|
||||||
let keyString = this.openmct.objects.makeKeyString(child.identifier);
|
if (this.isItemAlreadyTracked(child)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let type;
|
||||||
|
|
||||||
if (this.isTelemetry(child)) {
|
if (this.isTelemetry(child)) {
|
||||||
if (!this.telemetryViewMap[keyString] && !this.objectViewMap[keyString]) {
|
type = 'telemetry-view';
|
||||||
this.addItem('telemetry-view', child);
|
} else {
|
||||||
}
|
type = 'subobject-view';
|
||||||
} else if (!this.objectViewMap[keyString]) {
|
|
||||||
this.addItem('subobject-view', child);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.addItem(type, child);
|
||||||
},
|
},
|
||||||
removeChild(identifier) {
|
removeChild(identifier) {
|
||||||
let keyString = this.openmct.objects.makeKeyString(identifier);
|
let keyString = this.openmct.objects.makeKeyString(identifier);
|
||||||
@ -559,14 +587,17 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateTelemetryFormat(item, format) {
|
updateTelemetryFormat(item, format) {
|
||||||
let index = this.layoutItems.findIndex(item);
|
let index = this.layoutItems.findIndex((layoutItem) => {
|
||||||
|
return layoutItem.id === item.id;
|
||||||
|
});
|
||||||
|
|
||||||
item.format = format;
|
item.format = format;
|
||||||
this.mutate(`configuration.items[${index}]`, item);
|
this.mutate(`configuration.items[${index}]`, item);
|
||||||
},
|
},
|
||||||
createNewDomainObject(domainObject, composition, viewType, nameExtension, model) {
|
createNewDomainObject(domainObject, composition, viewType, nameExtension, model) {
|
||||||
let identifier = {
|
let identifier = {
|
||||||
key: uuid(),
|
key: uuid(),
|
||||||
namespace: domainObject.identifier.namespace
|
namespace: this.internalDomainObject.identifier.namespace
|
||||||
},
|
},
|
||||||
type = this.openmct.types.get(viewType),
|
type = this.openmct.types.get(viewType),
|
||||||
parentKeyString = this.openmct.objects.makeKeyString(this.internalDomainObject.identifier),
|
parentKeyString = this.openmct.objects.makeKeyString(this.internalDomainObject.identifier),
|
||||||
|
@ -31,10 +31,16 @@
|
|||||||
<div
|
<div
|
||||||
v-if="domainObject"
|
v-if="domainObject"
|
||||||
class="c-telemetry-view"
|
class="c-telemetry-view"
|
||||||
:class="styleClass"
|
:class="{
|
||||||
|
styleClass,
|
||||||
|
'is-missing': domainObject.status === 'missing'
|
||||||
|
}"
|
||||||
:style="styleObject"
|
:style="styleObject"
|
||||||
@contextmenu.prevent="showContextMenu"
|
@contextmenu.prevent="showContextMenu"
|
||||||
>
|
>
|
||||||
|
<div class="is-missing__indicator"
|
||||||
|
title="This item is missing"
|
||||||
|
></div>
|
||||||
<div
|
<div
|
||||||
v-if="showLabel"
|
v-if="showLabel"
|
||||||
class="c-telemetry-view__label"
|
class="c-telemetry-view__label"
|
||||||
|
@ -26,4 +26,15 @@
|
|||||||
@include abs();
|
@include abs();
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@include isMissing($absPos: true);
|
||||||
|
|
||||||
|
.is-missing__indicator {
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-missing {
|
||||||
|
border: $borderMissing;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<a
|
<a
|
||||||
class="l-grid-view__item c-grid-item"
|
class="l-grid-view__item c-grid-item"
|
||||||
:class="{ 'is-alias': item.isAlias === true }"
|
:class="{
|
||||||
|
'is-alias': item.isAlias === true,
|
||||||
|
'is-missing': item.model.status === 'missing',
|
||||||
|
'c-grid-item--unknown': item.type.cssClass === undefined || item.type.cssClass.indexOf('unknown') !== -1
|
||||||
|
}"
|
||||||
:href="objectLink"
|
:href="objectLink"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="c-grid-item__type-icon"
|
class="c-grid-item__type-icon"
|
||||||
:class="(item.type.cssClass != undefined) ? 'bg-' + item.type.cssClass : 'bg-icon-object-unknown'"
|
:class="(item.type.cssClass != undefined) ? 'bg-' + item.type.cssClass : 'bg-icon-object-unknown'"
|
||||||
></div>
|
>
|
||||||
|
</div>
|
||||||
<div class="c-grid-item__details">
|
<div class="c-grid-item__details">
|
||||||
<!-- Name and metadata -->
|
<!-- Name and metadata -->
|
||||||
<div
|
<div
|
||||||
@ -22,6 +27,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="c-grid-item__controls">
|
<div class="c-grid-item__controls">
|
||||||
|
<div class="is-missing__indicator"
|
||||||
|
title="This item is missing"
|
||||||
|
></div>
|
||||||
<div
|
<div
|
||||||
class="icon-people"
|
class="icon-people"
|
||||||
title="Shared"
|
title="Shared"
|
||||||
|
@ -7,13 +7,19 @@
|
|||||||
<td class="c-list-item__name">
|
<td class="c-list-item__name">
|
||||||
<a
|
<a
|
||||||
ref="objectLink"
|
ref="objectLink"
|
||||||
|
class="c-object-label"
|
||||||
|
:class="{ 'is-missing': item.model.status === 'missing' }"
|
||||||
:href="objectLink"
|
:href="objectLink"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="c-list-item__type-icon"
|
class="c-object-label__type-icon c-list-item__type-icon"
|
||||||
:class="item.type.cssClass"
|
:class="item.type.cssClass"
|
||||||
></div>
|
>
|
||||||
<div class="c-list-item__name-value">{{ item.model.name }}</div>
|
<span class="is-missing__indicator"
|
||||||
|
title="This item is missing"
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
|
<div class="c-object-label__name c-list-item__name">{{ item.model.name }}</div>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td class="c-list-item__type">
|
<td class="c-list-item__type">
|
||||||
|
@ -38,7 +38,15 @@
|
|||||||
// Object is an alias to an original.
|
// Object is an alias to an original.
|
||||||
[class*='__type-icon'] {
|
[class*='__type-icon'] {
|
||||||
@include isAlias();
|
@include isAlias();
|
||||||
color: $colorIconAliasForKeyFilter;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-missing {
|
||||||
|
@include isMissing();
|
||||||
|
|
||||||
|
[class*='__type-icon'],
|
||||||
|
[class*='__details'] {
|
||||||
|
opacity: $opacityMissing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,15 +93,14 @@
|
|||||||
body.desktop & {
|
body.desktop & {
|
||||||
$transOutMs: 300ms;
|
$transOutMs: 300ms;
|
||||||
flex-flow: column nowrap;
|
flex-flow: column nowrap;
|
||||||
transition: background $transOutMs ease-in-out;
|
transition: $transOutMs ease-in-out;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: $colorItemBgHov;
|
filter: $filterItemHoverFg;
|
||||||
transition: $transIn;
|
transition: $transIn;
|
||||||
|
|
||||||
.c-grid-item__type-icon {
|
.c-grid-item__type-icon {
|
||||||
filter: $colorKeyFilterHov;
|
transform: scale(1.1);
|
||||||
transform: scale(1);
|
|
||||||
transition: $transInBounce;
|
transition: $transInBounce;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,7 +110,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__controls {
|
&__controls {
|
||||||
align-items: start;
|
align-items: baseline;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
order: 1;
|
order: 1;
|
||||||
.c-info-button,
|
.c-info-button,
|
||||||
@ -115,7 +122,6 @@
|
|||||||
font-size: floor($gridItemDesk / 3);
|
font-size: floor($gridItemDesk / 3);
|
||||||
margin: $interiorMargin 22.5% $interiorMargin * 3 22.5%;
|
margin: $interiorMargin 22.5% $interiorMargin * 3 22.5%;
|
||||||
order: 2;
|
order: 2;
|
||||||
transform: scale(0.9);
|
|
||||||
transform-origin: center;
|
transform-origin: center;
|
||||||
transition: all $transOutMs ease-in-out;
|
transition: all $transOutMs ease-in-out;
|
||||||
}
|
}
|
||||||
|
@ -1,37 +1,17 @@
|
|||||||
/******************************* LIST ITEM */
|
/******************************* LIST ITEM */
|
||||||
.c-list-item {
|
.c-list-item {
|
||||||
&__name a {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
> * + * { margin-left: $interiorMarginSm; }
|
|
||||||
}
|
|
||||||
|
|
||||||
&__type-icon {
|
&__type-icon {
|
||||||
// Have to do it this way instead of using icon-* class, due to need to apply alias to the icon
|
color: $colorItemTreeIcon;
|
||||||
color: $colorKey;
|
|
||||||
display: inline-block;
|
|
||||||
width: 1em;
|
|
||||||
margin-right:$interiorMarginSm;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__name-value {
|
&__name {
|
||||||
@include ellipsize();
|
@include ellipsize();
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-alias {
|
&.is-alias {
|
||||||
// Object is an alias to an original.
|
// Object is an alias to an original.
|
||||||
[class*='__type-icon'] {
|
[class*='__type-icon'] {
|
||||||
&:after {
|
@include isAlias();
|
||||||
color: $colorIconAlias;
|
|
||||||
content: $glyph-icon-link;
|
|
||||||
font-family: symbolsfont;
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
text-shadow: rgba(black, 0.5) 0 1px 2px;
|
|
||||||
top: auto; left: -1px; bottom: 1px; right: auto;
|
|
||||||
transform-origin: bottom left;
|
|
||||||
transform: scale(0.65);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,8 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: $colorListItemBgHov;
|
background: $colorItemTreeHoverBg;
|
||||||
|
filter: $filterHov;
|
||||||
transition: $transIn;
|
transition: $transIn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,14 +24,14 @@ import uuid from 'uuid';
|
|||||||
|
|
||||||
export default class NewFolderAction {
|
export default class NewFolderAction {
|
||||||
constructor(openmct) {
|
constructor(openmct) {
|
||||||
this.name = 'New Folder';
|
this.name = 'Add New Folder';
|
||||||
this.key = 'newFolder';
|
this.key = 'newFolder';
|
||||||
this.description = 'Create a new folder';
|
this.description = 'Create a new folder';
|
||||||
this.cssClass = 'icon-folder';
|
this.cssClass = 'icon-folder-new';
|
||||||
|
|
||||||
this._openmct = openmct;
|
this._openmct = openmct;
|
||||||
this._dialogForm = {
|
this._dialogForm = {
|
||||||
name: "New Folder Name",
|
name: "Add New Folder",
|
||||||
sections: [
|
sections: [
|
||||||
{
|
{
|
||||||
rows: [
|
rows: [
|
||||||
@ -39,7 +39,9 @@ export default class NewFolderAction {
|
|||||||
key: "name",
|
key: "name",
|
||||||
control: "textfield",
|
control: "textfield",
|
||||||
name: "Folder Name",
|
name: "Folder Name",
|
||||||
required: false
|
pattern: "\\S+",
|
||||||
|
required: true,
|
||||||
|
cssClass: "l-input-lg"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -53,7 +55,7 @@ export default class NewFolderAction {
|
|||||||
dialogService = this._openmct.$injector.get('dialogService'),
|
dialogService = this._openmct.$injector.get('dialogService'),
|
||||||
folderType = this._openmct.types.get('folder');
|
folderType = this._openmct.types.get('folder');
|
||||||
|
|
||||||
dialogService.getUserInput(this._dialogForm, {}).then((userInput) => {
|
dialogService.getUserInput(this._dialogForm, {name: 'Unnamed Folder'}).then((userInput) => {
|
||||||
let name = userInput.name,
|
let name = userInput.name,
|
||||||
identifier = {
|
identifier = {
|
||||||
key: uuid(),
|
key: uuid(),
|
||||||
|
@ -2,14 +2,17 @@
|
|||||||
<div class="c-snapshots-h">
|
<div class="c-snapshots-h">
|
||||||
<div class="l-browse-bar">
|
<div class="l-browse-bar">
|
||||||
<div class="l-browse-bar__start">
|
<div class="l-browse-bar__start">
|
||||||
<div class="l-browse-bar__object-name--w icon-notebook">
|
<div class="l-browse-bar__object-name--w">
|
||||||
<div class="l-browse-bar__object-name">
|
<div class="l-browse-bar__object-name c-object-label">
|
||||||
|
<div class="c-object-label__type-icon icon-notebook"></div>
|
||||||
|
<div class="c-object-label__name">
|
||||||
Notebook Snapshots
|
Notebook Snapshots
|
||||||
<span v-if="snapshots.length"
|
<span v-if="snapshots.length"
|
||||||
class="l-browse-bar__object-details"
|
class="l-browse-bar__object-details"
|
||||||
> {{ snapshots.length }} of {{ getNotebookSnapshotMaxCount() }}
|
> {{ snapshots.length }} of {{ getNotebookSnapshotMaxCount() }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<PopupMenu v-if="snapshots.length > 0"
|
<PopupMenu v-if="snapshots.length > 0"
|
||||||
:popup-menu-items="popupMenuItems"
|
:popup-menu-items="popupMenuItems"
|
||||||
/>
|
/>
|
||||||
|
53
src/plugins/persistence/couch/CouchDocument.js
Normal file
53
src/plugins/persistence/couch/CouchDocument.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A CouchDocument describes domain object model in a format
|
||||||
|
* which is easily read-written to CouchDB. This includes
|
||||||
|
* Couch's _id and _rev fields, as well as a separate
|
||||||
|
* metadata field which contains a subset of information found
|
||||||
|
* in the model itself (to support search optimization with
|
||||||
|
* CouchDB views.)
|
||||||
|
* @memberof platform/persistence/couch
|
||||||
|
* @constructor
|
||||||
|
* @param {string} id the id under which to store this mode
|
||||||
|
* @param {object} model the model to store
|
||||||
|
* @param {string} rev the revision to include (or undefined,
|
||||||
|
* if no revision should be noted for couch)
|
||||||
|
* @param {boolean} whether or not to mark this document as
|
||||||
|
* deleted (see CouchDB docs for _deleted)
|
||||||
|
*/
|
||||||
|
export default function CouchDocument(id, model, rev, markDeleted) {
|
||||||
|
return {
|
||||||
|
"_id": id,
|
||||||
|
"_rev": rev,
|
||||||
|
"_deleted": markDeleted,
|
||||||
|
"metadata": {
|
||||||
|
"category": "domain object",
|
||||||
|
"type": model.type,
|
||||||
|
"owner": "admin",
|
||||||
|
"name": model.name,
|
||||||
|
"created": Date.now()
|
||||||
|
},
|
||||||
|
"model": model
|
||||||
|
};
|
||||||
|
}
|
65
src/plugins/persistence/couch/CouchObjectProvider.js
Normal file
65
src/plugins/persistence/couch/CouchObjectProvider.js
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import CouchDocument from "./CouchDocument";
|
||||||
|
|
||||||
|
const REV = "_rev";
|
||||||
|
const ID = "_id";
|
||||||
|
|
||||||
|
export default class CouchObjectProvider {
|
||||||
|
constructor(openmct, url, namespace) {
|
||||||
|
this.openmct = openmct;
|
||||||
|
this.url = url;
|
||||||
|
this.namespace = namespace; this.namespace = namespace; this.namespace = namespace;
|
||||||
|
this.revs = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
request(subPath, method, value) {
|
||||||
|
console.log(subPath, method, value);
|
||||||
|
return fetch(this.url + '/' + subPath, {
|
||||||
|
method: method,
|
||||||
|
body: value
|
||||||
|
}).then(response => response.json())
|
||||||
|
.then(function (response) {
|
||||||
|
return response;
|
||||||
|
}, function () {
|
||||||
|
return undefined;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the response to a create/update/delete request;
|
||||||
|
// track the rev if it's valid, otherwise return false to
|
||||||
|
// indicate that the request failed.
|
||||||
|
checkResponse(response) {
|
||||||
|
if (response && response.ok) {
|
||||||
|
this.revs[response.id] = response.rev;
|
||||||
|
return response.ok;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getModel(response) {
|
||||||
|
if (response && response.model) {
|
||||||
|
let key = response[ID];
|
||||||
|
this.revs[key] = response[REV];
|
||||||
|
let object = response.model;
|
||||||
|
object.identifier = {
|
||||||
|
namespace: this.namespace,
|
||||||
|
key: key
|
||||||
|
};
|
||||||
|
return object;
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get(identifier) {
|
||||||
|
return this.request(identifier.key, "GET").then(this.getModel.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
create(model) {
|
||||||
|
return this.request(model.identifier, "PUT", new CouchDocument(model.identifier.key, model)).then(this.checkResponse.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
update(model) {
|
||||||
|
return this.request(model.identifier, "PUT", model).then(this.checkResponse.bind(this));
|
||||||
|
}
|
||||||
|
}
|
8
src/plugins/persistence/couch/plugin.js
Normal file
8
src/plugins/persistence/couch/plugin.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import CouchObjectProvider from './CouchObjectProvider';
|
||||||
|
const NAMESPACE = '';
|
||||||
|
|
||||||
|
export default function CouchPlugin(config) {
|
||||||
|
return function install(openmct) {
|
||||||
|
openmct.objects.addProvider(NAMESPACE, new CouchObjectProvider(openmct, config, NAMESPACE));
|
||||||
|
}
|
||||||
|
}
|
77
src/plugins/persistence/couch/pluginSpec.js
Normal file
77
src/plugins/persistence/couch/pluginSpec.js
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
import CouchPlugin from './plugin.js';
|
||||||
|
import {
|
||||||
|
createOpenMct,
|
||||||
|
resetApplicationState
|
||||||
|
} from 'utils/testing';
|
||||||
|
|
||||||
|
fdescribe("the plugin", () => {
|
||||||
|
let openmct;
|
||||||
|
let element;
|
||||||
|
let child;
|
||||||
|
let provider;
|
||||||
|
let testSpace = "testSpace";
|
||||||
|
let testPath = "/test/db";
|
||||||
|
let mockDomainObject = { identifier: {namespace: '', key: "some-value"} };
|
||||||
|
|
||||||
|
beforeEach((done) => {
|
||||||
|
openmct = createOpenMct(false);
|
||||||
|
openmct.install(new CouchPlugin(testSpace, testPath));
|
||||||
|
|
||||||
|
element = document.createElement('div');
|
||||||
|
child = document.createElement('div');
|
||||||
|
element.appendChild(child);
|
||||||
|
|
||||||
|
openmct.on('start', done);
|
||||||
|
openmct.startHeadless();
|
||||||
|
|
||||||
|
provider = openmct.objects.getProvider(mockDomainObject.identifier);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('registers a provider for objects', () => {
|
||||||
|
expect(provider).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gets an object', () => {
|
||||||
|
openmct.objects.get(mockDomainObject.identifier).then((result) => {
|
||||||
|
expect(provider.get).toHaveBeenCalled();
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates an object', () => {
|
||||||
|
openmct.objects.save(mockDomainObject).then((result) => {
|
||||||
|
expect(provider.create).toHaveBeenCalled();
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('updates an object', () => {
|
||||||
|
openmct.objects.save(mockDomainObject).then((result) => {
|
||||||
|
openmct.objects.save(mockDomainObject).then((updatedResult) => {
|
||||||
|
expect(provider.update).toHaveBeenCalled();
|
||||||
|
expect(updatedResult).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -28,17 +28,22 @@
|
|||||||
ng-click="legend.set('expanded', !legend.get('expanded'));">
|
ng-click="legend.set('expanded', !legend.get('expanded'));">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="c-plot-legend__wrapper">
|
<div class="c-plot-legend__wrapper"
|
||||||
|
ng-class="{ 'is-cursor-locked': !!lockHighlightPoint }">
|
||||||
|
|
||||||
<!-- COLLAPSED PLOT LEGEND -->
|
<!-- COLLAPSED PLOT LEGEND -->
|
||||||
<div class="plot-wrapper-collapsed-legend"
|
<div class="plot-wrapper-collapsed-legend"
|
||||||
ng-class="{'icon-cursor-lock': !!lockHighlightPoint}">
|
ng-class="{'is-cursor-locked': !!lockHighlightPoint }">
|
||||||
|
<div class="c-state-indicator__alert-cursor-lock icon-cursor-lock" title="Cursor is point locked. Click anywhere in the plot to unlock."></div>
|
||||||
<div class="plot-legend-item"
|
<div class="plot-legend-item"
|
||||||
ng-repeat="series in series track by $index">
|
ng-class="{'is-missing': series.domainObject.status === 'missing'}"
|
||||||
|
ng-repeat="series in series track by $index"
|
||||||
|
>
|
||||||
<div class="plot-series-swatch-and-name">
|
<div class="plot-series-swatch-and-name">
|
||||||
<span class="plot-series-color-swatch"
|
<span class="plot-series-color-swatch "
|
||||||
ng-style="{ 'background-color': series.get('color').asHexString() }">
|
ng-style="{ 'background-color': series.get('color').asHexString() }">
|
||||||
</span>
|
</span>
|
||||||
|
<span class="is-missing__indicator" title="This item is missing"></span>
|
||||||
<span class="plot-series-name">{{ series.get('name') }}</span>
|
<span class="plot-series-name">{{ series.get('name') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="plot-series-value hover-value-enabled value-to-display-{{ legend.get('valueToShowWhenCollapsed') }} {{ series.closest.mctLimitState.cssClass }}"
|
<div class="plot-series-value hover-value-enabled value-to-display-{{ legend.get('valueToShowWhenCollapsed') }} {{ series.closest.mctLimitState.cssClass }}"
|
||||||
@ -55,7 +60,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- EXPANDED PLOT LEGEND -->
|
<!-- EXPANDED PLOT LEGEND -->
|
||||||
<div class="plot-wrapper-expanded-legend">
|
<div class="plot-wrapper-expanded-legend"
|
||||||
|
ng-class="{'is-cursor-locked': !!lockHighlightPoint }"
|
||||||
|
>
|
||||||
|
<div class="c-state-indicator__alert-cursor-lock--verbose icon-cursor-lock" title="Click anywhere in the plot to unlock."> Cursor locked to point</div>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -76,12 +84,15 @@
|
|||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tr ng-repeat="series in series" class="plot-legend-item">
|
<tr ng-repeat="series in series"
|
||||||
<td class="plot-series-swatch-and-name"
|
class="plot-legend-item"
|
||||||
ng-class="{'icon-cursor-lock': !!lockHighlightPoint}">
|
ng-class="{'is-missing': series.domainObject.status === 'missing'}"
|
||||||
|
>
|
||||||
|
<td class="plot-series-swatch-and-name">
|
||||||
<span class="plot-series-color-swatch"
|
<span class="plot-series-color-swatch"
|
||||||
ng-style="{ 'background-color': series.get('color').asHexString() }">
|
ng-style="{ 'background-color': series.get('color').asHexString() }">
|
||||||
</span>
|
</span>
|
||||||
|
<span class="is-missing__indicator" title="This item is missing"></span>
|
||||||
<span class="plot-series-name">{{ series.get('name') }}</span>
|
<span class="plot-series-name">{{ series.get('name') }}</span>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@
|
|||||||
</li>
|
</li>
|
||||||
<li class="grid-row">
|
<li class="grid-row">
|
||||||
<div class="grid-cell label"
|
<div class="grid-cell label"
|
||||||
title="The line rendering style for this series.">Line Style</div>
|
title="The rendering method to join lines for this series.">Line Method</div>
|
||||||
<div class="grid-cell value">{{ {
|
<div class="grid-cell value">{{ {
|
||||||
'none': 'None',
|
'none': 'None',
|
||||||
'linear': 'Linear interpolation',
|
'linear': 'Linear interpolation',
|
||||||
@ -56,7 +56,7 @@
|
|||||||
<div class="grid-cell label"
|
<div class="grid-cell label"
|
||||||
title="Whether markers are displayed, and their size.">Markers</div>
|
title="Whether markers are displayed, and their size.">Markers</div>
|
||||||
<div class="grid-cell value">
|
<div class="grid-cell value">
|
||||||
{{series.get('markers') ? "Enabled: " + series.get('markerSize') + "px" : "Disabled"}}
|
{{ series.markerOptionsDisplayText() }}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="grid-row">
|
<li class="grid-row">
|
||||||
|
@ -52,7 +52,7 @@
|
|||||||
</li>
|
</li>
|
||||||
<li class="grid-row">
|
<li class="grid-row">
|
||||||
<div class="grid-cell label"
|
<div class="grid-cell label"
|
||||||
title="The line rendering style for this series.">Line Style</div>
|
title="The rendering method to join lines for this series.">Line Method</div>
|
||||||
<div class="grid-cell value">
|
<div class="grid-cell value">
|
||||||
<select ng-model="form.interpolate">
|
<select ng-model="form.interpolate">
|
||||||
<option value="none">None</option>
|
<option value="none">None</option>
|
||||||
@ -64,12 +64,27 @@
|
|||||||
<li class="grid-row">
|
<li class="grid-row">
|
||||||
<div class="grid-cell label"
|
<div class="grid-cell label"
|
||||||
title="Whether markers are displayed.">Markers</div>
|
title="Whether markers are displayed.">Markers</div>
|
||||||
<div class="grid-cell value"><input type="checkbox" ng-model="form.markers"/></div>
|
<div class="grid-cell value">
|
||||||
|
<input type="checkbox" ng-model="form.markers"/>
|
||||||
|
<select
|
||||||
|
ng-show="form.markers"
|
||||||
|
ng-model="form.markerShape">
|
||||||
|
<option
|
||||||
|
ng-repeat="option in markerShapeOptions"
|
||||||
|
value="{{ option.value }}"
|
||||||
|
ng-selected="option.value == form.markerShape"
|
||||||
|
>
|
||||||
|
{{ option.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="grid-row">
|
<li class="grid-row">
|
||||||
<div class="grid-cell label"
|
<div class="grid-cell label"
|
||||||
title="Display markers visually denoting points in alarm.">Alarm Markers</div>
|
title="Display markers visually denoting points in alarm.">Alarm Markers</div>
|
||||||
<div class="grid-cell value"><input type="checkbox" ng-model="form.alarmMarkers"/></div>
|
<div class="grid-cell value">
|
||||||
|
<input type="checkbox" ng-model="form.alarmMarkers"/>
|
||||||
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="grid-row" ng-show="form.markers || form.alarmMarkers">
|
<li class="grid-row" ng-show="form.markers || form.alarmMarkers">
|
||||||
<div class="grid-cell label"
|
<div class="grid-cell label"
|
||||||
|
@ -371,7 +371,8 @@ function (
|
|||||||
chartElement.getBuffer(),
|
chartElement.getBuffer(),
|
||||||
chartElement.color().asRGBAArray(),
|
chartElement.color().asRGBAArray(),
|
||||||
chartElement.count,
|
chartElement.count,
|
||||||
chartElement.series.get('markerSize')
|
chartElement.series.get('markerSize'),
|
||||||
|
chartElement.series.get('markerShape')
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -395,9 +396,10 @@ function (
|
|||||||
this.offset.yVal(highlight.point, highlight.series)
|
this.offset.yVal(highlight.point, highlight.series)
|
||||||
]),
|
]),
|
||||||
color = highlight.series.get('color').asRGBAArray(),
|
color = highlight.series.get('color').asRGBAArray(),
|
||||||
pointCount = 1;
|
pointCount = 1,
|
||||||
|
shape = highlight.series.get('markerShape');
|
||||||
|
|
||||||
this.drawAPI.drawPoints(points, color, pointCount, HIGHLIGHT_SIZE);
|
this.drawAPI.drawPoints(points, color, pointCount, HIGHLIGHT_SIZE, shape);
|
||||||
};
|
};
|
||||||
|
|
||||||
MCTChartController.prototype.drawRectangles = function () {
|
MCTChartController.prototype.drawRectangles = function () {
|
||||||
|
@ -25,12 +25,14 @@ define([
|
|||||||
'lodash',
|
'lodash',
|
||||||
'../configuration/Model',
|
'../configuration/Model',
|
||||||
'../lib/extend',
|
'../lib/extend',
|
||||||
'EventEmitter'
|
'EventEmitter',
|
||||||
|
'../draw/MarkerShapes'
|
||||||
], function (
|
], function (
|
||||||
_,
|
_,
|
||||||
Model,
|
Model,
|
||||||
extend,
|
extend,
|
||||||
EventEmitter
|
EventEmitter,
|
||||||
|
MARKER_SHAPES
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -56,6 +58,7 @@ define([
|
|||||||
* `linear` (points are connected via straight lines), or
|
* `linear` (points are connected via straight lines), or
|
||||||
* `stepAfter` (points are connected by steps).
|
* `stepAfter` (points are connected by steps).
|
||||||
* `markers`: boolean, whether or not this series should render with markers.
|
* `markers`: boolean, whether or not this series should render with markers.
|
||||||
|
* `markerShape`: string, shape of markers.
|
||||||
* `markerSize`: number, size in pixels of markers for this series.
|
* `markerSize`: number, size in pixels of markers for this series.
|
||||||
* `alarmMarkers`: whether or not to display alarm markers for this series.
|
* `alarmMarkers`: whether or not to display alarm markers for this series.
|
||||||
* `stats`: An object that tracks the min and max y values observed in this
|
* `stats`: An object that tracks the min and max y values observed in this
|
||||||
@ -101,6 +104,7 @@ define([
|
|||||||
xKey: options.collection.plot.xAxis.get('key'),
|
xKey: options.collection.plot.xAxis.get('key'),
|
||||||
yKey: range.key,
|
yKey: range.key,
|
||||||
markers: true,
|
markers: true,
|
||||||
|
markerShape: 'point',
|
||||||
markerSize: 2.0,
|
markerSize: 2.0,
|
||||||
alarmMarkers: true
|
alarmMarkers: true
|
||||||
};
|
};
|
||||||
@ -410,6 +414,18 @@ define([
|
|||||||
} else {
|
} else {
|
||||||
this.filters = deepCopiedFilters;
|
this.filters = deepCopiedFilters;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
markerOptionsDisplayText: function () {
|
||||||
|
const showMarkers = this.get('markers');
|
||||||
|
if (!showMarkers) {
|
||||||
|
return "Disabled";
|
||||||
|
}
|
||||||
|
|
||||||
|
const markerShapeKey = this.get('markerShape');
|
||||||
|
const markerShape = MARKER_SHAPES[markerShapeKey].label;
|
||||||
|
const markerSize = this.get('markerSize');
|
||||||
|
|
||||||
|
return `${markerShape}: ${markerSize}px`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -23,10 +23,12 @@
|
|||||||
|
|
||||||
define([
|
define([
|
||||||
'EventEmitter',
|
'EventEmitter',
|
||||||
'../lib/eventHelpers'
|
'../lib/eventHelpers',
|
||||||
|
'./MarkerShapes'
|
||||||
], function (
|
], function (
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
eventHelpers
|
eventHelpers,
|
||||||
|
MARKER_SHAPES
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -121,18 +123,17 @@ define([
|
|||||||
buf,
|
buf,
|
||||||
color,
|
color,
|
||||||
points,
|
points,
|
||||||
pointSize
|
pointSize,
|
||||||
|
shape
|
||||||
) {
|
) {
|
||||||
var i = 0,
|
const drawC2DShape = MARKER_SHAPES[shape].drawC2D.bind(this);
|
||||||
offset = pointSize / 2;
|
|
||||||
|
|
||||||
this.setColor(color);
|
this.setColor(color);
|
||||||
|
|
||||||
for (; i < points; i++) {
|
for (let i = 0; i < points; i++) {
|
||||||
this.c2d.fillRect(
|
drawC2DShape(
|
||||||
this.x(buf[i * 2]) - offset,
|
this.x(buf[i * 2]),
|
||||||
this.y(buf[i * 2 + 1]) - offset,
|
this.y(buf[i * 2 + 1]),
|
||||||
pointSize,
|
|
||||||
pointSize
|
pointSize
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -23,30 +23,64 @@
|
|||||||
|
|
||||||
define([
|
define([
|
||||||
'EventEmitter',
|
'EventEmitter',
|
||||||
'../lib/eventHelpers'
|
'../lib/eventHelpers',
|
||||||
|
'./MarkerShapes'
|
||||||
], function (
|
], function (
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
eventHelpers
|
eventHelpers,
|
||||||
|
MARKER_SHAPES
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// WebGL shader sources (for drawing plain colors)
|
// WebGL shader sources (for drawing plain colors)
|
||||||
var FRAGMENT_SHADER = [
|
const FRAGMENT_SHADER = `
|
||||||
"precision mediump float;",
|
precision mediump float;
|
||||||
"uniform vec4 uColor;",
|
uniform vec4 uColor;
|
||||||
"void main(void) {",
|
uniform int uMarkerShape;
|
||||||
"gl_FragColor = uColor;",
|
|
||||||
"}"
|
void main(void) {
|
||||||
].join('\n'),
|
gl_FragColor = uColor;
|
||||||
VERTEX_SHADER = [
|
|
||||||
"attribute vec2 aVertexPosition;",
|
if (uMarkerShape > 1) {
|
||||||
"uniform vec2 uDimensions;",
|
vec2 clipSpacePointCoord = 2.0 * gl_PointCoord - 1.0;
|
||||||
"uniform vec2 uOrigin;",
|
|
||||||
"uniform float uPointSize;",
|
if (uMarkerShape == 2) { // circle
|
||||||
"void main(void) {",
|
float distance = length(clipSpacePointCoord);
|
||||||
"gl_Position = vec4(2.0 * ((aVertexPosition - uOrigin) / uDimensions) - vec2(1,1), 0, 1);",
|
|
||||||
"gl_PointSize = uPointSize;",
|
if (distance > 1.0) {
|
||||||
"}"
|
discard;
|
||||||
].join('\n');
|
}
|
||||||
|
} else if (uMarkerShape == 3) { // diamond
|
||||||
|
float distance = abs(clipSpacePointCoord.x) + abs(clipSpacePointCoord.y);
|
||||||
|
|
||||||
|
if (distance > 1.0) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
} else if (uMarkerShape == 4) { // triangle
|
||||||
|
float x = clipSpacePointCoord.x;
|
||||||
|
float y = clipSpacePointCoord.y;
|
||||||
|
float distance = 2.0 * x - 1.0;
|
||||||
|
float distance2 = -2.0 * x - 1.0;
|
||||||
|
|
||||||
|
if (distance > y || distance2 > y) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const VERTEX_SHADER = `
|
||||||
|
attribute vec2 aVertexPosition;
|
||||||
|
uniform vec2 uDimensions;
|
||||||
|
uniform vec2 uOrigin;
|
||||||
|
uniform float uPointSize;
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
gl_Position = vec4(2.0 * ((aVertexPosition - uOrigin) / uDimensions) - vec2(1,1), 0, 1);
|
||||||
|
gl_PointSize = uPointSize;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a draw api utilizing WebGL.
|
* Create a draw api utilizing WebGL.
|
||||||
@ -90,6 +124,7 @@ define([
|
|||||||
this.vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER);
|
this.vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER);
|
||||||
this.gl.shaderSource(this.vertexShader, VERTEX_SHADER);
|
this.gl.shaderSource(this.vertexShader, VERTEX_SHADER);
|
||||||
this.gl.compileShader(this.vertexShader);
|
this.gl.compileShader(this.vertexShader);
|
||||||
|
|
||||||
this.fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
|
this.fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
|
||||||
this.gl.shaderSource(this.fragmentShader, FRAGMENT_SHADER);
|
this.gl.shaderSource(this.fragmentShader, FRAGMENT_SHADER);
|
||||||
this.gl.compileShader(this.fragmentShader);
|
this.gl.compileShader(this.fragmentShader);
|
||||||
@ -105,6 +140,7 @@ define([
|
|||||||
// shader programs (to pass values into shaders at draw-time)
|
// shader programs (to pass values into shaders at draw-time)
|
||||||
this.aVertexPosition = this.gl.getAttribLocation(this.program, "aVertexPosition");
|
this.aVertexPosition = this.gl.getAttribLocation(this.program, "aVertexPosition");
|
||||||
this.uColor = this.gl.getUniformLocation(this.program, "uColor");
|
this.uColor = this.gl.getUniformLocation(this.program, "uColor");
|
||||||
|
this.uMarkerShape = this.gl.getUniformLocation(this.program, "uMarkerShape");
|
||||||
this.uDimensions = this.gl.getUniformLocation(this.program, "uDimensions");
|
this.uDimensions = this.gl.getUniformLocation(this.program, "uDimensions");
|
||||||
this.uOrigin = this.gl.getUniformLocation(this.program, "uOrigin");
|
this.uOrigin = this.gl.getUniformLocation(this.program, "uOrigin");
|
||||||
this.uPointSize = this.gl.getUniformLocation(this.program, "uPointSize");
|
this.uPointSize = this.gl.getUniformLocation(this.program, "uPointSize");
|
||||||
@ -114,9 +150,6 @@ define([
|
|||||||
// Create a buffer to holds points which will be drawn
|
// Create a buffer to holds points which will be drawn
|
||||||
this.buffer = this.gl.createBuffer();
|
this.buffer = this.gl.createBuffer();
|
||||||
|
|
||||||
// Use a line width of 2.0 for legibility
|
|
||||||
this.gl.lineWidth(2.0);
|
|
||||||
|
|
||||||
// Enable blending, for smoothness
|
// Enable blending, for smoothness
|
||||||
this.gl.enable(this.gl.BLEND);
|
this.gl.enable(this.gl.BLEND);
|
||||||
this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
|
this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
|
||||||
@ -138,14 +171,18 @@ define([
|
|||||||
((v - this.origin[1]) / this.dimensions[1]) * this.height;
|
((v - this.origin[1]) / this.dimensions[1]) * this.height;
|
||||||
};
|
};
|
||||||
|
|
||||||
DrawWebGL.prototype.doDraw = function (drawType, buf, color, points) {
|
DrawWebGL.prototype.doDraw = function (drawType, buf, color, points, shape) {
|
||||||
if (this.isContextLost) {
|
if (this.isContextLost) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const shapeCode = MARKER_SHAPES[shape] ? MARKER_SHAPES[shape].drawWebGL : 0;
|
||||||
|
|
||||||
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer);
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer);
|
||||||
this.gl.bufferData(this.gl.ARRAY_BUFFER, buf, this.gl.DYNAMIC_DRAW);
|
this.gl.bufferData(this.gl.ARRAY_BUFFER, buf, this.gl.DYNAMIC_DRAW);
|
||||||
this.gl.vertexAttribPointer(this.aVertexPosition, 2, this.gl.FLOAT, false, 0, 0);
|
this.gl.vertexAttribPointer(this.aVertexPosition, 2, this.gl.FLOAT, false, 0, 0);
|
||||||
this.gl.uniform4fv(this.uColor, color);
|
this.gl.uniform4fv(this.uColor, color);
|
||||||
|
this.gl.uniform1i(this.uMarkerShape, shapeCode)
|
||||||
this.gl.drawArrays(drawType, 0, points);
|
this.gl.drawArrays(drawType, 0, points);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -210,12 +247,12 @@ define([
|
|||||||
* Draw the buffer as points.
|
* Draw the buffer as points.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
DrawWebGL.prototype.drawPoints = function (buf, color, points, pointSize) {
|
DrawWebGL.prototype.drawPoints = function (buf, color, points, pointSize, shape) {
|
||||||
if (this.isContextLost) {
|
if (this.isContextLost) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.gl.uniform1f(this.uPointSize, pointSize);
|
this.gl.uniform1f(this.uPointSize, pointSize);
|
||||||
this.doDraw(this.gl.POINTS, buf, color, points);
|
this.doDraw(this.gl.POINTS, buf, color, points, shape);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
90
src/plugins/plot/src/draw/MarkerShapes.js
Normal file
90
src/plugins/plot/src/draw/MarkerShapes.js
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define([], function () {
|
||||||
|
/**
|
||||||
|
* @label string (required) display name of shape
|
||||||
|
* @drawWebGL integer (unique, required) index provided to WebGL Fragment Shader
|
||||||
|
* @drawC2D function (required) canvas2d draw function
|
||||||
|
*/
|
||||||
|
const MARKER_SHAPES = {
|
||||||
|
point: {
|
||||||
|
label: 'Point',
|
||||||
|
drawWebGL: 1,
|
||||||
|
drawC2D: function (x, y, size) {
|
||||||
|
const offset = size / 2;
|
||||||
|
|
||||||
|
this.c2d.fillRect(x - offset, y - offset, size, size);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
circle: {
|
||||||
|
label: 'Circle',
|
||||||
|
drawWebGL: 2,
|
||||||
|
drawC2D: function (x, y, size) {
|
||||||
|
const radius = size / 2;
|
||||||
|
|
||||||
|
this.c2d.beginPath();
|
||||||
|
this.c2d.arc(x, y, radius, 0, 2 * Math.PI, false);
|
||||||
|
this.c2d.closePath();
|
||||||
|
this.c2d.fill();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
diamond: {
|
||||||
|
label: 'Diamond',
|
||||||
|
drawWebGL: 3,
|
||||||
|
drawC2D: function (x, y, size) {
|
||||||
|
const offset = size / 2;
|
||||||
|
const top = [x, y + offset];
|
||||||
|
const right = [x + offset, y];
|
||||||
|
const bottom = [x, y - offset];
|
||||||
|
const left = [x - offset, y];
|
||||||
|
|
||||||
|
this.c2d.beginPath();
|
||||||
|
this.c2d.moveTo(...top);
|
||||||
|
this.c2d.lineTo(...right);
|
||||||
|
this.c2d.lineTo(...bottom);
|
||||||
|
this.c2d.lineTo(...left);
|
||||||
|
this.c2d.closePath();
|
||||||
|
this.c2d.fill();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
triangle: {
|
||||||
|
label: 'Triangle',
|
||||||
|
drawWebGL: 4,
|
||||||
|
drawC2D: function (x, y, size) {
|
||||||
|
const offset = size / 2;
|
||||||
|
const v1 = [x, y - offset];
|
||||||
|
const v2 = [x - offset, y + offset];
|
||||||
|
const v3 = [x + offset, y + offset];
|
||||||
|
|
||||||
|
this.c2d.beginPath();
|
||||||
|
this.c2d.moveTo(...v1);
|
||||||
|
this.c2d.lineTo(...v2);
|
||||||
|
this.c2d.lineTo(...v3);
|
||||||
|
this.c2d.closePath();
|
||||||
|
this.c2d.fill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return MARKER_SHAPES;
|
||||||
|
});
|
@ -22,9 +22,11 @@
|
|||||||
|
|
||||||
define([
|
define([
|
||||||
'./PlotModelFormController',
|
'./PlotModelFormController',
|
||||||
|
'../draw/MarkerShapes',
|
||||||
'lodash'
|
'lodash'
|
||||||
], function (
|
], function (
|
||||||
PlotModelFormController,
|
PlotModelFormController,
|
||||||
|
MARKER_SHAPES,
|
||||||
_
|
_
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -93,6 +95,13 @@ define([
|
|||||||
value: o.key
|
value: o.key
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
this.$scope.markerShapeOptions = Object.entries(MARKER_SHAPES)
|
||||||
|
.map(([key, obj]) => {
|
||||||
|
return {
|
||||||
|
name: obj.label,
|
||||||
|
value: key
|
||||||
|
};
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
fields: [
|
fields: [
|
||||||
@ -108,6 +117,10 @@ define([
|
|||||||
modelProp: 'markers',
|
modelProp: 'markers',
|
||||||
objectPath: dynamicPathForKey('markers')
|
objectPath: dynamicPathForKey('markers')
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
modelProp: 'markerShape',
|
||||||
|
objectPath: dynamicPathForKey('markerShape')
|
||||||
|
},
|
||||||
{
|
{
|
||||||
modelProp: 'markerSize',
|
modelProp: 'markerSize',
|
||||||
coerce: Number,
|
coerce: Number,
|
||||||
|
@ -71,8 +71,6 @@ define([
|
|||||||
this.listenTo(this.$canvas, 'mouseleave', this.untrackMousePosition, this);
|
this.listenTo(this.$canvas, 'mouseleave', this.untrackMousePosition, this);
|
||||||
this.listenTo(this.$canvas, 'mousedown', this.onMouseDown, this);
|
this.listenTo(this.$canvas, 'mousedown', this.onMouseDown, this);
|
||||||
this.listenTo(this.$canvas, 'wheel', this.wheelZoom, this);
|
this.listenTo(this.$canvas, 'wheel', this.wheelZoom, this);
|
||||||
|
|
||||||
this.watchForMarquee();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
MCTPlotController.prototype.initialize = function () {
|
MCTPlotController.prototype.initialize = function () {
|
||||||
@ -83,11 +81,6 @@ define([
|
|||||||
this.listenTo(this.$canvas, 'mousedown', this.onMouseDown, this);
|
this.listenTo(this.$canvas, 'mousedown', this.onMouseDown, this);
|
||||||
this.listenTo(this.$canvas, 'wheel', this.wheelZoom, this);
|
this.listenTo(this.$canvas, 'wheel', this.wheelZoom, this);
|
||||||
|
|
||||||
this.watchForMarquee();
|
|
||||||
|
|
||||||
this.listenTo(this.$window, 'keydown', this.toggleInteractionMode, this);
|
|
||||||
this.listenTo(this.$window, 'keyup', this.resetInteractionMode, this);
|
|
||||||
|
|
||||||
this.$scope.rectangles = [];
|
this.$scope.rectangles = [];
|
||||||
this.$scope.tickWidth = 0;
|
this.$scope.tickWidth = 0;
|
||||||
|
|
||||||
@ -243,12 +236,16 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
MCTPlotController.prototype.onMouseDown = function ($event) {
|
MCTPlotController.prototype.onMouseDown = function ($event) {
|
||||||
|
// do not monitor drag events on browser context click
|
||||||
|
if (event.ctrlKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.listenTo(this.$window, 'mouseup', this.onMouseUp, this);
|
this.listenTo(this.$window, 'mouseup', this.onMouseUp, this);
|
||||||
this.listenTo(this.$window, 'mousemove', this.trackMousePosition, this);
|
this.listenTo(this.$window, 'mousemove', this.trackMousePosition, this);
|
||||||
if (this.allowPan) {
|
if (event.altKey) {
|
||||||
return this.startPan($event);
|
return this.startPan($event);
|
||||||
}
|
} else {
|
||||||
if (this.allowMarquee) {
|
|
||||||
return this.startMarquee($event);
|
return this.startMarquee($event);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -261,11 +258,11 @@ define([
|
|||||||
this.$scope.lockHighlightPoint = !this.$scope.lockHighlightPoint;
|
this.$scope.lockHighlightPoint = !this.$scope.lockHighlightPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.allowPan) {
|
if (this.pan) {
|
||||||
return this.endPan($event);
|
return this.endPan($event);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.allowMarquee) {
|
if (this.marquee) {
|
||||||
return this.endMarquee($event);
|
return this.endMarquee($event);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -289,6 +286,9 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
MCTPlotController.prototype.startMarquee = function ($event) {
|
MCTPlotController.prototype.startMarquee = function ($event) {
|
||||||
|
this.$canvas.removeClass('plot-drag');
|
||||||
|
this.$canvas.addClass('plot-marquee');
|
||||||
|
|
||||||
this.trackMousePosition($event);
|
this.trackMousePosition($event);
|
||||||
if (this.positionOverPlot) {
|
if (this.positionOverPlot) {
|
||||||
this.freeze();
|
this.freeze();
|
||||||
@ -444,6 +444,9 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
MCTPlotController.prototype.startPan = function ($event) {
|
MCTPlotController.prototype.startPan = function ($event) {
|
||||||
|
this.$canvas.addClass('plot-drag');
|
||||||
|
this.$canvas.removeClass('plot-marquee');
|
||||||
|
|
||||||
this.trackMousePosition($event);
|
this.trackMousePosition($event);
|
||||||
this.freeze();
|
this.freeze();
|
||||||
this.pan = {
|
this.pan = {
|
||||||
@ -486,32 +489,6 @@ define([
|
|||||||
this.$scope.$emit('user:viewport:change:end');
|
this.$scope.$emit('user:viewport:change:end');
|
||||||
};
|
};
|
||||||
|
|
||||||
MCTPlotController.prototype.watchForMarquee = function () {
|
|
||||||
this.$canvas.removeClass('plot-drag');
|
|
||||||
this.$canvas.addClass('plot-marquee');
|
|
||||||
this.allowPan = false;
|
|
||||||
this.allowMarquee = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
MCTPlotController.prototype.watchForPan = function () {
|
|
||||||
this.$canvas.addClass('plot-drag');
|
|
||||||
this.$canvas.removeClass('plot-marquee');
|
|
||||||
this.allowPan = true;
|
|
||||||
this.allowMarquee = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
MCTPlotController.prototype.toggleInteractionMode = function (event) {
|
|
||||||
if (event.keyCode === 18) { // control key.
|
|
||||||
this.watchForPan();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
MCTPlotController.prototype.resetInteractionMode = function (event) {
|
|
||||||
if (event.keyCode === 18) {
|
|
||||||
this.watchForMarquee();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
MCTPlotController.prototype.freeze = function () {
|
MCTPlotController.prototype.freeze = function () {
|
||||||
this.config.yAxis.set('frozen', true);
|
this.config.yAxis.set('frozen', true);
|
||||||
this.config.xAxis.set('frozen', true);
|
this.config.xAxis.set('frozen', true);
|
||||||
|
@ -54,7 +54,8 @@ define([
|
|||||||
'./themes/snow',
|
'./themes/snow',
|
||||||
'./URLTimeSettingsSynchronizer/plugin',
|
'./URLTimeSettingsSynchronizer/plugin',
|
||||||
'./notificationIndicator/plugin',
|
'./notificationIndicator/plugin',
|
||||||
'./newFolderAction/plugin'
|
'./newFolderAction/plugin',
|
||||||
|
'./persistence/couch/plugin'
|
||||||
], function (
|
], function (
|
||||||
_,
|
_,
|
||||||
UTCTimeSystem,
|
UTCTimeSystem,
|
||||||
@ -89,12 +90,12 @@ define([
|
|||||||
Snow,
|
Snow,
|
||||||
URLTimeSettingsSynchronizer,
|
URLTimeSettingsSynchronizer,
|
||||||
NotificationIndicator,
|
NotificationIndicator,
|
||||||
NewFolderAction
|
NewFolderAction,
|
||||||
|
CouchDBPlugin
|
||||||
) {
|
) {
|
||||||
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',
|
|
||||||
Elasticsearch: 'platform/persistence/elastic'
|
Elasticsearch: 'platform/persistence/elastic'
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -126,27 +127,7 @@ define([
|
|||||||
|
|
||||||
plugins.Conductor = TimeConductorPlugin.default;
|
plugins.Conductor = TimeConductorPlugin.default;
|
||||||
|
|
||||||
plugins.CouchDB = function (url) {
|
plugins.CouchDB = CouchDBPlugin.default;
|
||||||
return function (openmct) {
|
|
||||||
if (url) {
|
|
||||||
var bundleName = "config/couch";
|
|
||||||
openmct.legacyRegistry.register(bundleName, {
|
|
||||||
"extensions": {
|
|
||||||
"constants": [
|
|
||||||
{
|
|
||||||
"key": "COUCHDB_PATH",
|
|
||||||
"value": url,
|
|
||||||
"priority": "mandatory"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
openmct.legacyRegistry.enable(bundleName);
|
|
||||||
}
|
|
||||||
|
|
||||||
openmct.legacyRegistry.enable(bundleMap.CouchDB);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
plugins.Elasticsearch = function (url) {
|
plugins.Elasticsearch = function (url) {
|
||||||
return function (openmct) {
|
return function (openmct) {
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
# Espresso Theme
|
|
||||||
A light colored theme for the Open MCT user interface.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
```js
|
|
||||||
openmct.install(openmct.plugins.Snow());
|
|
||||||
```
|
|
@ -3,7 +3,7 @@
|
|||||||
<div
|
<div
|
||||||
class="c-tabs-view__tabs-holder c-tabs"
|
class="c-tabs-view__tabs-holder c-tabs"
|
||||||
:class="{
|
:class="{
|
||||||
'is-dragging': isDragging,
|
'is-dragging': isDragging && allowEditing,
|
||||||
'is-mouse-over': allowDrop
|
'is-mouse-over': allowDrop
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
@ -22,14 +22,24 @@
|
|||||||
<button
|
<button
|
||||||
v-for="(tab,index) in tabsList"
|
v-for="(tab,index) in tabsList"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="c-tabs-view__tab c-tab"
|
class="c-tab c-tabs-view__tab"
|
||||||
:class="[
|
:class="{
|
||||||
{'is-current': isCurrent(tab)},
|
'is-current': isCurrent(tab)
|
||||||
tab.type.definition.cssClass
|
}"
|
||||||
]"
|
|
||||||
@click="showTab(tab, index)"
|
@click="showTab(tab, index)"
|
||||||
>
|
>
|
||||||
<span class="c-button__label">{{ tab.domainObject.name }}</span>
|
<div class="c-object-label"
|
||||||
|
:class="{'is-missing': tab.domainObject.status === 'missing'}"
|
||||||
|
>
|
||||||
|
<div class="c-object-label__type-icon"
|
||||||
|
:class="tab.type.definition.cssClass"
|
||||||
|
>
|
||||||
|
<span class="is-missing__indicator"
|
||||||
|
title="This item is missing"
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
|
<span class="c-button__label c-object-label__name">{{ tab.domainObject.name }}</span>
|
||||||
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@ -38,15 +48,6 @@
|
|||||||
class="c-tabs-view__object-holder"
|
class="c-tabs-view__object-holder"
|
||||||
:class="{'c-tabs-view__object-holder--hidden': !isCurrent(tab)}"
|
:class="{'c-tabs-view__object-holder--hidden': !isCurrent(tab)}"
|
||||||
>
|
>
|
||||||
<div
|
|
||||||
v-if="currentTab"
|
|
||||||
class="c-tabs-view__object-name c-object-label l-browse-bar__object-name--w"
|
|
||||||
:class="currentTab.type.definition.cssClass"
|
|
||||||
>
|
|
||||||
<div class="l-browse-bar__object-name c-object-label__name">
|
|
||||||
{{ currentTab.domainObject.name }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<object-view
|
<object-view
|
||||||
v-if="internalDomainObject.keep_alive ? currentTab : isCurrent(tab)"
|
v-if="internalDomainObject.keep_alive ? currentTab : isCurrent(tab)"
|
||||||
class="c-tabs-view__object"
|
class="c-tabs-view__object"
|
||||||
@ -58,6 +59,12 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ObjectView from '../../../ui/components/ObjectView.vue';
|
import ObjectView from '../../../ui/components/ObjectView.vue';
|
||||||
|
import {
|
||||||
|
getSearchParam,
|
||||||
|
setSearchParam,
|
||||||
|
deleteSearchParam
|
||||||
|
} from 'utils/openmctLocation';
|
||||||
|
|
||||||
|
|
||||||
var unknownObjectType = {
|
var unknownObjectType = {
|
||||||
definition: {
|
definition: {
|
||||||
@ -71,26 +78,45 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
ObjectView
|
ObjectView
|
||||||
},
|
},
|
||||||
|
props: {
|
||||||
|
isEditing: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
data: function () {
|
data: function () {
|
||||||
|
let keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
internalDomainObject: this.domainObject,
|
internalDomainObject: this.domainObject,
|
||||||
currentTab: {},
|
currentTab: {},
|
||||||
|
currentTabIndex: undefined,
|
||||||
tabsList: [],
|
tabsList: [],
|
||||||
setCurrentTab: true,
|
setCurrentTab: true,
|
||||||
isDragging: false,
|
isDragging: false,
|
||||||
allowDrop: false
|
allowDrop: false,
|
||||||
|
searchTabKey: `tabs.pos.${keyString}`
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
allowEditing() {
|
||||||
|
return !this.internalDomainObject.locked && this.isEditing;
|
||||||
|
}
|
||||||
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if (this.composition) {
|
if (this.composition) {
|
||||||
this.composition.on('add', this.addItem);
|
this.composition.on('add', this.addItem);
|
||||||
this.composition.on('remove', this.removeItem);
|
this.composition.on('remove', this.removeItem);
|
||||||
this.composition.on('reorder', this.onReorder);
|
this.composition.on('reorder', this.onReorder);
|
||||||
this.composition.load().then(() => {
|
this.composition.load().then(() => {
|
||||||
let currentTabIndex = this.domainObject.currentTabIndex;
|
let currentTabIndexFromURL = getSearchParam(this.searchTabKey);
|
||||||
|
let currentTabIndexFromDomainObject = this.internalDomainObject.currentTabIndex;
|
||||||
|
|
||||||
if (currentTabIndex !== undefined && this.tabsList.length > currentTabIndex) {
|
if (currentTabIndexFromURL !== null) {
|
||||||
this.currentTab = this.tabsList[currentTabIndex];
|
this.setCurrentTabByIndex(currentTabIndexFromURL);
|
||||||
|
} else if (currentTabIndexFromDomainObject !== undefined) {
|
||||||
|
this.setCurrentTabByIndex(currentTabIndexFromDomainObject);
|
||||||
|
this.storeCurrentTabIndexInURL(currentTabIndexFromDomainObject);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -100,20 +126,29 @@ export default {
|
|||||||
document.addEventListener('dragstart', this.dragstart);
|
document.addEventListener('dragstart', this.dragstart);
|
||||||
document.addEventListener('dragend', this.dragend);
|
document.addEventListener('dragend', this.dragend);
|
||||||
},
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.persistCurrentTabIndex(this.currentTabIndex);
|
||||||
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
this.composition.off('add', this.addItem);
|
this.composition.off('add', this.addItem);
|
||||||
this.composition.off('remove', this.removeItem);
|
this.composition.off('remove', this.removeItem);
|
||||||
this.composition.off('reorder', this.onReorder);
|
this.composition.off('reorder', this.onReorder);
|
||||||
|
|
||||||
this.unsubscribe();
|
this.unsubscribe();
|
||||||
|
this.clearCurrentTabIndexFromURL();
|
||||||
|
|
||||||
document.removeEventListener('dragstart', this.dragstart);
|
document.removeEventListener('dragstart', this.dragstart);
|
||||||
document.removeEventListener('dragend', this.dragend);
|
document.removeEventListener('dragend', this.dragend);
|
||||||
},
|
},
|
||||||
methods:{
|
methods:{
|
||||||
|
setCurrentTabByIndex(index) {
|
||||||
|
if (this.tabsList[index]) {
|
||||||
|
this.currentTab = this.tabsList[index];
|
||||||
|
}
|
||||||
|
},
|
||||||
showTab(tab, index) {
|
showTab(tab, index) {
|
||||||
if (index !== undefined) {
|
if (index !== undefined) {
|
||||||
this.storeCurrentTabIndex(index);
|
this.storeCurrentTabIndexInURL(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.currentTab = tab;
|
this.currentTab = tab;
|
||||||
@ -133,6 +168,10 @@ export default {
|
|||||||
this.setCurrentTab = false;
|
this.setCurrentTab = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
reset() {
|
||||||
|
this.currentTab = {};
|
||||||
|
this.setCurrentTab = true;
|
||||||
|
},
|
||||||
removeItem(identifier) {
|
removeItem(identifier) {
|
||||||
let pos = this.tabsList.findIndex(tab =>
|
let pos = this.tabsList.findIndex(tab =>
|
||||||
tab.domainObject.identifier.namespace === identifier.namespace && tab.domainObject.identifier.key === identifier.key
|
tab.domainObject.identifier.namespace === identifier.namespace && tab.domainObject.identifier.key === identifier.key
|
||||||
@ -144,6 +183,10 @@ export default {
|
|||||||
if (this.isCurrent(tabToBeRemoved)) {
|
if (this.isCurrent(tabToBeRemoved)) {
|
||||||
this.showTab(this.tabsList[this.tabsList.length - 1], this.tabsList.length - 1);
|
this.showTab(this.tabsList[this.tabsList.length - 1], this.tabsList.length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.tabsList.length) {
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onReorder(reorderPlan) {
|
onReorder(reorderPlan) {
|
||||||
let oldTabs = this.tabsList.slice();
|
let oldTabs = this.tabsList.slice();
|
||||||
@ -154,7 +197,7 @@ export default {
|
|||||||
},
|
},
|
||||||
onDrop(e) {
|
onDrop(e) {
|
||||||
this.setCurrentTab = true;
|
this.setCurrentTab = true;
|
||||||
this.storeCurrentTabIndex(this.tabsList.length);
|
this.storeCurrentTabIndexInURL(this.tabsList.length);
|
||||||
},
|
},
|
||||||
dragstart(e) {
|
dragstart(e) {
|
||||||
if (e.dataTransfer.types.includes('openmct/domain-object-path')) {
|
if (e.dataTransfer.types.includes('openmct/domain-object-path')) {
|
||||||
@ -177,8 +220,19 @@ export default {
|
|||||||
updateInternalDomainObject(domainObject) {
|
updateInternalDomainObject(domainObject) {
|
||||||
this.internalDomainObject = domainObject;
|
this.internalDomainObject = domainObject;
|
||||||
},
|
},
|
||||||
storeCurrentTabIndex(index) {
|
persistCurrentTabIndex(index) {
|
||||||
this.openmct.objects.mutate(this.internalDomainObject, 'currentTabIndex', index);
|
this.openmct.objects.mutate(this.internalDomainObject, 'currentTabIndex', index);
|
||||||
|
},
|
||||||
|
storeCurrentTabIndexInURL(index) {
|
||||||
|
let currentTabIndexInURL = getSearchParam(this.searchTabKey);
|
||||||
|
|
||||||
|
if (index !== currentTabIndexInURL) {
|
||||||
|
setSearchParam(this.searchTabKey, index);
|
||||||
|
this.currentTabIndex = index;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clearCurrentTabIndexFromURL() {
|
||||||
|
deleteSearchParam(this.searchTabKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,20 +42,28 @@ define([
|
|||||||
let component;
|
let component;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
show: function (element) {
|
show: function (element, editMode) {
|
||||||
component = new Vue({
|
component = new Vue({
|
||||||
el: element,
|
el: element,
|
||||||
components: {
|
components: {
|
||||||
TabsComponent: TabsComponent.default
|
TabsComponent: TabsComponent.default
|
||||||
},
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isEditing: editMode
|
||||||
|
};
|
||||||
|
},
|
||||||
provide: {
|
provide: {
|
||||||
openmct,
|
openmct,
|
||||||
domainObject,
|
domainObject,
|
||||||
composition: openmct.composition.get(domainObject)
|
composition: openmct.composition.get(domainObject)
|
||||||
},
|
},
|
||||||
template: '<tabs-component></tabs-component>'
|
template: '<tabs-component :isEditing="isEditing"></tabs-component>'
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
onEditModeChange(editMode) {
|
||||||
|
component.isEditing = editMode;
|
||||||
|
},
|
||||||
destroy: function (element) {
|
destroy: function (element) {
|
||||||
component.$destroy();
|
component.$destroy();
|
||||||
component = undefined;
|
component = undefined;
|
||||||
|
@ -151,6 +151,10 @@ $browseFrameColor: pullForward($colorBodyBg, 10%);
|
|||||||
$browseFrameBorder: 1px solid $browseFrameColor; // Frames in Disp and Flex Layouts when frame is showing
|
$browseFrameBorder: 1px solid $browseFrameColor; // Frames in Disp and Flex Layouts when frame is showing
|
||||||
$browseSelectableShdwHov: rgba($colorBodyFg, 0.3) 0 0 3px;
|
$browseSelectableShdwHov: rgba($colorBodyFg, 0.3) 0 0 3px;
|
||||||
$browseSelectedBorder: 1px solid rgba($colorBodyFg, 0.4);
|
$browseSelectedBorder: 1px solid rgba($colorBodyFg, 0.4);
|
||||||
|
$filterItemHoverFg: brightness(1.2) contrast(1.1);
|
||||||
|
$filterItemMissing: brightness(0.6) grayscale(1);
|
||||||
|
$opacityMissing: 0.5;
|
||||||
|
$borderMissing: 1px dashed $colorAlert !important;
|
||||||
|
|
||||||
/************************************************** EDITING */
|
/************************************************** EDITING */
|
||||||
$editUIColor: $uiColor; // Base color
|
$editUIColor: $uiColor; // Base color
|
||||||
|
@ -155,6 +155,10 @@ $browseFrameColor: pullForward($colorBodyBg, 10%);
|
|||||||
$browseFrameBorder: 1px solid $browseFrameColor; // Frames in Disp and Flex Layouts when frame is showing
|
$browseFrameBorder: 1px solid $browseFrameColor; // Frames in Disp and Flex Layouts when frame is showing
|
||||||
$browseSelectableShdwHov: rgba($colorBodyFg, 0.3) 0 0 3px;
|
$browseSelectableShdwHov: rgba($colorBodyFg, 0.3) 0 0 3px;
|
||||||
$browseSelectedBorder: 1px solid rgba($colorBodyFg, 0.4);
|
$browseSelectedBorder: 1px solid rgba($colorBodyFg, 0.4);
|
||||||
|
$filterItemHoverFg: brightness(1.2) contrast(1.1);
|
||||||
|
$filterItemMissing: contrast(0.2);
|
||||||
|
$opacityMissing: 0.5;
|
||||||
|
$borderMissing: 1px dashed $colorAlert !important;
|
||||||
|
|
||||||
/************************************************** EDITING */
|
/************************************************** EDITING */
|
||||||
$editUIColor: $uiColor; // Base color
|
$editUIColor: $uiColor; // Base color
|
||||||
|
@ -151,6 +151,10 @@ $browseFrameColor: pullForward($colorBodyBg, 10%);
|
|||||||
$browseFrameBorder: 1px solid $browseFrameColor; // Frames in Disp and Flex Layouts when frame is showing
|
$browseFrameBorder: 1px solid $browseFrameColor; // Frames in Disp and Flex Layouts when frame is showing
|
||||||
$browseSelectableShdwHov: rgba($colorBodyFg, 0.3) 0 0 3px;
|
$browseSelectableShdwHov: rgba($colorBodyFg, 0.3) 0 0 3px;
|
||||||
$browseSelectedBorder: 1px solid rgba($colorBodyFg, 0.4);
|
$browseSelectedBorder: 1px solid rgba($colorBodyFg, 0.4);
|
||||||
|
$filterItemHoverFg: brightness(0.9);
|
||||||
|
$filterItemMissing: contrast(0.2);
|
||||||
|
$opacityMissing: 0.4;
|
||||||
|
$borderMissing: 1px dashed $colorAlert !important;
|
||||||
|
|
||||||
/************************************************** EDITING */
|
/************************************************** EDITING */
|
||||||
$editUIColor: $uiColor; // Base color
|
$editUIColor: $uiColor; // Base color
|
||||||
|
@ -61,7 +61,7 @@ $plotXBarH: 35px;
|
|||||||
$plotLegendH: 20px;
|
$plotLegendH: 20px;
|
||||||
$plotLegendWidthCollapsed: 20%;
|
$plotLegendWidthCollapsed: 20%;
|
||||||
$plotLegendWidthExpanded: 50%;
|
$plotLegendWidthExpanded: 50%;
|
||||||
$plotSwatchD: 10px;
|
$plotSwatchD: 12px;
|
||||||
$plotDisplayArea: (0, 0, $plotXBarH, $plotYBarW); // 1: Top, 2: right, 3: bottom, 4: left
|
$plotDisplayArea: (0, 0, $plotXBarH, $plotYBarW); // 1: Top, 2: right, 3: bottom, 4: left
|
||||||
$plotMinH: 95px;
|
$plotMinH: 95px;
|
||||||
$controlBarH: 25px;
|
$controlBarH: 25px;
|
||||||
|
@ -411,7 +411,17 @@ select {
|
|||||||
|
|
||||||
.c-tab {
|
.c-tab {
|
||||||
// Used in Tab View, generic tabs
|
// Used in Tab View, generic tabs
|
||||||
background: $colorBtnBg;
|
$notchSize: 7px;
|
||||||
|
$clipPath:
|
||||||
|
polygon(
|
||||||
|
0% 0%,
|
||||||
|
calc(100% - #{$notchSize}) 0%,
|
||||||
|
100% #{$notchSize},
|
||||||
|
100% calc(100% - #{$notchSize}),
|
||||||
|
100% 100%,
|
||||||
|
0% 100%
|
||||||
|
);
|
||||||
|
background: rgba($colorBtnBg, 0.7);
|
||||||
color: $colorBtnFg;
|
color: $colorBtnFg;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -420,21 +430,15 @@ select {
|
|||||||
margin: 1px 1px 0 0;
|
margin: 1px 1px 0 0;
|
||||||
padding: $interiorMargin $interiorMarginLg;
|
padding: $interiorMargin $interiorMarginLg;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
clip-path: $clipPath;
|
||||||
|
-webkit-clip-path: $clipPath; // Safari
|
||||||
|
|
||||||
--notchSize: 7px;
|
> * + * {
|
||||||
|
margin-left: $interiorMargin;
|
||||||
clip-path:
|
}
|
||||||
polygon(
|
|
||||||
0% 0%,
|
|
||||||
calc(100% - var(--notchSize)) 0%,
|
|
||||||
100% var(--notchSize),
|
|
||||||
100% calc(100% - var(--notchSize)),
|
|
||||||
100% 100%,
|
|
||||||
0% 100%
|
|
||||||
);
|
|
||||||
|
|
||||||
@include hover() {
|
@include hover() {
|
||||||
background: $colorBtnBgHov;
|
filter: $filterHov;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-current {
|
&.is-current {
|
||||||
|
@ -43,6 +43,8 @@ mct-plot {
|
|||||||
|
|
||||||
.c-plot,
|
.c-plot,
|
||||||
.gl-plot {
|
.gl-plot {
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
.s-status-taking-snapshot & {
|
.s-status-taking-snapshot & {
|
||||||
.c-control-bar {
|
.c-control-bar {
|
||||||
display: none;
|
display: none;
|
||||||
@ -51,6 +53,17 @@ mct-plot {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*********************** MISSING ITEM INDICATORS */
|
||||||
|
.is-missing__indicator {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.is-missing {
|
||||||
|
@include isMissing();
|
||||||
|
.is-missing__indicator {
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.c-plot {
|
.c-plot {
|
||||||
@ -74,6 +87,7 @@ mct-plot {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
&--stacked {
|
&--stacked {
|
||||||
@ -102,18 +116,15 @@ mct-plot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.gl-plot {
|
.gl-plot {
|
||||||
display: flex;
|
display: flex;
|
||||||
position: relative;
|
flex: 1 1 auto;
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
min-height: $plotMinH;
|
|
||||||
|
|
||||||
/*********************** AXIS AND DISPLAY AREA */
|
/*********************** AXIS AND DISPLAY AREA */
|
||||||
.plot-wrapper-axis-and-display-area {
|
.plot-wrapper-axis-and-display-area {
|
||||||
position: relative;
|
position: relative;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
|
min-height: $plotMinH;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gl-plot-wrapper-display-area-and-x-axis {
|
.gl-plot-wrapper-display-area-and-x-axis {
|
||||||
@ -196,7 +207,6 @@ 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;
|
|
||||||
writing-mode: vertical-lr;
|
writing-mode: vertical-lr;
|
||||||
&:before {
|
&:before {
|
||||||
// Icon denoting configurability
|
// Icon denoting configurability
|
||||||
@ -368,12 +378,6 @@ mct-plot {
|
|||||||
z-index: -10;
|
z-index: -10;
|
||||||
|
|
||||||
.l-view-section {
|
.l-view-section {
|
||||||
//$m: $interiorMargin;
|
|
||||||
//top: $m !important;
|
|
||||||
//right: $m;
|
|
||||||
//bottom: $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 {
|
||||||
display: none;
|
display: none;
|
||||||
@ -429,14 +433,18 @@ mct-plot {
|
|||||||
/****************** _LEGEND.SCSS */
|
/****************** _LEGEND.SCSS */
|
||||||
.gl-plot-legend,
|
.gl-plot-legend,
|
||||||
.c-plot-legend {
|
.c-plot-legend {
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
&__wrapper {
|
&__wrapper {
|
||||||
// Holds view-control and both collapsed and expanded legends
|
// Holds view-control and both collapsed and expanded legends
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
overflow: auto; // Prevents collapsed legend from forcing scrollbars on higher parent containers
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__view-control {
|
&__view-control {
|
||||||
padding-top: 2px;
|
padding-top: 4px;
|
||||||
margin-right: $interiorMarginSm;
|
margin-right: $interiorMarginSm;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -481,15 +489,21 @@ mct-plot {
|
|||||||
/***************** GENERAL STYLES, ALL STATES */
|
/***************** GENERAL STYLES, ALL STATES */
|
||||||
.plot-legend-item {
|
.plot-legend-item {
|
||||||
// General styles for legend items, both expanded and collapsed legend states
|
// General styles for legend items, both expanded and collapsed legend states
|
||||||
|
> * + * {
|
||||||
|
margin-left: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
|
||||||
.plot-series-color-swatch {
|
.plot-series-color-swatch {
|
||||||
border-radius: $smallCr;
|
border-radius: 30%; //$smallCr;
|
||||||
border: 1px solid $colorBodyBg;
|
border: 1px solid $colorBodyBg;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
flex: 0 0 auto;
|
||||||
height: $plotSwatchD;
|
height: $plotSwatchD;
|
||||||
width: $plotSwatchD;
|
width: $plotSwatchD;
|
||||||
}
|
}
|
||||||
.plot-series-name {
|
.plot-series-name {
|
||||||
display: inline;
|
display: inline;
|
||||||
|
@include ellipsize();
|
||||||
}
|
}
|
||||||
|
|
||||||
.plot-series-value {
|
.plot-series-value {
|
||||||
@ -497,6 +511,16 @@ mct-plot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.plot-series-swatch-and-name {
|
||||||
|
display: flex;
|
||||||
|
flex: 0 1 auto;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
> * + * {
|
||||||
|
margin-left: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.plot-wrapper-expanded-legend {
|
.plot-wrapper-expanded-legend {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
@ -505,9 +529,6 @@ mct-plot {
|
|||||||
&.plot-legend-collapsed .plot-wrapper-expanded-legend { display: none; }
|
&.plot-legend-collapsed .plot-wrapper-expanded-legend { display: none; }
|
||||||
&.plot-legend-expanded .plot-wrapper-collapsed-legend { display: none; }
|
&.plot-legend-expanded .plot-wrapper-collapsed-legend { display: none; }
|
||||||
|
|
||||||
&.plot-legend-collapsed .icon-cursor-lock::before { padding-right: 5px; }
|
|
||||||
&.plot-legend-expanded .icon-cursor-lock::before { padding-right: 5px; }
|
|
||||||
|
|
||||||
&.plot-legend-top .gl-plot-legend { margin-bottom: $interiorMargin; }
|
&.plot-legend-top .gl-plot-legend { margin-bottom: $interiorMargin; }
|
||||||
&.plot-legend-bottom .gl-plot-legend { margin-top: $interiorMargin; }
|
&.plot-legend-bottom .gl-plot-legend { margin-top: $interiorMargin; }
|
||||||
&.plot-legend-right .gl-plot-legend { margin-left: $interiorMargin; }
|
&.plot-legend-right .gl-plot-legend { margin-left: $interiorMargin; }
|
||||||
@ -521,19 +542,13 @@ mct-plot {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: stretch;
|
justify-content: stretch;
|
||||||
&:not(:first-child) {
|
|
||||||
margin-left: $interiorMarginLg;
|
|
||||||
}
|
|
||||||
.plot-series-swatch-and-name,
|
.plot-series-swatch-and-name,
|
||||||
.plot-series-value {
|
.plot-series-value {
|
||||||
@include ellipsize();
|
@include ellipsize();
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.plot-series-swatch-and-name {
|
|
||||||
margin-right: $interiorMarginSm;
|
|
||||||
}
|
|
||||||
|
|
||||||
.plot-series-value {
|
.plot-series-value {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
@ -543,7 +558,7 @@ mct-plot {
|
|||||||
/***************** GENERAL STYLES, EXPANDED */
|
/***************** GENERAL STYLES, EXPANDED */
|
||||||
&.plot-legend-expanded {
|
&.plot-legend-expanded {
|
||||||
.gl-plot-legend {
|
.gl-plot-legend {
|
||||||
// max-height: 70%;
|
max-height: 70%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.plot-wrapper-expanded-legend {
|
.plot-wrapper-expanded-legend {
|
||||||
@ -564,6 +579,11 @@ mct-plot {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
|
> .plot-legend-item + .plot-legend-item {
|
||||||
|
// Space between plot items
|
||||||
|
margin-left: $interiorMarginLg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -595,12 +615,17 @@ mct-plot {
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
||||||
|
> * + * {
|
||||||
|
// Space between plot items
|
||||||
|
margin-top: $interiorMarginSm;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.plot-legend-item {
|
.plot-legend-item {
|
||||||
margin-bottom: 1px;
|
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
flex-wrap: wrap;
|
flex-wrap: nowrap;
|
||||||
.plot-series-swatch-and-name {
|
.plot-series-swatch-and-name {
|
||||||
|
@include ellipsize();
|
||||||
flex: 0 1 auto;
|
flex: 0 1 auto;
|
||||||
min-width: 20%;
|
min-width: 20%;
|
||||||
}
|
}
|
||||||
@ -654,3 +679,24 @@ mct-plot {
|
|||||||
display: inline-block !important;
|
display: inline-block !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*********************** CURSOR LOCK INDICATOR */
|
||||||
|
[class*='c-state-indicator__alert-cursor-lock'] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[class*='is-cursor-locked'] {
|
||||||
|
background: rgba($colorInfo, 0.1);
|
||||||
|
|
||||||
|
[class*='c-state-indicator__alert-cursor-lock'] {
|
||||||
|
@include userSelectNone();
|
||||||
|
color: $colorInfo;
|
||||||
|
display: block;
|
||||||
|
margin-right: $interiorMarginSm;
|
||||||
|
|
||||||
|
&[class*='--verbose'] {
|
||||||
|
padding: $interiorMarginSm;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -117,6 +117,33 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mixin isMissing($absPos: false) {
|
||||||
|
// Common styles to be applied to tree items, object labels, grid and list item views
|
||||||
|
//opacity: 0.7;
|
||||||
|
//pointer-events: none; // Don't think we can do this, as disables title hover on icon element
|
||||||
|
|
||||||
|
.is-missing__indicator {
|
||||||
|
display: none ;
|
||||||
|
text-shadow: $colorBodyBg 0 0 2px;
|
||||||
|
color: $colorAlert;
|
||||||
|
font-family: symbolsfont;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: $glyph-icon-alert-triangle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $absPos {
|
||||||
|
.is-missing__indicator {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-missing .is-missing__indicator,
|
||||||
|
.is-missing .is-missing__indicator { display: block !important; }
|
||||||
|
}
|
||||||
|
|
||||||
@mixin bgDiagonalStripes($c: yellow, $a: 0.1, $d: 40px) {
|
@mixin bgDiagonalStripes($c: yellow, $a: 0.1, $d: 40px) {
|
||||||
background-image: linear-gradient(-45deg,
|
background-image: linear-gradient(-45deg,
|
||||||
rgba($c, $a) 25%, transparent 25%,
|
rgba($c, $a) 25%, transparent 25%,
|
||||||
|
@ -49,8 +49,6 @@ table {
|
|||||||
td {
|
td {
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
a { color: $colorBtnMajorBg; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.is-editing {
|
.is-editing {
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
@import "../plugins/filters/components/filters-view.scss";
|
@import "../plugins/filters/components/filters-view.scss";
|
||||||
@import "../plugins/filters/components/global-filters.scss";
|
@import "../plugins/filters/components/global-filters.scss";
|
||||||
@import "../plugins/flexibleLayout/components/flexible-layout.scss";
|
@import "../plugins/flexibleLayout/components/flexible-layout.scss";
|
||||||
@import "../plugins/folderView/components/grid-item.scss";
|
|
||||||
@import "../plugins/folderView/components/grid-view.scss";
|
@import "../plugins/folderView/components/grid-view.scss";
|
||||||
@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";
|
||||||
|
@ -24,13 +24,24 @@
|
|||||||
class="c-so-view has-local-controls"
|
class="c-so-view has-local-controls"
|
||||||
:class="{
|
:class="{
|
||||||
'c-so-view--no-frame': !hasFrame,
|
'c-so-view--no-frame': !hasFrame,
|
||||||
'has-complex-content': complexContent
|
'has-complex-content': complexContent,
|
||||||
|
'is-missing': domainObject.status === 'missing'
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div class="c-so-view__header">
|
<div class="c-so-view__header">
|
||||||
<div class="c-object-label"
|
<div class="c-object-label"
|
||||||
:class="[cssClass, classList]"
|
:class="{
|
||||||
|
classList,
|
||||||
|
'is-missing': domainObject.status === 'missing'
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
|
<div class="c-object-label__type-icon"
|
||||||
|
:class="cssClass"
|
||||||
|
>
|
||||||
|
<span class="is-missing__indicator"
|
||||||
|
title="This item is missing"
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
<div class="c-object-label__name">
|
<div class="c-object-label__name">
|
||||||
{{ domainObject && domainObject.name }}
|
{{ domainObject && domainObject.name }}
|
||||||
</div>
|
</div>
|
||||||
@ -46,6 +57,9 @@
|
|||||||
@click="expand"
|
@click="expand"
|
||||||
></button>
|
></button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="is-missing__indicator"
|
||||||
|
title="This item is missing"
|
||||||
|
></div>
|
||||||
<object-view
|
<object-view
|
||||||
ref="objectView"
|
ref="objectView"
|
||||||
class="c-so-view__object-view"
|
class="c-so-view__object-view"
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<a
|
<a
|
||||||
class="c-tree__item__label c-object-label"
|
class="c-tree__item__label c-object-label"
|
||||||
:class="classList"
|
:class="{
|
||||||
|
classList,
|
||||||
|
'is-missing': observedObject.status === 'missing'
|
||||||
|
}"
|
||||||
draggable="true"
|
draggable="true"
|
||||||
:href="objectLink"
|
:href="objectLink"
|
||||||
@dragstart="dragStart"
|
@dragstart="dragStart"
|
||||||
@ -10,8 +13,14 @@
|
|||||||
<div
|
<div
|
||||||
class="c-tree__item__type-icon c-object-label__type-icon"
|
class="c-tree__item__type-icon c-object-label__type-icon"
|
||||||
:class="typeClass"
|
:class="typeClass"
|
||||||
></div>
|
>
|
||||||
<div class="c-tree__item__name c-object-label__name">{{ observedObject.name }}</div>
|
<span class="is-missing__indicator"
|
||||||
|
title="This item is missing"
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
|
<div class="c-tree__item__name c-object-label__name">
|
||||||
|
{{ observedObject.name }}
|
||||||
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -53,7 +53,9 @@ export default {
|
|||||||
mounted() {
|
mounted() {
|
||||||
this.currentObject = this.object;
|
this.currentObject = this.object;
|
||||||
this.updateView();
|
this.updateView();
|
||||||
this.$el.addEventListener('dragover', this.onDragOver);
|
this.$el.addEventListener('dragover', this.onDragOver, {
|
||||||
|
capture: true
|
||||||
|
});
|
||||||
this.$el.addEventListener('drop', this.editIfEditable, {
|
this.$el.addEventListener('drop', this.editIfEditable, {
|
||||||
capture: true
|
capture: true
|
||||||
});
|
});
|
||||||
@ -269,6 +271,7 @@ export default {
|
|||||||
if (provider &&
|
if (provider &&
|
||||||
provider.canEdit &&
|
provider.canEdit &&
|
||||||
provider.canEdit(this.currentObject) &&
|
provider.canEdit(this.currentObject) &&
|
||||||
|
this.isEditingAllowed() &&
|
||||||
!this.openmct.editor.isEditing()) {
|
!this.openmct.editor.isEditing()) {
|
||||||
this.openmct.editor.edit();
|
this.openmct.editor.edit();
|
||||||
}
|
}
|
||||||
@ -301,7 +304,7 @@ export default {
|
|||||||
objectPath= this.currentObjectPath || this.objectPath,
|
objectPath= this.currentObjectPath || this.objectPath,
|
||||||
parentObject = objectPath[1];
|
parentObject = objectPath[1];
|
||||||
|
|
||||||
return [browseObject, parentObject, this.currentObject].every(object => !object.locked);
|
return [browseObject, parentObject, this.currentObject].every(object => object && !object.locked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,10 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
|
&.is-missing {
|
||||||
|
border: $borderMissing;
|
||||||
|
}
|
||||||
|
|
||||||
/*************************** HEADER */
|
/*************************** HEADER */
|
||||||
&__header {
|
&__header {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
@ -39,6 +43,15 @@
|
|||||||
> .c-so-view__local-controls {
|
> .c-so-view__local-controls {
|
||||||
top: $interiorMarginSm; right: $interiorMarginSm;
|
top: $interiorMarginSm; right: $interiorMarginSm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.is-missing {
|
||||||
|
@include isMissing($absPos: true);
|
||||||
|
|
||||||
|
.is-missing__indicator {
|
||||||
|
top: $interiorMargin;
|
||||||
|
left: $interiorMargin;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__local-controls {
|
&__local-controls {
|
||||||
@ -61,13 +74,11 @@
|
|||||||
height: 0; // Chrome 73 overflow bug fix
|
height: 0; // Chrome 73 overflow bug fix
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
||||||
.u-angular-object-view-wrapper {
|
|
||||||
.u-fills-container {
|
.u-fills-container {
|
||||||
// Expand component types that fill a container
|
// Expand component types that fill a container
|
||||||
@include abs();
|
@include abs();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.c-click-icon,
|
.c-click-icon,
|
||||||
.c-button {
|
.c-button {
|
||||||
@ -78,8 +89,5 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.u-angular-object-view-wrapper {
|
.u-angular-object-view-wrapper {
|
||||||
flex: 1 1 auto;
|
display: contents;
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
@ -7,19 +7,33 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
|
> * + * { margin-left: $interiorMargin; }
|
||||||
|
|
||||||
&__name {
|
&__name {
|
||||||
@include ellipsize();
|
@include ellipsize();
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__type-icon,
|
&__type-icon {
|
||||||
&:before {
|
|
||||||
// Type icon. Must be an HTML entity to allow inclusion of alias indicator.
|
// Type icon. Must be an HTML entity to allow inclusion of alias indicator.
|
||||||
display: block;
|
display: block;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
opacity: 0.6;
|
}
|
||||||
margin-right: $interiorMargin;
|
|
||||||
|
&.is-missing {
|
||||||
|
@include isMissing($absPos: true);
|
||||||
|
|
||||||
|
[class*='__type-icon']:before,
|
||||||
|
[class*='__type-icon']:after{
|
||||||
|
opacity: $opacityMissing;
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-missing__indicator {
|
||||||
|
right: -3px;
|
||||||
|
top: -3px;
|
||||||
|
transform: scale(0.7);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,6 +41,8 @@
|
|||||||
border-radius: $controlCr;
|
border-radius: $controlCr;
|
||||||
padding: $interiorMarginSm 1px;
|
padding: $interiorMarginSm 1px;
|
||||||
|
|
||||||
|
> * + * { margin-left: $interiorMarginSm; }
|
||||||
|
|
||||||
&__name {
|
&__name {
|
||||||
display: inline;
|
display: inline;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -1,16 +1,24 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="c-inspector__header">
|
<div class="c-inspector__header">
|
||||||
<div v-if="!multiSelect && !singleSelectNonObject"
|
<div v-if="!multiSelect"
|
||||||
class="c-inspector__selected-w c-object-label"
|
class="c-inspector__selected-w c-object-label"
|
||||||
|
:class="{'is-missing': domainObject.status === 'missing' }"
|
||||||
|
>
|
||||||
|
<div class="c-object-label__type-icon"
|
||||||
:class="typeCssClass"
|
:class="typeCssClass"
|
||||||
>
|
>
|
||||||
<span class="c-inspector__selected c-object-label__name">{{ item.name }}</span>
|
<span class="is-missing__indicator"
|
||||||
|
title="This item is missing"
|
||||||
|
></span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="singleSelectNonObject"
|
|
||||||
class="c-inspector__selected-w c-object-label"
|
<span v-if="!singleSelectNonObject"
|
||||||
:class="typeCssClass"
|
class="c-inspector__selected c-object-label__name"
|
||||||
>
|
>{{ item.name }}</span>
|
||||||
<span class="c-inspector__selected c-object-label__name c-inspector__selected--non-domain-object">Layout Object</span>
|
<span v-if="singleSelectNonObject"
|
||||||
|
class="c-inspector__selected c-object-label__name c-inspector__selected--non-domain-object"
|
||||||
|
>Layout Object</span>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div v-if="multiSelect"
|
<div v-if="multiSelect"
|
||||||
class="c-inspector__multiple-selected-w"
|
class="c-inspector__multiple-selected-w"
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
// Thi is on purpose: want extra margin on top object-name element
|
// This is on purpose: want extra margin on top object-name element
|
||||||
margin-top: $interiorMargin;
|
margin-top: $interiorMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,6 +41,8 @@
|
|||||||
|
|
||||||
&__content {
|
&__content {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__elements {
|
&__elements {
|
||||||
|
@ -8,8 +8,18 @@
|
|||||||
></button>
|
></button>
|
||||||
<div
|
<div
|
||||||
class="l-browse-bar__object-name--w c-object-label"
|
class="l-browse-bar__object-name--w c-object-label"
|
||||||
:class="[ type.cssClass, classList ]"
|
:class="{
|
||||||
|
classList,
|
||||||
|
'is-missing': domainObject.status === 'missing'
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
|
<div class="c-object-label__type-icon"
|
||||||
|
:class="type.cssClass"
|
||||||
|
>
|
||||||
|
<span class="is-missing__indicator"
|
||||||
|
title="This item is missing"
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
<span
|
<span
|
||||||
class="l-browse-bar__object-name c-object-label__name c-input-inline"
|
class="l-browse-bar__object-name c-object-label__name c-input-inline"
|
||||||
contenteditable
|
contenteditable
|
||||||
|
@ -354,9 +354,9 @@
|
|||||||
@include headerFont(1.4em);
|
@include headerFont(1.4em);
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|
||||||
&:before {
|
.is-missing__indicator {
|
||||||
// Icon
|
right: -5px !important;
|
||||||
margin-right: $interiorMargin;
|
top: -4px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,12 +52,13 @@
|
|||||||
padding: $interiorMarginSm $interiorMargin;
|
padding: $interiorMarginSm $interiorMargin;
|
||||||
transition: background 150ms ease;
|
transition: background 150ms ease;
|
||||||
|
|
||||||
|
&__type-icon {
|
||||||
|
color: $colorItemTreeIcon;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: $colorItemTreeHoverBg;
|
background: $colorItemTreeHoverBg;
|
||||||
|
filter: $filterHov;
|
||||||
[class*="__name"] {
|
|
||||||
color: $colorItemTreeHoverFg;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-navigated-object,
|
&.is-navigated-object,
|
||||||
@ -81,12 +82,6 @@
|
|||||||
margin-left: $interiorMarginSm;
|
margin-left: $interiorMarginSm;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
|
||||||
.c-tree__item__type-icon:before {
|
|
||||||
color: $colorItemTreeIconHover;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-navigated-object,
|
&.is-navigated-object,
|
||||||
&.is-selected {
|
&.is-selected {
|
||||||
.c-tree__item__type-icon:before {
|
.c-tree__item__type-icon:before {
|
||||||
@ -115,10 +110,6 @@
|
|||||||
color: $colorItemTreeFg;
|
color: $colorItemTreeFg;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__type-icon {
|
|
||||||
color: $colorItemTreeIcon;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-alias {
|
&.is-alias {
|
||||||
// Object is an alias to an original.
|
// Object is an alias to an original.
|
||||||
[class*='__type-icon'] {
|
[class*='__type-icon'] {
|
||||||
|
@ -76,16 +76,23 @@
|
|||||||
[class*='minify-indicators'] {
|
[class*='minify-indicators'] {
|
||||||
// All styles for minified Indicators should go in here
|
// All styles for minified Indicators should go in here
|
||||||
.c-indicator:not(.no-minify) {
|
.c-indicator:not(.no-minify) {
|
||||||
|
border: 1px solid transparent; // Hack to make minified sizing work in Safari. Have no idea why this works.
|
||||||
|
overflow: visible;
|
||||||
|
transition: transform;
|
||||||
|
|
||||||
@include hover() {
|
@include hover() {
|
||||||
background: $colorIndicatorBgHov;
|
background: $colorIndicatorBgHov;
|
||||||
|
transition: transform 250ms ease-in 200ms; // Go-away transition
|
||||||
|
|
||||||
.c-indicator__label {
|
.c-indicator__label {
|
||||||
box-shadow: $colorIndicatorMenuBgShdw;
|
box-shadow: $colorIndicatorMenuBgShdw;
|
||||||
transform: scale(1.0);
|
transform: scale(1.0);
|
||||||
transition: all 100ms ease-out 100ms;
|
overflow: visible;
|
||||||
|
transition: transform 100ms ease-out 100ms; // Appear transition
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.c-indicator__label {
|
.c-indicator__label {
|
||||||
transition: all 250ms ease-in 200ms;
|
transition: transform 250ms ease-in 200ms; // Go-away transition
|
||||||
background: $colorIndicatorMenuBg;
|
background: $colorIndicatorMenuBg;
|
||||||
color: $colorIndicatorMenuFg;
|
color: $colorIndicatorMenuFg;
|
||||||
border-radius: $controlCr;
|
border-radius: $controlCr;
|
||||||
@ -95,7 +102,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
transform-origin: 90% 0;
|
transform-origin: 90% 0;
|
||||||
transform: scale(0.0);
|
transform: scale(0.0);
|
||||||
overflow: visible;
|
overflow: hidden;
|
||||||
z-index: 50;
|
z-index: 50;
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
|
Reference in New Issue
Block a user