Merge pull request #1663 from dtailor90/master

persist user preference width for MCTSplitPanes issue #1646
This commit is contained in:
Pete Richards 2017-08-16 15:31:26 -07:00 committed by GitHub
commit 586901aee7
7 changed files with 89 additions and 37 deletions

2
.gitignore vendored
View File

@ -36,3 +36,5 @@ protractor/logs
# npm-debug log # npm-debug log
npm-debug.log npm-debug.log
package-lock.json

View File

@ -26,7 +26,7 @@
ng-controller="PaneController as modelPaneTree" ng-controller="PaneController as modelPaneTree"
ng-class="modelPaneTree.visible() ? 'pane-tree-showing' : 'pane-tree-hidden'"> ng-class="modelPaneTree.visible() ? 'pane-tree-showing' : 'pane-tree-hidden'">
<mct-split-pane class='abs contents' <mct-split-pane class='abs contents'
anchor='left'> anchor='left' alias="leftSide">
<div class='split-pane-component treeview pane left'> <div class='split-pane-component treeview pane left'>
<div class="abs holder l-flex-col holder-treeview-elements"> <div class="abs holder l-flex-col holder-treeview-elements">
<mct-representation key="'create-button'" <mct-representation key="'create-button'"
@ -60,7 +60,7 @@
ng-controller="InspectorPaneController as modelPaneInspect" ng-controller="InspectorPaneController as modelPaneInspect"
ng-class="modelPaneInspect.visible() ? 'pane-inspect-showing' : 'pane-inspect-hidden'"> ng-class="modelPaneInspect.visible() ? 'pane-inspect-showing' : 'pane-inspect-hidden'">
<mct-split-pane class='l-object-and-inspector contents abs' anchor='right'> <mct-split-pane class='l-object-and-inspector contents abs' anchor='right' alias="rightSide">
<div class='split-pane-component t-object pane primary-pane left'> <div class='split-pane-component t-object pane primary-pane left'>
<mct-representation mct-object="navigatedObject" <mct-representation mct-object="navigatedObject"
key="navigatedObject.getCapability('status').get('editing') ? 'edit-object' : 'browse-object'" key="navigatedObject.getCapability('status').get('editing') ? 'edit-object' : 'browse-object'"
@ -87,4 +87,3 @@
</div> </div>
<mct-include key="'bottombar'"></mct-include> <mct-include key="'bottombar'"></mct-include>
</div> </div>

View File

