Merge branch 'master' into adding-units

Merg'n master
This commit is contained in:
Jamie Vigliotta
2020-07-24 10:41:17 -07:00
69 changed files with 898 additions and 412 deletions

View File

@ -113,7 +113,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>

View File

@ -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
}); });
} }

View File

@ -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",

View File

@ -31,13 +31,13 @@
</mct-form> </mct-form>
</div> </div>
<div class="c-overlay__button-bar"> <div class="c-overlay__button-bar">
<a class='c-button c-button--major' <button class='c-button c-button--major'
ng-class="{ disabled: !createForm.$valid }" ng-class="{ disabled: !createForm.$valid }"
ng-click="ngModel.confirm()"> ng-click="ngModel.confirm()">
OK OK
</a> </button>
<a class='c-button ' <button class='c-button '
ng-click="ngModel.cancel()"> ng-click="ngModel.cancel()">
Cancel Cancel
</a> </button>
</div> </div>

View File

@ -31,13 +31,13 @@
</mct-include> </mct-include>
</div> </div>
<div class="c-overlay__button-bar"> <div class="c-overlay__button-bar">
<a ng-repeat="option in ngModel.dialog.options" <button ng-repeat="option in ngModel.dialog.options"
href='' href=''
class="s-button lg" class="s-button lg"
title="{{option.description}}" title="{{option.description}}"
ng-click="ngModel.confirm(option.key)" ng-click="ngModel.confirm(option.key)"
ng-class="{ major: $first, subtle: !$first }"> ng-class="{ major: $first, subtle: !$first }">
{{option.name}} {{option.name}}
</a> </button>
</div> </div>
</mct-container> </mct-container>

View File

@ -24,7 +24,7 @@
<div class="c-overlay__outer"> <div class="c-overlay__outer">
<button ng-click="ngModel.cancel()" <button ng-click="ngModel.cancel()"
ng-if="ngModel.cancel" ng-if="ngModel.cancel"
class="c-click-icon c-overlay__close-button icon-x-in-circle"></button> class="c-click-icon c-overlay__close-button icon-x"></button>
<div class="c-overlay__contents" ng-transclude></div> <div class="c-overlay__contents" ng-transclude></div>
</div> </div>
</div> </div>

View File

@ -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;

View File

@ -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);
}); });
}); });

View File

@ -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;
}; };

View File

@ -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();
}); });
}); });

View File

@ -297,7 +297,8 @@ define([
"persistenceService", "persistenceService",
"identifierService", "identifierService",
"notificationService", "notificationService",
"$q" "$q",
"openmct"
] ]
}, },
{ {

View File

@ -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);

View File

@ -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();
}); });
}); });
}); });
});
} }
); );

View File

@ -29,9 +29,9 @@
type="text" tabindex="10000" type="text" tabindex="10000"
ng-model="ngModel.input" ng-model="ngModel.input"
ng-keyup="controller.search()"/> ng-keyup="controller.search()"/>
<a class="c-search__clear-input clear-icon icon-x-in-circle" <button class="c-search__clear-input clear-icon icon-x-in-circle"
ng-class="{show: !(ngModel.input === '' || ngModel.input === undefined)}" ng-class="{show: !(ngModel.input === '' || ngModel.input === undefined)}"
ng-click="ngModel.input = ''; controller.search()"></a> ng-click="ngModel.input = ''; controller.search()"></button>
<!-- To prevent double triggering of clicks on click away, render <!-- To prevent double triggering of clicks on click away, render
non-clickable version of the button when menu active--> non-clickable version of the button when menu active-->
<a ng-if="!toggle.isActive()" class="menu-icon context-available" <a ng-if="!toggle.isActive()" class="menu-icon context-available"
@ -45,16 +45,16 @@
</mct-include> </mct-include>
</div> </div>
<a class="c-button c-search__btn-cancel" <button class="c-button c-search__btn-cancel"
ng-show="!(ngModel.input === '' || ngModel.input === undefined)" ng-show="!(ngModel.input === '' || ngModel.input === undefined)"
ng-click="ngModel.input = ''; ngModel.checkAll = true; menuController.checkAll(); controller.search()"> ng-click="ngModel.input = ''; ngModel.checkAll = true; menuController.checkAll(); controller.search()">
Cancel</a> Cancel</button>
</div> </div>
<div class="active-filter-display flex-elem holder" <div class="active-filter-display flex-elem holder"
ng-class="{invisible: ngModel.filtersString === '' || ngModel.filtersString === undefined || !ngModel.search}"> ng-class="{invisible: ngModel.filtersString === '' || ngModel.filtersString === undefined || !ngModel.search}">
<a class="clear-filters icon-x-in-circle s-icon-button" <button class="clear-filters icon-x-in-circle s-icon-button"
ng-click="ngModel.checkAll = true; menuController.checkAll()"></a>Filtered by: {{ ngModel.filtersString }} ng-click="ngModel.checkAll = true; menuController.checkAll()"></button>Filtered by: {{ ngModel.filtersString }}
</div> </div>
<div class="flex-elem holder results-msg" ng-model="ngModel" ng-show="!loading && ngModel.search"> <div class="flex-elem holder results-msg" ng-model="ngModel" ng-show="!loading && ngModel.search">
@ -72,7 +72,7 @@
ng-model="ngModel" ng-model="ngModel"
class="l-flex-row flex-elem grows"> class="l-flex-row flex-elem grows">
</mct-representation> </mct-representation>
<a class="load-more-button s-button vsm" ng-if="controller.areMore()" ng-click="controller.loadMore()">More Results</a> <button class="load-more-button s-button vsm" ng-if="controller.areMore()" ng-click="controller.loadMore()">More Results</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -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
) )
); );

View File

@ -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;
}); });

View 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();
});
});
})
});

View File

@ -7,7 +7,7 @@
<div class="c-overlay__outer"> <div class="c-overlay__outer">
<button <button
v-if="dismissable" v-if="dismissable"
class="c-click-icon c-overlay__close-button icon-x-in-circle" class="c-click-icon c-overlay__close-button icon-x"
@click="destroy" @click="destroy"
></button> ></button>
<div <div

View File

@ -29,13 +29,12 @@
} }
&__close-button { &__close-button {
$p: $interiorMargin; $p: $interiorMargin + 2px;
border-radius: 100% !important;
color: $overlayColorFg; color: $overlayColorFg;
display: inline-block; font-size: 1.5em;
font-size: 1.25em;
position: absolute; position: absolute;
top: $p; right: $p; top: $p; right: $p;
z-index: 99;
} }
&__contents { &__contents {
@ -43,7 +42,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
outline: none; outline: none;
overflow: hidden; overflow: auto;
} }
&__top-bar { &__top-bar {
@ -87,6 +86,10 @@
.c-click-icon { .c-click-icon {
filter: $overlayBrightnessAdjust; filter: $overlayBrightnessAdjust;
} }
.c-object-label__name {
filter: $objectLabelNameFilter;
}
} }
body.desktop { body.desktop {
@ -100,7 +103,6 @@ body.desktop {
} }
// Overlay types, styling for desktop. Appended to .l-overlay-wrapper element. // Overlay types, styling for desktop. Appended to .l-overlay-wrapper element.
.l-overlay-large,
.l-overlay-small, .l-overlay-small,
.l-overlay-fit { .l-overlay-fit {
.c-overlay__outer { .c-overlay__outer {
@ -118,8 +120,28 @@ body.desktop {
.l-overlay-large { .l-overlay-large {
// Default // Default
.c-overlay__outer { $pad: $interiorMarginLg;
@include overlaySizing($overlayOuterMarginLg); $tbPad: floor($pad * 0.8);
$lrPad: $pad;
.c-overlay {
&__blocker {
display: none;
}
&__outer {
@include overlaySizing($overlayOuterMarginFullscreen);
padding: $tbPad $lrPad;
}
&__close-button {
//top: $interiorMargin;
//right: $interiorMargin;
}
}
.l-browse-bar {
margin-right: 50px; // Don't cover close button
margin-bottom: $interiorMargin;
} }
} }

View File

@ -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);
}; };

View File

@ -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);

View File

@ -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));
}; };

View File

@ -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;
} }

View File

@ -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)

View File

