mirror of
https://github.com/nasa/openmct.git
synced 2025-06-02 07:30:49 +00:00
Merge branch 'master' into data-dropout-fixes
This commit is contained in:
commit
8fa1770885
@ -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>
|
||||||
|
@ -36,8 +36,6 @@ define(
|
|||||||
}
|
}
|
||||||
|
|
||||||
EditPersistableObjectsPolicy.prototype.allow = function (action, context) {
|
EditPersistableObjectsPolicy.prototype.allow = function (action, context) {
|
||||||
var identifier;
|
|
||||||
var provider;
|
|
||||||
var domainObject = context.domainObject;
|
var domainObject = context.domainObject;
|
||||||
var key = action.getMetadata().key;
|
var key = action.getMetadata().key;
|
||||||
var category = (context || {}).category;
|
var category = (context || {}).category;
|
||||||
@ -46,9 +44,8 @@ define(
|
|||||||
// is also invoked during the create process which should be allowed,
|
// is also invoked during the create process which should be allowed,
|
||||||
// because it may be saved elsewhere
|
// because it may be saved elsewhere
|
||||||
if ((key === 'edit' && category === 'view-control') || key === 'properties') {
|
if ((key === 'edit' && category === 'view-control') || key === 'properties') {
|
||||||
identifier = objectUtils.parseKeyString(domainObject.getId());
|
let newStyleObject = objectUtils.toNewFormat(domainObject, domainObject.getId());
|
||||||
provider = this.openmct.objects.getProvider(identifier);
|
return this.openmct.objects.isPersistable(newStyleObject);
|
||||||
return provider.save !== undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -43,7 +43,7 @@ define(
|
|||||||
);
|
);
|
||||||
|
|
||||||
mockObjectAPI = jasmine.createSpyObj('objectAPI', [
|
mockObjectAPI = jasmine.createSpyObj('objectAPI', [
|
||||||
'getProvider'
|
'isPersistable'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
mockAPI = {
|
mockAPI = {
|
||||||
@ -69,34 +69,31 @@ define(
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("Applies to edit action", function () {
|
it("Applies to edit action", function () {
|
||||||
mockObjectAPI.getProvider.and.returnValue({});
|
expect(mockObjectAPI.isPersistable).not.toHaveBeenCalled();
|
||||||
expect(mockObjectAPI.getProvider).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
policy.allow(mockEditAction, testContext);
|
policy.allow(mockEditAction, testContext);
|
||||||
expect(mockObjectAPI.getProvider).toHaveBeenCalled();
|
expect(mockObjectAPI.isPersistable).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Applies to properties action", function () {
|
it("Applies to properties action", function () {
|
||||||
mockObjectAPI.getProvider.and.returnValue({});
|
expect(mockObjectAPI.isPersistable).not.toHaveBeenCalled();
|
||||||
expect(mockObjectAPI.getProvider).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
policy.allow(mockPropertiesAction, testContext);
|
policy.allow(mockPropertiesAction, testContext);
|
||||||
expect(mockObjectAPI.getProvider).toHaveBeenCalled();
|
expect(mockObjectAPI.isPersistable).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not apply to other actions", function () {
|
it("does not apply to other actions", function () {
|
||||||
mockObjectAPI.getProvider.and.returnValue({});
|
expect(mockObjectAPI.isPersistable).not.toHaveBeenCalled();
|
||||||
expect(mockObjectAPI.getProvider).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
policy.allow(mockOtherAction, testContext);
|
policy.allow(mockOtherAction, testContext);
|
||||||
expect(mockObjectAPI.getProvider).not.toHaveBeenCalled();
|
expect(mockObjectAPI.isPersistable).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Tests object provider for editability", function () {
|
it("Tests object provider for editability", function () {
|
||||||
mockObjectAPI.getProvider.and.returnValue({});
|
mockObjectAPI.isPersistable.and.returnValue(false);
|
||||||
expect(policy.allow(mockEditAction, testContext)).toBe(false);
|
expect(policy.allow(mockEditAction, testContext)).toBe(false);
|
||||||
expect(mockObjectAPI.getProvider).toHaveBeenCalled();
|
expect(mockObjectAPI.isPersistable).toHaveBeenCalled();
|
||||||
mockObjectAPI.getProvider.and.returnValue({save: function () {}});
|
mockObjectAPI.isPersistable.and.returnValue(true);
|
||||||
expect(policy.allow(mockEditAction, testContext)).toBe(true);
|
expect(policy.allow(mockEditAction, testContext)).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -48,9 +48,8 @@ define(
|
|||||||
// prevents editing of objects that cannot be persisted, so we can assume that this
|
// prevents editing of objects that cannot be persisted, so we can assume that this
|
||||||
// is a new object.
|
// is a new object.
|
||||||
if (!(parent.hasCapability('editor') && parent.getCapability('editor').isEditContextRoot())) {
|
if (!(parent.hasCapability('editor') && parent.getCapability('editor').isEditContextRoot())) {
|
||||||
var identifier = objectUtils.parseKeyString(parent.getId());
|
let newStyleObject = objectUtils.toNewFormat(parent, parent.getId());
|
||||||
var provider = this.openmct.objects.getProvider(identifier);
|
return this.openmct.objects.isPersistable(newStyleObject);
|
||||||
return provider.save !== undefined;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
@ -33,7 +33,7 @@ define(
|
|||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
objectAPI = jasmine.createSpyObj('objectsAPI', [
|
objectAPI = jasmine.createSpyObj('objectsAPI', [
|
||||||
'getProvider'
|
'isPersistable'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
mockOpenMCT = {
|
mockOpenMCT = {
|
||||||
@ -51,10 +51,6 @@ define(
|
|||||||
'isEditContextRoot'
|
'isEditContextRoot'
|
||||||
]);
|
]);
|
||||||
mockParent.getCapability.and.returnValue(mockEditorCapability);
|
mockParent.getCapability.and.returnValue(mockEditorCapability);
|
||||||
|
|
||||||
objectAPI.getProvider.and.returnValue({
|
|
||||||
save: function () {}
|
|
||||||
});
|
|
||||||
persistableCompositionPolicy = new PersistableCompositionPolicy(mockOpenMCT);
|
persistableCompositionPolicy = new PersistableCompositionPolicy(mockOpenMCT);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -65,19 +61,22 @@ define(
|
|||||||
|
|
||||||
it("Does not allow composition for objects that are not persistable", function () {
|
it("Does not allow composition for objects that are not persistable", function () {
|
||||||
mockEditorCapability.isEditContextRoot.and.returnValue(false);
|
mockEditorCapability.isEditContextRoot.and.returnValue(false);
|
||||||
|
objectAPI.isPersistable.and.returnValue(true);
|
||||||
expect(persistableCompositionPolicy.allow(mockParent, mockChild)).toBe(true);
|
expect(persistableCompositionPolicy.allow(mockParent, mockChild)).toBe(true);
|
||||||
objectAPI.getProvider.and.returnValue({});
|
objectAPI.isPersistable.and.returnValue(false);
|
||||||
expect(persistableCompositionPolicy.allow(mockParent, mockChild)).toBe(false);
|
expect(persistableCompositionPolicy.allow(mockParent, mockChild)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Always allows composition of objects in edit mode to support object creation", function () {
|
it("Always allows composition of objects in edit mode to support object creation", function () {
|
||||||
mockEditorCapability.isEditContextRoot.and.returnValue(true);
|
mockEditorCapability.isEditContextRoot.and.returnValue(true);
|
||||||
|
objectAPI.isPersistable.and.returnValue(true);
|
||||||
expect(persistableCompositionPolicy.allow(mockParent, mockChild)).toBe(true);
|
expect(persistableCompositionPolicy.allow(mockParent, mockChild)).toBe(true);
|
||||||
expect(objectAPI.getProvider).not.toHaveBeenCalled();
|
expect(objectAPI.isPersistable).not.toHaveBeenCalled();
|
||||||
|
|
||||||
mockEditorCapability.isEditContextRoot.and.returnValue(false);
|
mockEditorCapability.isEditContextRoot.and.returnValue(false);
|
||||||
|
objectAPI.isPersistable.and.returnValue(true);
|
||||||
expect(persistableCompositionPolicy.allow(mockParent, mockChild)).toBe(true);
|
expect(persistableCompositionPolicy.allow(mockParent, mockChild)).toBe(true);
|
||||||
expect(objectAPI.getProvider).toHaveBeenCalled();
|
expect(objectAPI.isPersistable).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -297,7 +297,8 @@ define([
|
|||||||
"persistenceService",
|
"persistenceService",
|
||||||
"identifierService",
|
"identifierService",
|
||||||
"notificationService",
|
"notificationService",
|
||||||
"$q"
|
"$q",
|
||||||
|
"openmct"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -20,8 +20,8 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(
|
define(["objectUtils"],
|
||||||
function () {
|
function (objectUtils) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the `persistence` capability, used to trigger the
|
* Defines the `persistence` capability, used to trigger the
|
||||||
@ -47,6 +47,7 @@ define(
|
|||||||
identifierService,
|
identifierService,
|
||||||
notificationService,
|
notificationService,
|
||||||
$q,
|
$q,
|
||||||
|
openmct,
|
||||||
domainObject
|
domainObject
|
||||||
) {
|
) {
|
||||||
// Cache modified timestamp
|
// Cache modified timestamp
|
||||||
@ -58,6 +59,7 @@ define(
|
|||||||
this.persistenceService = persistenceService;
|
this.persistenceService = persistenceService;
|
||||||
this.notificationService = notificationService;
|
this.notificationService = notificationService;
|
||||||
this.$q = $q;
|
this.$q = $q;
|
||||||
|
this.openmct = openmct;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,7 +68,7 @@ define(
|
|||||||
*/
|
*/
|
||||||
function rejectIfFalsey(value, $q) {
|
function rejectIfFalsey(value, $q) {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return $q.reject("Error persisting object");
|
return Promise.reject("Error persisting object");
|
||||||
} else {
|
} else {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@ -98,7 +100,7 @@ define(
|
|||||||
dismissable: true
|
dismissable: true
|
||||||
});
|
});
|
||||||
|
|
||||||
return $q.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -110,34 +112,16 @@ 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...
|
return rejectIfFalsey(result, self.$q);
|
||||||
domainObject.useCapability("mutation", function (m) {
|
}).catch(function (error) {
|
||||||
m.persisted = modified;
|
return notifyOnError(error, domainObject, self.notificationService, self.$q);
|
||||||
}, modified);
|
});
|
||||||
|
|
||||||
// ...and persist
|
|
||||||
return persistenceFn.apply(persistenceService, [
|
|
||||||
this.getSpace(),
|
|
||||||
this.getKey(),
|
|
||||||
domainObject.getModel()
|
|
||||||
]).then(function (result) {
|
|
||||||
return rejectIfFalsey(result, self.$q);
|
|
||||||
}).catch(function (error) {
|
|
||||||
return notifyOnError(error, domainObject, self.notificationService, self.$q);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PersistenceCapabilitySpec. Created by vwoeltje on 11/6/14.
|
* PersistenceCapabilitySpec. Created by vwoeltje on 11/6/14.
|
||||||
*/
|
*/
|
||||||
@ -40,7 +39,8 @@ define(
|
|||||||
model,
|
model,
|
||||||
SPACE = "some space",
|
SPACE = "some space",
|
||||||
persistence,
|
persistence,
|
||||||
happyPromise;
|
mockOpenMCT,
|
||||||
|
mockNewStyleDomainObject;
|
||||||
|
|
||||||
function asPromise(value, doCatch) {
|
function asPromise(value, doCatch) {
|
||||||
return (value || {}).then ? value : {
|
return (value || {}).then ? value : {
|
||||||
@ -56,7 +56,6 @@ define(
|
|||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
happyPromise = asPromise(true);
|
|
||||||
model = { someKey: "some value", name: "domain object"};
|
model = { someKey: "some value", name: "domain object"};
|
||||||
|
|
||||||
mockPersistenceService = jasmine.createSpyObj(
|
mockPersistenceService = jasmine.createSpyObj(
|
||||||
@ -94,12 +93,23 @@ define(
|
|||||||
},
|
},
|
||||||
useCapability: jasmine.createSpy()
|
useCapability: jasmine.createSpy()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mockNewStyleDomainObject = Object.assign({}, model);
|
||||||
|
mockNewStyleDomainObject.identifier = {
|
||||||
|
namespace: "",
|
||||||
|
key: id
|
||||||
|
}
|
||||||
|
|
||||||
// Simulate mutation capability
|
// Simulate mutation capability
|
||||||
mockDomainObject.useCapability.and.callFake(function (capability, mutator) {
|
mockDomainObject.useCapability.and.callFake(function (capability, mutator) {
|
||||||
if (capability === 'mutation') {
|
if (capability === 'mutation') {
|
||||||
model = mutator(model) || model;
|
model = mutator(model) || model;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
mockOpenMCT = {};
|
||||||
|
mockOpenMCT.objects = jasmine.createSpyObj('Object API', ['save']);
|
||||||
|
|
||||||
mockIdentifierService.parse.and.returnValue(mockIdentifier);
|
mockIdentifierService.parse.and.returnValue(mockIdentifier);
|
||||||
mockIdentifier.getSpace.and.returnValue(SPACE);
|
mockIdentifier.getSpace.and.returnValue(SPACE);
|
||||||
mockIdentifier.getKey.and.returnValue(key);
|
mockIdentifier.getKey.and.returnValue(key);
|
||||||
@ -110,51 +120,28 @@ define(
|
|||||||
mockIdentifierService,
|
mockIdentifierService,
|
||||||
mockNofificationService,
|
mockNofificationService,
|
||||||
mockQ,
|
mockQ,
|
||||||
|
mockOpenMCT,
|
||||||
mockDomainObject
|
mockDomainObject
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("successful persistence", function () {
|
describe("successful persistence", function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockPersistenceService.updateObject.and.returnValue(happyPromise);
|
mockOpenMCT.objects.save.and.returnValue(Promise.resolve(true));
|
||||||
mockPersistenceService.createObject.and.returnValue(happyPromise);
|
|
||||||
});
|
});
|
||||||
it("creates unpersisted objects with the persistence service", function () {
|
it("creates unpersisted objects with the persistence service", function () {
|
||||||
// Verify precondition; no call made during constructor
|
// Verify precondition; no call made during constructor
|
||||||
expect(mockPersistenceService.createObject).not.toHaveBeenCalled();
|
expect(mockOpenMCT.objects.save).not.toHaveBeenCalled();
|
||||||
|
|
||||||
persistence.persist();
|
persistence.persist();
|
||||||
|
|
||||||
expect(mockPersistenceService.createObject).toHaveBeenCalledWith(
|
expect(mockOpenMCT.objects.save).toHaveBeenCalledWith(mockNewStyleDomainObject);
|
||||||
SPACE,
|
|
||||||
key,
|
|
||||||
model
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("updates previously persisted objects with the persistence service", function () {
|
|
||||||
// Verify precondition; no call made during constructor
|
|
||||||
expect(mockPersistenceService.updateObject).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
model.persisted = 12321;
|
|
||||||
persistence.persist();
|
|
||||||
|
|
||||||
expect(mockPersistenceService.updateObject).toHaveBeenCalledWith(
|
|
||||||
SPACE,
|
|
||||||
key,
|
|
||||||
model
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("reports which persistence space an object belongs to", function () {
|
it("reports which persistence space an object belongs to", function () {
|
||||||
expect(persistence.getSpace()).toEqual(SPACE);
|
expect(persistence.getSpace()).toEqual(SPACE);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("updates persisted timestamp on persistence", function () {
|
|
||||||
model.modified = 12321;
|
|
||||||
persistence.persist();
|
|
||||||
expect(model.persisted).toEqual(12321);
|
|
||||||
});
|
|
||||||
it("refreshes the domain object model from persistence", function () {
|
it("refreshes the domain object model from persistence", function () {
|
||||||
var refreshModel = {someOtherKey: "some other value"};
|
var refreshModel = {someOtherKey: "some other value"};
|
||||||
model.persisted = 1;
|
model.persisted = 1;
|
||||||
@ -165,30 +152,37 @@ 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()
|
||||||
expect(mockNofificationService.error).not.toHaveBeenCalled();
|
.catch(() => rejected = true)
|
||||||
|
.then(() => {
|
||||||
|
expect(rejected).toBe(false);
|
||||||
|
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()
|
||||||
expect(mockNofificationService.error).toHaveBeenCalled();
|
.catch(() => rejected = true)
|
||||||
|
.then(() => {
|
||||||
|
expect(rejected).toBe(true);
|
||||||
|
expect(mockNofificationService.error).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -101,14 +101,25 @@ define([
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save this domain object in its current state.
|
* Create the given domain object in the corresponding persistence store
|
||||||
*
|
*
|
||||||
* @method save
|
* @method create
|
||||||
* @memberof module:openmct.ObjectProvider#
|
* @memberof module:openmct.ObjectProvider#
|
||||||
* @param {module:openmct.DomainObject} domainObject the domain object to
|
* @param {module:openmct.DomainObject} domainObject the domain object to
|
||||||
* save
|
* create
|
||||||
* @returns {Promise} a promise which will resolve when the domain object
|
* @returns {Promise} a promise which will resolve when the domain object
|
||||||
* has been saved, or be rejected if it cannot be saved
|
* has been created, or be rejected if it cannot be saved
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update this domain object in its persistence store
|
||||||
|
*
|
||||||
|
* @method update
|
||||||
|
* @memberof module:openmct.ObjectProvider#
|
||||||
|
* @param {module:openmct.DomainObject} domainObject the domain object to
|
||||||
|
* update
|
||||||
|
* @returns {Promise} a promise which will resolve when the domain object
|
||||||
|
* has been updated, or be rejected if it cannot be saved
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -161,8 +172,41 @@ define([
|
|||||||
throw new Error('Delete not implemented');
|
throw new Error('Delete not implemented');
|
||||||
};
|
};
|
||||||
|
|
||||||
ObjectAPI.prototype.save = function () {
|
ObjectAPI.prototype.isPersistable = function (domainObject) {
|
||||||
throw new Error('Save not implemented');
|
let provider = this.getProvider(domainObject.identifier);
|
||||||
|
return provider !== undefined &&
|
||||||
|
provider.create !== undefined &&
|
||||||
|
provider.update !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save this domain object in its current state. EXPERIMENTAL
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @memberof module:openmct.ObjectAPI#
|
||||||
|
* @param {module:openmct.DomainObject} domainObject the domain object to
|
||||||
|
* save
|
||||||
|
* @returns {Promise} a promise which will resolve when the domain object
|
||||||
|
* has been saved, or be rejected if it cannot be saved
|
||||||
|
*/
|
||||||
|
ObjectAPI.prototype.save = function (domainObject) {
|
||||||
|
let provider = this.getProvider(domainObject.identifier);
|
||||||
|
let result;
|
||||||
|
|
||||||
|
if (!this.isPersistable(domainObject)) {
|
||||||
|
result = Promise.reject('Object provider does not support saving');
|
||||||
|
} else if (hasAlreadyBeenPersisted(domainObject)) {
|
||||||
|
result = Promise.resolve(true);
|
||||||
|
} else {
|
||||||
|
if (domainObject.persisted === undefined) {
|
||||||
|
this.mutate(domainObject, 'persisted', domainObject.modified);
|
||||||
|
result = provider.create(domainObject);
|
||||||
|
} else {
|
||||||
|
this.mutate(domainObject, 'persisted', domainObject.modified);
|
||||||
|
result = provider.update(domainObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -276,5 +320,9 @@ define([
|
|||||||
* @memberof module:openmct
|
* @memberof module:openmct
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
function hasAlreadyBeenPersisted(domainObject) {
|
||||||
|
return domainObject.persisted !== undefined &&
|
||||||
|
domainObject.persisted === domainObject.modified;
|
||||||
|
}
|
||||||
return ObjectAPI;
|
return ObjectAPI;
|
||||||
});
|
});
|
||||||
|
60
src/api/objects/ObjectAPISpec.js
Normal file
60
src/api/objects/ObjectAPISpec.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import ObjectAPI from './ObjectAPI.js';
|
||||||
|
|
||||||
|
describe("The Object API", () => {
|
||||||
|
let objectAPI;
|
||||||
|
let mockDomainObject;
|
||||||
|
const TEST_NAMESPACE = "test-namespace";
|
||||||
|
const FIFTEEN_MINUTES = 15 * 60 * 1000;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
objectAPI = new ObjectAPI();
|
||||||
|
mockDomainObject = {
|
||||||
|
identifier: {
|
||||||
|
namespace: TEST_NAMESPACE,
|
||||||
|
key: "test-key"
|
||||||
|
},
|
||||||
|
name: "test object",
|
||||||
|
type: "test-type"
|
||||||
|
};
|
||||||
|
})
|
||||||
|
describe("The save function", () => {
|
||||||
|
it("Rejects if no provider available", () => {
|
||||||
|
let rejected = false;
|
||||||
|
return objectAPI.save(mockDomainObject)
|
||||||
|
.catch(() => rejected = true)
|
||||||
|
.then(() => expect(rejected).toBe(true));
|
||||||
|
});
|
||||||
|
describe("when a provider is available", () => {
|
||||||
|
let mockProvider;
|
||||||
|
beforeEach(() => {
|
||||||
|
mockProvider = jasmine.createSpyObj("mock provider", [
|
||||||
|
"create",
|
||||||
|
"update"
|
||||||
|
]);
|
||||||
|
objectAPI.addProvider(TEST_NAMESPACE, mockProvider);
|
||||||
|
})
|
||||||
|
it("Calls 'create' on provider if object is new", () => {
|
||||||
|
objectAPI.save(mockDomainObject);
|
||||||
|
expect(mockProvider.create).toHaveBeenCalled();
|
||||||
|
expect(mockProvider.update).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
it("Calls 'update' on provider if object is not new", () => {
|
||||||
|
mockDomainObject.persisted = Date.now() - FIFTEEN_MINUTES;
|
||||||
|
mockDomainObject.modified = Date.now();
|
||||||
|
|
||||||
|
objectAPI.save(mockDomainObject);
|
||||||
|
expect(mockProvider.create).not.toHaveBeenCalled();
|
||||||
|
expect(mockProvider.update).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Does not persist if the object is unchanged", () => {
|
||||||
|
mockDomainObject.persisted =
|
||||||
|
mockDomainObject.modified = Date.now();
|
||||||
|
|
||||||
|
objectAPI.save(mockDomainObject);
|
||||||
|
expect(mockProvider.create).not.toHaveBeenCalled();
|
||||||
|
expect(mockProvider.update).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
});
|
@ -29,24 +29,28 @@ 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) {
|
||||||
let component = new Vue ({
|
if (installIndicator) {
|
||||||
provide: {
|
let component = new Vue ({
|
||||||
openmct
|
provide: {
|
||||||
},
|
openmct
|
||||||
components: {
|
},
|
||||||
GlobalClearIndicator: GlobaClearIndicator.default
|
components: {
|
||||||
},
|
GlobalClearIndicator: GlobaClearIndicator.default
|
||||||
template: '<GlobalClearIndicator></GlobalClearIndicator>'
|
},
|
||||||
}),
|
template: '<GlobalClearIndicator></GlobalClearIndicator>'
|
||||||
indicator = {
|
}),
|
||||||
element: component.$mount().$el
|
indicator = {
|
||||||
};
|
element: component.$mount().$el
|
||||||
|
};
|
||||||
|
|
||||||
openmct.indicators.add(indicator);
|
openmct.indicators.add(indicator);
|
||||||
|
}
|
||||||
|
|
||||||
openmct.contextMenu.registerAction(new ClearDataAction.default(openmct, appliesToObjects));
|
openmct.contextMenu.registerAction(new ClearDataAction.default(openmct, appliesToObjects));
|
||||||
};
|
};
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,16 @@
|
|||||||
<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">
|
||||||
Notebook Snapshots
|
<div class="c-object-label__type-icon icon-notebook"></div>
|
||||||
<span v-if="snapshots.length"
|
<div class="c-object-label__name">
|
||||||
class="l-browse-bar__object-details"
|
Notebook Snapshots
|
||||||
> {{ snapshots.length }} of {{ getNotebookSnapshotMaxCount() }}
|
<span v-if="snapshots.length"
|
||||||
</span>
|
class="l-browse-bar__object-details"
|
||||||
|
> {{ snapshots.length }} of {{ getNotebookSnapshotMaxCount() }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<PopupMenu v-if="snapshots.length > 0"
|
<PopupMenu v-if="snapshots.length > 0"
|
||||||
:popup-menu-items="popupMenuItems"
|
:popup-menu-items="popupMenuItems"
|
||||||
|
@ -22,21 +22,24 @@
|
|||||||
<button
|
<button
|
||||||
v-for="(tab,index) in tabsList"
|
v-for="(tab,index) in tabsList"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="c-tabs-view__tab c-tab 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"
|
<div class="c-object-label"
|
||||||
:class="tab.type.definition.cssClass"
|
:class="{'is-missing': tab.domainObject.status === 'missing'}"
|
||||||
>
|
>
|
||||||
<span class="is-missing__indicator"
|
<div class="c-object-label__type-icon"
|
||||||
title="This item is missing"
|
:class="tab.type.definition.cssClass"
|
||||||
></span>
|
>
|
||||||
|
<span class="is-missing__indicator"
|
||||||
|
title="This item is missing"
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
|
<span class="c-button__label c-object-label__name">{{ tab.domainObject.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="c-button__label c-object-label__name">{{ tab.domainObject.name }}</span>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
@ -411,7 +411,17 @@ select {
|
|||||||
|
|
||||||
.c-tab {
|
.c-tab {
|
||||||
// Used in Tab View, generic tabs
|
// Used in Tab View, generic tabs
|
||||||
background: $colorBtnBg;
|
$notchSize: 7px;
|
||||||
|
$clipPath:
|
||||||
|
polygon(
|
||||||
|
0% 0%,
|
||||||
|
calc(100% - #{$notchSize}) 0%,
|
||||||
|
100% #{$notchSize},
|
||||||
|
100% calc(100% - #{$notchSize}),
|
||||||
|
100% 100%,
|
||||||
|
0% 100%
|
||||||
|
);
|
||||||
|
background: rgba($colorBtnBg, 0.7);
|
||||||
color: $colorBtnFg;
|
color: $colorBtnFg;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -420,16 +430,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;
|
||||||
|
@ -118,9 +118,7 @@ mct-plot {
|
|||||||
|
|
||||||
.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 {
|
||||||
|
@ -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) {
|
||||||
|
@ -53,7 +53,9 @@ export default {
|
|||||||
mounted() {
|
mounted() {
|
||||||
this.currentObject = this.object;
|
this.currentObject = this.object;
|
||||||
this.updateView();
|
this.updateView();
|
||||||
this.$el.addEventListener('dragover', this.onDragOver);
|
this.$el.addEventListener('dragover', this.onDragOver, {
|
||||||
|
capture: true
|
||||||
|
});
|
||||||
this.$el.addEventListener('drop', this.editIfEditable, {
|
this.$el.addEventListener('drop', this.editIfEditable, {
|
||||||
capture: true
|
capture: true
|
||||||
});
|
});
|
||||||
@ -269,6 +271,7 @@ export default {
|
|||||||
if (provider &&
|
if (provider &&
|
||||||
provider.canEdit &&
|
provider.canEdit &&
|
||||||
provider.canEdit(this.currentObject) &&
|
provider.canEdit(this.currentObject) &&
|
||||||
|
this.isEditingAllowed() &&
|
||||||
!this.openmct.editor.isEditing()) {
|
!this.openmct.editor.isEditing()) {
|
||||||
this.openmct.editor.edit();
|
this.openmct.editor.edit();
|
||||||
}
|
}
|
||||||
|
@ -74,11 +74,9 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,8 +89,5 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.u-angular-object-view-wrapper {
|
.u-angular-object-view-wrapper {
|
||||||
flex: 1 1 auto;
|
display: contents;
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
@ -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,6 @@
|
|||||||
display: block;
|
display: block;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
//margin-right: $interiorMargin;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-missing {
|
&.is-missing {
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
// Thi is on purpose: want extra margin on top object-name element
|
// This is on purpose: want extra margin on top object-name element
|
||||||
margin-top: $interiorMargin;
|
margin-top: $interiorMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,6 +41,8 @@
|
|||||||
|
|
||||||
&__content {
|
&__content {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__elements {
|
&__elements {
|
||||||
|
@ -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 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user