@ -374,7 +374,8 @@ define([
"depends": [ "depends": [
"$parse", "$parse",
"$log", "$log",
"$interval" "$interval",
"$window"
] ]
}, },
{ {

View File

@ -93,13 +93,21 @@ define(
* @memberof platform/commonUI/general * @memberof platform/commonUI/general
* @constructor * @constructor
*/ */
function MCTSplitPane($parse, $log, $interval) { function MCTSplitPane($parse, $log, $interval, $window) {
function controller($scope, $element, $attrs) { function controller($scope, $element, $attrs) {
var anchorKey = $attrs.anchor || DEFAULT_ANCHOR, var anchorKey = $attrs.anchor || DEFAULT_ANCHOR,
positionParsed = $parse($attrs.position),
anchor, anchor,
activeInterval, activeInterval,
positionParsed = $parse($attrs.position), position,
position; // Start undefined, until explicitly set splitterSize,
alias = $attrs.alias !== undefined ?
"mctSplitPane-" + $attrs.alias : undefined,
//convert string to number from localStorage
userWidthPreference = $window.localStorage.getItem(alias) === null ?
undefined : Number($window.localStorage.getItem(alias));
// Get relevant size (height or width) of DOM element // Get relevant size (height or width) of DOM element
function getSize(domElement) { function getSize(domElement) {
@ -114,11 +122,11 @@ define(
var first = children.eq(anchor.reversed ? 2 : 0), var first = children.eq(anchor.reversed ? 2 : 0),
splitter = children.eq(1), splitter = children.eq(1),
last = children.eq(anchor.reversed ? 0 : 2), last = children.eq(anchor.reversed ? 0 : 2),
splitterSize = getSize(splitter[0]),
firstSize; firstSize;
splitterSize = getSize(splitter[0]);
first.css(anchor.edge, "0px"); first.css(anchor.edge, "0px");
first.css(anchor.dimension, (position - splitterSize) + 'px'); first.css(anchor.dimension, (userWidthPreference || position) + 'px');
// Get actual size (to obey min-width etc.) // Get actual size (to obey min-width etc.)
firstSize = getSize(first[0]); firstSize = getSize(first[0]);
@ -126,9 +134,8 @@ define(
splitter.css(anchor.edge, firstSize + 'px'); splitter.css(anchor.edge, firstSize + 'px');
splitter.css(anchor.opposite, "auto"); splitter.css(anchor.opposite, "auto");
last.css(anchor.edge, (firstSize + splitterSize) + 'px'); last.css(anchor.edge, firstSize + splitterSize + 'px');
last.css(anchor.opposite, "0px"); last.css(anchor.opposite, "0px");
position = firstSize + splitterSize; position = firstSize + splitterSize;
} }
@ -169,6 +176,17 @@ define(
return position; return position;
} }
function setUserWidthPreference(value) {
userWidthPreference = value - splitterSize;
}
function persistToLocalStorage(value) {
if (alias) {
userWidthPreference = value - splitterSize;
$window.localStorage.setItem(alias, userWidthPreference);
}
}
// Dynamically apply a CSS class to elements when the user // Dynamically apply a CSS class to elements when the user
// is actively resizing // is actively resizing
function toggleClass(classToToggle) { function toggleClass(classToToggle) {
@ -196,18 +214,31 @@ define(
activeInterval = $interval(function () { activeInterval = $interval(function () {
getSetPosition(getSetPosition()); getSetPosition(getSetPosition());
}, POLLING_INTERVAL, 0, false); }, POLLING_INTERVAL, 0, false);
// ...and stop polling when we're destroyed. // ...and stop polling when we're destroyed.
$scope.$on('$destroy', function () { $scope.$on('$destroy', function () {
$interval.cancel(activeInterval); $interval.cancel(activeInterval);
}); });
// Interface exposed by controller, for mct-splitter to user // Interface exposed by controller, for mct-splitter to user
return { return {
position: getSetPosition,
toggleClass: toggleClass,
anchor: function () { anchor: function () {
return anchor; return anchor;
},
position: function (value) {
if (arguments.length > 0) {
setUserWidthPreference(value);
return getSetPosition(value);
} else {
return getSetPosition();
}
},
startResizing: function () {
toggleClass('resizing');
},
endResizing: function (finalPosition) {
persistToLocalStorage(finalPosition);
toggleClass('resizing');
} }
}; };
} }
@ -219,9 +250,7 @@ define(
controller: ['$scope', '$element', '$attrs', controller] controller: ['$scope', '$element', '$attrs', controller]
}; };
} }
return MCTSplitPane; return MCTSplitPane;
} }
); );

View File

@ -37,15 +37,16 @@ define(
*/ */
function MCTSplitter() { function MCTSplitter() {
function link(scope, element, attrs, mctSplitPane) { function link(scope, element, attrs, mctSplitPane) {
var initialPosition; var initialPosition,
newPosition;
element.addClass("splitter"); element.addClass("splitter");
scope.splitter = { scope.splitter = {
// Begin moving this splitter // Begin moving this splitter
startMove: function () { startMove: function () {
mctSplitPane.startResizing();
initialPosition = mctSplitPane.position(); initialPosition = mctSplitPane.position();
mctSplitPane.toggleClass('resizing');
}, },
// Handle user changes to splitter position // Handle user changes to splitter position
move: function (delta) { move: function (delta) {
@ -55,12 +56,13 @@ define(
(anchor.reversed ? -1 : 1); (anchor.reversed ? -1 : 1);
// Update the position of this splitter // Update the position of this splitter
mctSplitPane.position(initialPosition + pixelDelta); newPosition = initialPosition + pixelDelta;
mctSplitPane.position(newPosition);
}, },
// Grab the event when the user is done moving // Grab the event when the user is done moving
// the splitter and pass it on // the splitter and pass it on
endMove: function () { endMove: function () {
mctSplitPane.toggleClass('resizing'); mctSplitPane.endResizing(newPosition);
} }
}; };
} }
@ -83,4 +85,3 @@ define(
} }
); );

View File

@ -38,7 +38,8 @@ define(
mockLog, mockLog,
mockInterval, mockInterval,
mockParsed, mockParsed,
mctSplitPane; mctSplitPane,
mockWindow = {};
beforeEach(function () { beforeEach(function () {
mockParse = jasmine.createSpy('$parse'); mockParse = jasmine.createSpy('$parse');
@ -48,13 +49,23 @@ define(
mockInterval.cancel = jasmine.createSpy('mockCancel'); mockInterval.cancel = jasmine.createSpy('mockCancel');
mockParsed = jasmine.createSpy('parsed'); mockParsed = jasmine.createSpy('parsed');
mockParsed.assign = jasmine.createSpy('assign'); mockParsed.assign = jasmine.createSpy('assign');
mockParse.andReturn(mockParsed); mockParse.andReturn(mockParsed);
mockWindow.localStorage = {
store: {},
setItem: function (key, value) {
this.store[key] = value;
},
getItem: function (key) {
return this.store[key];
}
};
mctSplitPane = new MCTSplitPane( mctSplitPane = new MCTSplitPane(
mockParse, mockParse,
mockLog, mockLog,
mockInterval mockInterval,
mockWindow
); );
}); });
@ -85,7 +96,7 @@ define(
jasmine.createSpyObj('$scope', ['$apply', '$watch', '$on']); jasmine.createSpyObj('$scope', ['$apply', '$watch', '$on']);
mockElement = mockElement =
jasmine.createSpyObj('element', JQLITE_METHODS); jasmine.createSpyObj('element', JQLITE_METHODS);
testAttrs = {}; testAttrs = {alias: 'rightSide'};
mockChildren = mockChildren =
jasmine.createSpyObj('children', JQLITE_METHODS); jasmine.createSpyObj('children', JQLITE_METHODS);
mockFirstPane = mockFirstPane =
@ -142,10 +153,14 @@ define(
}); });
}); });
it("allows classes to be toggled on contained elements", function () { it("applies resizing class to children when resizing", function () {
controller.toggleClass('resizing'); controller.startResizing();
expect(mockChildren.toggleClass) expect(mockChildren.toggleClass).toHaveBeenCalledWith('resizing');
.toHaveBeenCalledWith('resizing'); });
it("removes resizing class from children when resizing action ends", function () {
controller.endResizing(0);
expect(mockChildren.toggleClass).toHaveBeenCalledWith('resizing');
}); });
it("allows positions to be set", function () { it("allows positions to be set", function () {
@ -198,6 +213,12 @@ define(
fireOn('$destroy'); fireOn('$destroy');
expect(mockInterval.cancel).toHaveBeenCalled(); expect(mockInterval.cancel).toHaveBeenCalled();
}); });
it("saves user preference to localStorage when user is done resizing", function () {
controller.endResizing(100);
expect(Number(mockWindow.localStorage.getItem('mctSplitPane-rightSide'))).toEqual(100 - mockSplitter[0].offsetWidth);
});
}); });
}); });

