From 9ff8b1b3fff62267e1039d2769d4b06ac252617f Mon Sep 17 00:00:00 2001 From: Chris Massey Date: Sat, 7 Feb 2015 14:52:25 -0500 Subject: [PATCH] feat: add DrawerLayout directive Closes #296 --- famous-angular-examples | 2 +- src/scripts/directives/fa-drawer-layout.js | 117 +++++++++++++++++++++ src/scripts/services/famous.js | 1 + test/directives/faDrawerLayoutSpec.js | 112 ++++++++++++++++++++ 4 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 src/scripts/directives/fa-drawer-layout.js create mode 100644 test/directives/faDrawerLayoutSpec.js diff --git a/famous-angular-examples b/famous-angular-examples index ceaad838..fca48990 160000 --- a/famous-angular-examples +++ b/famous-angular-examples @@ -1 +1 @@ -Subproject commit ceaad83824fb1c9c1cf2ff138fefcb88237ac8f4 +Subproject commit fca48990e8045a3cdbf334d3dc6e55c72f215f68 diff --git a/src/scripts/directives/fa-drawer-layout.js b/src/scripts/directives/fa-drawer-layout.js new file mode 100644 index 00000000..609722d4 --- /dev/null +++ b/src/scripts/directives/fa-drawer-layout.js @@ -0,0 +1,117 @@ +/** + * @ngdoc directive + * @name faDrawerLayout + * @module famous.angular + * @restrict EA + * @description + * This directive will create a Famo.us DrawerLayout containing + * a Content and a Drawer based on the order of its child elements. + * See [https://famo.us/docs/views/DrawerLayout] + * + * @usage + * ```html + * + * + * + * + * ``` + * @example + * `fa-drawer-layout` is a View that arranges two renderables into a drawer area with a defined size and a content area that fills up the remaining space. + * + * To use it, declare it in the html and nest 2 renderables. + * + * + */ + +angular.module('famous.angular') + .directive('faDrawerLayout', ["$famous", "$famousDecorator", function ($famous, $famousDecorator) { + return { + template: '
', + restrict: 'E', + transclude: true, + scope: true, + compile: function (tElem, tAttrs) { + return { + pre: function (scope, element, attrs) { + var DrawerLayout = $famous["famous/views/DrawerLayout"]; + var isolate = $famousDecorator.ensureIsolate(scope); + + var options = scope.$eval(attrs.faOptions) || {}; + + if (options.drawerLength == null) { + options.drawerLength = scope.$eval(attrs.faDrawerLength); + } + + if (options.side == null) { + options.side = scope.$eval(attrs.faSide); + } + switch (options.side) { + case 'left': + options.side = DrawerLayout.SIDES.LEFT; + break; + case 'top': + options.side = DrawerLayout.SIDES.TOP; + break; + case 'right': + options.side = DrawerLayout.SIDES.RIGHT; + break; + case 'bottom': + options.side = DrawerLayout.SIDES.BOTTOM; + break; + } + + isolate.renderNode = new DrawerLayout(options); + $famousDecorator.addRole('renderable', isolate); + isolate.show(); + + isolate.toggle = function (overrideOptions) { + isolate.renderNode.toggle(overrideOptions || scope.$eval(attrs.faOptions)); + }; + + isolate.open = function (overrideOptions) { + isolate.renderNode.open(overrideOptions || scope.$eval(attrs.faOptions)); + }; + + isolate.close = function (overrideOptions) { + isolate.renderNode.close(overrideOptions || scope.$eval(attrs.faOptions)); + }; + + var _numberOfChildren = 0; + + $famousDecorator.sequenceWith( + scope, + function addChild(data) { + _numberOfChildren++; + + if (_numberOfChildren === 1) { + isolate.renderNode.content.add(data.renderGate); + } else if (_numberOfChildren === 2){ + isolate.renderNode.drawer.add(data.renderGate); + } else { + throw new Error('fa-drawer-layout can accept no more than 2 children'); + } + }, + function removeChild(childScopeId) { + if (_numberOfChildren === 1) { + isolate.renderNode.content.set({}); + } else if (_numberOfChildren === 2) { + isolate.renderNode.drawer.set({}); + } + + _numberOfChildren--; + } + ); + }, + post: function (scope, element, attrs, ctrl, transclude) { + var isolate = $famousDecorator.ensureIsolate(scope); + + transclude(scope, function (clone) { + element.find('div').append(clone); + }); + + $famousDecorator.registerChild(scope, element, isolate); + } + }; + } + }; + }]); diff --git a/src/scripts/services/famous.js b/src/scripts/services/famous.js index b7751f90..0cf03311 100644 --- a/src/scripts/services/famous.js +++ b/src/scripts/services/famous.js @@ -67,6 +67,7 @@ ngFameApp.provider('$famous', function() { "famous/utilities/Timer": famous.utilities.Timer, "famous/utilities/Utility": famous.utilities.Utility, "famous/views/Deck": famous.views.Deck, + "famous/views/DrawerLayout": famous.views.DrawerLayout, "famous/views/EdgeSwapper": famous.views.EdgeSwapper, "famous/views/FlexibleLayout": famous.views.FlexibleLayout, "famous/views/Flipper": famous.views.Flipper, diff --git a/test/directives/faDrawerLayoutSpec.js b/test/directives/faDrawerLayoutSpec.js new file mode 100644 index 00000000..e1858158 --- /dev/null +++ b/test/directives/faDrawerLayoutSpec.js @@ -0,0 +1,112 @@ +'use strict'; + +describe('faDrawerLayout', function () { + var common, element, $compile, $scope, $famous, DrawerLayout, Surface, View; + + beforeEach(module('famous.angular')); + + beforeEach(inject(function(_$compile_, _$rootScope_, _$famous_) { + $compile = _$compile_; + $scope = _$rootScope_.$new(); + $famous = _$famous_; + + common = window.famousAngularCommon($scope, $compile); + DrawerLayout = $famous['famous/views/DrawerLayout']; + Surface = $famous['famous/core/Surface']; + View = $famous['famous/core/View']; + })); + + it("should create an instance of Famo.us DrawerLayout", function () { + var faDrawerLayout = $compile('')($scope); + var drawerLayout = common.getIsolateFromElement(faDrawerLayout).renderNode; + + expect(typeof drawerLayout).toBe('object'); + expect(drawerLayout instanceof DrawerLayout).toBe(true); + }); + + it("should set its children as the content and drawer properties of the DrawerLayout", function () { + var surfaceContent; + $scope.$on('registerChild', function(event, isolate) { + surfaceContent = angular.element(isolate.renderNode.content)[0]; + }); + var faDrawerLayout = $compile('')($scope); + var drawerLayout = common.getIsolateFromElement(faDrawerLayout).renderNode; + + expect(drawerLayout.content._child._object instanceof Surface).toBe(true); + expect(drawerLayout.drawer._child._object instanceof View).toBe(true); + + }); + + it("should throw an exception if more than two child elements are added", function () { + expect(function () { + var faDrawerLayout = $compile('')($scope); + }).toThrow(); + }); + + describe("hide and show", function () { + it("hide and show properties on the DrawerLayout", function () { + var faDrawerLayout = $compile('')($scope); + var scope = faDrawerLayout.scope(); + var isolate = faDrawerLayout.scope().isolate[scope.$id]; + + expect(isolate.renderGate._object === isolate.renderNode).toEqual(true); + isolate.hide() + $scope.$apply(); + expect(isolate.renderGate._object === isolate.emptyNode).toEqual(true); + + isolate.show() + $scope.$apply(); + expect(isolate.renderGate._object === isolate.renderNode).toEqual(true); + }); + }); + + it("should unregister a child when it is destroyed", function () { + var faDrawerLayout = $compile('')($scope); + var drawerLayout = common.getIsolateFromElement(faDrawerLayout).renderNode; + var children = common.getIsolateFromElement(faDrawerLayout).children; + var destroyMe = faDrawerLayout[0].querySelectorAll('.destroy-me'); + + expect(drawerLayout.drawer._child).not.toBe(null); + angular.element(destroyMe[0]).remove(); + expect(drawerLayout.drawer._child).toBe(null); + }); + + describe('should accept attribute', function () { + it('fa-drawer-length - to set the size of the concealed drawer', function () { + var faDrawerLayout = $compile('')($scope); + var drawerLayout = common.getIsolateFromElement(faDrawerLayout).renderNode; + + expect(drawerLayout.options.drawerLength).toEqual(240); + }); + + describe('fa-side - to set the position of the concealed drawer', function () { + it('\'left\' - aligns to the LEFT side', function () { + var faDrawerLayout = $compile('')($scope); + var drawerLayout = common.getIsolateFromElement(faDrawerLayout).renderNode; + + expect(drawerLayout.options.side).toEqual(DrawerLayout.SIDES.LEFT); + }); + + it('\'top\' - aligns to the TOP side', function () { + var faDrawerLayout = $compile('')($scope); + var drawerLayout = common.getIsolateFromElement(faDrawerLayout).renderNode; + + expect(drawerLayout.options.side).toEqual(DrawerLayout.SIDES.TOP); + }); + + it('\'right\' - aligns to the RIGHT side', function () { + var faDrawerLayout = $compile('')($scope); + var drawerLayout = common.getIsolateFromElement(faDrawerLayout).renderNode; + + expect(drawerLayout.options.side).toEqual(DrawerLayout.SIDES.RIGHT); + }); + + it('\'bottom\' - aligns to the BOTTOM side', function () { + var faDrawerLayout = $compile('')($scope); + var drawerLayout = common.getIsolateFromElement(faDrawerLayout).renderNode; + + expect(drawerLayout.options.side).toEqual(DrawerLayout.SIDES.BOTTOM); + }); + }); + }); +});