From b9b66e1b459609c1458abb2976c905acebe906f7 Mon Sep 17 00:00:00 2001 From: Hannes Widmoser Date: Thu, 7 Jan 2016 15:13:21 +0100 Subject: [PATCH 01/18] refactor(toggle): renamed local variable 'result' to 'isCollapsed' to make code more readable --- src/ui-layout.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ui-layout.js b/src/ui-layout.js index f4ff7b3..cdd0c18 100644 --- a/src/ui-layout.js +++ b/src/ui-layout.js @@ -709,7 +709,7 @@ angular.module('ui.layout', []) prevButton.on('click', function() { var prevSplitbarBeforeButton, prevSplitbarAfterButton; - var result = ctrl.toggleBefore(scope.splitbar); + var isCollapsed = ctrl.toggleBefore(scope.splitbar); var previousSplitbar = ctrl.getPreviousSplitbarContainer(scope.splitbar); if(previousSplitbar !== null) { @@ -718,7 +718,7 @@ angular.module('ui.layout', []) } if(ctrl.isUsingColumnFlow) { - if(result) { + if(isCollapsed) { afterButton.css('display', 'none'); prevIcon.removeClass(iconLeft); prevIcon.addClass(iconRight); @@ -740,7 +740,7 @@ angular.module('ui.layout', []) } } } else { - if(result) { + if(isCollapsed) { afterButton.css('display', 'none'); prevIcon.removeClass(iconUp); prevIcon.addClass(iconDown); @@ -769,7 +769,7 @@ angular.module('ui.layout', []) afterButton.on('click', function() { var nextSplitbarBeforeButton, nextSplitbarAfterButton; - var result = ctrl.toggleAfter(scope.splitbar); + var isCollapsed = ctrl.toggleAfter(scope.splitbar); var nextSplitbar = ctrl.getNextSplitbarContainer(scope.splitbar); if(nextSplitbar !== null) { @@ -778,7 +778,7 @@ angular.module('ui.layout', []) } if(ctrl.isUsingColumnFlow) { - if(result) { + if(isCollapsed) { prevButton.css('display', 'none'); afterIcon.removeClass(iconRight); afterIcon.addClass(iconLeft); @@ -800,7 +800,7 @@ angular.module('ui.layout', []) } } } else { - if(result) { + if(isCollapsed) { prevButton.css('display', 'none'); afterIcon.removeClass(iconDown); afterIcon.addClass(iconUp); From a03f328f7d5c87df052e97be854dc46b47be77ae Mon Sep 17 00:00:00 2001 From: Hannes Widmoser Date: Thu, 7 Jan 2016 15:29:54 +0100 Subject: [PATCH 02/18] refactor(toggle): simplified code that modifies the toggle buttons after a toggle operation --- src/ui-layout.js | 106 +++++++++++++++++++---------------------------- 1 file changed, 42 insertions(+), 64 deletions(-) diff --git a/src/ui-layout.js b/src/ui-layout.js index cdd0c18..7dfe01a 100644 --- a/src/ui-layout.js +++ b/src/ui-layout.js @@ -717,51 +717,40 @@ angular.module('ui.layout', []) prevSplitbarAfterButton = angular.element(previousSplitbar.element.children()[1]); } - if(ctrl.isUsingColumnFlow) { - if(isCollapsed) { - afterButton.css('display', 'none'); + if(isCollapsed) { + afterButton.css('display', 'none'); + + if (ctrl.isUsingColumnFlow) { prevIcon.removeClass(iconLeft); prevIcon.addClass(iconRight); - - // hide previous splitbar buttons - if(previousSplitbar !== null) { - prevSplitbarBeforeButton.css('display', 'none'); - prevSplitbarAfterButton.css('display', 'none'); - } } else { - afterButton.css('display', 'inline'); - prevIcon.removeClass(iconRight); - prevIcon.addClass(iconLeft); + prevIcon.removeClass(iconUp); + prevIcon.addClass(iconDown); + } - // show previous splitbar icons - if(previousSplitbar !== null) { - prevSplitbarBeforeButton.css('display', 'inline'); - prevSplitbarAfterButton.css('display', 'inline'); - } + // hide previous splitbar buttons + if(previousSplitbar !== null) { + prevSplitbarBeforeButton.css('display', 'none'); + prevSplitbarAfterButton.css('display', 'none'); } } else { - if(isCollapsed) { - afterButton.css('display', 'none'); - prevIcon.removeClass(iconUp); - prevIcon.addClass(iconDown); + afterButton.css('display', 'inline'); - // hide previous splitbar buttons - if(previousSplitbar !== null) { - prevSplitbarBeforeButton.css('display', 'none'); - prevSplitbarAfterButton.css('display', 'none'); - } + if (ctrl.isUsingColumnFlow) { + prevIcon.removeClass(iconRight); + prevIcon.addClass(iconLeft); } else { - afterButton.css('display', 'inline'); prevIcon.removeClass(iconDown); prevIcon.addClass(iconUp); + } - // show previous splitbar icons - if(previousSplitbar !== null) { - prevSplitbarBeforeButton.css('display', 'inline'); - prevSplitbarAfterButton.css('display', 'inline'); - } + // show previous splitbar icons + if(previousSplitbar !== null) { + prevSplitbarBeforeButton.css('display', 'inline'); + prevSplitbarAfterButton.css('display', 'inline'); } } + scope.$evalAsync(function() { ctrl.calculate(); }); @@ -777,51 +766,40 @@ angular.module('ui.layout', []) nextSplitbarAfterButton = angular.element(nextSplitbar.element.children()[1]); } - if(ctrl.isUsingColumnFlow) { - if(isCollapsed) { - prevButton.css('display', 'none'); + if(isCollapsed) { + prevButton.css('display', 'none'); + + if(ctrl.isUsingColumnFlow) { afterIcon.removeClass(iconRight); afterIcon.addClass(iconLeft); - - // hide next splitbar buttons - if(nextSplitbar !== null) { - nextSplitbarBeforeButton.css('display', 'none'); - nextSplitbarAfterButton.css('display', 'none'); - } } else { - prevButton.css('display', 'inline'); - afterIcon.removeClass(iconLeft); - afterIcon.addClass(iconRight); + afterIcon.removeClass(iconDown); + afterIcon.addClass(iconUp); + } - // show next splitbar buttons - if(nextSplitbar !== null) { - nextSplitbarBeforeButton.css('display', 'inline'); - nextSplitbarAfterButton.css('display', 'inline'); - } + // hide next splitbar buttons + if(nextSplitbar !== null) { + nextSplitbarBeforeButton.css('display', 'none'); + nextSplitbarAfterButton.css('display', 'none'); } } else { - if(isCollapsed) { - prevButton.css('display', 'none'); - afterIcon.removeClass(iconDown); - afterIcon.addClass(iconUp); + prevButton.css('display', 'inline'); - // hide next splitbar buttons - if(nextSplitbar !== null) { - nextSplitbarBeforeButton.css('display', 'none'); - nextSplitbarAfterButton.css('display', 'none'); - } + if(ctrl.isUsingColumnFlow) { + afterIcon.removeClass(iconLeft); + afterIcon.addClass(iconRight); } else { - prevButton.css('display', 'inline'); afterIcon.removeClass(iconUp); afterIcon.addClass(iconDown); + } - // show next splitbar buttons - if(nextSplitbar !== null) { - nextSplitbarBeforeButton.css('display', 'inline'); - nextSplitbarAfterButton.css('display', 'inline'); - } + // show next splitbar buttons + if(nextSplitbar !== null) { + nextSplitbarBeforeButton.css('display', 'inline'); + nextSplitbarAfterButton.css('display', 'inline'); } } + scope.$evalAsync(function() { ctrl.calculate(); }); From 97030ecaeca95a2e30a9be2738099502fccc44ac Mon Sep 17 00:00:00 2001 From: Hannes Widmoser Date: Thu, 7 Jan 2016 16:20:42 +0100 Subject: [PATCH 03/18] refactor(toggle): removed unnecessary toggle code --- src/ui-layout.js | 82 -------------------------------------- test/layout-scenar.spec.js | 4 ++ 2 files changed, 4 insertions(+), 82 deletions(-) diff --git a/src/ui-layout.js b/src/ui-layout.js index 7dfe01a..b6c0011 100644 --- a/src/ui-layout.js +++ b/src/ui-layout.js @@ -441,39 +441,6 @@ angular.module('ui.layout', []) var c = ctrl.containers[index]; c.collapsed = !ctrl.containers[index].collapsed; - var nextSplitbar = ctrl.containers[index+1]; - var nextContainer = ctrl.containers[index+2]; - - // uncollapsedSize is undefined in case of 'auto' sized containers. - // Perhaps there's a place where we could set... could find it though. @see also toggleBefore - if (c.uncollapsedSize === undefined) { - c.uncollapsedSize = c.size; - } else { - c.uncollapsedSize = parseInt(c.uncollapsedSize); - } - // FIXME: collapse:resize:uncollapse: works well "visually" without the nextSplitbar and nextContainer calculations - // but removing those breaks few test - $scope.$apply(function() { - if(c.collapsed) { - - c.size = 0; - - if(nextSplitbar) nextSplitbar[ctrl.sizeProperties.flowProperty] -= c.uncollapsedSize; - if(nextContainer) { - nextContainer[ctrl.sizeProperties.flowProperty] -= c.uncollapsedSize; - nextContainer.uncollapsedSize += c.uncollapsedSize; - } - - } else { - c.size = c.uncollapsedSize; - - if(nextSplitbar) nextSplitbar[ctrl.sizeProperties.flowProperty] += c.uncollapsedSize; - if(nextContainer) { - nextContainer[ctrl.sizeProperties.flowProperty] += c.uncollapsedSize; - nextContainer.uncollapsedSize -= c.uncollapsedSize; - } - } - }); $scope.$broadcast('ui.layout.toggle', c); Layout.toggled(); @@ -489,58 +456,9 @@ angular.module('ui.layout', []) ctrl.toggleAfter = function(splitbar) { var index = ctrl.containers.indexOf(splitbar) + 1; var c = ctrl.containers[index]; - var prevSplitbar = ctrl.containers[index-1]; - var prevContainer = ctrl.containers[index-2]; - var isLastContainer = index === (ctrl.containers.length - 1); - var endDiff; - var flowProperty = ctrl.sizeProperties.flowProperty; - var sizeProperty = ctrl.sizeProperties.sizeProperty; - - ctrl.bounds = $element[0].getBoundingClientRect(); c.collapsed = !ctrl.containers[index].collapsed; - // uncollapsedSize is undefined in case of 'auto' sized containers. - // Perhaps there's a place where we could set... could find it though. @see also toggleBefore - if (c.uncollapsedSize === undefined) { - c.uncollapsedSize = c.size; - } else { - c.uncollapsedSize = parseInt(c.uncollapsedSize); - } - - // FIXME: collapse:resize:uncollapse: works well "visually" without the prevSplitbar and prevContainer calculations - // but removing those breaks few test - $scope.$apply(function() { - if(c.collapsed) { - - c.size = 0; - - // adds additional space so the splitbar moves to the very end of the container - // to offset the lost space when converting from percents to pixels - endDiff = (isLastContainer) ? ctrl.bounds[sizeProperty] - c[flowProperty] - c.uncollapsedSize : 0; - - if(prevSplitbar) { - prevSplitbar[flowProperty] += (c.uncollapsedSize + endDiff); - } - if(prevContainer) { - prevContainer.size += (c.uncollapsedSize + endDiff); - } - - } else { - c.size = c.uncollapsedSize; - - // adds additional space so the splitbar moves back to the proper position - // to offset the additional space added when collapsing - endDiff = (isLastContainer) ? ctrl.bounds[sizeProperty] - c[flowProperty] - c.uncollapsedSize : 0; - - if(prevSplitbar) { - prevSplitbar[flowProperty] -= (c.uncollapsedSize + endDiff); - } - if(prevContainer) { - prevContainer.size -= (c.uncollapsedSize + endDiff); - } - } - }); $scope.$broadcast('ui.layout.toggle', c); Layout.toggled(); return c.collapsed; diff --git a/test/layout-scenar.spec.js b/test/layout-scenar.spec.js index a545d67..c60ac02 100644 --- a/test/layout-scenar.spec.js +++ b/test/layout-scenar.spec.js @@ -158,12 +158,14 @@ function splitMoveTests(description, startEvent, moveEvent, endEvent) { expect($header.getBoundingClientRect().height).toEqual(expectedAutosized); browserTrigger(toggleBeforeButton, 'click'); + scope.$digest(); expect(parseInt($splitbar[0].style.top)).toEqual(0); expect($header.getBoundingClientRect().height).toEqual(0); expect(toggleAfterButton.style.display).toBe('none'); browserTrigger(toggleBeforeButton, 'click'); + scope.$digest(); expect(parseInt($splitbar[0].style.top)).toEqual(expectedAutosized); expect($header.getBoundingClientRect().height).toEqual(expectedAutosized); @@ -185,11 +187,13 @@ function splitMoveTests(description, startEvent, moveEvent, endEvent) { expect($footer.getBoundingClientRect().height).toEqual(expectedLastAutosized); browserTrigger(toggleAfterButton, 'click'); + scope.$digest(); expect(parseInt($splitbar[0].style.top)).toEqual(element_bb.height - defaultDividerSize); expect($footer.getBoundingClientRect().height).toEqual(0); expect(toggleBeforeButton.style.display).toBe('none'); browserTrigger(toggleAfterButton, 'click'); + scope.$digest(); expect(parseInt($splitbar[0].style.top)).toEqual(expectedAutosized); expect($footer.getBoundingClientRect().height).toEqual(expectedLastAutosized); expect(toggleBeforeButton.style.display).toBe('inline'); From 6d1ca1c56cf34b3139aca4d44fd63bbda9f56619 Mon Sep 17 00:00:00 2001 From: Hannes Widmoser Date: Thu, 7 Jan 2016 16:48:46 +0100 Subject: [PATCH 04/18] feat(toggle): removed manual 'click' event when toggling programmatically --- src/ui-layout.js | 105 +++++++++++++---------------------------------- 1 file changed, 28 insertions(+), 77 deletions(-) diff --git a/src/ui-layout.js b/src/ui-layout.js index b6c0011..9ad8f7e 100644 --- a/src/ui-layout.js +++ b/src/ui-layout.js @@ -414,20 +414,28 @@ angular.module('ui.layout', []) }; ctrl.toggleContainer = function(index) { + var c = ctrl.containers[index]; + c.collapsed = !ctrl.containers[index].collapsed; - var splitter = ctrl.containers[index + 1], - el; + $scope.$broadcast('ui.layout.toggle', c); + Layout.toggled(); - if (splitter) { - el = splitter.element[0].children[0]; - } else { - splitter = ctrl.containers[index - 1]; - el = splitter.element[0].children[1]; + var splitbarBefore = ctrl.containers[index - 1]; + var splitbarAfter = ctrl.containers[index + 1]; + + if (splitbarBefore) { + splitbarBefore.notifyToggleAfter(c.collapsed); + } + + if (splitbarAfter) { + splitbarAfter.notifyToggleBefore(c.collapsed); } - $timeout(function(){ - angular.element(el).triggerHandler('click'); + $scope.$evalAsync(function() { + ctrl.calculate(); }); + + return c.collapsed; }; /** @@ -437,14 +445,7 @@ angular.module('ui.layout', []) */ ctrl.toggleBefore = function(splitbar) { var index = ctrl.containers.indexOf(splitbar) - 1; - - var c = ctrl.containers[index]; - c.collapsed = !ctrl.containers[index].collapsed; - - $scope.$broadcast('ui.layout.toggle', c); - Layout.toggled(); - - return c.collapsed; + return ctrl.toggleContainer(index); }; @@ -455,13 +456,7 @@ angular.module('ui.layout', []) */ ctrl.toggleAfter = function(splitbar) { var index = ctrl.containers.indexOf(splitbar) + 1; - var c = ctrl.containers[index]; - - c.collapsed = !ctrl.containers[index].collapsed; - - $scope.$broadcast('ui.layout.toggle', c); - Layout.toggled(); - return c.collapsed; + return ctrl.toggleContainer(index); }; /** @@ -624,17 +619,7 @@ angular.module('ui.layout', []) prevIcon.addClass(prevIconClass); afterIcon.addClass(afterIconClass); - - prevButton.on('click', function() { - var prevSplitbarBeforeButton, prevSplitbarAfterButton; - var isCollapsed = ctrl.toggleBefore(scope.splitbar); - var previousSplitbar = ctrl.getPreviousSplitbarContainer(scope.splitbar); - - if(previousSplitbar !== null) { - prevSplitbarBeforeButton = angular.element(previousSplitbar.element.children()[0]); - prevSplitbarAfterButton = angular.element(previousSplitbar.element.children()[1]); - } - + scope.splitbar.notifyToggleBefore = function(isCollapsed) { if(isCollapsed) { afterButton.css('display', 'none'); @@ -645,12 +630,6 @@ angular.module('ui.layout', []) prevIcon.removeClass(iconUp); prevIcon.addClass(iconDown); } - - // hide previous splitbar buttons - if(previousSplitbar !== null) { - prevSplitbarBeforeButton.css('display', 'none'); - prevSplitbarAfterButton.css('display', 'none'); - } } else { afterButton.css('display', 'inline'); @@ -661,29 +640,10 @@ angular.module('ui.layout', []) prevIcon.removeClass(iconDown); prevIcon.addClass(iconUp); } - - // show previous splitbar icons - if(previousSplitbar !== null) { - prevSplitbarBeforeButton.css('display', 'inline'); - prevSplitbarAfterButton.css('display', 'inline'); - } - } - - scope.$evalAsync(function() { - ctrl.calculate(); - }); - }); - - afterButton.on('click', function() { - var nextSplitbarBeforeButton, nextSplitbarAfterButton; - var isCollapsed = ctrl.toggleAfter(scope.splitbar); - var nextSplitbar = ctrl.getNextSplitbarContainer(scope.splitbar); - - if(nextSplitbar !== null) { - nextSplitbarBeforeButton = angular.element(nextSplitbar.element.children()[0]); - nextSplitbarAfterButton = angular.element(nextSplitbar.element.children()[1]); } + }; + scope.splitbar.notifyToggleAfter = function(isCollapsed) { if(isCollapsed) { prevButton.css('display', 'none'); @@ -694,12 +654,6 @@ angular.module('ui.layout', []) afterIcon.removeClass(iconDown); afterIcon.addClass(iconUp); } - - // hide next splitbar buttons - if(nextSplitbar !== null) { - nextSplitbarBeforeButton.css('display', 'none'); - nextSplitbarAfterButton.css('display', 'none'); - } } else { prevButton.css('display', 'inline'); @@ -710,17 +664,14 @@ angular.module('ui.layout', []) afterIcon.removeClass(iconUp); afterIcon.addClass(iconDown); } - - // show next splitbar buttons - if(nextSplitbar !== null) { - nextSplitbarBeforeButton.css('display', 'inline'); - nextSplitbarAfterButton.css('display', 'inline'); - } } + }; - scope.$evalAsync(function() { - ctrl.calculate(); - }); + prevButton.on('click', function() { + ctrl.toggleBefore(scope.splitbar); + }); + afterButton.on('click', function() { + ctrl.toggleAfter(scope.splitbar); }); element.on('mousedown touchstart', function(e) { From 07491d307314a5d75cc0486f86bbef66bfa9a50e Mon Sep 17 00:00:00 2001 From: Hannes Widmoser Date: Thu, 7 Jan 2016 17:14:55 +0100 Subject: [PATCH 05/18] feat(toggle): removed the ui-layout-loaded indirection now that the programmatic toggling is properly implemented, the initialisation of collapsed containers can be done in a more straight-forward style. We only have to make sure that the splitbar is informed about changes to the collapsed state. --- src/ui-layout.js | 131 ++++++++++------------------------------------- 1 file changed, 26 insertions(+), 105 deletions(-) diff --git a/src/ui-layout.js b/src/ui-layout.js index 9ad8f7e..2f4edb0 100644 --- a/src/ui-layout.js +++ b/src/ui-layout.js @@ -4,8 +4,8 @@ * UI.Layout */ angular.module('ui.layout', []) - .controller('uiLayoutCtrl', ['$scope', '$attrs', '$element', '$timeout', '$window', 'LayoutContainer', 'Layout', - function uiLayoutCtrl($scope, $attrs, $element, $timeout, $window, LayoutContainer, Layout) { + .controller('uiLayoutCtrl', ['$scope', '$attrs', '$element', '$timeout', '$window', 'LayoutContainer', + function uiLayoutCtrl($scope, $attrs, $element, $timeout, $window, LayoutContainer) { var ctrl = this; var opts = angular.extend({}, $scope.$eval($attrs.uiLayout), $scope.$eval($attrs.options)); @@ -17,8 +17,6 @@ angular.module('ui.layout', []) // regex to verify size is properly set to pixels or percent var sizePattern = /\d+\s*(px|%)\s*$/i; - Layout.addLayout(ctrl); - ctrl.containers = []; ctrl.movingSplitbar = null; ctrl.bounds = $element[0].getBoundingClientRect(); @@ -416,9 +414,13 @@ angular.module('ui.layout', []) ctrl.toggleContainer = function(index) { var c = ctrl.containers[index]; c.collapsed = !ctrl.containers[index].collapsed; + ctrl.processToggleContainer(index); + }; + + ctrl.processToggleContainer = function(index) { + var c = ctrl.containers[index]; $scope.$broadcast('ui.layout.toggle', c); - Layout.toggled(); var splitbarBefore = ctrl.containers[index - 1]; var splitbarAfter = ctrl.containers[index + 1]; @@ -707,6 +709,17 @@ angular.module('ui.layout', []) //Add splitbar to layout container list ctrl.addContainer(scope.splitbar); + // initialize the button visibility according to the collapsed state of the adjacent containers: + var index = ctrl.containers.indexOf(scope.splitbar); + var before = ctrl.containers[index - 1]; + var after = ctrl.containers[index + 1]; + if (before) { + scope.splitbar.notifyToggleBefore(before.collapsed); + } + if (after) { + scope.splitbar.notifyToggleAfter(after.collapsed); + } + element.on('$destroy', function() { ctrl.removeContainer(scope.splitbar); scope.$evalAsync(); @@ -717,8 +730,8 @@ angular.module('ui.layout', []) }]) .directive('uiLayoutContainer', - ['LayoutContainer', '$compile', '$timeout', 'Layout', - function(LayoutContainer, $compile, $timeout, Layout) { + ['LayoutContainer', '$compile', '$timeout', + function(LayoutContainer, $compile, $timeout) { return { restrict: 'AE', require: '^uiLayout', @@ -740,14 +753,9 @@ angular.module('ui.layout', []) scope.container.layoutId = ctrl.id; scope.container.isCentral = attrs.uiLayoutContainer === 'central'; - if (scope.collapsed === true) { - scope.collapsed = false; - Layout.addCollapsed(scope.container); + if (angular.isDefined(scope.collapsed)) { + scope.container.collapsed = scope.collapsed; } - // FIXME: collapsed: @see uiLayoutLoaded for explanation - //if (angular.isDefined(scope.collapsed)) { - // scope.container.collapsed = scope.collapsed; - //} if (angular.isDefined(scope.resizable)) { scope.container.resizable = scope.resizable; @@ -770,9 +778,10 @@ angular.module('ui.layout', []) var animationClass = ctrl.isUsingColumnFlow ? 'animate-column' : 'animate-row'; element.addClass(animationClass); - scope.$watch('collapsed', function (val, old) { - if (angular.isDefined(old) && val !== old) { - ctrl.toggleContainer(scope.container.index); + scope.$watch('collapsed', function (collapsed) { + if (angular.isDefined(scope.collapsed)) { + scope.container.collapsed = scope.collapsed; + ctrl.processToggleContainer(ctrl.containers.indexOf(scope.container)); } }); @@ -810,94 +819,6 @@ angular.module('ui.layout', []) }; }]) - .directive('uiLayoutLoaded', function($timeout, Layout) { - // Currently necessary for programmatic toggling to work with "initially" collapsed containers, - // because prog. toggling depends on the logic of prevButton and nextButton (which should be probably refactored out) - // - // This is how it currently works: - // 1. uiLayoutContainer in prelink phase resets @collapsed to false, because layout has to be calculated - // with all containers uncollapsed to get the correct dimensions - // 2. layout with ui-layout-loaded attributes broadcasts "ui.layout.loaded" - // 3. user changes values of @collapsed which triggers 'click' on either of the buttons - // 3. the other button is hidden and container size set to 0 - return { - require: '^uiLayout', - restrict: 'A', - priority: -100, - link: function($scope, el, attrs){ - - // negation is safe here, because we are expecting non-empty string - if (!attrs['uiLayoutLoaded']) { - Layout.toggle().then( - function(){ - $scope.$broadcast('ui.layout.loaded', null); - } - ); - } else { - $scope.$broadcast('ui.layout.loaded', attrs['uiLayoutLoaded']); - } - } - }; - }) - - .factory('Layout', ['$q', '$timeout', function($q, $timeout) { - var layouts = [], - collapsing = [], - toBeCollapsed = 0, - toggledDeffered = null; - - function toggleContainer(container) { - try { - layouts[container.layoutId].toggleContainer(container.index); - } catch (e) { - e.message = 'Could not toggle container [' + container.layoutId + '/' + container.index + ']: ' + e.message; - throw e; - } - } - - return { - addLayout: function (ctrl) { - ctrl.id = layouts.length; - layouts.push(ctrl); - }, - addCollapsed: function(container) { - collapsing.push(container); - }, - hasCollapsed: function() { - return collapsing.length > 0; - }, - toggled: function() { - // event already dispatched, do nothing - if (toBeCollapsed === 0) { - if (toggledDeffered) { - toggledDeffered.reject(); - } else { - return false; - } - } - toBeCollapsed--; - if (toBeCollapsed === 0) { - toggledDeffered.resolve(); - } - }, - toggle: function() { - toggledDeffered = $q.defer(); - toBeCollapsed = collapsing.length; - if (toBeCollapsed === 0) { - $timeout(function(){ - toggledDeffered.resolve(); - }); - } - collapsing.reverse(); - var c; - while(c = collapsing.pop()) { - toggleContainer(c); - } - return toggledDeffered.promise; - } - }; - }]) - .factory('LayoutContainer', function() { function BaseContainer() { From 2ccb399b6b152390d2cdc3f68650188715022ebf Mon Sep 17 00:00:00 2001 From: Hannes Widmoser Date: Thu, 7 Jan 2016 17:27:35 +0100 Subject: [PATCH 06/18] test(toggle): fixed tests --- test/layout-scenar.spec.js | 16 ---------------- test/uiLayoutContainer.spec.js | 5 ++--- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/test/layout-scenar.spec.js b/test/layout-scenar.spec.js index c60ac02..02ec182 100644 --- a/test/layout-scenar.spec.js +++ b/test/layout-scenar.spec.js @@ -238,18 +238,6 @@ describe('toggle programmatically', function() { return elm; } - - it('should reset container to uncollapsed state when loaded', function() { - //@see explanation for uiLayoutLoaded - var elm = compileDirective(true, true); - - var divs = elm.find('div'), - beforeContainer = divs[0], - afterContainer = divs[2]; - expect(beforeContainer.style.width).toEqual('100px'); - expect(afterContainer.style.width).toEqual('200px'); - }); - it('should collapse and uncollapse beforeContainer', function() { var elm = compileDirective(false, false); @@ -261,13 +249,11 @@ describe('toggle programmatically', function() { scope.layout.beforeContainer = true; scope.$apply(); - $timeout.flush(); expect(beforeContainer.style.width).toEqual('0px'); scope.layout.beforeContainer = false; scope.$apply(); - $timeout.flush(); expect(beforeContainer.style.width).toEqual('100px'); }); @@ -283,13 +269,11 @@ describe('toggle programmatically', function() { scope.layout.afterContainer = true; scope.$apply(); - $timeout.flush(); expect(afterContainer.style.width).toEqual('0px'); scope.layout.afterContainer = false; scope.$apply(); - $timeout.flush(); expect(afterContainer.style.width).toEqual('200px'); }); diff --git a/test/uiLayoutContainer.spec.js b/test/uiLayoutContainer.spec.js index ade2406..3b12141 100644 --- a/test/uiLayoutContainer.spec.js +++ b/test/uiLayoutContainer.spec.js @@ -45,10 +45,9 @@ describe('Directive: uiLayoutContainer', function () { bcScope = angular.element(beforeContainer).isolateScope(), acScope = angular.element(afterContainer).isolateScope(); - // you would expect true, but see explanation in uiLayoutLoaded - expect(bcScope.container.collapsed).toEqual(false); + expect(bcScope.container.collapsed).toEqual(true); expect(bcScope.container.resizable).toEqual(false); - expect(bcScope.container.size).toEqual(100); + expect(bcScope.container.size).toEqual(0); // collapsed expect(bcScope.container.uncollapsedSize).toEqual('100px'); expect(bcScope.container.minSize).toEqual(50); expect(bcScope.container.maxSize).toEqual(200); From 710c2fad5476b3e7466278c86a6cfbf2423faf1e Mon Sep 17 00:00:00 2001 From: Hannes Widmoser Date: Thu, 7 Jan 2016 17:29:10 +0100 Subject: [PATCH 07/18] test(toggle): removed tests related to ui-layout-loaded --- test/uiLayoutLoaded.spec.js | 60 ------------------------------------- 1 file changed, 60 deletions(-) delete mode 100644 test/uiLayoutLoaded.spec.js diff --git a/test/uiLayoutLoaded.spec.js b/test/uiLayoutLoaded.spec.js deleted file mode 100644 index e474098..0000000 --- a/test/uiLayoutLoaded.spec.js +++ /dev/null @@ -1,60 +0,0 @@ -'use strict'; - - -describe('Directive: uiLayoutLoaded', function () { - var scope, element, $compile, $timeout, - template = '
'; - - function compileDirective(before, after, tpl) { - var elm; - - elm = angular.element(tpl || template); - angular.element(document.body).prepend(elm); - $compile(elm)(scope); - scope.$digest(); - - return elm; - } - - beforeEach(function () { - - module('ui.layout'); - - inject(function ($rootScope, _$compile_, _$timeout_) { - scope = $rootScope.$new(); - $compile = _$compile_; - $timeout = _$timeout_; - }); - }); - - afterEach(function () { - if (element) element.remove(); - }); - - it('should broadcast ui.layout.loaded and return attribute value', function () { - spyOn(scope, '$broadcast'); - compileDirective(false, false, '
'); - expect(scope.$broadcast).toHaveBeenCalledWith('ui.layout.loaded','attrValue'); - }); - - it('should broadcast ui.layout.loaded and return null', function () { - spyOn(scope, '$broadcast'); - compileDirective(false, false, '
'); - $timeout.flush(); - expect(scope.$broadcast).toHaveBeenCalledWith('ui.layout.loaded',null); - }); - - it('should broadcast ui.layout.loaded after all ui.layout directives have been loaded and return attribute value not an evaluated expression', function () { - spyOn(scope, '$broadcast'); - scope.data = { - someMessage: "dummy" - }; - - var elm = angular.element('
'); - angular.element(document.body).prepend(elm); - $compile(elm)(scope); - scope.$digest(); - expect(scope.$broadcast).toHaveBeenCalledWith('ui.layout.loaded','data.someMessage'); - }); - -}); \ No newline at end of file From ae4a70b92a33fcd83e6f1abc45ff1cf96171c673 Mon Sep 17 00:00:00 2001 From: Hannes Widmoser Date: Thu, 7 Jan 2016 17:29:31 +0100 Subject: [PATCH 08/18] doc(toggle): removed documentation related to ui-layout-loaded --- README.md | 54 ------------------------------------------------------ 1 file changed, 54 deletions(-) diff --git a/README.md b/README.md index 2ec2ab7..f60483a 100644 --- a/README.md +++ b/README.md @@ -205,60 +205,6 @@ percentage Events are broadcast on the scope where ui-layout is attached. This means they are available to any controller inside of a ui-layout container. -### ui.layout.loaded -Returns: `string` or `null` - - -Dispatched when the layout container finished loading. It returns the value of the attribute, e.g. `ui-layout-loaded="my-loaded-message"`, or `null`. The `null` also means that the layout has finished collapsing all the containers that should be collapsed (per application request when setting the [`collapsed`][collapsed] attribute). - -Collapsing container on application load currently goes through these steps: -1. layout is first loaded with all containers uncollapsed (disregarding user set values), then -2. containers are collapsed either: - - _automatically_: application has not set a string return value for the `ui.layout.loaded` event. - - _manually_: application sets collapsed flags in the callback passed to `ui.layout.loaded` - -All this means that the user will notice a flicker. If the flicker is not desirable, hide the layout behind an overlay, wait for the `ui.layout.loaded` event. In the "automatic" mode, all is done and the layout should be presentable. In the "manual" mode it is up to the application to count the `ui.layout.toggle` events. - - - -```xml -
-
-
-
- -
-
-
-
-``` - -```javascript -$scope.$on('ui.layout.loaded', function(evt, id) => { - switch (id) { - case 'main-container': - ... - break; - case 'child-container': - ... - break; - default: - break; - } -}); -``` - -Note: the value of the attribute is not evaluated, so: - -``` -$scope.layout = { - mySidebar: {someKey: 'some value'} -} - -
-// $scope.$on will receive the string 'layout.mySidebar.someKey' -``` - ### ui.layout.toggle [event-toggle] Dispatched when a container is opened or closed. Element can be identified `container.id`, which is the same as `element.id` if provided, otherwise it is `null`. From 4724ebf78e36cf6dc4e90c88125e03dee5988a19 Mon Sep 17 00:00:00 2001 From: Hannes Widmoser Date: Fri, 8 Jan 2016 11:50:22 +0100 Subject: [PATCH 09/18] feat(toggle): removed references that are not needed --- src/ui-layout.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ui-layout.js b/src/ui-layout.js index 2f4edb0..b8c52e0 100644 --- a/src/ui-layout.js +++ b/src/ui-layout.js @@ -730,8 +730,8 @@ angular.module('ui.layout', []) }]) .directive('uiLayoutContainer', - ['LayoutContainer', '$compile', '$timeout', - function(LayoutContainer, $compile, $timeout) { + ['LayoutContainer', '$compile', + function(LayoutContainer, $compile) { return { restrict: 'AE', require: '^uiLayout', @@ -778,7 +778,7 @@ angular.module('ui.layout', []) var animationClass = ctrl.isUsingColumnFlow ? 'animate-column' : 'animate-row'; element.addClass(animationClass); - scope.$watch('collapsed', function (collapsed) { + scope.$watch('collapsed', function () { if (angular.isDefined(scope.collapsed)) { scope.container.collapsed = scope.collapsed; ctrl.processToggleContainer(ctrl.containers.indexOf(scope.container)); From c2fc1518ad9fe8804549187af12d3f4c8347844f Mon Sep 17 00:00:00 2001 From: Hannes Widmoser Date: Wed, 20 Jan 2016 17:28:21 +0100 Subject: [PATCH 10/18] fix(toggle): central containers could not be collapsed --- src/ui-layout.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui-layout.js b/src/ui-layout.js index b8c52e0..4bf57eb 100644 --- a/src/ui-layout.js +++ b/src/ui-layout.js @@ -263,7 +263,7 @@ angular.module('ui.layout', []) if(!LayoutContainer.isSplitbar(ctrl.containers[i])) { c = ctrl.containers[i]; - opts.sizes[i] = c.isCentral ? 'auto' : c.collapsed ? '0px' : optionValue(c.uncollapsedSize) || 'auto'; + opts.sizes[i] = c.collapsed ? '0px' : c.isCentral ? 'auto' : optionValue(c.uncollapsedSize) || 'auto'; opts.minSizes[i] = optionValue(c.minSize); opts.maxSizes[i] = optionValue(c.maxSize); From 8864d830fa38e49d13981cbb5251edf919fe2d8c Mon Sep 17 00:00:00 2001 From: Hannes Widmoser Date: Sat, 20 Feb 2016 09:54:28 +0100 Subject: [PATCH 11/18] feat(toggle): re-added ui-layout-loaded directive to simulate previous behaviour --- README.md | 63 +++++++++++++++++++++++++++++++--- src/ui-layout.js | 20 +++++++++++ test/uiLayoutContainer.spec.js | 4 +-- test/uiLayoutLoaded.spec.js | 58 +++++++++++++++++++++++++++++++ 4 files changed, 139 insertions(+), 6 deletions(-) create mode 100644 test/uiLayoutLoaded.spec.js diff --git a/README.md b/README.md index f60483a..e878715 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,7 @@ $scope.layout { } ``` -Changing those values will toggle container. See also [`ui.layout.toggle`][event-toggle]. +Changing those values will toggle container. See also [`ui.layout.toggle`][event-toggle]. ### size Type: `String` @@ -203,7 +203,62 @@ percentage ## Events -Events are broadcast on the scope where ui-layout is attached. This means they are available to any controller inside of a ui-layout container. +Events are broadcast on the scope where ui-layout is attached. This means they are available to any controller inside of a ui-layout container. + +### ui.layout.loaded +Returns: `string` or `null` + +**Deprecated:** *This event is not needed any more because programmatic toggle works now as expected. It is only kept for compatibility reasons. The event will be triggered immediately, when the directive links.* + +Dispatched when the layout container finished loading. It returns the value of the attribute, e.g. `ui-layout-loaded="my-loaded-message"`, or `null`. The `null` also means that the layout has finished collapsing all the containers that should be collapsed (per application request when setting the [`collapsed`][collapsed] attribute). + +Collapsing container on application load currently goes through these steps: +1. layout is first loaded with all containers uncollapsed (disregarding user set values), then +2. containers are collapsed either: + - _automatically_: application has not set a string return value for the `ui.layout.loaded` event. + - _manually_: application sets collapsed flags in the callback passed to `ui.layout.loaded` + +All this means that the user will notice a flicker. If the flicker is not desirable, hide the layout behind an overlay, wait for the `ui.layout.loaded` event. In the "automatic" mode, all is done and the layout should be presentable. In the "manual" mode it is up to the application to count the `ui.layout.toggle` events. + + + +```xml +
+
+
+
+ +
+
+
+
+``` + +```javascript +$scope.$on('ui.layout.loaded', function(evt, id) => { + switch (id) { + case 'main-container': + ... + break; + case 'child-container': + ... + break; + default: + break; + } +}); +``` + +Note: the value of the attribute is not evaluated, so: + +``` +$scope.layout = { + mySidebar: {someKey: 'some value'} +} + +
+// $scope.$on will receive the string 'layout.mySidebar.someKey' +``` ### ui.layout.toggle [event-toggle] @@ -217,8 +272,8 @@ $scope.$on('ui.layout.toggle', function(e, container){ }); ``` -Manually toggling (clicking the arrow button on the splitbar) will not update the `collapsed` attribute. -If the application is using the `collapsed` attribute of `ui-layout-container` to programmatically control the collapsed state, the application should update it's state when this event occurs to stay in sync with the UI. +Manually toggling (clicking the arrow button on the splitbar) will not update the `collapsed` attribute. +If the application is using the `collapsed` attribute of `ui-layout-container` to programmatically control the collapsed state, the application should update it's state when this event occurs to stay in sync with the UI. ### ui.layout.resize diff --git a/src/ui-layout.js b/src/ui-layout.js index b8a9ff8..3c0a8f8 100644 --- a/src/ui-layout.js +++ b/src/ui-layout.js @@ -729,6 +729,26 @@ angular.module('ui.layout', []) }]) + .directive('uiLayoutLoaded', [function() { + // This is not needed any more, because toggling does not depend on the logic + // of prevButton and nextButton. It is only kept to simulate the previous + // behaviour and avoid a breaking change. It should be removed with the next + // major version upgrade. + return { + require: '^uiLayout', + restrict: 'A', + priority: -100, + link: function($scope, el, attrs){ + // negation is safe here, because we are expecting non-empty string + if (!attrs['uiLayoutLoaded']) { + $scope.$broadcast('ui.layout.loaded', null); + } else { + $scope.$broadcast('ui.layout.loaded', attrs['uiLayoutLoaded']); + } + } + }; + }]) + .directive('uiLayoutContainer', ['LayoutContainer', '$compile', function(LayoutContainer, $compile) { diff --git a/test/uiLayoutContainer.spec.js b/test/uiLayoutContainer.spec.js index 3b12141..945644a 100644 --- a/test/uiLayoutContainer.spec.js +++ b/test/uiLayoutContainer.spec.js @@ -47,7 +47,7 @@ describe('Directive: uiLayoutContainer', function () { expect(bcScope.container.collapsed).toEqual(true); expect(bcScope.container.resizable).toEqual(false); - expect(bcScope.container.size).toEqual(0); // collapsed + expect(bcScope.container.size).toEqual(50); // collapsed: min size expect(bcScope.container.uncollapsedSize).toEqual('100px'); expect(bcScope.container.minSize).toEqual(50); expect(bcScope.container.maxSize).toEqual(200); @@ -60,4 +60,4 @@ describe('Directive: uiLayoutContainer', function () { }); -}); \ No newline at end of file +}); diff --git a/test/uiLayoutLoaded.spec.js b/test/uiLayoutLoaded.spec.js new file mode 100644 index 0000000..b226e4b --- /dev/null +++ b/test/uiLayoutLoaded.spec.js @@ -0,0 +1,58 @@ +'use strict'; + +describe('Directive: uiLayoutLoaded', function () { + var scope, element, $compile, $timeout, + template = '
'; + + function compileDirective(before, after, tpl) { + var elm; + + elm = angular.element(tpl || template); + angular.element(document.body).prepend(elm); + $compile(elm)(scope); + scope.$digest(); + + return elm; + } + + beforeEach(function () { + + module('ui.layout'); + + inject(function ($rootScope, _$compile_, _$timeout_) { + scope = $rootScope.$new(); + $compile = _$compile_; + $timeout = _$timeout_; + }); + }); + + afterEach(function () { + if (element) element.remove(); + }); + + it('should broadcast ui.layout.loaded and return attribute value', function () { + spyOn(scope, '$broadcast'); + compileDirective(false, false, '
'); + expect(scope.$broadcast).toHaveBeenCalledWith('ui.layout.loaded','attrValue'); + }); + + it('should broadcast ui.layout.loaded and return null', function () { + spyOn(scope, '$broadcast'); + compileDirective(false, false, '
'); + expect(scope.$broadcast).toHaveBeenCalledWith('ui.layout.loaded',null); + }); + + it('should broadcast ui.layout.loaded after all ui.layout directives have been loaded and return attribute value not an evaluated expression', function () { + spyOn(scope, '$broadcast'); + scope.data = { + someMessage: "dummy" + }; + + var elm = angular.element('
'); + angular.element(document.body).prepend(elm); + $compile(elm)(scope); + scope.$digest(); + expect(scope.$broadcast).toHaveBeenCalledWith('ui.layout.loaded','data.someMessage'); + }); + +}); From 56a26b46ca707db4300d26b8fa851bfeaf039381 Mon Sep 17 00:00:00 2001 From: Hannes Widmoser Date: Sat, 20 Feb 2016 10:11:48 +0100 Subject: [PATCH 12/18] style: cleaned up diff --- README.md | 10 ++++----- src/ui-layout.js | 40 +++++++++++++++++----------------- test/uiLayoutContainer.spec.js | 2 +- test/uiLayoutLoaded.spec.js | 3 ++- 4 files changed, 28 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index e878715..0fbd53b 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,7 @@ $scope.layout { } ``` -Changing those values will toggle container. See also [`ui.layout.toggle`][event-toggle]. +Changing those values will toggle container. See also [`ui.layout.toggle`][event-toggle]. ### size Type: `String` @@ -203,7 +203,7 @@ percentage ## Events -Events are broadcast on the scope where ui-layout is attached. This means they are available to any controller inside of a ui-layout container. +Events are broadcast on the scope where ui-layout is attached. This means they are available to any controller inside of a ui-layout container. ### ui.layout.loaded Returns: `string` or `null` @@ -227,7 +227,7 @@ All this means that the user will notice a flicker. If the flicker is not desira
- +
@@ -272,8 +272,8 @@ $scope.$on('ui.layout.toggle', function(e, container){ }); ``` -Manually toggling (clicking the arrow button on the splitbar) will not update the `collapsed` attribute. -If the application is using the `collapsed` attribute of `ui-layout-container` to programmatically control the collapsed state, the application should update it's state when this event occurs to stay in sync with the UI. +Manually toggling (clicking the arrow button on the splitbar) will not update the `collapsed` attribute. +If the application is using the `collapsed` attribute of `ui-layout-container` to programmatically control the collapsed state, the application should update it's state when this event occurs to stay in sync with the UI. ### ui.layout.resize diff --git a/src/ui-layout.js b/src/ui-layout.js index 3c0a8f8..967e5b3 100644 --- a/src/ui-layout.js +++ b/src/ui-layout.js @@ -729,26 +729,6 @@ angular.module('ui.layout', []) }]) - .directive('uiLayoutLoaded', [function() { - // This is not needed any more, because toggling does not depend on the logic - // of prevButton and nextButton. It is only kept to simulate the previous - // behaviour and avoid a breaking change. It should be removed with the next - // major version upgrade. - return { - require: '^uiLayout', - restrict: 'A', - priority: -100, - link: function($scope, el, attrs){ - // negation is safe here, because we are expecting non-empty string - if (!attrs['uiLayoutLoaded']) { - $scope.$broadcast('ui.layout.loaded', null); - } else { - $scope.$broadcast('ui.layout.loaded', attrs['uiLayoutLoaded']); - } - } - }; - }]) - .directive('uiLayoutContainer', ['LayoutContainer', '$compile', function(LayoutContainer, $compile) { @@ -839,6 +819,26 @@ angular.module('ui.layout', []) }; }]) + .directive('uiLayoutLoaded', [function() { + // This is not needed any more, because toggling does not depend on the logic + // of prevButton and nextButton. It is only kept to simulate the previous + // behaviour and avoid a breaking change. It should be removed with the next + // major version upgrade. + return { + require: '^uiLayout', + restrict: 'A', + priority: -100, + link: function($scope, el, attrs){ + // negation is safe here, because we are expecting non-empty string + if (!attrs['uiLayoutLoaded']) { + $scope.$broadcast('ui.layout.loaded', null); + } else { + $scope.$broadcast('ui.layout.loaded', attrs['uiLayoutLoaded']); + } + } + }; + }]) + .factory('LayoutContainer', function() { function BaseContainer() { diff --git a/test/uiLayoutContainer.spec.js b/test/uiLayoutContainer.spec.js index 945644a..748e454 100644 --- a/test/uiLayoutContainer.spec.js +++ b/test/uiLayoutContainer.spec.js @@ -60,4 +60,4 @@ describe('Directive: uiLayoutContainer', function () { }); -}); +}); \ No newline at end of file diff --git a/test/uiLayoutLoaded.spec.js b/test/uiLayoutLoaded.spec.js index b226e4b..2973669 100644 --- a/test/uiLayoutLoaded.spec.js +++ b/test/uiLayoutLoaded.spec.js @@ -1,5 +1,6 @@ 'use strict'; + describe('Directive: uiLayoutLoaded', function () { var scope, element, $compile, $timeout, template = '
'; @@ -55,4 +56,4 @@ describe('Directive: uiLayoutLoaded', function () { expect(scope.$broadcast).toHaveBeenCalledWith('ui.layout.loaded','data.someMessage'); }); -}); +}); \ No newline at end of file From 3c888d847e00743a8da04848bc3e95816c418f34 Mon Sep 17 00:00:00 2001 From: Hannes Widmoser Date: Wed, 13 Apr 2016 14:49:36 +0200 Subject: [PATCH 13/18] feat(toggle): Add option to hide a collapsed splitbar --- README.md | 16 +++++++++++----- src/ui-layout.js | 13 +++++++++++-- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 0fbd53b..048b1d4 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,12 @@ Default: `false` Like `disableToggle` above but only removes the arrows on mobile devices (max-device-width: 480px). +### hideCollapsedSplitbar + +Type: `Boolean` +Default: `false` + +If set to `true`, the splitbar between two containers will not be visible when one of them is collapsed. ## Child Attributes @@ -145,7 +151,7 @@ $scope.layout { } ``` -Changing those values will toggle container. See also [`ui.layout.toggle`][event-toggle]. +Changing those values will toggle container. See also [`ui.layout.toggle`][event-toggle]. ### size Type: `String` @@ -203,7 +209,7 @@ percentage ## Events -Events are broadcast on the scope where ui-layout is attached. This means they are available to any controller inside of a ui-layout container. +Events are broadcast on the scope where ui-layout is attached. This means they are available to any controller inside of a ui-layout container. ### ui.layout.loaded Returns: `string` or `null` @@ -227,7 +233,7 @@ All this means that the user will notice a flicker. If the flicker is not desira
- +
@@ -272,8 +278,8 @@ $scope.$on('ui.layout.toggle', function(e, container){ }); ``` -Manually toggling (clicking the arrow button on the splitbar) will not update the `collapsed` attribute. -If the application is using the `collapsed` attribute of `ui-layout-container` to programmatically control the collapsed state, the application should update it's state when this event occurs to stay in sync with the UI. +Manually toggling (clicking the arrow button on the splitbar) will not update the `collapsed` attribute. +If the application is using the `collapsed` attribute of `ui-layout-container` to programmatically control the collapsed state, the application should update it's state when this event occurs to stay in sync with the UI. ### ui.layout.resize diff --git a/src/ui-layout.js b/src/ui-layout.js index 967e5b3..a8dfea8 100644 --- a/src/ui-layout.js +++ b/src/ui-layout.js @@ -252,7 +252,13 @@ angular.module('ui.layout', []) var c, i; var dividerSize = parseInt(opts.dividerSize); var elementSize = $element[0].getBoundingClientRect()[ctrl.sizeProperties.sizeProperty]; - var availableSize = elementSize - (dividerSize * numOfSplitbars); + var numOfVisibleSplitbars = numOfSplitbars; + if (opts.hideCollapsedSplitbar) { + numOfVisibleSplitbars = ctrl.containers.filter(function(c) { + return LayoutContainer.isSplitbar(c) && !c.collapsed; + }).length; + } + var availableSize = elementSize - (dividerSize * numOfVisibleSplitbars); var originalSize = availableSize; var usedSpace = 0; var numOfAutoContainers = 0; @@ -335,7 +341,7 @@ angular.module('ui.layout', []) c.size = (newSize !== null) ? newSize : autoSize; } else { - c.size = dividerSize; + c.size = (c.collapsed && opts.hideCollapsedSplitbar) ? 0 : dividerSize; } usedSpace += c.size; @@ -622,6 +628,7 @@ angular.module('ui.layout', []) afterIcon.addClass(afterIconClass); scope.splitbar.notifyToggleBefore = function(isCollapsed) { + scope.splitbar.collapsed = isCollapsed; if(isCollapsed) { afterButton.css('display', 'none'); @@ -646,6 +653,7 @@ angular.module('ui.layout', []) }; scope.splitbar.notifyToggleAfter = function(isCollapsed) { + scope.splitbar.collapsed = isCollapsed; if(isCollapsed) { prevButton.css('display', 'none'); @@ -885,6 +893,7 @@ angular.module('ui.layout', []) this.left = 0; this.top = 0; this.element = null; + this.collapsed = false; } return { From 55f725fc56d94df6e44d5b9b7ad985c1a79b6c0b Mon Sep 17 00:00:00 2001 From: Hannes Widmoser Date: Wed, 13 Apr 2016 15:12:16 +0200 Subject: [PATCH 14/18] feat(toggle): Add option to control whether a container collapses to its min size or to 0px --- README.md | 7 +++++++ src/ui-layout.js | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 048b1d4..0dfca86 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,13 @@ Default: `false` If set to `true`, the splitbar between two containers will not be visible when one of them is collapsed. +### collapseToMinSize + +Type: `Boolean` +Default: `false` + +Whether to limit a collapsed container size to its minimum size, even when it is collapsed. + ## Child Attributes ### uiLayoutContainer diff --git a/src/ui-layout.js b/src/ui-layout.js index a8dfea8..11fb13a 100644 --- a/src/ui-layout.js +++ b/src/ui-layout.js @@ -269,7 +269,7 @@ angular.module('ui.layout', []) if(!LayoutContainer.isSplitbar(ctrl.containers[i])) { c = ctrl.containers[i]; - opts.sizes[i] = c.collapsed ? (optionValue(c.minSize) || '0px') : c.isCentral ? 'auto' : optionValue(c.uncollapsedSize) || 'auto'; + opts.sizes[i] = c.collapsed ? (opts.collapseToMinSize ? (optionValue(c.minSize) || '0px') : '0px') : c.isCentral ? 'auto' : optionValue(c.uncollapsedSize) || 'auto'; opts.minSizes[i] = optionValue(c.minSize); opts.maxSizes[i] = optionValue(c.maxSize); From 9d8430d9c7d7782f450564b25f28fb267afe7d9a Mon Sep 17 00:00:00 2001 From: Hannes Widmoser Date: Wed, 13 Apr 2016 15:18:22 +0200 Subject: [PATCH 15/18] feat(toggle): Change collapseToMinSize to hideCollapsedContainer to keep default behaviour --- README.md | 4 ++-- src/ui-layout.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0dfca86..d762184 100644 --- a/README.md +++ b/README.md @@ -107,12 +107,12 @@ Default: `false` If set to `true`, the splitbar between two containers will not be visible when one of them is collapsed. -### collapseToMinSize +### hideCollapsedContainer Type: `Boolean` Default: `false` -Whether to limit a collapsed container size to its minimum size, even when it is collapsed. +Whether to completely hide a collapsed container, even if it has a minimum size set. If this option is set to `false` a collapsed container will have a size equal to the `min-size` property. ## Child Attributes diff --git a/src/ui-layout.js b/src/ui-layout.js index 11fb13a..27b42b8 100644 --- a/src/ui-layout.js +++ b/src/ui-layout.js @@ -269,7 +269,7 @@ angular.module('ui.layout', []) if(!LayoutContainer.isSplitbar(ctrl.containers[i])) { c = ctrl.containers[i]; - opts.sizes[i] = c.collapsed ? (opts.collapseToMinSize ? (optionValue(c.minSize) || '0px') : '0px') : c.isCentral ? 'auto' : optionValue(c.uncollapsedSize) || 'auto'; + opts.sizes[i] = c.collapsed ? (opts.hideCollapsedContainer ? '0px' : (optionValue(c.minSize) || '0px')) : c.isCentral ? 'auto' : optionValue(c.uncollapsedSize) || 'auto'; opts.minSizes[i] = optionValue(c.minSize); opts.maxSizes[i] = optionValue(c.maxSize); From 1e677ccafd4f955214e4466373633281007170de Mon Sep 17 00:00:00 2001 From: Hannes Widmoser Date: Wed, 13 Apr 2016 16:00:20 +0200 Subject: [PATCH 16/18] feat(toggle): Trigger ui.layout.resize event whenever a size changes during calculate --- src/ui-layout.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ui-layout.js b/src/ui-layout.js index 27b42b8..6e852a1 100644 --- a/src/ui-layout.js +++ b/src/ui-layout.js @@ -339,7 +339,13 @@ angular.module('ui.layout', []) newSize = opts.sizes[i]; } - c.size = (newSize !== null) ? newSize : autoSize; + newSize = (newSize !== null) ? newSize : autoSize; + if (c.size !== newSize) { + c.size = newSize; + if (i >= 2) { + $scope.$broadcast('ui.layout.resize', ctrl.containers[i], ctrl.containers[i-2]); + } + } } else { c.size = (c.collapsed && opts.hideCollapsedSplitbar) ? 0 : dividerSize; } From 34048ff8c7b0d3fcdc72af7e4ef581c29a25d2af Mon Sep 17 00:00:00 2001 From: Hannes Widmoser Date: Thu, 26 May 2016 10:33:56 +0200 Subject: [PATCH 17/18] feat(toggle): Fix collapsed state of splitbar --- src/ui-layout.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ui-layout.js b/src/ui-layout.js index 6e852a1..4424989 100644 --- a/src/ui-layout.js +++ b/src/ui-layout.js @@ -634,7 +634,7 @@ angular.module('ui.layout', []) afterIcon.addClass(afterIconClass); scope.splitbar.notifyToggleBefore = function(isCollapsed) { - scope.splitbar.collapsed = isCollapsed; + scope.splitbar.collapsed = (before && before.collapsed) || (after && after.collapsed); if(isCollapsed) { afterButton.css('display', 'none'); @@ -659,7 +659,7 @@ angular.module('ui.layout', []) }; scope.splitbar.notifyToggleAfter = function(isCollapsed) { - scope.splitbar.collapsed = isCollapsed; + scope.splitbar.collapsed = (before && before.collapsed) || (after && after.collapsed); if(isCollapsed) { prevButton.css('display', 'none'); From 68403bca39b179c534a416db97038b587af9a8eb Mon Sep 17 00:00:00 2001 From: Hannes Widmoser Date: Thu, 26 May 2016 11:02:47 +0200 Subject: [PATCH 18/18] feat(toggle): Fix collapse state of splitbar (right container) --- src/ui-layout.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ui-layout.js b/src/ui-layout.js index 4424989..acc6717 100644 --- a/src/ui-layout.js +++ b/src/ui-layout.js @@ -632,9 +632,15 @@ angular.module('ui.layout', []) prevIcon.addClass(prevIconClass); afterIcon.addClass(afterIconClass); + + function getCollapse() { + var before = ctrl.containers[index - 1]; + var after = ctrl.containers[index + 1]; + return (before !== undefined && before.collapsed) || (after !== undefined && after.collapsed); + } scope.splitbar.notifyToggleBefore = function(isCollapsed) { - scope.splitbar.collapsed = (before && before.collapsed) || (after && after.collapsed); + scope.splitbar.collapsed = getCollapse(); if(isCollapsed) { afterButton.css('display', 'none'); @@ -659,7 +665,7 @@ angular.module('ui.layout', []) }; scope.splitbar.notifyToggleAfter = function(isCollapsed) { - scope.splitbar.collapsed = (before && before.collapsed) || (after && after.collapsed); + scope.splitbar.collapsed = getCollapse(); if(isCollapsed) { prevButton.css('display', 'none');