@ -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) {

View File

@ -127,7 +127,7 @@ export default class AllTelemetryCriterion extends TelemetryCriterion {
if (validatedData) { if (validatedData) {
if (this.isStalenessCheck()) { if (this.isStalenessCheck()) {
if (this.stalenessSubscription[validatedData.id]) { if (this.stalenessSubscription && this.stalenessSubscription[validatedData.id]) {
this.stalenessSubscription[validatedData.id].update(validatedData); this.stalenessSubscription[validatedData.id].update(validatedData);
} }
this.telemetryDataCache[validatedData.id] = false; this.telemetryDataCache[validatedData.id] = false;

View File

@ -142,12 +142,14 @@ export default class TelemetryCriterion extends EventEmitter {
}; };
} }
let telemetryObject = this.telemetryObject;
return this.openmct.telemetry.request( return this.openmct.telemetry.request(
this.telemetryObject, this.telemetryObject,
options options
).then(results => { ).then(results => {
const latestDatum = results.length ? results[results.length - 1] : {}; const latestDatum = results.length ? results[results.length - 1] : {};
const normalizedDatum = this.createNormalizedDatum(latestDatum, this.telemetryObject); const normalizedDatum = this.createNormalizedDatum(latestDatum, telemetryObject);
return { return {
id: this.id, id: this.id,

View File

@ -21,12 +21,14 @@
*****************************************************************************/ *****************************************************************************/
import TelemetryCriterion from "./TelemetryCriterion"; import TelemetryCriterion from "./TelemetryCriterion";
import { getMockTelemetry } from "utils/testing";
let openmct = {}, let openmct = {},
mockListener, mockListener,
testCriterionDefinition, testCriterionDefinition,
testTelemetryObject, testTelemetryObject,
telemetryCriterion; telemetryCriterion,
mockTelemetry = getMockTelemetry();
describe("The telemetry criterion", function () { describe("The telemetry criterion", function () {
@ -60,7 +62,7 @@ describe("The telemetry criterion", function () {
}; };
openmct.objects = jasmine.createSpyObj('objects', ['get', 'makeKeyString']); openmct.objects = jasmine.createSpyObj('objects', ['get', 'makeKeyString']);
openmct.objects.makeKeyString.and.returnValue(testTelemetryObject.identifier.key); openmct.objects.makeKeyString.and.returnValue(testTelemetryObject.identifier.key);
openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject', "subscribe", "getMetadata", "getValueFormatter"]); openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject', "subscribe", "getMetadata", "getValueFormatter", "request"]);
openmct.telemetry.isTelemetryObject.and.returnValue(true); openmct.telemetry.isTelemetryObject.and.returnValue(true);
openmct.telemetry.subscribe.and.returnValue(function () {}); openmct.telemetry.subscribe.and.returnValue(function () {});
openmct.telemetry.getValueFormatter.and.returnValue({ openmct.telemetry.getValueFormatter.and.returnValue({
@ -109,4 +111,30 @@ describe("The telemetry criterion", function () {
}); });
expect(telemetryCriterion.result).toBeTrue(); expect(telemetryCriterion.result).toBeTrue();
}); });
describe('the LAD request', () => {
beforeEach(async () => {
let telemetryRequestResolve;
let telemetryRequestPromise = new Promise((resolve) => {
telemetryRequestResolve = resolve;
});
openmct.telemetry.request.and.callFake(() => {
setTimeout(() => {
telemetryRequestResolve(mockTelemetry);
}, 100);
return telemetryRequestPromise;
});
});
it("returns results for slow LAD requests", async function () {
const criteriaRequest = telemetryCriterion.requestLAD();
telemetryCriterion.destroy();
expect(telemetryCriterion.telemetryObject).toBeUndefined();
setTimeout(() => {
criteriaRequest.then((result) => {
expect(result).toBeDefined();
});
}, 300);
});
});
}); });

View File

@ -25,14 +25,14 @@
class="l-layout__frame c-frame" class="l-layout__frame c-frame"
:class="{ :class="{
'no-frame': !item.hasFrame, 'no-frame': !item.hasFrame,
'u-inspectable': inspectable 'u-inspectable': inspectable,
'is-in-small-container': size.width < 600 || size.height < 600
}" }"
:style="style" :style="style"
> >
<slot></slot> <slot></slot>
<div <div
class="c-frame-edit__move" class="c-frame__move-bar"
@mousedown="isEditing ? startMove([1,1], [0,0], $event) : null" @mousedown="isEditing ? startMove([1,1], [0,0], $event) : null"
></div> ></div>
</div> </div>
@ -61,6 +61,13 @@ export default {
} }
}, },
computed: { computed: {
size() {
let {width, height} = this.item;
return {
width: (this.gridSize[0] * width),
height: (this.gridSize[1] * height)
};
},
style() { style() {
let {x, y, width, height} = this.item; let {x, y, width, height} = this.item;
return { return {

View File

@ -22,7 +22,7 @@
<template> <template>
<div <div
class="l-layout__frame c-frame no-frame" class="l-layout__frame c-frame no-frame c-line-view"
:class="[styleClass]" :class="[styleClass]"
:style="style" :style="style"
> >
@ -31,14 +31,20 @@
height="100%" height="100%"
> >
<line <line
class="c-line-view__hover-indicator"
v-bind="linePosition"
vector-effect="non-scaling-stroke"
/>
<line
class="c-line-view__line"
v-bind="linePosition" v-bind="linePosition"
:stroke="stroke" :stroke="stroke"
stroke-width="2" vector-effect="non-scaling-stroke"
/> />
</svg> </svg>
<div <div
class="c-frame-edit__move" class="c-frame__move-bar"
@mousedown="startDrag($event)" @mousedown="startDrag($event)"
></div> ></div>
<div <div
@ -49,7 +55,8 @@
class="c-frame-edit__handle" class="c-frame-edit__handle"
:class="startHandleClass" :class="startHandleClass"
@mousedown="startDrag($event, 'start')" @mousedown="startDrag($event, 'start')"
></div> >
</div>
<div <div
class="c-frame-edit__handle" class="c-frame-edit__handle"
:class="endHandleClass" :class="endHandleClass"
@ -68,14 +75,18 @@ const START_HANDLE_QUADRANTS = {
1: 'c-frame-edit__handle--sw', 1: 'c-frame-edit__handle--sw',
2: 'c-frame-edit__handle--se', 2: 'c-frame-edit__handle--se',
3: 'c-frame-edit__handle--ne', 3: 'c-frame-edit__handle--ne',
4: 'c-frame-edit__handle--nw' 4: 'c-frame-edit__handle--nw',
5: 'c-frame-edit__handle--nw',
6: 'c-frame-edit__handle--ne'
}; };
const END_HANDLE_QUADRANTS = { const END_HANDLE_QUADRANTS = {
1: 'c-frame-edit__handle--ne', 1: 'c-frame-edit__handle--ne',
2: 'c-frame-edit__handle--nw', 2: 'c-frame-edit__handle--nw',
3: 'c-frame-edit__handle--sw', 3: 'c-frame-edit__handle--sw',
4: 'c-frame-edit__handle--se' 4: 'c-frame-edit__handle--se',
5: 'c-frame-edit__handle--sw',
6: 'c-frame-edit__handle--nw'
}; };
export default { export default {
@ -158,6 +169,12 @@ export default {
}, },
vectorQuadrant() { vectorQuadrant() {
let {x, y, x2, y2} = this.position; let {x, y, x2, y2} = this.position;
if (x2 === x) {
return 5; // Vertical line
}
if (y2 === y) {
return 6; // Horizontal line
}
if (x2 > x) { if (x2 > x) {
if (y2 < y) { if (y2 < y) {
return 1; return 1;
@ -170,21 +187,27 @@ export default {
return 3; return 3;
}, },
linePosition() { linePosition() {
return this.vectorQuadrant % 2 !== 0 let pos = {};
// odd vectorQuadrant slopes up switch(this.vectorQuadrant) {
? { case 1:
x1: '0%', case 3:
y1: '100%', // slopes up
x2: '100%', pos = {x1: '0%', y1: '100%', x2: '100%', y2: '0%'};
y2: '0%' break;
case 5:
// vertical
pos = {x1: '0%', y1: '0%', x2: '0%', y2: '100%'};
break;
case 6:
// horizontal
pos = {x1: '0%', y1: '0%', x2: '100%', y2: '0%'};
break;
default:
// slopes down
pos = {x1: '0%', y1: '0%', x2: '100%', y2: '100%'};
break;
} }
// even vectorQuadrant slopes down return pos;
: {
x1: '0%',
y1: '0%',
x2: '100%',
y2: '100%'
};
} }
}, },
watch: { watch: {
@ -209,8 +232,7 @@ export default {
layoutItem: this.item, layoutItem: this.item,
index: this.index index: this.index
}; };
this.removeSelectable = this.openmct.selection.selectable( this.removeSelectable = this.openmct.selection.selectable(this.$el, this.context, this.initSelect);
this.$el, this.context, this.initSelect);
}, },
destroyed() { destroyed() {
if (this.removeSelectable) { if (this.removeSelectable) {
@ -224,12 +246,17 @@ export default {
document.body.addEventListener('mousemove', this.continueDrag); document.body.addEventListener('mousemove', this.continueDrag);
document.body.addEventListener('mouseup', this.endDrag); document.body.addEventListener('mouseup', this.endDrag);
this.startPosition = [event.pageX, event.pageY]; this.startPosition = [event.pageX, event.pageY];
this.dragPosition = { let {x, y, x2, y2} = this.item;
x: this.item.x, this.dragPosition = {x, y, x2, y2};
y: this.item.y, if (x === x2 || y === y2) {
x2: this.item.x2, if (y > y2 || x < x2) {
y2: this.item.y2 if (this.dragging === 'start') {
}; this.dragging = 'end';
} else if (this.dragging === 'end') {
this.dragging = 'start';
}
}
}
event.preventDefault(); event.preventDefault();
}, },
continueDrag(event) { continueDrag(event) {
@ -263,7 +290,7 @@ export default {
}, },
calculateDragPosition(pxDeltaX, pxDeltaY) { calculateDragPosition(pxDeltaX, pxDeltaY) {
let gridDeltaX = Math.round(pxDeltaX / this.gridSize[0]); let gridDeltaX = Math.round(pxDeltaX / this.gridSize[0]);
let gridDeltaY = Math.round(pxDeltaY / this.gridSize[0]); // TODO: should this be gridSize[1]? let gridDeltaY = Math.round(pxDeltaY / this.gridSize[1]);
let {x, y, x2, y2} = this.item; let {x, y, x2, y2} = this.item;
let dragPosition = {x, y, x2, y2}; let dragPosition = {x, y, x2, y2};

View File

@ -0,0 +1,60 @@
.c-box-view {
border-width: $drawingObjBorderW !important;
display: flex;
align-items: stretch;
.c-frame & {
@include abs();
}
}
.c-line-view {
&.c-frame {
box-shadow: none !important;
}
.c-frame-edit {
border: none;
}
.c-handle-info {
background: rgba(#999, 0.2);
padding: 2px;
position: absolute;
top: 5px; left: 5px;
white-space: nowrap;
}
svg {
// Prevent clipping when line is horizontal and vertical
min-height: 1px;
min-width: 1px;
// Must use !important to counteract setting in normalize.min.css
overflow: visible;
}
&__line {
stroke-linecap: round;
stroke-width: $drawingObjBorderW;
}
&__hover-indicator {
display: none;
opacity: 0.5;
stroke: $editFrameColorHov;
stroke-width: $drawingObjBorderW + 4;
}
.is-editing & {
// Needed to allow line to be moved
$w: 4px;
min-width: $w;
min-height: $w;
&:hover {
[class*='__hover-indicator'] {
display: inline;
}
}
}
}

View File

@ -1,8 +0,0 @@
.c-box-view {
display: flex;
align-items: stretch;
.c-frame & {
@include abs();
}
}

View File

@ -7,9 +7,13 @@
> *:first-child { > *:first-child {
flex: 1 1 auto; flex: 1 1 auto;
} }
&.is-in-small-container {
//background: rgba(blue, 0.1);
}
} }
.c-frame-edit__move { .c-frame__move-bar {
display: none; display: none;
} }
@ -29,7 +33,7 @@
border: $editFrameSelectedBorder; border: $editFrameSelectedBorder;
box-shadow: $editFrameSelectedShdw; box-shadow: $editFrameSelectedShdw;
.c-frame-edit__move { .c-frame__move-bar {
cursor: move; cursor: move;
} }
} }
@ -37,7 +41,7 @@
/******************* DEFAULT STYLES FOR -EDIT__MOVE */ /******************* DEFAULT STYLES FOR -EDIT__MOVE */
// All object types // All object types
.c-frame-edit__move { .c-frame__move-bar {
@include abs(); @include abs();
display: block; display: block;
} }
@ -52,7 +56,7 @@
transition-delay: $moveBarOutDelay; transition-delay: $moveBarOutDelay;
} }
+ .c-frame-edit__move { + .c-frame__move-bar {
display: none; display: none;
} }
@ -61,14 +65,14 @@
.l-layout { .l-layout {
/******************* 0 - 1 ITEM SELECTED */ /******************* 0 - 1 ITEM SELECTED */
&:not(.is-multi-selected) { &:not(.is-multi-selected) {
> .l-layout__frame[s-selected] { > .l-layout__frame {
> .c-so-view.has-complex-content { > .c-so-view.has-complex-content {
> .c-so-view__local-controls { > .c-so-view__local-controls {
transition: transform $transOutTime ease-in-out; transition: transform $transOutTime ease-in-out;
transition-delay: $moveBarOutDelay; transition-delay: $moveBarOutDelay;
} }
+ .c-frame-edit__move { + .c-frame__move-bar {
transition: $transOut; transition: $transOut;
transition-delay: $moveBarOutDelay; transition-delay: $moveBarOutDelay;
@include userSelectNone(); @include userSelectNone();
@ -89,7 +93,7 @@
$lrOffset: 25%; $lrOffset: 25%;
@include grippy($editFrameMovebarColorFg); @include grippy($editFrameMovebarColorFg);
content: ''; content: '';
display: block; display: none;
position: absolute; position: absolute;
top: $tbOffset; top: $tbOffset;
right: $lrOffset; right: $lrOffset;
@ -111,7 +115,7 @@
transition-delay: 0s; transition-delay: 0s;
} }
+ .c-frame-edit__move { + .c-frame__move-bar {
transition: $transIn; transition: $transIn;
transition-delay: 0s; transition-delay: 0s;
height: $editFrameMovebarH; height: $editFrameMovebarH;
@ -119,12 +123,19 @@
} }
} }
} }
> .l-layout__frame[s-selected] {
> .c-so-view.has-complex-content {
+ .c-frame__move-bar:before {
display: block;
}
}
}
} }
/******************* > 1 ITEMS SELECTED */ /******************* > 1 ITEMS SELECTED */
&.is-multi-selected { &.is-multi-selected {
.l-layout__frame[s-selected] { .l-layout__frame[s-selected] {
> .c-so-view.has-complex-content + .c-frame-edit__move { > .c-so-view.has-complex-content + .c-frame__move-bar {
display: block; display: block;
} }
} }

View File

@ -27,13 +27,14 @@
border: 1px solid transparent; border: 1px solid transparent;
} }
&.is-missing {
@include isMissing($absPos: true); @include isMissing($absPos: true);
border: $borderMissing;
.is-missing__indicator { .is-missing__indicator {
top: 0; top: 0;
left: 0; left: 0;
} }
&.is-missing {
border: $borderMissing;
} }
} }

View File

@ -31,11 +31,11 @@
<div class="c-imagery__control-bar"> <div class="c-imagery__control-bar">
<div class="c-imagery__timestamp">{{ getTime() }}</div> <div class="c-imagery__timestamp">{{ getTime() }}</div>
<div class="h-local-controls flex-elem"> <div class="h-local-controls flex-elem">
<a <button
class="c-button icon-pause pause-play" class="c-button icon-pause pause-play"
:class="{'is-paused': paused()}" :class="{'is-paused': paused()}"
@click="paused(!paused())" @click="paused(!paused())"
></a> ></button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -42,6 +42,7 @@
height: 135px; height: 135px;
overflow-x: auto; overflow-x: auto;
overflow-y: hidden; overflow-y: hidden;
padding-bottom: $interiorMarginSm;
&.is-paused { &.is-paused {
background: rgba($colorPausedBg, 0.4); background: rgba($colorPausedBg, 0.4);
@ -99,7 +100,7 @@
.c-imagery { .c-imagery {
.h-local-controls--overlay-content { .h-local-controls--overlay-content {
position: absolute; position: absolute;
right: $interiorMargin; top: $interiorMargin; left: $interiorMargin; top: $interiorMargin;
z-index: 2; z-index: 2;
background: $colorLocalControlOvrBg; background: $colorLocalControlOvrBg;
border-radius: $basicCr; border-radius: $basicCr;

View File

@ -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"
>&nbsp;{{ snapshots.length }} of {{ getNotebookSnapshotMaxCount() }} >&nbsp;{{ 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"
/> />

View File

@ -21,8 +21,11 @@
--> -->
<div class="gl-plot plot-legend-{{legend.get('position')}} {{legend.get('expanded')? 'plot-legend-expanded' : 'plot-legend-collapsed'}}"> <div class="gl-plot plot-legend-{{legend.get('position')}} {{legend.get('expanded')? 'plot-legend-expanded' : 'plot-legend-collapsed'}}">
<div class="c-plot-legend gl-plot-legend" <div class="c-plot-legend gl-plot-legend"
ng-class="{ 'hover-on-plot': !!highlights.length }" ng-class="{
ng-show="legend.get('position') !== 'hidden'"> 'hover-on-plot': !!highlights.length,
'is-legend-hidden': legend.get('hideLegendWhenSmall')
}"
>
<div class="c-plot-legend__view-control gl-plot-legend__view-control c-disclosure-triangle is-enabled" <div class="c-plot-legend__view-control gl-plot-legend__view-control c-disclosure-triangle is-enabled"
ng-class="{ 'c-disclosure-triangle--expanded': legend.get('expanded') }" ng-class="{ 'c-disclosure-triangle--expanded': legend.get('expanded') }"
ng-click="legend.set('expanded', !legend.get('expanded'));"> ng-click="legend.set('expanded', !legend.get('expanded'));">
@ -36,7 +39,9 @@
ng-class="{'is-cursor-locked': !!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="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-class="{'is-missing': series.domainObject.status === 'missing'}" ng-class="{
'is-missing': series.domainObject.status === 'missing'
}"
ng-repeat="series in series track by $index" ng-repeat="series in series track by $index"
> >
<div class="plot-series-swatch-and-name"> <div class="plot-series-swatch-and-name">
@ -89,7 +94,9 @@
</thead> </thead>
<tr ng-repeat="series in series" <tr ng-repeat="series in series"
class="plot-legend-item" class="plot-legend-item"
ng-class="{'is-missing': series.domainObject.status === 'missing'}" ng-class="{
'is-missing': series.domainObject.status === 'missing'
}"
> >
<td class="plot-series-swatch-and-name"> <td class="plot-series-swatch-and-name">
<span class="plot-series-color-swatch" <span class="plot-series-color-swatch"
@ -136,7 +143,7 @@
<div class="plot-wrapper-axis-and-display-area flex-elem grows"> <div class="plot-wrapper-axis-and-display-area flex-elem grows">
<div class="gl-plot-axis-area gl-plot-y has-local-controls" <div class="gl-plot-axis-area gl-plot-y has-local-controls"
ng-style="{ ng-style="{
width: (tickWidth + 30) + 'px' width: (tickWidth + 20) + 'px'
}"> }">
<div class="gl-plot-label gl-plot-y-label" <div class="gl-plot-label gl-plot-y-label"
@ -154,7 +161,6 @@
</option> </option>
</select> </select>
<mct-ticks axis="yAxis"> <mct-ticks axis="yAxis">
<div ng-repeat="tick in ticks track by tick.value" <div ng-repeat="tick in ticks track by tick.value"
class="gl-plot-tick gl-plot-y-tick-label" class="gl-plot-tick gl-plot-y-tick-label"
@ -167,17 +173,15 @@
</div> </div>
<div class="gl-plot-wrapper-display-area-and-x-axis" <div class="gl-plot-wrapper-display-area-and-x-axis"
ng-style="{ ng-style="{
left: (tickWidth + 30) + 'px' left: (tickWidth + 20) + 'px'
}"> }">
<div class="gl-plot-display-area has-local-controls has-cursor-guides">
<div class="l-state-indicators"> <div class="l-state-indicators">
<span class="l-state-indicators__alert-no-lad t-object-alert t-alert-unsynced icon-alert-triangle" <span class="l-state-indicators__alert-no-lad t-object-alert t-alert-unsynced icon-alert-triangle"
title="This plot is not currently displaying the latest data. Reset pan/zoom to view latest data."></span> title="This plot is not currently displaying the latest data. Reset pan/zoom to view latest data."></span>
<span class="l-state-indicators__alert-cursor-lock icon-cursor-lock"
title="Telemetry point selection is locked. Click anywhere in the plot to unlock."
ng-if="lockHighlightPoint"></span>
</div> </div>
<div class="gl-plot-display-area has-local-controls has-cursor-guides">
<mct-ticks axis="xAxis"> <mct-ticks axis="xAxis">
<div class="gl-plot-hash hash-v" <div class="gl-plot-hash hash-v"
ng-repeat="tick in ticks track by tick.value" ng-repeat="tick in ticks track by tick.value"

View File

@ -114,6 +114,11 @@
title="The position of the legend relative to the plot display area.">Position</div> title="The position of the legend relative to the plot display area.">Position</div>
<div class="grid-cell value capitalize">{{ config.legend.get('position') }}</div> <div class="grid-cell value capitalize">{{ config.legend.get('position') }}</div>
</li> </li>
<li class="grid-row">
<div class="grid-cell label"
title="Hide the legend when the plot is small">Hide when plot small</div>
<div class="grid-cell value">{{ config.legend.get('hideLegendWhenSmall') ? "Yes" : "No" }}</div>
</li>
<li class="grid-row"> <li class="grid-row">
<div class="grid-cell label" <div class="grid-cell label"
title="Show the legend expanded by default">Expand by Default</div> title="Show the legend expanded by default">Expand by Default</div>

View File

@ -174,7 +174,6 @@
title="The position of the legend relative to the plot display area.">Position</div> title="The position of the legend relative to the plot display area.">Position</div>
<div class="grid-cell value"> <div class="grid-cell value">
<select ng-model="form.position"> <select ng-model="form.position">
<option value="hidden">Hidden</option>
<option value="top">Top</option> <option value="top">Top</option>
<option value="right">Right</option> <option value="right">Right</option>
<option value="bottom">Bottom</option> <option value="bottom">Bottom</option>
@ -182,6 +181,11 @@
</select> </select>
</div> </div>
</li> </li>
<li class="grid-row">
<div class="grid-cell label"
title="Hide the legend when the plot is small">Hide when plot small</div>
<div class="grid-cell value"><input type="checkbox" ng-model="form.hideLegendWhenSmall"/></div>
</li>
<li class="grid-row"> <li class="grid-row">
<div class="grid-cell label" <div class="grid-cell label"
title="Show the legend expanded by default">Expand by default</div> title="Show the legend expanded by default">Expand by default</div>

View File

@ -48,6 +48,7 @@ define([
return { return {
position: 'top', position: 'top',
expandByDefault: false, expandByDefault: false,
hideLegendWhenSmall: false,
valueToShowWhenCollapsed: 'nearestValue', valueToShowWhenCollapsed: 'nearestValue',
showTimestampWhenExpanded: true, showTimestampWhenExpanded: true,
showValueWhenExpanded: true, showValueWhenExpanded: true,

View File

@ -32,6 +32,11 @@ define([
modelProp: 'position', modelProp: 'position',
objectPath: 'configuration.legend.position' objectPath: 'configuration.legend.position'
}, },
{
modelProp: 'hideLegendWhenSmall',
coerce: Boolean,
objectPath: 'configuration.legend.hideLegendWhenSmall'
},
{ {
modelProp: 'expandByDefault', modelProp: 'expandByDefault',
coerce: Boolean, coerce: Boolean,

View File

@ -17,6 +17,19 @@
margin-right: $interiorMarginSm; margin-right: $interiorMarginSm;
opacity: 0.7; opacity: 0.7;
} }
&__label {
flex: 1 1 auto;
}
&__close-btn {
flex: 0 0 auto;
pointer-events: all;
}
> * + * {
margin-left: $interiorMargin;
}
} }
&__object-holder { &__object-holder {

View File

@ -19,25 +19,21 @@
> >
Drag objects here to add them to this view. Drag objects here to add them to this view.
</div> </div>
<button <div
v-for="(tab,index) in tabsList" v-for="(tab,index) in tabsList"
:key="index" :key="index"
class="c-tabs-view__tab c-tab c-object-label" class="c-tab c-tabs-view__tab"
:class="{ :class="{
'is-current': isCurrent(tab), 'is-current': isCurrent(tab)
'is-missing': tab.domainObject.status === 'missing'
}" }"
@click="showTab(tab, index)" @click="showTab(tab, index)"
> >
<div class="c-object-label__type-icon" <span class="c-button__label c-tabs-view__tab__label">{{ tab.domainObject.name }}</span>
:class="tab.type.definition.cssClass" <button v-if="isEditing"
> class="icon-x c-click-icon c-tabs-view__tab__close-btn"
<span class="is-missing__indicator" @click="showRemoveDialog(index)"
title="This item is missing" ></button>
></span>
</div> </div>
<span class="c-button__label c-object-label__name">{{ tab.domainObject.name }}</span>
</button>
</div> </div>
<div <div
v-for="(tab, index) in tabsList" v-for="(tab, index) in tabsList"
@ -56,6 +52,7 @@
<script> <script>
import ObjectView from '../../../ui/components/ObjectView.vue'; import ObjectView from '../../../ui/components/ObjectView.vue';
import RemoveAction from '../../remove/RemoveAction.js';
import { import {
getSearchParam, getSearchParam,
setSearchParam, setSearchParam,
@ -120,6 +117,7 @@ export default {
this.unsubscribe = this.openmct.objects.observe(this.internalDomainObject, '*', this.updateInternalDomainObject); this.unsubscribe = this.openmct.objects.observe(this.internalDomainObject, '*', this.updateInternalDomainObject);
this.RemoveAction = new RemoveAction(this.openmct);
document.addEventListener('dragstart', this.dragstart); document.addEventListener('dragstart', this.dragstart);
document.addEventListener('dragend', this.dragend); document.addEventListener('dragend', this.dragend);
}, },
@ -150,6 +148,38 @@ export default {
this.currentTab = tab; this.currentTab = tab;
}, },
showRemoveDialog(index) {
if(!this.tabsList[index]) {
return;
}
let activeTab = this.tabsList[index];
let childDomainObject = activeTab.domainObject
let prompt = this.openmct.overlays.dialog({
iconClass: 'alert',
message: `This action will remove this tab from the Tabs Layout. Do you want to continue?`,
buttons: [
{
label: 'Ok',
emphasis: 'true',
callback: () => {
this.removeFromComposition(childDomainObject);
prompt.dismiss();
}
},
{
label: 'Cancel',
callback: () => {
prompt.dismiss();
}
}
]
});
},
removeFromComposition(childDomainObject) {
this.composition.remove(childDomainObject);
},
addItem(domainObject) { addItem(domainObject) {
let type = this.openmct.types.get(domainObject.type) || unknownObjectType, let type = this.openmct.types.get(domainObject.type) || unknownObjectType,
tabItem = { tabItem = {

View File

@ -54,6 +54,16 @@
width: 100%; width: 100%;
} }
} }
&__filter {
.c-table__search {
padding-top: 0;
padding-bottom: 0;
}
.is-in-small-container & {
display: none;
}
}
} }
&__headers__label { &__headers__label {
@ -86,6 +96,10 @@
height: 0; // Fixes Chrome 73 overflow bug height: 0; // Fixes Chrome 73 overflow bug
overflow-x: auto; overflow-x: auto;
overflow-y: scroll; overflow-y: scroll;
.is-editing & {
pointer-events: none;
}
} }
/******************************* TABLES */ /******************************* TABLES */
@ -138,7 +152,7 @@
} }
} }
/******************************* EDITING */ /******************************* SPECIFIC CASE WRAPPERS */
.is-editing { .is-editing {
.c-telemetry-table__headers__labels { .c-telemetry-table__headers__labels {
th[draggable], th[draggable],
@ -158,8 +172,10 @@
} }
} }
.paused { .is-paused {
border: 1px solid #ff9900; .c-table__body-w {
border: 1px solid rgba($colorPausedBg, 0.8);
}
} }
/******************************* LEGACY */ /******************************* LEGACY */

View File

@ -20,13 +20,16 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
<template> <template>
<div class="c-table-wrapper"> <div class="c-table-wrapper"
:class="{ 'is-paused': paused }"
>
<!-- main contolbar start--> <!-- main contolbar start-->
<div v-if="!marking.useAlternateControlBar" <div v-if="!marking.useAlternateControlBar"
class="c-table-control-bar c-control-bar" class="c-table-control-bar c-control-bar"
> >
<button <button
v-if="allowExport" v-if="allowExport"
v-show="!markedRows.length"
class="c-button icon-download labeled" class="c-button icon-download labeled"
title="Export this view's data" title="Export this view's data"
@click="exportAllDataAsCSV()" @click="exportAllDataAsCSV()"
@ -125,7 +128,7 @@
class="c-table c-telemetry-table c-table--filterable c-table--sortable has-control-bar" class="c-table c-telemetry-table c-table--filterable c-table--sortable has-control-bar"
:class="{ :class="{
'loading': loading, 'loading': loading,
'paused' : paused 'is-paused' : paused
}" }"
> >
<div :style="{ 'max-width': widthWithScroll, 'min-width': '150px'}"> <div :style="{ 'max-width': widthWithScroll, 'min-width': '150px'}">

View File

@ -84,6 +84,10 @@ $filterHov: brightness(1.3); // Tree, location items
$colorSelectedBg: pushBack($colorKey, 10%); $colorSelectedBg: pushBack($colorKey, 10%);
$colorSelectedFg: pullForward($colorBodyFg, 20%); $colorSelectedFg: pullForward($colorBodyFg, 20%);
// Object labels
$objectLabelTypeIconOpacity: 0.7;
$objectLabelNameFilter: brightness(1.3);
// Layout // Layout
$shellMainPad: 4px 0; $shellMainPad: 4px 0;
$shellPanePad: $interiorMargin, 7px; $shellPanePad: $interiorMargin, 7px;
@ -94,7 +98,7 @@ $sideBarHeaderBg: rgba($colorBodyFg, 0.2);
$sideBarHeaderFg: rgba($colorBodyFg, 0.7); $sideBarHeaderFg: rgba($colorBodyFg, 0.7);
// Status colors, mainly used for messaging and item ancillary symbols // Status colors, mainly used for messaging and item ancillary symbols
$colorStatusFg: #999; $colorStatusFg: #888;
$colorStatusDefault: #ccc; $colorStatusDefault: #ccc;
$colorStatusInfo: #60ba7b; $colorStatusInfo: #60ba7b;
$colorStatusInfoFilter: invert(58%) sepia(44%) saturate(405%) hue-rotate(85deg) brightness(102%) contrast(92%); $colorStatusInfoFilter: invert(58%) sepia(44%) saturate(405%) hue-rotate(85deg) brightness(102%) contrast(92%);
@ -205,9 +209,9 @@ $colorBtnMajorBg: $colorKey;
$colorBtnMajorBgHov: $colorKeyHov; $colorBtnMajorBgHov: $colorKeyHov;
$colorBtnMajorFg: $colorKeyFg; $colorBtnMajorFg: $colorKeyFg;
$colorBtnMajorFgHov: pushBack($colorBtnMajorFg, 10%); $colorBtnMajorFgHov: pushBack($colorBtnMajorFg, 10%);
$colorBtnCautionBg: #f16f6f; $colorBtnCautionBg: $colorStatusAlert;
$colorBtnCautionBgHov: #f1504e; $colorBtnCautionBgHov: #f1504e;
$colorBtnCautionFg: $colorBtnFg; $colorBtnCautionFg: $colorBtnBg;
$colorBtnActiveBg: $colorOk; $colorBtnActiveBg: $colorOk;
$colorBtnActiveFg: $colorOkFg; $colorBtnActiveFg: $colorOkFg;
$colorBtnSelectedBg: $colorSelectedBg; $colorBtnSelectedBg: $colorSelectedBg;
@ -335,7 +339,7 @@ $shdwItemText: none;
$colorTabBorder: pullForward($colorBodyBg, 10%); $colorTabBorder: pullForward($colorBodyBg, 10%);
$colorTabBodyBg: $colorBodyBg; $colorTabBodyBg: $colorBodyBg;
$colorTabBodyFg: pullForward($colorBodyFg, 20%); $colorTabBodyFg: pullForward($colorBodyFg, 20%);
$colorTabHeaderBg: rgba($colorBodyFg, 0.2); $colorTabHeaderBg: rgba($colorBodyFg, 0.15);
$colorTabHeaderFg: $colorBodyFg; $colorTabHeaderFg: $colorBodyFg;
$colorTabHeaderBorder: $colorBodyBg; $colorTabHeaderBorder: $colorBodyBg;
$colorTabGroupHeaderBg: pullForward($colorBodyBg, 5%); $colorTabGroupHeaderBg: pullForward($colorBodyBg, 5%);
@ -353,7 +357,7 @@ $stylePlotHash: dashed;
$colorPlotAreaBorder: $colorInteriorBorder; $colorPlotAreaBorder: $colorInteriorBorder;
$colorPlotLabelFg: pushBack($colorPlotFg, 20%); $colorPlotLabelFg: pushBack($colorPlotFg, 20%);
$legendHoverValueBg: rgba($colorBodyFg, 0.2); $legendHoverValueBg: rgba($colorBodyFg, 0.2);
$legendTableHeadBg: rgba($colorBodyFg, 0.15); $legendTableHeadBg: $colorTabHeaderBg;
// Tree // Tree
$colorTreeBg: transparent; $colorTreeBg: transparent;

View File

@ -88,6 +88,10 @@ $filterHov: brightness(1.3); // Tree, location items
$colorSelectedBg: pushBack($colorKey, 10%); $colorSelectedBg: pushBack($colorKey, 10%);
$colorSelectedFg: pullForward($colorBodyFg, 20%); $colorSelectedFg: pullForward($colorBodyFg, 20%);
// Object labels
$objectLabelTypeIconOpacity: 0.7;
$objectLabelNameFilter: brightness(1.3);
// Layout // Layout
$shellMainPad: 4px 0; $shellMainPad: 4px 0;
$shellPanePad: $interiorMargin, 7px; $shellPanePad: $interiorMargin, 7px;
@ -209,9 +213,9 @@ $colorBtnMajorBg: $colorKey;
$colorBtnMajorBgHov: $colorKeyHov; $colorBtnMajorBgHov: $colorKeyHov;
$colorBtnMajorFg: $colorKeyFg; $colorBtnMajorFg: $colorKeyFg;
$colorBtnMajorFgHov: pushBack($colorBtnMajorFg, 10%); $colorBtnMajorFgHov: pushBack($colorBtnMajorFg, 10%);
$colorBtnCautionBg: #f16f6f; $colorBtnCautionBg: $colorStatusAlert;
$colorBtnCautionBgHov: #f1504e; $colorBtnCautionBgHov: #f1504e;
$colorBtnCautionFg: $colorBtnFg; $colorBtnCautionFg: $colorBtnBg;
$colorBtnActiveBg: $colorOk; $colorBtnActiveBg: $colorOk;
$colorBtnActiveFg: $colorOkFg; $colorBtnActiveFg: $colorOkFg;
$colorBtnSelectedBg: $colorSelectedBg; $colorBtnSelectedBg: $colorSelectedBg;
@ -339,7 +343,7 @@ $shdwItemText: none;
$colorTabBorder: pullForward($colorBodyBg, 10%); $colorTabBorder: pullForward($colorBodyBg, 10%);
$colorTabBodyBg: $colorBodyBg; $colorTabBodyBg: $colorBodyBg;
$colorTabBodyFg: pullForward($colorBodyFg, 20%); $colorTabBodyFg: pullForward($colorBodyFg, 20%);
$colorTabHeaderBg: rgba($colorBodyFg, 0.2); $colorTabHeaderBg: rgba($colorBodyFg, 0.15);
$colorTabHeaderFg: $colorBodyFg; $colorTabHeaderFg: $colorBodyFg;
$colorTabHeaderBorder: $colorBodyBg; $colorTabHeaderBorder: $colorBodyBg;
$colorTabGroupHeaderBg: pullForward($colorBodyBg, 5%); $colorTabGroupHeaderBg: pullForward($colorBodyBg, 5%);

View File

@ -80,10 +80,14 @@ $uiColor: #289fec; // Resize bars, splitter bars, etc.
$colorInteriorBorder: rgba($colorBodyFg, 0.2); $colorInteriorBorder: rgba($colorBodyFg, 0.2);
$colorA: #999; $colorA: #999;
$colorAHov: $colorKey; $colorAHov: $colorKey;
$filterHov: brightness(1.3); // Tree, location items $filterHov: brightness(0.8) contrast(2); // Tree, location items
$colorSelectedBg: pushBack($colorKey, 40%); $colorSelectedBg: pushBack($colorKey, 40%);
$colorSelectedFg: pullForward($colorBodyFg, 10%); $colorSelectedFg: pullForward($colorBodyFg, 10%);
// Object labels
$objectLabelTypeIconOpacity: 0.5;
$objectLabelNameFilter: brightness(0.5);
// Layout // Layout
$shellMainPad: 4px 0; $shellMainPad: 4px 0;
$shellPanePad: $interiorMargin, 7px; $shellPanePad: $interiorMargin, 7px;
@ -224,7 +228,7 @@ $colorDisclosureCtrlHov: rgba($colorBodyFg, 0.7);
$btnStdH: 24px; $btnStdH: 24px;
$colorCursorGuide: rgba(black, 0.6); $colorCursorGuide: rgba(black, 0.6);
$shdwCursorGuide: rgba(white, 0.4) 0 0 2px; $shdwCursorGuide: rgba(white, 0.4) 0 0 2px;
$colorLocalControlOvrBg: rgba($colorBodyFg, 0.8); $colorLocalControlOvrBg: rgba($colorBodyBg, 0.8);
$colorSelectBg: $colorBtnBg; // This must be a solid color, not a gradient, due to usage of SVG bg in selects $colorSelectBg: $colorBtnBg; // This must be a solid color, not a gradient, due to usage of SVG bg in selects
$colorSelectFg: $colorBtnFg; $colorSelectFg: $colorBtnFg;
$colorSelectArw: lighten($colorBtnBg, 20%); $colorSelectArw: lighten($colorBtnBg, 20%);

View File

@ -40,11 +40,10 @@ $inputTextP: $inputTextPTopBtm $inputTextPLeftRight;
$menuLineH: 1.5rem; $menuLineH: 1.5rem;
$treeItemIndent: 16px; $treeItemIndent: 16px;
$treeTypeIconW: 18px; $treeTypeIconW: 18px;
$overlayOuterMarginLg: 5%;
$overlayOuterMarginFullscreen: 0%; $overlayOuterMarginFullscreen: 0%;
$overlayOuterMarginDialog: 20%; $overlayOuterMarginDialog: 20%;
$overlayInnerMargin: 25px; $overlayInnerMargin: 25px;
$mainViewPad: 2px; $mainViewPad: 0px;
/*************** Items */ /*************** Items */
$itemPadLR: 5px; $itemPadLR: 5px;
$gridItemDesk: 175px; $gridItemDesk: 175px;
@ -57,7 +56,7 @@ $tabularTdPadTB: 2px;
$plotYBarW: 60px; $plotYBarW: 60px;
$plotYLabelMinH: 20px; $plotYLabelMinH: 20px;
$plotYLabelW: 10px; $plotYLabelW: 10px;
$plotXBarH: 35px; $plotXBarH: 32px;
$plotLegendH: 20px; $plotLegendH: 20px;
$plotLegendWidthCollapsed: 20%; $plotLegendWidthCollapsed: 20%;
$plotLegendWidthExpanded: 50%; $plotLegendWidthExpanded: 50%;
@ -90,6 +89,8 @@ $messageIconD: 80px;
$messageListIconD: 32px; $messageListIconD: 32px;
/*************** Tables */ /*************** Tables */
$tableResizeColHitareaD: 6px; $tableResizeColHitareaD: 6px;
/*************** Misc */
$drawingObjBorderW: 3px;
/************************** MOBILE */ /************************** MOBILE */
$mobileMenuIconD: 24px; // Used $mobileMenuIconD: 24px; // Used

View File

@ -57,11 +57,6 @@ button {
line-height: 90%; line-height: 90%;
padding: 3px 10px; padding: 3px 10px;
@include hover() {
background: $colorBtnBgHov;
color: $colorBtnFgHov;
}
@include desktop() { @include desktop() {
font-size: 6px; font-size: 6px;
} }
@ -411,7 +406,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,16 +425,8 @@ select {
margin: 1px 1px 0 0; margin: 1px 1px 0 0;
padding: $interiorMargin $interiorMarginLg; padding: $interiorMargin $interiorMarginLg;
white-space: nowrap; white-space: nowrap;
--notchSize: 7px; clip-path: $clipPath;
clip-path: -webkit-clip-path: $clipPath; // Safari
polygon(
0% 0%,
calc(100% - var(--notchSize)) 0%,
100% var(--notchSize),
100% calc(100% - var(--notchSize)),
100% 100%,
0% 100%
);
> * + * { > * + * {
margin-left: $interiorMargin; margin-left: $interiorMargin;

View File

@ -206,10 +206,6 @@ body.desktop .has-local-controls {
&:hover { &:hover {
box-shadow: $browseSelectableShdwHov; box-shadow: $browseSelectableShdwHov;
} }
&[s-selected] {
border: $browseSelectedBorder;
}
} }
/**************************** EDITING */ /**************************** EDITING */

View File

@ -1,5 +1,5 @@
/***************************************************************************** /*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government * Open MCT, Copyright (c) 2014-2020, United States Government
* as represented by the Administrator of the National Aeronautics and Space * as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved. * Administration. All rights reserved.
* *
@ -29,16 +29,19 @@ mct-plot {
.gl-plot.child-frame { .gl-plot.child-frame {
&:hover { &:hover {
background: rgba($editUIColorBg, 0.1); background: rgba($editUIColorBg, 0.1);
box-shadow: inset rgba($editUIColorBg, 0.8) 0 0 0 1px; box-shadow: inset rgba($editUIColorBg, 0.3) 0 0 0 1px;
} }
&[s-selected] { &[s-selected] {
border: 1px solid $editUIColorFg !important; background: rgba($editUIColorBg, 0.2);
color: $editUIColorFg !important; box-shadow: inset rgba($editUIColorBg, 0.8) 0 0 0 1px;
box-shadow: $editFrameSelectedShdw;
z-index: 2; z-index: 2;
} }
} }
.plot-wrapper-axis-and-display-area {
pointer-events: none;
}
} }
.c-plot, .c-plot,
@ -67,20 +70,14 @@ mct-plot {
} }
.c-plot { .c-plot {
//$p: $mainViewPad;
@include abs($mainViewPad); @include abs($mainViewPad);
//position: absolute;
//top: $p; right: $p; bottom: $p; left: $p;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
> * + * { .c-control-bar {
margin-top: $interiorMargin;
}
.l-control-bar {
flex: 0 0 auto; flex: 0 0 auto;
margin-bottom: $interiorMargin;
} }
.l-view-section { .l-view-section {
@ -114,13 +111,17 @@ mct-plot {
} }
} }
} }
.is-in-small-container & {
.c-control-bar {
display: none;
}
}
} }
.gl-plot { .gl-plot {
display: flex; display: flex;
position: relative; flex: 1 1 auto;
width: 100%;
height: 100%;
/*********************** AXIS AND DISPLAY AREA */ /*********************** AXIS AND DISPLAY AREA */
.plot-wrapper-axis-and-display-area { .plot-wrapper-axis-and-display-area {
@ -207,7 +208,6 @@ mct-plot {
&.gl-plot-y-label { &.gl-plot-y-label {
display: block; display: block;
left: 0; top: 0; right: auto; bottom: 0; left: 0; top: 0; right: auto; bottom: 0;
padding-left: 5px;
text-orientation: mixed; text-orientation: mixed;
writing-mode: vertical-lr; writing-mode: vertical-lr;
&:before { &:before {
@ -273,7 +273,7 @@ mct-plot {
align-items: center; align-items: center;
position: absolute; position: absolute;
top: $m; top: $m;
right: $m; left: $m;
z-index: 9; z-index: 9;
&__reset { &__reset {
@ -286,15 +286,18 @@ mct-plot {
top: $m; top: $m;
right: $m; right: $m;
} }
.c-button {
box-shadow: $colorLocalControlOvrBg 0 0 0 2px;
}
} }
.l-state-indicators { .l-state-indicators {
color: $colorPausedBg; color: $colorPausedBg;
position: absolute; position: absolute;
display: block; cursor: help;
font-size: 1.5em; font-size: 1.2em;
pointer-events: none; bottom: $interiorMarginSm;
top: $interiorMarginSm;
left: $interiorMarginSm; left: $interiorMarginSm;
z-index: 2; z-index: 2;
@ -353,11 +356,11 @@ mct-plot {
.gl-plot-tick { .gl-plot-tick {
&.gl-plot-x-tick-label { &.gl-plot-x-tick-label {
top: $interiorMargin; top: $interiorMarginSm;
} }
&.gl-plot-y-tick-label { &.gl-plot-y-tick-label {
right: $interiorMargin; right: $interiorMarginSm;
left: $interiorMargin; left: auto;
} }
} }
@ -454,7 +457,21 @@ mct-plot {
@include propertiesHeader(); @include propertiesHeader();
margin-bottom: $interiorMarginSm; margin-bottom: $interiorMarginSm;
} }
.is-in-small-container & {
&.is-legend-hidden {
display: none;
} }
}
}
.c-plot--stacked {
.is-legend-hidden {
// Always show the legend in a stacked plot
display: flex !important;
}
}
.gl-plot-legend { .gl-plot-legend {
display: flex; display: flex;
@ -527,10 +544,19 @@ mct-plot {
flex: 1 1 auto; flex: 1 1 auto;
} }
.gl-plot { .plot-legend-top .gl-plot-legend { margin-bottom: $interiorMargin; }
.plot-legend-bottom .gl-plot-legend { margin-top: $interiorMargin; }
.plot-legend-left .gl-plot-legend { margin-right: $interiorMargin; }
.plot-legend-right .gl-plot-legend { margin-left: $interiorMargin; }
.gl-plot,
.c-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; }
@ -542,7 +568,6 @@ mct-plot {
.plot-legend-item { .plot-legend-item {
display: flex; display: flex;
align-items: center;
justify-content: stretch; justify-content: stretch;
.plot-series-swatch-and-name, .plot-series-swatch-and-name,
@ -571,8 +596,10 @@ mct-plot {
/***************** TOP OR BOTTOM */ /***************** TOP OR BOTTOM */
&.plot-legend-top, &.plot-legend-top,
&.plot-legend-bottom { &.plot-legend-bottom,
&.plot-legend-hidden {
// General styles when legend is on the top or bottom // General styles when legend is on the top or bottom
// -hidden included for legacy plots
flex-direction: column; flex-direction: column;
&.plot-legend-collapsed { &.plot-legend-collapsed {
@ -594,6 +621,7 @@ mct-plot {
&.plot-legend-left, &.plot-legend-left,
&.plot-legend-right { &.plot-legend-right {
// General styles when legend is on left or right // General styles when legend is on left or right
.gl-plot-legend { .gl-plot-legend {
max-height: inherit; max-height: inherit;
} }
@ -624,6 +652,7 @@ mct-plot {
} }
} }
.plot-legend-item { .plot-legend-item {
margin-bottom: $interiorMarginSm;
margin-left: 0; margin-left: 0;
flex-wrap: nowrap; flex-wrap: nowrap;
.plot-series-swatch-and-name { .plot-series-swatch-and-name {
@ -651,6 +680,31 @@ mct-plot {
} }
} }
/***************** STACKED PLOT LEGEND OVERRIDES */
.c-plot--stacked {
// Always show the legend on top, ignore any position setting
.c-plot,
.gl-plot {
flex-direction: column !important;
.c-plot-legend,
.gl-plot-legend {
margin: 0;
margin-bottom: $interiorMargin;
order: 1 !important;
width: 100% !important;
.plot-wrapper-collapsed-legend {
flex-direction: row !important;
}
}
.plot-wrapper-axis-and-display-area {
order: 2 !important;
}
}
}
/***************** CURSOR GUIDES */ /***************** CURSOR GUIDES */
[class*='c-cursor-guide'] { [class*='c-cursor-guide'] {
box-shadow: $shdwCursorGuide; box-shadow: $shdwCursorGuide;

View File

@ -123,7 +123,7 @@
//pointer-events: none; // Don't think we can do this, as disables title hover on icon element //pointer-events: none; // Don't think we can do this, as disables title hover on icon element
.is-missing__indicator { .is-missing__indicator {
display: block; display: none ;
text-shadow: $colorBodyBg 0 0 2px; text-shadow: $colorBodyBg 0 0 2px;
color: $colorAlert; color: $colorAlert;
font-family: symbolsfont; font-family: symbolsfont;
@ -139,6 +139,9 @@
z-index: 3; 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) {
@ -440,28 +443,18 @@
} }
@include hover() { @include hover() {
background: $colorBtnBgHov; filter: $filterHov;
color: $colorBtnFgHov;
} }
&[class*="--major"], &[class*="--major"],
&[class*='is-active']{ &[class*='is-active']{
background: $colorBtnMajorBg; background: $colorBtnMajorBg;
color: $colorBtnMajorFg; color: $colorBtnMajorFg;
@include hover() {
background: $colorBtnMajorBgHov;
color: $colorBtnMajorFgHov;
}
} }
&[class*='--caution'] { &[class*='--caution'] {
background: $colorBtnCautionBg; background: $colorBtnCautionBg !important;
color: $colorBtnCautionFg; color: $colorBtnCautionFg !important;
&:hover {
background: $colorBtnCautionBgHov;
}
} }
} }

View File

@ -90,14 +90,28 @@ div.c-table {
flex: 1 1 auto; flex: 1 1 auto;
} }
> * + * { .is-in-small-container & {
margin-top: $interiorMarginSm; &:not(.is-paused) {
.c-table-control-bar {
display: none;
}
}
.c-table-control-bar {
.c-click-icon,
.c-button {
&__label {
display: none;
}
}
}
} }
} }
.c-table-control-bar { .c-table-control-bar {
display: flex; display: flex;
flex: 0 0 auto; flex: 0 0 auto;
//margin-bottom: $interiorMarginSm; // This approach to allow margin to go away when control bar is hidden
padding: $interiorMarginSm 0;
> * + * { > * + * {
margin-left: $interiorMarginSm; margin-left: $interiorMarginSm;

View File

@ -3,7 +3,7 @@
@import "../plugins/condition/components/conditionals.scss"; @import "../plugins/condition/components/conditionals.scss";
@import "../plugins/conditionWidget/components/condition-widget.scss"; @import "../plugins/conditionWidget/components/condition-widget.scss";
@import "../plugins/condition/components/inspector/conditional-styles.scss"; @import "../plugins/condition/components/inspector/conditional-styles.scss";
@import "../plugins/displayLayout/components/box-view.scss"; @import "../plugins/displayLayout/components/box-and-line-views";
@import "../plugins/displayLayout/components/display-layout.scss"; @import "../plugins/displayLayout/components/display-layout.scss";
@import "../plugins/displayLayout/components/edit-marquee.scss"; @import "../plugins/displayLayout/components/edit-marquee.scss";
@import "../plugins/displayLayout/components/image-view.scss"; @import "../plugins/displayLayout/components/image-view.scss";

View File

@ -142,8 +142,11 @@ export default {
getOverlayElement(childElement) { getOverlayElement(childElement) {
const fragment = new DocumentFragment(); const fragment = new DocumentFragment();
const header = this.getPreviewHeader(); const header = this.getPreviewHeader();
const wrapper = document.createElement('div');
wrapper.classList.add('l-preview-window__object-view');
wrapper.append(childElement);
fragment.append(header); fragment.append(header);
fragment.append(childElement); fragment.append(wrapper);
return fragment; return fragment;
}, },

View File

@ -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
}); });
@ -140,7 +142,7 @@ export default {
} }
this.viewContainer = document.createElement('div'); this.viewContainer = document.createElement('div');
this.viewContainer.classList.add('u-angular-object-view-wrapper'); this.viewContainer.classList.add('l-angular-ov-wrapper');
this.$el.append(this.viewContainer); this.$el.append(this.viewContainer);
let provider = this.getViewProvider(); let provider = this.getViewProvider();
if (!provider) { if (!provider) {
@ -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();
} }

View File

@ -10,25 +10,21 @@
&__header { &__header {
flex: 0 0 auto; flex: 0 0 auto;
display: flex; display: flex;
font-size: 1.05em;
align-items: center; align-items: center;
margin-bottom: $interiorMargin; margin-bottom: $interiorMarginSm;
padding: 1px 2px;
&__icon {
flex: 0 0 auto;
margin-right: $interiorMarginSm;
opacity: 0.5;
}
.c-object-label {
&__name { &__name {
@include headerFont(1em); filter: $objectLabelNameFilter;
@include ellipsize(); }
flex: 0 1 auto;
} }
} }
&:not(.c-so-view--no-frame) { &:not(.c-so-view--no-frame) {
border: $browseFrameBorder; border: $browseFrameBorder;
padding: $interiorMargin; padding: 0 $interiorMarginSm;
.is-editing & { .is-editing & {
background: rgba($colorBodyBg, 0.8); background: rgba($colorBodyBg, 0.8);
@ -40,10 +36,6 @@
display: none; display: none;
} }
> .c-so-view__local-controls {
top: $interiorMarginSm; right: $interiorMarginSm;
}
&.is-missing { &.is-missing {
@include isMissing($absPos: true); @include isMissing($absPos: true);
@ -55,9 +47,19 @@
} }
&__local-controls { &__local-controls {
// View Large button
box-shadow: $colorLocalControlOvrBg 0 0 0 2px;
position: absolute; position: absolute;
top: $interiorMargin; right: $interiorMargin; top: $interiorMargin; right: $interiorMargin;
z-index: 2; z-index: 10;
}
.c-click-icon,
.c-button {
// Shrink buttons a bit when they appear in a frame
border-radius: $smallCr !important;
font-size: 0.9em;
padding: 3px 5px;
} }
&__view-large { &__view-large {
@ -68,13 +70,11 @@
> .c-so-view__view-large { display: block; } > .c-so-view__view-large { display: block; }
} }
/*************************** OBJECT VIEW */
&__object-view { &__object-view {
flex: 1 1 auto; flex: 1 1 auto;
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();
@ -82,17 +82,8 @@
} }
} }
.c-click-icon, .l-angular-ov-wrapper {
.c-button { // This element is the recipient for object styling; cannot be display: contents
// Shrink buttons a bit when they appear in a frame
font-size: 0.9em;
padding: 3px 5px;
}
}
.u-angular-object-view-wrapper {
flex: 1 1 auto;
height: 100%; height: 100%;
width: 100%;
overflow: hidden; overflow: hidden;
} }

View File

@ -3,7 +3,7 @@
// Used mostly in trees and lists // Used mostly in trees and lists
display: flex; display: flex;
align-items: center; align-items: center;
flex: 1 1 auto; flex: 0 1 auto;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
@ -19,7 +19,7 @@
display: block; display: block;
flex: 0 0 auto; flex: 0 0 auto;
font-size: 1.1em; font-size: 1.1em;
//margin-right: $interiorMargin; opacity: $objectLabelTypeIconOpacity;
} }
&.is-missing { &.is-missing {

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="c-inspector__header"> <div class="c-inspector__header">
<div v-if="!multiSelect" <div v-if="!multiSelect"
class="c-inspector__selected-w c-object-label" class="c-inspector__selected c-object-label"
:class="{'is-missing': domainObject.status === 'missing' }" :class="{'is-missing': domainObject.status === 'missing' }"
> >
<div class="c-object-label__type-icon" <div class="c-object-label__type-icon"
@ -11,17 +11,20 @@
title="This item is missing" title="This item is missing"
></span> ></span>
</div> </div>
<span v-if="!singleSelectNonObject" <span v-if="!singleSelectNonObject"
class="c-inspector__selected c-object-label__name" class="c-inspector__selected c-object-label__name"
>{{ item.name }}</span> >{{ item.name }}</span>
<span v-if="singleSelectNonObject" <div v-if="singleSelectNonObject"
class="c-inspector__selected c-object-label__name c-inspector__selected--non-domain-object" class="c-inspector__selected c-inspector__selected--non-domain-object c-object-label"
>Layout Object</span> >
<span class="c-object-label__type-icon"
:class="typeCssClass"
></span>
<span class="c-object-label__name">Layout Object</span>
</div>
</div> </div>
<div v-if="multiSelect" <div v-if="multiSelect"
class="c-inspector__multiple-selected-w" class="c-inspector__multiple-selected"
> >
{{ itemsSelected }} items selected {{ itemsSelected }} items selected
</div> </div>

View File

@ -4,17 +4,17 @@
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;
} }
&__selected-w, &__selected,
&__multiple-selected-w { &__multiple-selected {
@include headerFont(1.1em); @include headerFont(1.1em);
padding: $interiorMarginSm 0; padding: $interiorMarginSm 0;
} }
&__multiple-selected-w { &__multiple-selected {
$p: $interiorMarginLg; $p: $interiorMarginLg;
background: rgba($colorWarningLo, 0.3); background: rgba($colorWarningLo, 0.3);
border-radius: $basicCr; border-radius: $basicCr;
@ -25,10 +25,11 @@
} }
&__selected { &__selected {
@include ellipsize(); .c-object-label__name {
flex: 1 1 auto; filter: $objectLabelNameFilter;
}
&--non-domain-object { &--non-domain-object .c-object-label__name {
font-style: italic; font-style: italic;
} }
} }
@ -41,6 +42,8 @@
&__content { &__content {
flex: 1 1 auto; flex: 1 1 auto;
display: flex;
flex-direction: column;
} }
&__elements { &__elements {

View File

@ -55,7 +55,7 @@
:title="lockedOrUnlocked" :title="lockedOrUnlocked"
class="c-button" class="c-button"
:class="{ :class="{
'icon-lock': domainObject.locked, 'icon-lock c-button--caution': domainObject.locked,
'icon-unlocked': !domainObject.locked 'icon-unlocked': !domainObject.locked
}" }"
@click="toggleLock(!domainObject.locked)" @click="toggleLock(!domainObject.locked)"

View File

@ -313,6 +313,13 @@
&__actions, &__actions,
&__end { &__end {
.c-button {
&[class*='icon-']:before {
min-width: 1em;
text-align: center;
}
}
> * + * { > * + * {
margin-left: $interiorMargin; margin-left: $interiorMargin;
} }
@ -350,6 +357,10 @@
flex: 0 1 auto; flex: 0 1 auto;
} }
.c-object-label__name {
filter: $objectLabelNameFilter;
}
&__object-name--w { &__object-name--w {
@include headerFont(1.4em); @include headerFont(1.4em);
min-width: 0; min-width: 0;

View File

@ -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 {

View File

@ -95,7 +95,7 @@ export default {
this.viewKey = view.key; this.viewKey = view.key;
this.viewContainer = document.createElement('div'); this.viewContainer = document.createElement('div');
this.viewContainer.classList.add('u-angular-object-view-wrapper'); this.viewContainer.classList.add('l-angular-ov-wrapper');
this.$refs.objectView.append(this.viewContainer); this.$refs.objectView.append(this.viewContainer);
this.view = this.currentView.view(this.domainObject, this.objectPath); this.view = this.currentView.view(this.domainObject, this.objectPath);

View File

@ -1,11 +1,13 @@
<template> <template>
<div class="l-browse-bar"> <div class="c-preview-header l-browse-bar">
<div class="l-browse-bar__start"> <div class="l-browse-bar__start">
<div <div
class="l-browse-bar__object-name--w" class="l-browse-bar__object-name--w c-object-label"
:class="type.cssClass"
> >
<span class="l-browse-bar__object-name"> <div class="c-object-label__type-icon"
:class="type.cssClass"
></div>
<span class="l-browse-bar__object-name c-object-label__name">
{{ domainObject.name }} {{ domainObject.name }}
</span> </span>
<context-menu-drop-down :object-path="objectPath" /> <context-menu-drop-down :object-path="objectPath" />

View File

@ -13,11 +13,9 @@
} }
&__object-view { &__object-view {
background: $colorBodyBg;
border: 1px solid $colorInteriorBorder;
flex: 1 1 auto; flex: 1 1 auto;
height: 0; // Chrome 73
overflow: auto; overflow: auto;
padding: $interiorMargin;
> div:not([class]) { > div:not([class]) {
// Target an immediate child div without a class and make it display: contents // Target an immediate child div without a class and make it display: contents

View File

@ -14,6 +14,9 @@ const gitRevision = require('child_process')
const gitBranch = require('child_process') const gitBranch = require('child_process')
.execSync('git rev-parse --abbrev-ref HEAD') .execSync('git rev-parse --abbrev-ref HEAD')
.toString().trim(); .toString().trim();
const vueFile = devMode ?
path.join(__dirname, "node_modules/vue/dist/vue.js") :
path.join(__dirname, "node_modules/vue/dist/vue.min.js");
const webpackConfig = { const webpackConfig = {
mode: devMode ? 'development' : 'production', mode: devMode ? 'development' : 'production',
@ -37,7 +40,7 @@ const webpackConfig = {
"csv": "comma-separated-values", "csv": "comma-separated-values",
"EventEmitter": "eventemitter3", "EventEmitter": "eventemitter3",
"bourbon": "bourbon.scss", "bourbon": "bourbon.scss",
"vue": path.join(__dirname, "node_modules/vue/dist/vue.js"), "vue": vueFile,
"d3-scale": path.join(__dirname, "node_modules/d3-scale/build/d3-scale.min.js"), "d3-scale": path.join(__dirname, "node_modules/d3-scale/build/d3-scale.min.js"),
"printj": path.join(__dirname, "node_modules/printj/dist/printj.min.js"), "printj": path.join(__dirname, "node_modules/printj/dist/printj.min.js"),
"styles": path.join(__dirname, "src/styles"), "styles": path.join(__dirname, "src/styles"),