Compare commits

...

7 Commits

Author SHA1 Message Date
8f155b1423 Merge pull request #472 from nasa/nem470
Review and integrate nem470
2016-01-13 09:05:53 -08:00
6bdcce033c [Frontend] Fixed z-index of .primary-pane
open #470
Cherry-pick this commit into nem_prototype branch;
2016-01-11 15:17:12 -08:00
2a74117294 merging markup from prototype 2016-01-11 14:52:45 -08:00
f8aa123122 [New Edit Mode] Merged styles and markup from prototype 2016-01-11 14:42:49 -08:00
6d4a20c07a Adding elements pane 2016-01-11 13:33:56 -08:00
2c4d53883a [New Edit Mode] Added additional tests for Seamless Edit Mode #464 2016-01-11 13:07:56 -08:00
c71aa43581 [New Edit Mode] “Seamless” Edit Mode #464
* html changes and Edit Action
* Fixed browse controller to correctly navigate to non-editable object
* Added save and cancel actions
2016-01-08 14:25:58 -08:00
41 changed files with 2399 additions and 1015 deletions

View File

@ -19,8 +19,8 @@
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.
--> -->
<span ng-controller="BrowseObjectController"> <div ng-controller="BrowseObjectController" class="abs l-flex-col">
<div class="object-browse-bar l-flex-row"> <div class="holder flex-elem l-flex-row object-browse-bar ">
<div class="items-select left flex-elem l-flex-row grows"> <div class="items-select left flex-elem l-flex-row grows">
<mct-representation key="'back-arrow'" <mct-representation key="'back-arrow'"
mct-object="domainObject" mct-object="domainObject"
@ -43,8 +43,26 @@
</mct-representation> </mct-representation>
</div> </div>
</div> </div>
<mct-representation key="representation.selected.key" <div class="holder l-flex-col flex-elem grows l-object-wrapper">
mct-object="representation.selected.key && domainObject" <div class="holder l-flex-col flex-elem grows l-object-wrapper-inner">
class="abs object-holder"> <!-- Toolbar and Save/Cancel buttons -->
</mct-representation> <div class="l-edit-controls flex-elem l-flex-row flex-align-end">
</span> <mct-toolbar name="mctToolbar"
structure="toolbar.structure"
ng-model="toolbar.state"
class="flex-elem grows">
</mct-toolbar>
<mct-representation key="'edit-action-buttons'"
mct-object="domainObject"
class='flex-elem conclude-editing'>
</mct-representation>
</div>
<mct-representation key="representation.selected.key"
mct-object="representation.selected.key && domainObject"
class="abs flex-elem grows object-holder-main scroll"
toolbar="toolbar">
</mct-representation>
</div><!--/ l-object-wrapper-inner -->
</div>
</div>

View File

@ -20,7 +20,7 @@
at runtime from the About dialog for additional information. at runtime from the About dialog for additional information.
--> -->
<div class="abs holder-all browse-mode" ng-controller="BrowseController"> <div class="abs holder-all" ng-controller="BrowseController">
<mct-include key="'topbar-browse'"></mct-include> <mct-include key="'topbar-browse'"></mct-include>
<div class="abs holder holder-main browse-area s-browse-area browse-wrapper" <div class="abs holder holder-main browse-area s-browse-area browse-wrapper"
ng-controller="PaneController as modelPaneTree" ng-controller="PaneController as modelPaneTree"
@ -72,7 +72,7 @@
<div class="split-pane-component t-inspect pane right mobile-hide"> <div class="split-pane-component t-inspect pane right mobile-hide">
<mct-representation key="'object-inspector'" <mct-representation key="'object-inspector'"
mct-object="domainObject" mct-object="navigatedObject"
ng-model="treeModel"> ng-model="treeModel">
</mct-representation> </mct-representation>
<a class="mini-tab-icon anchor-right mobile-hide toggle-pane toggle-inspect" <a class="mini-tab-icon anchor-right mobile-hide toggle-pane toggle-inspect"

View File