View File

@ -57,7 +57,7 @@ define(
testAttrs = {}; testAttrs = {};
mockSplitPane = jasmine.createSpyObj( mockSplitPane = jasmine.createSpyObj(
'mctSplitPane', 'mctSplitPane',
['position', 'toggleClass', 'anchor'] ['position', 'startResizing', 'endResizing', 'anchor']
); );
mctSplitter.link( mctSplitter.link(
@ -86,9 +86,9 @@ define(
mockScope.splitter.startMove(); mockScope.splitter.startMove();
}); });
it("adds a 'resizing' class", function () { it("tell's the splitter when it is resizing", function () {
expect(mockSplitPane.toggleClass) expect(mockSplitPane.startResizing)
.toHaveBeenCalledWith('resizing'); .toHaveBeenCalled();
}); });
it("repositions during drag", function () { it("repositions during drag", function () {
@ -97,11 +97,10 @@ define(
.toHaveBeenCalledWith(testPosition + 10); .toHaveBeenCalledWith(testPosition + 10);
}); });
it("removes the 'resizing' class when finished", function () { it("tell's the splitter when it is done resizing", function () {
mockSplitPane.toggleClass.reset(); mockScope.splitter.move([10,0]);
mockScope.splitter.endMove(); mockScope.splitter.endMove();
expect(mockSplitPane.toggleClass) expect(mockSplitPane.endResizing).toHaveBeenCalledWith(testPosition + 10);
.toHaveBeenCalledWith('resizing');
}); });
}); });