7

How can I make angular bootstrap collapse, collapsing horizontally? Something like here?

1 Answer 1

8

You are going to need to either modify the collapse directive or create a new directive based on that to handle collapsing the width only. I would suggest the latter unless you want all of the collapse directives in your app to collapse horizontally.

Please see the Plunk here demonstrating the use of a collapse-with directive based on the bootstrap collapse directive.

On top of changing the directive you will need to add new classes to handle the transition and set a width for the element you want to collapse (you could also change the directive to collapse to and from 100% width, not sure on your use case but hopefully you get the idea):

.well {
  width: 400px;  
}

.collapsing-width {
  position: relative;
  overflow: hidden;
  -webkit-transition: width 0.35s ease;
    -moz-transition: width 0.35s ease;
    -o-transition: width 0.35s ease;
    transition: width 0.35s ease;
}

And the directive just requires a few changes to the expand, expandDone, collapse and collapseDone functions and adding/removing the css class above as follows:

.directive('collapseWidth', ['$transition', function ($transition, $timeout) {

    return {
      link: function (scope, element, attrs) {

        var initialAnimSkip = true;
        var currentTransition;

        function doTransition(change) {
          var newTransition = $transition(element, change);
          if (currentTransition) {
            currentTransition.cancel();
          }
          currentTransition = newTransition;
          newTransition.then(newTransitionDone, newTransitionDone);
          return newTransition;

          function newTransitionDone() {
            // Make sure it's this transition, otherwise, leave it alone.
            if (currentTransition === newTransition) {
              currentTransition = undefined;
            }
          }
        }

        function expand() {
          if (initialAnimSkip) {
            initialAnimSkip = false;
            expandDone();
          } else {
            element.removeClass('collapse').addClass('collapsing-width');
            doTransition({ width: element[0].scrollWidth + 'px' }).then(expandDone);
          }
        }

        function expandDone() {
          element.removeClass('collapsing-width');
          element.addClass('collapse in');
          element.css({width: 'auto'});
        }

        function collapse() {
          if (initialAnimSkip) {
            initialAnimSkip = false;
            collapseDone();
            element.css({width: 0});
          } else {
            // CSS transitions don't work with height: auto, so we have to manually change the height to a specific value
            element.css({ width: element[0].scrollWidth + 'px' });
            //trigger reflow so a browser realizes that height was updated from auto to a specific value
            var x = element[0].offsetHeight;

            element.removeClass('collapse in').addClass('collapsing-width');

            doTransition({ width: 0 }).then(collapseDone);
          }
        }

        function collapseDone() {
          element.removeClass('collapsing-width');
          element.addClass('collapse');
        }

        scope.$watch(attrs.collapseWidth, function (shouldCollapse) {
          if (shouldCollapse) {
            collapse();
          } else {
            expand();
          }
        });
      }
    };
}]);

You may need to tweak the css a little to ensure the spacing and margins are consistent with your use cases but hopefully that helps.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you, @adamK, I was looking for something like this as well. Any idea how to have the div right-aligned and have it collapse to the right? When I add position: absolute to try and set the right and top properties, the transition stops working.
You could simply float the collapse and collapse-width classes to the right, see here. This doesn't have the same right-to-left swipe effect though but if the animation duration is short enough it might still be suitable. Otherwise I think it will require a few container elements and same margin manipulation to get the same effect.
@adamK Very nice solution, nice job

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.