@ -71,10 +71,14 @@ define(
// Callback for updating the in-scope reference to the object // Callback for updating the in-scope reference to the object
// that is currently navigated-to. // that is currently navigated-to.
function setNavigation(domainObject) { function setNavigation(domainObject) {
//If the domain object has editor capability, retrieve the
// original uneditable domain object
var nonEditableObject = domainObject.hasCapability("editor") ? domainObject.getCapability("editor").getDomainObject() : domainObject;
$scope.navigatedObject = domainObject; $scope.navigatedObject = domainObject;
$scope.treeModel.selectedObject = domainObject; $scope.treeModel.selectedObject = nonEditableObject;
navigationService.setNavigation(domainObject); navigationService.setNavigation(domainObject);
updateRoute(domainObject); updateRoute(nonEditableObject);
} }
function navigateTo(domainObject) { function navigateTo(domainObject) {

View File

@ -50,6 +50,7 @@ define(
} }
beforeEach(function () { beforeEach(function () {
mockScope = jasmine.createSpyObj( mockScope = jasmine.createSpyObj(
"$scope", "$scope",
[ "$on", "$watch" ] [ "$on", "$watch" ]
@ -82,11 +83,11 @@ define(
); );
mockDomainObject = jasmine.createSpyObj( mockDomainObject = jasmine.createSpyObj(
"domainObject", "domainObject",
[ "getId", "getCapability", "getModel", "useCapability" ] [ "getId", "hasCapability", "getCapability", "getModel", "useCapability" ]
); );
mockNextObject = jasmine.createSpyObj( mockNextObject = jasmine.createSpyObj(
"nextObject", "nextObject",
[ "getId", "getCapability", "getModel", "useCapability" ] [ "getId", "hasCapability", "getCapability", "getModel", "useCapability" ]
); );
mockObjectService.getObjects.andReturn(mockPromise({ mockObjectService.getObjects.andReturn(mockPromise({
@ -98,9 +99,13 @@ define(
mockDomainObject.useCapability.andReturn(mockPromise([ mockDomainObject.useCapability.andReturn(mockPromise([
mockNextObject mockNextObject
])); ]));
mockNextObject.useCapability.andReturn(undefined); mockNextObject.useCapability.andReturn(undefined);
mockNextObject.getId.andReturn("next"); mockNextObject.getId.andReturn("next");
mockNextObject.hasCapability.andReturn(false);
mockDomainObject.getId.andReturn("mine"); mockDomainObject.getId.andReturn("mine");
mockDomainObject.hasCapability.andReturn(false);
controller = new BrowseController( controller = new BrowseController(
mockScope, mockScope,

View File

@ -38,7 +38,7 @@
{ {
"key": "edit", "key": "edit",
"implementation": "actions/EditAction.js", "implementation": "actions/EditAction.js",
"depends": [ "$location", "navigationService", "$log" ], "depends": [ "$q", "navigationService", "$log" ],
"description": "Edit this object.", "description": "Edit this object.",
"category": "view-control", "category": "view-control",
"glyph": "p" "glyph": "p"
@ -67,7 +67,7 @@
"implementation": "actions/SaveAction.js", "implementation": "actions/SaveAction.js",
"name": "Save", "name": "Save",
"description": "Save changes made to these objects.", "description": "Save changes made to these objects.",
"depends": [ "$location", "urlService" ], "depends": [ "navigationService" ],
"priority": "mandatory" "priority": "mandatory"
}, },
{ {
@ -76,7 +76,7 @@
"implementation": "actions/CancelAction.js", "implementation": "actions/CancelAction.js",
"name": "Cancel", "name": "Cancel",
"description": "Discard changes made to these objects.", "description": "Discard changes made to these objects.",
"depends": [ "$location", "urlService" ] "depends": ["navigationService" ]
} }
], ],
"policies": [ "policies": [

View File

@ -21,10 +21,11 @@
--> -->
<span ng-controller="EditActionController"> <span ng-controller="EditActionController">
<span ng-repeat="currentAction in editActions"> <span ng-repeat="currentAction in editActions">
<a class='s-btn' <a class='s-btn t-{{currentAction.getMetadata().key}}'
title='{{currentAction.getMetadata().name}}'
ng-click="currentAction.perform()" ng-click="currentAction.perform()"
ng-class="{ major: $index === 0, subtle: $index !== 0 }"> ng-class="{ major: $index === 0 }">
{{currentAction.getMetadata().name}} <span class="title-label">{{currentAction.getMetadata().name}}</span>
</a> </a>
</span> </span>
</span> </span>

View File

@ -33,10 +33,9 @@ define(
* @memberof platform/commonUI/edit * @memberof platform/commonUI/edit
* @implements {Action} * @implements {Action}
*/ */
function CancelAction($location, urlService, context) { function CancelAction(navigationService, context) {
this.domainObject = context.domainObject; this.domainObject = context.domainObject;
this.$location = $location; this.navigationService = navigationService;
this.urlService = urlService;
} }
/** /**
@ -47,8 +46,7 @@ define(
*/ */
CancelAction.prototype.perform = function () { CancelAction.prototype.perform = function () {
var domainObject = this.domainObject, var domainObject = this.domainObject,
$location = this.$location, navigationService = this.navigationService;
urlService = this.urlService;
// Look up the object's "editor.completion" capability; // Look up the object's "editor.completion" capability;
// this is introduced by EditableDomainObject which is // this is introduced by EditableDomainObject which is
@ -64,13 +62,9 @@ define(
return editor.cancel(); return editor.cancel();
} }
// Discard the current root view (which will be the editing //Return navigation state to the non-editable version of the object.
// UI, which will have been pushed atop the Browise UI.) function returnToBrowse(nonEditableDomainObject) {
function returnToBrowse() { navigationService.setNavigation(nonEditableDomainObject);
$location.path($location.path(urlService.urlForLocation(
"browse",
domainObject
)));
} }
return doCancel(getEditorCapability()) return doCancel(getEditorCapability())
@ -86,7 +80,7 @@ define(
CancelAction.appliesTo = function (context) { CancelAction.appliesTo = function (context) {
var domainObject = (context || {}).domainObject; var domainObject = (context || {}).domainObject;
return domainObject !== undefined && return domainObject !== undefined &&
domainObject.hasCapability("editor"); domainObject.getCapability('status').get('editing');
}; };
return CancelAction; return CancelAction;

View File

@ -25,8 +25,8 @@
* Module defining EditAction. Created by vwoeltje on 11/14/14. * Module defining EditAction. Created by vwoeltje on 11/14/14.
*/ */
define( define(
[], ['../objects/EditableDomainObject'],
function () { function (EditableDomainObject) {
"use strict"; "use strict";
// A no-op action to return in the event that the action cannot // A no-op action to return in the event that the action cannot
@ -46,7 +46,7 @@ define(
* @constructor * @constructor
* @implements {Action} * @implements {Action}
*/ */
function EditAction($location, navigationService, $log, context) { function EditAction($q, navigationService, $log, context) {
var domainObject = (context || {}).domainObject; var domainObject = (context || {}).domainObject;
// We cannot enter Edit mode if we have no domain object to // We cannot enter Edit mode if we have no domain object to
@ -63,16 +63,20 @@ define(
} }
this.domainObject = domainObject; this.domainObject = domainObject;
this.$location = $location;
this.navigationService = navigationService; this.navigationService = navigationService;
this.$q = $q;
} }
EditAction.prototype.createEditableObject = function(domainObject) {
return new EditableDomainObject(domainObject, this.$q);
};
/** /**
* Enter edit mode. * Enter edit mode.
*/ */
EditAction.prototype.perform = function () { EditAction.prototype.perform = function () {
this.navigationService.setNavigation(this.domainObject); this.domainObject.getCapability('status').set('editing', true);
this.$location.path("/edit"); this.navigationService.setNavigation(this.createEditableObject(this.domainObject));
}; };
/** /**
@ -85,8 +89,11 @@ define(
var domainObject = (context || {}).domainObject, var domainObject = (context || {}).domainObject,
type = domainObject && domainObject.getCapability('type'); type = domainObject && domainObject.getCapability('type');
// Only allow creatable types to be edited // Only allow creatable types to be edited, and objects that are
return type && type.hasFeature('creation'); // not already being edited
return type
&& type.hasFeature('creation')
&& !domainObject.getCapability('status').get('editing');
}; };
return EditAction; return EditAction;

View File

@ -34,10 +34,9 @@ define(
* @implements {Action} * @implements {Action}
* @memberof platform/commonUI/edit * @memberof platform/commonUI/edit
*/ */
function SaveAction($location, urlService, context) { function SaveAction(navigationService, context) {
this.domainObject = (context || {}).domainObject; this.domainObject = (context || {}).domainObject;
this.$location = $location; this.navigationService = navigationService;
this.urlService = urlService;
} }
/** /**
@ -49,8 +48,7 @@ define(
*/ */
SaveAction.prototype.perform = function () { SaveAction.prototype.perform = function () {
var domainObject = this.domainObject, var domainObject = this.domainObject,
$location = this.$location, navigationService = this.navigationService;
urlService = this.urlService;
// Invoke any save behavior introduced by the editor capability; // Invoke any save behavior introduced by the editor capability;
// this is introduced by EditableDomainObject which is // this is introduced by EditableDomainObject which is
@ -62,11 +60,8 @@ define(
// Discard the current root view (which will be the editing // Discard the current root view (which will be the editing
// UI, which will have been pushed atop the Browise UI.) // UI, which will have been pushed atop the Browise UI.)
function returnToBrowse() { function returnToBrowse(nonEditableDomainObject) {
return $location.path(urlService.urlForLocation( navigationService.setNavigation(nonEditableDomainObject);
"browse",
domainObject
));
} }
return doSave().then(returnToBrowse); return doSave().then(returnToBrowse);
@ -81,7 +76,7 @@ define(
SaveAction.appliesTo = function (context) { SaveAction.appliesTo = function (context) {
var domainObject = (context || {}).domainObject; var domainObject = (context || {}).domainObject;
return domainObject !== undefined && return domainObject !== undefined &&
domainObject.hasCapability("editor"); domainObject.getCapability("status").get("editing");
}; };
return SaveAction; return SaveAction;

View File

@ -95,9 +95,16 @@ define(
return domainObject.getCapability('persistence').persist(); return domainObject.getCapability('persistence').persist();
} }
return nonrecursive ? function saveChanges() {
resolvePromise(doMutate()).then(doPersist) : return nonrecursive ?
resolvePromise(cache.saveAll()); resolvePromise(doMutate()).then(doPersist) :
resolvePromise(cache.saveAll());
}
return saveChanges().then(function(){
domainObject.getCapability('status').set('editing', false);
return domainObject;
});
}; };
/** /**
@ -109,7 +116,8 @@ define(
* @memberof platform/commonUI/edit.EditorCapability# * @memberof platform/commonUI/edit.EditorCapability#
*/ */
EditorCapability.prototype.cancel = function () { EditorCapability.prototype.cancel = function () {
return resolvePromise(undefined); this.domainObject.getCapability('status').set('editing', false);
return resolvePromise(this.domainObject);
}; };
/** /**
@ -121,6 +129,10 @@ define(
return this.cache.dirty(); return this.cache.dirty();
}; };
EditorCapability.prototype.getDomainObject = function () {
return this.domainObject;
};
return EditorCapability; return EditorCapability;
} }
); );

View File

@ -47,6 +47,7 @@ define(
*/ */
function EditRepresenter($q, $log, scope) { function EditRepresenter($q, $log, scope) {
var self = this; var self = this;
this.scope = scope;
// Mutate and persist a new version of a domain object's model. // Mutate and persist a new version of a domain object's model.
function doPersist(model) { function doPersist(model) {
@ -100,6 +101,7 @@ define(
this.key = (representation || {}).key; this.key = (representation || {}).key;
// Track the represented object // Track the represented object
this.domainObject = representedObject; this.domainObject = representedObject;
this.scope.editMode = representedObject.hasCapability("status") && representedObject.getCapability("status").get("editing");
// Ensure existing watches are released // Ensure existing watches are released
this.destroy(); this.destroy();
}; };

View File

@ -27,10 +27,11 @@ define(
"use strict"; "use strict";
describe("The Cancel action", function () { describe("The Cancel action", function () {
var mockLocation, var mockDomainObject,
mockDomainObject, mockCapabilities,
mockEditorCapability, mockEditorCapability,
mockUrlService, mockStatusCapability,
mockNavigationService,
actionContext, actionContext,
action; action;
@ -43,45 +44,50 @@ define(
} }
beforeEach(function () { beforeEach(function () {
mockLocation = jasmine.createSpyObj(
"$location",
[ "path" ]
);
mockDomainObject = jasmine.createSpyObj( mockDomainObject = jasmine.createSpyObj(
"domainObject", "domainObject",
[ "getCapability", "hasCapability" ] [ "getCapability" ]
); );
mockEditorCapability = jasmine.createSpyObj( mockEditorCapability = jasmine.createSpyObj(
"editor", "editor",
[ "save", "cancel" ] [ "save", "cancel" ]
); );
mockUrlService = jasmine.createSpyObj( mockStatusCapability = jasmine.createSpyObj(
"urlService", "status",
["urlForLocation"] [ "get"]
); );
mockNavigationService = jasmine.createSpyObj(
"navigationService",
["setNavigation"]
);
mockCapabilities = {
"editor": mockEditorCapability,
"status": mockStatusCapability
};
actionContext = { actionContext = {
domainObject: mockDomainObject domainObject: mockDomainObject
}; };
mockDomainObject.hasCapability.andReturn(true); mockDomainObject.getCapability.andCallFake(function(capability){
mockDomainObject.getCapability.andReturn(mockEditorCapability); return mockCapabilities[capability];
mockEditorCapability.cancel.andReturn(mockPromise(true)); });
action = new CancelAction(mockLocation, mockUrlService, actionContext); mockEditorCapability.cancel.andReturn(mockPromise(mockDomainObject));
mockStatusCapability.get.andReturn(true);
action = new CancelAction( mockNavigationService, actionContext);
}); });
it("only applies to domain object with an editor capability", function () { it("only applies to domain object that is being edited", function () {
expect(CancelAction.appliesTo(actionContext)).toBeTruthy(); expect(CancelAction.appliesTo(actionContext)).toBeTruthy();
expect(mockDomainObject.hasCapability).toHaveBeenCalledWith("editor");
mockDomainObject.hasCapability.andReturn(false); mockStatusCapability.get.andReturn(false);
mockDomainObject.getCapability.andReturn(undefined);
expect(CancelAction.appliesTo(actionContext)).toBeFalsy(); expect(CancelAction.appliesTo(actionContext)).toBeFalsy();
}); });
it("invokes the editor capability's save functionality when performed", function () { it("invokes the editor capability's cancel functionality when" +
" performed", function () {
// Verify precondition // Verify precondition
expect(mockEditorCapability.cancel).not.toHaveBeenCalled(); expect(mockEditorCapability.cancel).not.toHaveBeenCalled();
action.perform(); action.perform();
@ -95,8 +101,8 @@ define(
it("returns to browse when performed", function () { it("returns to browse when performed", function () {
action.perform(); action.perform();
expect(mockLocation.path).toHaveBeenCalledWith( expect(mockNavigationService.setNavigation).toHaveBeenCalledWith(
mockUrlService.urlForLocation("browse", mockDomainObject) mockDomainObject
); );
}); });
}); });

View File

@ -19,27 +19,41 @@
* 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.
*****************************************************************************/ *****************************************************************************/
/*global define,describe,it,expect,beforeEach,jasmine*/ /*global define,describe,it,expect,beforeEach,jasmine,spyOn*/
define( define(
["../../src/actions/EditAction"], [
"../../src/actions/EditAction"
],
function (EditAction) { function (EditAction) {
"use strict"; "use strict";
describe("The Edit action", function () { describe("The Edit action", function () {
var mockLocation, var mockNavigationService,
mockNavigationService,
mockLog, mockLog,
mockDomainObject, mockDomainObject,
mockStatusCapability,
mockType, mockType,
actionContext, actionContext,
mockCapabilities,
mockQ,
action; action;
function mockPromise(value) {
return {
then: function (callback) {
return mockPromise(callback(value));
}
};
}
beforeEach(function () { beforeEach(function () {
mockLocation = jasmine.createSpyObj(
"$location", mockStatusCapability = jasmine.createSpyObj(
[ "path" ] "statusCapability",
[ "get", "set" ]
); );
mockNavigationService = jasmine.createSpyObj( mockNavigationService = jasmine.createSpyObj(
"navigationService", "navigationService",
[ "setNavigation", "getNavigation" ] [ "setNavigation", "getNavigation" ]
@ -50,20 +64,41 @@ define(
); );
mockDomainObject = jasmine.createSpyObj( mockDomainObject = jasmine.createSpyObj(
"domainObject", "domainObject",
[ "getId", "getModel", "getCapability" ] [ "getId", "getModel", "getCapability", "hasCapability" ]
); );
mockType = jasmine.createSpyObj( mockType = jasmine.createSpyObj(
"type", "type",
[ "hasFeature" ] [ "hasFeature" ]
); );
mockDomainObject.getCapability.andReturn(mockType); mockQ = jasmine.createSpyObj(
"q",
[ "when", "all" ]
);
mockQ.when.andReturn(function(value){
return mockPromise(value);
});
mockQ.all.andReturn(mockPromise(undefined));
mockDomainObject.getCapability.andCallFake(function(capability){
return mockCapabilities[capability];
});
mockDomainObject.getModel.andReturn({});
mockDomainObject.getId.andReturn("testId");
mockStatusCapability.get.andReturn(false);
mockType.hasFeature.andReturn(true); mockType.hasFeature.andReturn(true);
mockCapabilities = {
"status": mockStatusCapability,
"type": mockType
};
actionContext = { domainObject: mockDomainObject }; actionContext = { domainObject: mockDomainObject };
action = new EditAction( action = new EditAction(
mockLocation, mockQ,
mockNavigationService, mockNavigationService,
mockLog, mockLog,
actionContext actionContext
@ -77,15 +112,30 @@ define(
expect(mockType.hasFeature).toHaveBeenCalledWith('creation'); expect(mockType.hasFeature).toHaveBeenCalledWith('creation');
}); });
it("changes URL path to edit mode when performed", function () { it("is only applicable when domain object is not in edit mode", function () {
// Indicates whether object is in edit mode
mockStatusCapability.get.andReturn(false);
expect(EditAction.appliesTo(actionContext)).toBeTruthy();
mockStatusCapability.get.andReturn(true);
expect(EditAction.appliesTo(actionContext)).toBeFalsy();
});
it("navigates to editable domain object", function () {
spyOn(action, 'createEditableObject');
action.perform(); action.perform();
expect(mockLocation.path).toHaveBeenCalledWith("/edit");
expect(mockNavigationService.setNavigation).toHaveBeenCalled();
expect(action.createEditableObject).toHaveBeenCalled();
}); });
it("ensures that the edited object is navigated-to", function () { it("ensures that the edited object is navigated-to", function () {
var navigatedObject;
action.perform(); action.perform();
expect(mockNavigationService.setNavigation) navigatedObject = mockNavigationService.setNavigation.mostRecentCall.args[0];
.toHaveBeenCalledWith(mockDomainObject); expect(navigatedObject.getId())
.toEqual(mockDomainObject.getId());
expect(navigatedObject).not.toBe(mockDomainObject);
}); });
it("logs a warning if constructed when inapplicable", function () { it("logs a warning if constructed when inapplicable", function () {
@ -94,7 +144,7 @@ define(
// Should not have hit an exception... // Should not have hit an exception...
new EditAction( new EditAction(
mockLocation, mockQ,
mockNavigationService, mockNavigationService,
mockLog, mockLog,
{} {}
@ -104,8 +154,6 @@ define(
expect(mockLog.warn).toHaveBeenCalled(); expect(mockLog.warn).toHaveBeenCalled();
// And should not have had other interactions // And should not have had other interactions
expect(mockLocation.path)
.not.toHaveBeenCalled();
expect(mockNavigationService.setNavigation) expect(mockNavigationService.setNavigation)
.not.toHaveBeenCalled(); .not.toHaveBeenCalled();
}); });

View File

@ -27,10 +27,11 @@ define(
"use strict"; "use strict";
describe("The Save action", function () { describe("The Save action", function () {
var mockLocation, var mockDomainObject,
mockDomainObject, mockNavigationService,
mockStatusCapability,
mockEditorCapability, mockEditorCapability,
mockUrlService, mockCapabilities,
actionContext, actionContext,
action; action;
@ -43,10 +44,6 @@ define(
} }
beforeEach(function () { beforeEach(function () {
mockLocation = jasmine.createSpyObj(
"$location",
[ "path" ]
);
mockDomainObject = jasmine.createSpyObj( mockDomainObject = jasmine.createSpyObj(
"domainObject", "domainObject",
[ "getCapability", "hasCapability" ] [ "getCapability", "hasCapability" ]
@ -55,30 +52,39 @@ define(
"editor", "editor",
[ "save", "cancel" ] [ "save", "cancel" ]
); );
mockUrlService = jasmine.createSpyObj( mockStatusCapability = jasmine.createSpyObj(
"urlService", "statusCapability",
["urlForLocation"] ["get"]
); );
mockNavigationService = jasmine.createSpyObj(
"mockNavigationService",
[ "setNavigation"]
);
mockCapabilities = {
"editor": mockEditorCapability,
"status": mockStatusCapability
};
actionContext = { actionContext = {
domainObject: mockDomainObject domainObject: mockDomainObject
}; };
mockDomainObject.hasCapability.andReturn(true); mockDomainObject.hasCapability.andReturn(true);
mockDomainObject.getCapability.andReturn(mockEditorCapability); mockDomainObject.getCapability.andCallFake(function(capability){
mockEditorCapability.save.andReturn(mockPromise(true)); return mockCapabilities[capability];
});
action = new SaveAction(mockLocation, mockUrlService, actionContext); mockEditorCapability.save.andReturn(mockPromise(mockDomainObject));
mockStatusCapability.get.andReturn(true);
action = new SaveAction(mockNavigationService, actionContext);
}); });
it("only applies to domain object with an editor capability", function () { it("only applies to domain object that is being edited", function () {
expect(SaveAction.appliesTo(actionContext)).toBeTruthy(); expect(SaveAction.appliesTo(actionContext)).toBeTruthy();
expect(mockDomainObject.hasCapability).toHaveBeenCalledWith("editor");
mockDomainObject.hasCapability.andReturn(false); mockStatusCapability.get.andReturn(false);
mockDomainObject.getCapability.andReturn(undefined);
expect(SaveAction.appliesTo(actionContext)).toBeFalsy(); expect(SaveAction.appliesTo(actionContext)).toBeFalsy();
}); });
@ -96,8 +102,8 @@ define(
it("returns to browse when performed", function () { it("returns to browse when performed", function () {
action.perform(); action.perform();
expect(mockLocation.path).toHaveBeenCalledWith( expect(mockNavigationService.setNavigation).toHaveBeenCalledWith(
mockUrlService.urlForLocation("browse", mockDomainObject) mockDomainObject
); );
}); });
}); });

View File

@ -28,6 +28,8 @@ define(
describe("The editor capability", function () { describe("The editor capability", function () {
var mockPersistence, var mockPersistence,
mockStatusCapability,
mockCapabilities,
mockEditableObject, mockEditableObject,
mockDomainObject, mockDomainObject,
mockCache, mockCache,
@ -40,6 +42,14 @@ define(
"persistence", "persistence",
[ "persist" ] [ "persist" ]
); );
mockStatusCapability = jasmine.createSpyObj(
"status",
[ "set" ]
);
mockCapabilities = {
"persistence":mockPersistence,
"status": mockStatusCapability
};
mockEditableObject = { mockEditableObject = {
getModel: function () { return model; } getModel: function () { return model; }
}; };
@ -53,7 +63,9 @@ define(
); );
mockCallback = jasmine.createSpy("callback"); mockCallback = jasmine.createSpy("callback");
mockDomainObject.getCapability.andReturn(mockPersistence); mockDomainObject.getCapability.andCallFake(function(capability){
return mockCapabilities[capability];
});
model = { someKey: "some value", x: 42 }; model = { someKey: "some value", x: 42 };
@ -96,6 +108,19 @@ define(
}); });
}); });
it("resets the editing status on successful save", function () {
capability.save().then(mockCallback);
// Wait for promise to resolve
waitsFor(function () {
return mockCallback.calls.length > 0;
}, 250);
runs(function () {
expect(mockStatusCapability.set).toHaveBeenCalledWith('editing', false);
});
});
it("has no interactions on cancel", function () { it("has no interactions on cancel", function () {
capability.cancel().then(mockCallback); capability.cancel().then(mockCallback);
@ -111,6 +136,19 @@ define(
}); });
}); });
it("resets editing status on cancel", function () {
capability.cancel().then(mockCallback);
// Wait for promise to resolve
waitsFor(function () {
return mockCallback.calls.length > 0;
}, 250);
runs(function () {
expect(mockStatusCapability.set).toHaveBeenCalledWith('editing', false);
});
});
}); });
} }

View File

@ -32,7 +32,9 @@ define(
mockScope, mockScope,
testRepresentation, testRepresentation,
mockDomainObject, mockDomainObject,
mockStatusCapability,
mockPersistence, mockPersistence,
mockCapabilities,
representer; representer;
function mockPromise(value) { function mockPromise(value) {
@ -57,11 +59,21 @@ define(
]); ]);
mockPersistence = mockPersistence =
jasmine.createSpyObj("persistence", ["persist"]); jasmine.createSpyObj("persistence", ["persist"]);
mockStatusCapability = jasmine.createSpyObj("domainObject", [
"get"]);
mockCapabilities = {
"persistence": mockPersistence,
"status": mockStatusCapability
};
mockDomainObject.getModel.andReturn({}); mockDomainObject.getModel.andReturn({});
mockDomainObject.hasCapability.andReturn(true); mockDomainObject.hasCapability.andReturn(true);
mockDomainObject.useCapability.andReturn(true); mockDomainObject.useCapability.andReturn(true);
mockDomainObject.getCapability.andReturn(mockPersistence); mockStatusCapability.get.andReturn(true);
mockDomainObject.getCapability.andCallFake(function(capability){
return mockCapabilities[capability];
});
representer = new EditRepresenter(mockQ, mockLog, mockScope); representer = new EditRepresenter(mockQ, mockLog, mockScope);
representer.represent(testRepresentation, mockDomainObject); representer.represent(testRepresentation, mockDomainObject);
@ -98,6 +110,14 @@ define(
}); });
}); });
it("sets an 'editMode' flag on scope if the object is editable", function() {
expect(mockScope.editMode).toBe(true);
mockStatusCapability.get.andReturn(false);
representer.represent(testRepresentation, mockDomainObject);
expect(mockScope.editMode).toBeFalsy();
});
}); });
} }

View File

@ -47,14 +47,11 @@ a.disabled {
@include animation-name(pulse, 0.2); @include animation-name(pulse, 0.2);
} }
@include keyframes(pulse) { @mixin pulse($dur: 500ms, $iteration: infinite, $opacity0: 0.5, $opacity100: 1) {
0% { opacity: 0.5; } @include keyframes(pulse) {
100% { opacity: 1; } 0% { opacity: $opacity0; }
} 100% { opacity: $opacity100; }
}
@mixin pulse($dur: 500ms, $iteration: infinite) {
//@include customKeyframes(pulse, 0.2);
@include animation-name(pulse); @include animation-name(pulse);
@include animation-duration($dur); @include animation-duration($dur);
@include animation-direction(alternate); @include animation-direction(alternate);
@ -62,6 +59,19 @@ a.disabled {
@include animation-timing-function(ease-in-out); @include animation-timing-function(ease-in-out);
} }
@mixin pulseBorder($c: red, $dur: 500ms, $iteration: infinite, $delay: 0s, $opacity0: 0, $opacity100: 1) {
@include keyframes(pulseBorder) {
0% { border-color: rgba($c, $opacity0); }
100% { border-color: rgba($c, $opacity100); }
}
@include animation-name(pulseBorder);
@include animation-duration($dur);
@include animation-direction(alternate);
@include animation-iteration-count($iteration);
@include animation-timing-function(ease);
@include animation-delay($delay);
}
.pulse { .pulse {
@include pulse(750ms); @include pulse(750ms);
} }

View File

@ -121,7 +121,12 @@ mct-container {
text-align: center; text-align: center;
} }
.scrolling { .ellipsis {
@include ellipsize();
}
.scrolling,
.scroll {
overflow: auto; overflow: auto;
} }

View File

@ -23,7 +23,7 @@
.l-inspect, .l-inspect,
.l-inspect table tr td { .l-inspect table tr td {
font-size: 0.7rem; font-size: 0.75rem;
} }
.l-inspect { .l-inspect {
@ -31,6 +31,9 @@
background: $colorInspectorBg; background: $colorInspectorBg;
color: $colorInspectorFg; color: $colorInspectorFg;
line-height: 140%; line-height: 140%;
.flex-elem.holder:not(:last-child) { margin-bottom: $interiorMargin; }
.pane-header { .pane-header {
color: pushBack($colorInspectorFg, 20%); color: pushBack($colorInspectorFg, 20%);
font-size: 0.8rem; font-size: 0.8rem;
@ -43,6 +46,16 @@
vertical-align: bottom; vertical-align: bottom;
} }
} }
.split-layout {
.split-pane-component.pane {
&.bottom {
height: 30%;
min-height: 20%;
max-height: 80%;
}
}
}
ul { ul {
@include box-sizing(border-box); @include box-sizing(border-box);
@ -118,4 +131,34 @@
width: 4px; width: 4px;
} }
} }
} .holder-elements {
.current-elements {
position: relative;
.tree-item {
.t-object-label {
// Elements pool is a flat list, so don't indent items.
font-size: 0.75rem;
left: 0;
}
}
}
}
}
.l-inspect {
.splitter-inspect-panel,
.split-pane-component.pane.bottom {
@include trans-prop-nice(opacity, 250ms); // Not working as desired currently; entire inspector seems to be destroyed and recreated when switching into and out of edit mode.
opacity: 0;
pointer-events: none;
}
}
.s-status-editing .l-inspect {
.location-item { pointer-events: none; }
.splitter-inspect-panel,
.split-pane-component.pane.bottom {
opacity: 1;
pointer-events: inherit;
}
}

View File

@ -64,6 +64,12 @@
} }
} }
@mixin trans-prop-nice-resize($t: 0.5s, $tf: ease-in-out) {
@include transition-property(height, width, top, right, bottom, left, opacity);
@include transition-duration($t);
@include transition-timing-function($tf);
}
@mixin trans-prop-nice-resize-h($dur: 500ms, $delay: 0) { @mixin trans-prop-nice-resize-h($dur: 500ms, $delay: 0) {
@include transition-property(height, bottom, top); @include transition-property(height, bottom, top);
@include transition-duration($dur); @include transition-duration($dur);
@ -309,11 +315,11 @@
} }
} }
@mixin input-base($bg: $colorBodyBg, $fg: $colorBodyFg) { @mixin input-base($bg: $colorInputBg, $fg: $colorInputFg, $shdw: rgba(black, 0.6) 0 1px 3px) {
@include appearance(none); @include appearance(none);
@include border-radius($controlCr); @include border-radius($controlCr);
@include box-sizing(border-box); @include box-sizing(border-box);
@include box-shadow(inset rgba(black, 0.4) 0 1px 3px); @include box-shadow(inset $shdw);
background: $bg; background: $bg;
border: none; border: none;
color: $fg; color: $fg;
@ -323,7 +329,7 @@
} }
} }
@mixin nice-input($bg: $colorBodyBg, $fg: $colorBodyFg) { @mixin nice-input($bg: $colorInputBg, $fg: $colorInputFg) {
@include input-base($bg, $fg); @include input-base($bg, $fg);
padding: 0 $interiorMarginSm; padding: 0 $interiorMarginSm;
} }

View File

@ -35,6 +35,7 @@ $pad: $interiorMargin * $baseRatio;
@include box-sizing(border-box); @include box-sizing(border-box);
padding: 0 $pad; padding: 0 $pad;
font-size: 0.7rem; font-size: 0.7rem;
vertical-align: top;
.icon { .icon {
font-size: 0.8rem; font-size: 0.8rem;
@ -70,6 +71,18 @@ $pad: $interiorMargin * $baseRatio;
&.pause-play { &.pause-play {
} }
&.t-save:before {
content:'\e612';
font-family: symbolsfont;
margin-right: $interiorMarginSm;
}
&.t-cancel {
.title-label { display: none; }
&:before {
content:'\78';
font-family: symbolsfont;
}
}
&.pause-play { &.pause-play {
.icon:before { .icon:before {

View File

@ -6,10 +6,6 @@
} }
} }
.l-time-controller-visible {
}
mct-include.l-time-controller { mct-include.l-time-controller {
$minW: 500px; $minW: 500px;
$knobHOffset: 0px; $knobHOffset: 0px;
@ -22,10 +18,10 @@ mct-include.l-time-controller {
$r2H: nth($ueTimeControlH,2); $r2H: nth($ueTimeControlH,2);
$r3H: nth($ueTimeControlH,3); $r3H: nth($ueTimeControlH,3);
@include absPosDefault(); //@include absPosDefault();
//@include test(); //@include test();
display: block; display: block;
top: auto; //top: auto;
height: $r1H + $r2H + $r3H + ($interiorMargin * 2); height: $r1H + $r2H + $r3H + ($interiorMargin * 2);
min-width: $minW; min-width: $minW;
font-size: 0.8rem; font-size: 0.8rem;

View File

@ -19,8 +19,10 @@
* 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.
*****************************************************************************/ *****************************************************************************/
.s-status-editing .l-object-wrapper,
.edit-main { .edit-main {
// .s-status-editing .l-object-wrapper is relevant to New Edit Mode;
// .edit-main is legacy for old edit mode.
$handleD: 15px; $handleD: 15px;
$cr: 5px; $cr: 5px;
.edit-corner, .edit-corner,
@ -92,12 +94,10 @@
} }
} }
.frame.child-frame.panel { .frame.child-frame.panel {
&:hover { &:hover {
@include boxShdwLarge(); @include boxShdwLarge();
border-color: $colorKey; border-color: $colorKey;
//z-index: 2;
.view-switcher { .view-switcher {
opacity: 1; opacity: 1;
} }

View File

@ -31,7 +31,6 @@
} }
.form { .form {
// @include test(orange);
color: $colorFormText; color: $colorFormText;
.form-section { .form-section {
position: relative; position: relative;
@ -112,12 +111,8 @@
.selector-list { .selector-list {
// Used in create overlay to display tree view // Used in create overlay to display tree view
@include nice-input($colorInputBg, $colorInputFg); @include nice-input();
$h: 150px; $h: 150px;
//@include border-radius($basicCr);
//@include box-sizing(border-box);
//background: rgba(black, 0.2);
//padding: $interiorMargin;
position: relative; position: relative;
height: $h; height: $h;
// max-width: 50%; // max-width: 50%;
@ -162,13 +157,9 @@ label.form-control.checkbox {
} }
} }
input[type="text"] { input[type="text"],
@include nice-input($colorInputBg, $colorInputFg); input[type="search"] {
&.filter { @include nice-input();
&.ng-dirty {
// background: red;
}
}
&.numeric { &.numeric {
text-align: right; text-align: right;
} }
@ -176,7 +167,6 @@ input[type="text"] {
textarea { textarea {
@include nice-textarea($colorInputBg, $colorInputFg); @include nice-textarea($colorInputBg, $colorInputFg);
// font-size: 0.9em;
position: absolute; position: absolute;
height: 100%; height: 100%;
width: 100%; width: 100%;

View File

@ -21,20 +21,7 @@
*****************************************************************************/ *****************************************************************************/
.filter, .filter,
.t-filter { .t-filter {
input.filter,
input.t-filter-input { input.t-filter-input {
@include subdued-input();
}
input.t-filter-input {
height: $formInputH;
width: 200px;
&:not(.ng-dirty) {
// TO-DO: Update compass install to support this
// @include input-placeholder {
// color: rgba(#fff, 0.3);
// font-style: italic;
// }
}
&:not(.ng-dirty) + .t-a-clear { &:not(.ng-dirty) + .t-a-clear {
display: none; display: none;
} }
@ -76,13 +63,6 @@
background-color: $colorKey; background-color: $colorKey;
} }
} }
// &:not(ng-dirty)
}
.l-filter {
// Holds an input and a clear button
display:inline-block;
position: relative;
} }
.top-bar { .top-bar {
@ -100,4 +80,75 @@
.icon-filter { .icon-filter {
font-size: 1.4em; font-size: 1.4em;
} }
}
.l-filter {
$iconEdgeM: 4px;
$iconD: $formInputH - ($iconEdgeM * 2);
// Adds a magnifying glass before, holds an input and a clear button
display: inline-block;
position: relative;
input[type="search"] {
padding: 2px ($iconD + $interiorMargin);
}
.clear-icon,
.menu-icon,
&:before {
@include box-sizing(border-box);
display: inline-block;
line-height: inherit;
position: absolute;
top: 50%;
@include transform(translateY(-50%));
z-index: 1;
}
&:before {
// Magnify glass icon
content:'\4d';
left: $interiorMargin;
@include trans-prop-nice(color, 250ms);
pointer-events: none;
}
.clear-icon {
right: $iconEdgeM;
// Icon is visible only when there is text input
visibility: hidden;
opacity: 0;
&.show {
visibility: visible;
opacity: 1;
}
&:hover {
color: pullForward($colorInputIcon, 10%);
}
}
}
.s-filter {
input[type="search"] {
@include input-base();
}
.clear-icon,
.menu-icon,
&:before {
color: $colorInputIcon;
cursor: pointer;
font-family: symbolsfont;
@include trans-prop-nice((opacity, color), 150ms);
}
// Make icon lighten when hovering over search bar
&:hover:before {
color: pullForward($colorInputIcon, 10%);
}
.clear-icon {
// 'x' in circle icon
&:before {
content: '\e607';
}
}
} }

View File

@ -102,7 +102,7 @@
} }
.object-browse-bar { .object-browse-bar {
left: 45px !important; margin-left: 45px;
.context-available { .context-available {
opacity: 1 !important; opacity: 1 !important;
} }

View File

@ -149,3 +149,32 @@ mct-representation {
border-color: rgba($colorItemTreeSelectedFg, 0.25); border-color: rgba($colorItemTreeSelectedFg, 0.25);
border-top-color: rgba($colorItemTreeSelectedFg, 1.0); border-top-color: rgba($colorItemTreeSelectedFg, 1.0);
} }
.tree .s-status-editing,
.search-results .s-status-editing {
// Item is being edited
.tree-item,
.search-result-item {
background: $colorItemTreeEditingBg;
pointer-events: none;
&:before {
// Pencil icon
@extend .ui-symbol;
@include pulse($dur: 1s, $opacity0: 0.25);
color: $colorItemTreeEditingFg;
content: '\70';
margin-right: $interiorMarginSm;
}
.t-object-label {
.t-item-icon,
.t-title-label {
color: $colorItemTreeEditingFg;
@include text-shadow(none);
}
.t-title-label {
font-style: italic;
}
}
.view-control, + .tree-item-subtree { display: none; }
}
}

View File

@ -25,6 +25,7 @@
&.child-frame.panel { &.child-frame.panel {
background: $colorBodyBg; background: $colorBodyBg;
border: 1px solid $bc; border: 1px solid $bc;
z-index: 0; // Needed to prevent child-frame controls from showing through when another child-frame is above
&:hover { &:hover {
border-color: lighten($bc, 10%); border-color: lighten($bc, 10%);
} }

View File

@ -77,38 +77,38 @@
} }
} }
// from _bottom-bar.scss .ue-bottom-bar {
.ue-bottom-bar { @include absPosDefault(0);// New status bar design
@include absPosDefault(0);// New status bar design top: auto;
top: auto; height: $ueFooterH;
height: $ueFooterH; line-height: $ueFooterH - ($interiorMargin * 2);
line-height: $ueFooterH - ($interiorMargin * 2); background: $colorFooterBg;
background: $colorFooterBg; color: lighten($colorBodyBg, 30%);
color: lighten($colorBodyBg, 30%); font-size: .7rem;
font-size: .7rem; .status-holder {
.status-holder { @include box-sizing(border-box);
@include box-sizing(border-box); @include absPosDefault($interiorMargin);
@include absPosDefault($interiorMargin); @include ellipsize();
@include ellipsize(); right: 120px;
right: 120px; text-transform: uppercase;
text-transform: uppercase; z-index: 1;
z-index: 1; }
} .app-logo {
.app-logo { @include box-sizing(border-box);
@include box-sizing(border-box); @include absPosDefault($interiorMargin);
@include absPosDefault($interiorMargin); cursor: pointer;
cursor: pointer; left: auto;
left: auto; width: $ueAppLogoW;
width: $ueAppLogoW; z-index: 2;
z-index: 2; &.logo-openmctweb {
&.logo-openmctweb { background: url($dirImgs + 'logo-openmctweb.svg') no-repeat center center;
background: url($dirImgs + 'logo-openmctweb.svg') no-repeat center center; }
} }
} }
}
} }
.edit-mode { .edit-mode {
// Old edit mode
.split-layout { .split-layout {
.split-pane-component.pane.right { .split-pane-component.pane.right {
width: 15%; width: 15%;
@ -132,7 +132,7 @@
.primary-pane { .primary-pane {
// Need to lift up this pane to ensure that 'collapsed' panes don't block user interactions // Need to lift up this pane to ensure that 'collapsed' panes don't block user interactions
z-index: 2; z-index: 4;
} }
.mini-tab-icon.toggle-pane { .mini-tab-icon.toggle-pane {
@ -172,7 +172,7 @@
&.toggle-inspect.anchor-right { &.toggle-inspect.anchor-right {
right: $bodyMargin; right: $bodyMargin;
&:after { &:after {
content: '\e615'; // e615: Crosshair icon; was e608: Info "i" icon content: '\e615'; // Eye icon
} }
&.collapsed { &.collapsed {
right: $interiorMargin; right: $interiorMargin;
@ -197,6 +197,16 @@
right: 0; right: 0;
bottom: $bodyMargin; bottom: $bodyMargin;
left: $bodyMargin; left: $bodyMargin;
.create-btn-holder {
&.s-status-editing {
display: none;
& + .search-holder .search-bar {
// .search-holder is adjacent sibling to .create-btn-holder
// Add right margin when create button is hidden, to make room for the collapse pane 'x' button
margin-right: $interiorMarginLg * 2;
}
}
}
} }
.holder.holder-object-and-inspector { .holder.holder-object-and-inspector {
@ -208,25 +218,50 @@
top: $bodyMargin; top: $bodyMargin;
bottom: $bodyMargin; bottom: $bodyMargin;
} }
.holder-inspector-elements { .holder-inspector {
top: $bodyMargin; top: $bodyMargin;
bottom: $bodyMargin; bottom: $bodyMargin;
left: $bodyMargin; left: $bodyMargin;
right: $bodyMargin; right: $bodyMargin;
} }
.holder-elements {
top: 0;
bottom: $bodyMargin;
left: $bodyMargin;
right: $bodyMargin;
}
} }
} }
.object-holder { .object-holder {
@include absPosDefault(0, auto); @include absPosDefault(0, auto);
top: $ueTopBarH + $interiorMarginLg; top: $ueTopBarH + $interiorMarginLg;
&.l-controls-visible { }
&.l-time-controller-visible {
bottom: nth($ueTimeControlH,1) + nth($ueTimeControlH,2) +nth($ueTimeControlH,3) + ($interiorMargin * 3); .l-object-wrapper {
@extend .abs;
.object-holder-main {
@extend .abs;
}
.l-edit-controls {
//@include trans-prop-nice((opacity, height), 0.25s);
border-bottom: 1px solid $colorInteriorBorder;
line-height: $ueEditToolBarH;
height: 0px;
opacity: 0;
.tool-bar {
right: $interiorMargin;
} }
} }
} }
.l-object-wrapper-inner {
@include trans-prop-nice-resize(0.25s);
}
.object-browse-bar .s-btn, .object-browse-bar .s-btn,
.top-bar .buttons-main .s-btn, .top-bar .buttons-main .s-btn,
.top-bar .s-menu-btn, .top-bar .s-menu-btn,
@ -247,7 +282,6 @@
/***************************************************** OBJECT BROWSE BAR */ /***************************************************** OBJECT BROWSE BAR */
.object-browse-bar { .object-browse-bar {
@include absPosDefault(0, visible); // Must use visible to avoid hiding view switcher menu
@include box-sizing(border-box); @include box-sizing(border-box);
height: $ueTopBarH; height: $ueTopBarH;
line-height: $ueTopBarH; line-height: $ueTopBarH;
@ -255,15 +289,13 @@
.left { .left {
padding-right: $interiorMarginLg; padding-right: $interiorMarginLg;
.l-back { .l-back:not(.s-status-editing) {
margin-right: $interiorMarginLg; margin-right: $interiorMarginLg;
} }
} }
} }
// When the tree is hidden, these are the // When the tree is hidden, these are the classes used for the left menu and the right representation.
// classes used for the left menu and the
// right representation.
.pane-tree-hidden { .pane-tree-hidden {
// Sets the left tree menu when the tree is hidden. // Sets the left tree menu when the tree is hidden.
.tree-holder, .tree-holder,
@ -299,9 +331,6 @@
.pane-inspect-hidden { .pane-inspect-hidden {
.l-object-and-inspector { .l-object-and-inspector {
.t-inspect {
z-index: 1 !important; // Move down so that primary pane elements are clickable
}
.l-inspect, .l-inspect,
.splitter-inspect { .splitter-inspect {
opacity: 0; opacity: 0;
@ -345,3 +374,22 @@
min-width: 200px; // Needed for nice display when primary pane is constrained severely via splitters min-width: 200px; // Needed for nice display when primary pane is constrained severely via splitters
} }
} }
.s-status-editing {
.l-object-wrapper {
@include pulseBorder($colorEditAreaFg, $dur: 1s, $opacity0: 0.3);
@include border-radius($controlCr);
background-color: $colorEditAreaBg;
border-color: $colorEditAreaFg;
border-width: 2px;
border-style: dotted;
.l-object-wrapper-inner {
@include absPosDefault(3px, hidden);
}
.l-edit-controls {
height: $ueEditToolBarH + $interiorMargin;
margin-bottom: $interiorMargin;
opacity: 1;
}
}
}

View File

@ -20,7 +20,9 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
.tool-bar { .tool-bar {
border-bottom: 1px solid $colorInteriorBorder; &.btn-bar {
white-space: nowrap;
}
.l-control-group { .l-control-group {
height: $btnToolbarH; height: $btnToolbarH;
} }

View File

@ -46,8 +46,7 @@
.edit-mode { .edit-mode {
.top-bar { .top-bar {
.buttons-main { .buttons-main {
// background: red; // Old edit mode
// width: 600px;
white-space: nowrap; white-space: nowrap;
&.abs { &.abs {
bottom: auto; bottom: auto;

View File

@ -20,15 +20,13 @@
at runtime from the About dialog for additional information. at runtime from the About dialog for additional information.
--> -->
<!-- look at action-button for example --> <!-- look at action-button for example -->
<span class="t-filter l-filter" <span class="t-filter l-filter s-filter"
ng-controller="GetterSetterController"> ng-controller="GetterSetterController">
<input type="search" <input type="search"
class="t-filter-input" class="t-filter-input"
ng-model="getterSetter.value" ng-model="getterSetter.value"/>
placeholder="Filter..."/> <a class="clear-icon"
<a class="ui-symbol t-a-clear s-a-clear" ng-class="{show: !(getterSetter.value === '' || getterSetter.value === undefined)}"
ng-show="getterSetter.value !== ''"
ng-click="getterSetter.value = ''"> ng-click="getterSetter.value = ''">
x
</a> </a>
</span> </span>

View File

@ -20,44 +20,61 @@
at runtime from the About dialog for additional information. at runtime from the About dialog for additional information.
--> -->
<span class="l-inspect" ng-controller="ObjectInspectorController as controller"> <span class="l-inspect" ng-controller="ObjectInspectorController as controller">
<div class="abs holder holder-inspector-elements l-flex-col"> <div ng-controller="PaneController as modelPaneEdit">
<div class="pane-header flex-elem">Inspection</div> <mct-split-pane class='abs contents split-layout' anchor='bottom'>
<ul class="flex-elem grows vscroll"> <div class="split-pane-component pane top">
<li> <div class="abs holder holder-inspector l-flex-col">
<em>Properties</em> <div class="pane-header flex-elem">Inspection</div>
<div class="inspector-properties" <ul class="flex-elem grows vscroll">
ng-repeat="data in metadata" <li>
ng-class="{ first:$index === 0 }"> <em>Properties</em>
<div class="label">{{ data.name }}</div> <div class="inspector-properties"
<div class="value">{{ data.value }}</div> ng-repeat="data in metadata"
ng-class="{ first:$index === 0 }">
<div class="label">{{ data.name }}</div>
<div class="value">{{ data.value }}</div>
</div>
</li>
<li ng-if="contextutalParents.length > 0">
<em title="The location of this linked object.">Location</em>
<span class="inspector-location"
ng-repeat="parent in contextutalParents"
ng-class="{ last:($index + 1) === contextualParents.length }">
<mct-representation key="'label'"
mct-object="parent"
ng-model="ngModel"
ng-click="ngModel.selectedObject = parent"
class="location-item">
</mct-representation>
</span>
</li>
<li ng-if="primaryParents.length > 0">
<em title="The location of the original object that this was linked from.">Original Location</em>
<span class="inspector-location"
ng-repeat="parent in primaryParents"
ng-class="{ last:($index + 1) === primaryParents.length }">
<mct-representation key="'label'"
mct-object="parent"
ng-model="ngModel"
ng-click="ngModel.selectedObject = parent"
class="location-item">
</mct-representation>
</span>
</li>
</ul>
</div><!--/ holder-inspector -->
</div><!--/ split-pane-component -->
<mct-splitter class="splitter-inspect-panel mobile-hide"></mct-splitter>
<div class="split-pane-component pane bottom">
<div class="abs holder holder-elements l-flex-col">
<em class="flex-elem">Elements</em>
<mct-representation
key="'edit-elements'"
mct-object="domainObject"
class="flex-elem holder grows vscroll current-elements">
</mct-representation>
</div>
</div> </div>
</li> </mct-split-pane>
<li ng-if="contextutalParents.length > 0"> </div><!--/ PaneController -->
<em title="The location of this linked object.">Location</em>
<span class="inspector-location"
ng-repeat="parent in contextutalParents"
ng-class="{ last:($index + 1) === contextualParents.length }">
<mct-representation key="'label'"
mct-object="parent"
ng-model="ngModel"
ng-click="ngModel.selectedObject = parent"
class="location-item rep-object-label">
</mct-representation>
</span>
</li>
<li ng-if="primaryParents.length > 0">
<em title="The location of the original object that this was linked from.">Original Location</em>
<span class="inspector-location"
ng-repeat="parent in primaryParents"
ng-class="{ last:($index + 1) === primaryParents.length }">
<mct-representation key="'label'"
mct-object="parent"
ng-model="ngModel"
ng-click="ngModel.selectedObject = parent"
class="location-item rep-object-label">
</mct-representation>
</span>
</li>
</ul>
</div>
</span> </span>

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,8 @@ $colorFooterBg: #000;
$colorKey: #0099cc; $colorKey: #0099cc;
$colorKeySelectedBg: #005177; $colorKeySelectedBg: #005177;
$colorKeyFg: #fff; $colorKeyFg: #fff;
$colorEditAreaBg: #31363e;
$colorEditAreaFg: #587ab5;
$colorInteriorBorder: rgba($colorBodyFg, 0.1); $colorInteriorBorder: rgba($colorBodyFg, 0.1);
$colorA: #ccc; $colorA: #ccc;
$colorAHov: #fff; $colorAHov: #fff;
@ -43,8 +45,6 @@ $colorPausedBg: #c56f01;
$colorPausedFg: #fff; $colorPausedFg: #fff;
$colorCreateBtn: $colorKey; $colorCreateBtn: $colorKey;
$colorGridLines: rgba(#fff, 0.05); $colorGridLines: rgba(#fff, 0.05);
$colorFormLines: rgba(#fff, 0.1);
$colorFormSectionHeader: rgba(#000, 0.2);
$colorInvokeMenu: #fff; $colorInvokeMenu: #fff;
$colorObjHdrTxt: $colorBodyFg; $colorObjHdrTxt: $colorBodyFg;
$colorObjHdrIc: pullForward($colorObjHdrTxt, 20%); $colorObjHdrIc: pullForward($colorObjHdrTxt, 20%);
@ -69,8 +69,8 @@ $colorFormValid: #33cc33;
$colorFormError: #cc0000; $colorFormError: #cc0000;
$colorFormInvalid: #ff3300; $colorFormInvalid: #ff3300;
$colorFormLines: rgba(#fff, 0.1); $colorFormLines: rgba(#fff, 0.1);
$colorFormSectionHeader: rgba(#000, 0.2); $colorFormSectionHeader: rgba(#fff, 0.1);
$colorInputBg: rgba(#fff, 0.1); $colorInputBg: rgba(#000, 0.1);
$colorInputFg: pullForward($colorBodyFg, 20%); $colorInputFg: pullForward($colorBodyFg, 20%);
$colorFormText: rgba(#fff, 0.5); $colorFormText: rgba(#fff, 0.5);
$colorInputIcon: pushBack($colorBodyFg, 15%); $colorInputIcon: pushBack($colorBodyFg, 15%);
@ -80,7 +80,7 @@ $colorInspectorBg: pullForward($colorBodyBg, 3%);
$colorInspectorFg: $colorBodyFg; $colorInspectorFg: $colorBodyFg;
$colorInspectorPropName: pushBack($colorBodyFg, 15%); $colorInspectorPropName: pushBack($colorBodyFg, 15%);
$colorInspectorPropVal: pullForward($colorInspectorFg, 15%); $colorInspectorPropVal: pullForward($colorInspectorFg, 15%);
$colorInspectorSectionHeaderBg: pullForward($colorInspectorBg, 5%); $colorInspectorSectionHeaderBg: $colorFormSectionHeader;
$colorInspectorSectionHeaderFg: pullForward($colorInspectorBg, 40%); $colorInspectorSectionHeaderFg: pullForward($colorInspectorBg, 40%);
// Status colors, mainly used for messaging and item ancillary symbols // Status colors, mainly used for messaging and item ancillary symbols
@ -109,8 +109,8 @@ $colorLimitRedBg: rgba(red, 0.3);
$colorLimitRedIc: red; $colorLimitRedIc: red;
// Bubble colors // Bubble colors
$colorInfoBubbleFg: #666;
$colorInfoBubbleBg: #ddd; $colorInfoBubbleBg: #ddd;
$colorInfoBubbleFg: #666;
$colorThumbsBubbleFg: pullForward($colorBodyFg, 10%); $colorThumbsBubbleFg: pullForward($colorBodyFg, 10%);
$colorThumbsBubbleBg: pullForward($colorBodyBg, 10%); $colorThumbsBubbleBg: pullForward($colorBodyBg, 10%);
@ -126,7 +126,7 @@ $colorItemBg: lighten($colorBodyBg, 5%);
$colorItemBgHov: pullForward($colorItemBg, 15%); $colorItemBgHov: pullForward($colorItemBg, 15%);
$colorItemFg: lighten($colorItemBg, 50%); $colorItemFg: lighten($colorItemBg, 50%);
$colorItemFgDetails: lighten($colorItemBg, 30%); $colorItemFgDetails: lighten($colorItemBg, 30%);
$colorItemIc: $colorKey; //pullForward($colorItemFg, 20%); $colorItemIc: $colorKey;
$colorItemSubIcons: $colorItemFgDetails; $colorItemSubIcons: $colorItemFgDetails;
$colorItemOpenIcon: $colorItemFgDetails; $colorItemOpenIcon: $colorItemFgDetails;
$shdwItemText: rgba(black, 0.1) 0 1px 2px; $shdwItemText: rgba(black, 0.1) 0 1px 2px;
@ -153,12 +153,14 @@ $colorItemTreeHoverBg: rgba($colorBodyFg, 0.1);
$colorItemTreeHoverFg: pullForward($colorBodyFg, 20%); $colorItemTreeHoverFg: pullForward($colorBodyFg, 20%);
$colorItemTreeIcon: $colorKey; $colorItemTreeIcon: $colorKey;
$colorItemTreeIconHover: lighten($colorItemTreeIcon, 20%); $colorItemTreeIconHover: lighten($colorItemTreeIcon, 20%);
$colorItemTreeVCHover: $colorAlt1;
$colorItemTreeFg: $colorBodyFg; $colorItemTreeFg: $colorBodyFg;
$colorItemTreeSelectedBg: pushBack($colorKey, 15%); $colorItemTreeSelectedBg: pushBack($colorKey, 15%);
$colorItemTreeSelectedFg: pullForward($colorBodyFg, 20%); $colorItemTreeSelectedFg: pullForward($colorBodyFg, 20%);
$colorItemTreeEditingBg: #344154;
$colorItemTreeEditingFg: $colorEditAreaFg;
$colorItemTreeVC: rgba(#fff, 0.3); $colorItemTreeVC: rgba(#fff, 0.3);
$colorItemTreeSelectedVC: $colorItemTreeVC; $colorItemTreeSelectedVC: $colorItemTreeVC;
$colorItemTreeVCHover: $colorAlt1;
$shdwItemTreeIcon: 0.6; $shdwItemTreeIcon: 0.6;
// Scrollbar // Scrollbar
@ -171,7 +173,7 @@ $scrollbarTrackShdw: rgba(#000, 0.7) 0 1px 5px;
// Splitter // Splitter
$splitterD: 25px; // splitterD and HandleD should both be odd, or even $splitterD: 25px; // splitterD and HandleD should both be odd, or even
$splitterHandleD: 1px; $splitterHandleD: 1px;
$colorSplitterBg: pullForward($colorBodyBg, 5%); $colorSplitterBg: rgba(#fff, 0.1); //pullForward($colorBodyBg, 5%);
$splitterShdw: rgba(black, 0.4) 0 0 3px; $splitterShdw: rgba(black, 0.4) 0 0 3px;
$splitterEndCr: none; $splitterEndCr: none;
$colorSplitterHover: pullForward($colorBodyBg, 15%); $colorSplitterHover: pullForward($colorBodyBg, 15%);
@ -191,5 +193,5 @@ $colorCalCellInMonthBg: pushBack($colorMenuBg, 5%);
$colorAboutLink: #84b3ff; $colorAboutLink: #84b3ff;
// Loading // Loading
$colorLoadingBg: rgba($colorBodyFg, 0.2);
$colorLoadingFg: $colorAlt1; $colorLoadingFg: $colorAlt1;
$colorLoadingBg: rgba($colorBodyFg, 0.2);

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,8 @@ $colorFooterBg: #000;
$colorKey: #0099cc; $colorKey: #0099cc;
$colorKeySelectedBg: $colorKey; $colorKeySelectedBg: $colorKey;
$colorKeyFg: #fff; $colorKeyFg: #fff;
$colorEditAreaBg: #eafaff;
$colorEditAreaFg: #4bb1c7; //587ab5;
$colorInteriorBorder: rgba($colorBodyFg, 0.2); $colorInteriorBorder: rgba($colorBodyFg, 0.2);
$colorA: #999; $colorA: #999;
$colorAHov: $colorKey; $colorAHov: $colorKey;
@ -55,10 +57,10 @@ $colorMenuIc: $colorKey;
$colorMenuHovBg: pullForward($colorMenuBg, 10%); $colorMenuHovBg: pullForward($colorMenuBg, 10%);
$colorMenuHovFg: $colorMenuFg; $colorMenuHovFg: $colorMenuFg;
$colorMenuHovIc: $colorMenuIc; $colorMenuHovIc: $colorMenuIc;
$colorCreateMenuLgIcon: $colorKey;
$colorCreateMenuText: $colorBodyFg;
$shdwMenu: rgba(black, 0.5) 0 1px 5px; $shdwMenu: rgba(black, 0.5) 0 1px 5px;
$shdwMenuText: none; $shdwMenuText: none;
$colorCreateMenuLgIcon: $colorKey;
$colorCreateMenuText: $colorBodyFg;
// Form colors // Form colors
$colorCheck: $colorKey; $colorCheck: $colorKey;
@ -107,8 +109,8 @@ $colorLimitRedBg: rgba(red, 0.3);
$colorLimitRedIc: red; $colorLimitRedIc: red;
// Bubble colors // Bubble colors
$colorInfoBubbleFg: #666;
$colorInfoBubbleBg: $colorMenuBg; $colorInfoBubbleBg: $colorMenuBg;
$colorInfoBubbleFg: #666;
$colorThumbsBubbleFg: pullForward($colorBodyFg, 10%); $colorThumbsBubbleFg: pullForward($colorBodyFg, 10%);
$colorThumbsBubbleBg: pullForward($colorBodyBg, 10%); $colorThumbsBubbleBg: pullForward($colorBodyBg, 10%);
@ -127,12 +129,11 @@ $colorItemFgDetails: pushBack($colorItemFg, 15%);
$colorItemIc: $colorKey; $colorItemIc: $colorKey;
$colorItemSubIcons: $colorItemFgDetails; $colorItemSubIcons: $colorItemFgDetails;
$colorItemOpenIcon: $colorItemFgDetails; $colorItemOpenIcon: $colorItemFgDetails;
$shdwItemText: none; //rgba(black, 0.2) 0 1px 2px; $shdwItemText: none;
$colorItemBgSelected: $colorKey; $colorItemBgSelected: $colorKey;
// Tabular // Tabular
$colorTabBorder: pullForward($colorBodyBg, 10%); $colorTabBorder: pullForward($colorBodyBg, 10%);
$colorItemTreeHoverFg: pullForward($colorBodyFg, 20%);
$colorTabBodyBg: $colorBodyBg; $colorTabBodyBg: $colorBodyBg;
$colorTabBodyFg: pullForward($colorBodyFg, 20%); $colorTabBodyFg: pullForward($colorBodyFg, 20%);
$colorTabHeaderBg: pullForward($colorBodyBg, 10%); $colorTabHeaderBg: pullForward($colorBodyBg, 10%);
@ -149,12 +150,15 @@ $colorPlotLabelFg: pushBack($colorPlotFg, 20%);
// Tree // Tree
$colorItemTreeHoverBg: rgba($colorBodyFg, 0.1); $colorItemTreeHoverBg: rgba($colorBodyFg, 0.1);
$colorItemTreeHoverFg: pullForward($colorBodyFg, 20%);
$colorItemTreeIcon: $colorKey; $colorItemTreeIcon: $colorKey;
$colorItemTreeIconHover: $colorItemTreeIcon; //pushBack($colorItemTreeIcon, 20%); $colorItemTreeIconHover: $colorItemTreeIcon; //pushBack($colorItemTreeIcon, 20%);
$colorItemTreeVCHover: $colorKey; $colorItemTreeVCHover: $colorKey;
$colorItemTreeFg: $colorBodyFg; $colorItemTreeFg: $colorBodyFg;
$colorItemTreeSelectedBg: pushBack($colorKey, 15%); $colorItemTreeSelectedBg: pushBack($colorKey, 15%);
$colorItemTreeSelectedFg: $colorBodyBg; $colorItemTreeSelectedFg: $colorBodyBg;
$colorItemTreeEditingBg: #caf1ff; //#c6e3ff;
$colorItemTreeEditingFg: $colorEditAreaFg;
$colorItemTreeVC: $colorBodyFg; $colorItemTreeVC: $colorBodyFg;
$colorItemTreeSelectedVC: $colorBodyBg; $colorItemTreeSelectedVC: $colorBodyBg;
$shdwItemTreeIcon: none; $shdwItemTreeIcon: none;

View File

@ -20,7 +20,8 @@
at runtime from the About dialog for additional information. at runtime from the About dialog for additional information.
--> -->
<span ng-controller="PlotController as plot" <span ng-controller="PlotController as plot"
ng-mouseleave="representation.showControls = false"> ng-mouseleave="representation.showControls = false"
class="abs holder holder-plot">
<div class="gl-plot" <div class="gl-plot"
ng-style="{ height: 100 / plot.getSubPlots().length + '%'}" ng-style="{ height: 100 / plot.getSubPlots().length + '%'}"

View File

@ -1,5 +1,5 @@
<!-- <!--
Open MCT Web, Copyright (c) 2014-2015, United States Government Open MCT Web, Copyright (c) 2014-2015, United Statobes 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.
@ -23,7 +23,7 @@
<div class="w2" <div class="w2"
ng-controller="ScrollingListController"> ng-controller="ScrollingListController">
<!-- To add filtering, add class 'filterable' to <table> and uncomment 2nd <tr> in <thead> --> <!-- To add filtering, add class 'filterable' to <table> and uncomment 2nd <tr> in <thead> -->
<table class="tabular fixed-header"> <table class="tabular">
<thead> <thead>
<tr> <tr>
<th ng-repeat="header in headers"> <th ng-repeat="header in headers">

View File

@ -54,7 +54,8 @@
} }
} }
.edit-mode .s-timeline-gantt { .edit-mode .s-timeline-gantt,
.s-status-editing .s-timeline-gantt {
.handle { .handle {
cursor: col-resize; cursor: col-resize;
&.mid { &.mid {

View File

@ -69,7 +69,8 @@
} }
} }
.edit-mode .s-timeline-gantt { .edit-mode .s-timeline-gantt,
.s-status-editing .s-timeline-gantt {
.bar { .bar {
&:hover { &:hover {
@include background-image(linear-gradient(lighten($colorGanttBarBg, 20), lighten($colorGanttBarBg, 10))); @include background-image(linear-gradient(lighten($colorGanttBarBg, 20), lighten($colorGanttBarBg, 10)));
@ -140,7 +141,8 @@
} }
} }
.edit-mode .s-swimlane { .edit-mode .s-swimlane,
.s-status-editing .s-swimlane {
cursor: pointer; cursor: pointer;
.t-object-label { .t-object-label {
@include border-radius($controlCr); @include border-radius($controlCr);