From 5590a00c2d1148555b05cc2df1e6b9c87154c344 Mon Sep 17 00:00:00 2001 From: Samir Bhatt Date: Wed, 22 Oct 2014 16:31:21 -0700 Subject: [PATCH 1/6] Add fa-deck directive --- src/scripts/directives/fa-deck.js | 143 ++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 src/scripts/directives/fa-deck.js diff --git a/src/scripts/directives/fa-deck.js b/src/scripts/directives/fa-deck.js new file mode 100644 index 00000000..6b081978 --- /dev/null +++ b/src/scripts/directives/fa-deck.js @@ -0,0 +1,143 @@ +/** + * @ngdoc directive + * @name faDeck + * @module famous.angular + * @restrict EA + * @description + * This directive will create a Famo.us Deck containing the + * specified child elements. The provided `options` object + * will pass directly through to the Famo.us faDeck's + * constructor. See [https://famo.us/docs/views/Deck] + * + * @usage + * ```html + * + * + * + * ``` + * @example + * `Fa-sequential-layout` is a Famous View that arranges a collection of renderables sequentially in a specified direction. Pass options (such as `direction`) by binding an object with the property to `fa-options`. + * + * In the example below, an ng-repeat is used on an `fa-view` and the elements nested below it. The size of each `fa-surface` is `[undefined, 100]`, specifying that the width will fill the parent container, and the height will be 100 pixels. + * + * There are no positioning properties (such as `fa-translate`) specified on the `fa-modifier`, but these `fa-surface`s will translate automatically in the specified direction as not to overlap each other. + * + + + + + + + + + + + + + + + + fa-app { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + } + + + */ + +angular.module('famous.angular') + .directive('faDeck', ["$famous", "$famousDecorator", function ($famous, $famousDecorator) { + return { + template: '
', + restrict: 'E', + transclude: true, + scope: true, + compile: function (tElem, tAttrs, transclude) { + window.$f = $famous; + return { + pre: function (scope, element, attrs) { + var isolate = $famousDecorator.ensureIsolate(scope); + + var Deck = $famous["famous/views/Deck"]; + + var _children = []; + + var options = scope.$eval(attrs.faOptions) || {}; + + isolate.renderNode = new Deck(options); + + $famousDecorator.addRole('renderable',isolate); + isolate.show(); + + var _updateDeck = function() { + _children.sort(function(a, b) { + return a.index - b.index; + }); + isolate.renderNode.sequenceFrom(function(_children) { + var _ch = []; + angular.forEach(_children, function(c, i) { + _ch[i] = c.renderGate; + }); + return _ch; + }(_children)); + }; + + $famousDecorator.sequenceWith( + scope, + function(data) { + _children.push(data); + _updateDeck(); + }, + function(childScopeId) { + _children = function (_children) { + var _ch = []; + angular.forEach(_children, function (c) { + if (c.id !== childScopeId) { + _ch.push(c); + } + }); + return _ch; + }(_children); + _updateDeck(); + } + ); + + }, + post: function (scope, element, attrs) { + var isolate = $famousDecorator.ensureIsolate(scope); + var deckOpen = scope.$eval(attrs.faOpen); + + scope.$watch(function(){ + return scope.$eval(attrs.faOpen); + }, function () { + deckOpen = scope.$eval(attrs.faOpen); + (scope.$eval(deckOpen) + || attrs.faOpen == "true" + || deckOpen === true + || scope[attrs.faOpen] === true) ? isolate.renderNode.open() : isolate.renderNode.close(); + }); + + transclude(scope, function (clone) { + element.find('div').append(clone); + }); + + $famousDecorator.registerChild(scope, element, isolate); + } + }; + } + }; + }]); From 969bfda8c99ea3f72eaa3006fabe2de9aa17a72d Mon Sep 17 00:00:00 2001 From: Samir Bhatt Date: Wed, 22 Oct 2014 18:43:39 -0700 Subject: [PATCH 2/6] Added Tests for faDeck Directive --- test/directives/faDeckSpec.js | 279 ++++++++++++++++++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 test/directives/faDeckSpec.js diff --git a/test/directives/faDeckSpec.js b/test/directives/faDeckSpec.js new file mode 100644 index 00000000..bfdf0c36 --- /dev/null +++ b/test/directives/faDeckSpec.js @@ -0,0 +1,279 @@ +'use strict'; + +describe('faDeck', function() { + var eventHandler, common, $compile, $timeout, $scope, $famous; + + beforeEach(module('famous.angular')); + + beforeEach(inject(function(_$compile_, _$timeout_, _$rootScope_, _$famous_) { + $compile = _$compile_; + $timeout = _$timeout_; + $scope = _$rootScope_.$new(); + $famous = _$famous_; + + eventHandler = new $famous['famous/core/EventHandler'](); + + common = window.famousAngularCommon($scope, $compile); + })); + + + it('should work with ng-repeated views', function() { + var app = common.createApp( + '' + + '' + + '' + + '' + + '' + ); + + $scope.eventHandler = eventHandler; + $scope.views = [0, 1]; + $scope.$apply(); + + var deck = $famous.find('fa-deck')[0].renderNode; + + expect(deck._items).not.toBeNull(); + + common.destroyApp(app); + }); + + it('should allow specification of a start index with fa-start-index', function() { + //not yet implemented in lib + pending(); + + var app = common.createApp( + '' + + '' + + '' + + '' + + '' + ); + + $scope.eventHandler = eventHandler; + $scope.views = [0, 1]; + $scope.$apply(); + + var deck = $famous.find('fa-deck')[0].renderNode; + expect(deck._items.getIndex()).toBe(1); + common.destroyApp(app); + }); + + it('should unregister children when their scopes are destroyed', function() { + var app = common.createApp( + '' + + '' + + '' + + '' + + '' + ); + + $scope.eventHandler = eventHandler; + $scope.views = [0, 1, 2]; + $scope.$apply(); + + + var deck = $famous.find('fa-deck')[0].renderNode; + + expect(deck._items._.array.length).toBe(3); + + // Pop out the current index + $scope.views.splice(1, 1); + $scope.$apply(); + + expect(deck._items._.array.length).toBe(2); + + common.destroyApp(app); + }); + describe("hide and show", function() { + + it("hide and show properties on the Deck", function() { + var deck = $compile( + '' + + '' + + '' + + '' + + '' + )($scope); + $scope.eventHandler = eventHandler; + $scope.views = [0, 1, 2]; + $scope.$apply(); + + var scope = deck.scope(); + var isolate = 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); + }); + }); + + describe("open and close Deck", function(){ + + describe("open", function(){ + it("using a direct value - fa-open=\"true\"", function() { + var app = common.createApp( + '' + + '' + + '' + + '' + + '' + ); + $scope.views = [0, 1, 2]; + $scope.$apply(); + + var deck = $famous.find('fa-deck')[0].renderNode; + + expect(deck.isOpen()).toEqual(true); + + common.destroyApp(app); + }); + + it("using the return value of a function - fa-open=\"open()\"", function() { + var app = common.createApp( + '' + + '' + + '' + + '' + + '' + ); + $scope.views = [0, 1, 2]; + $scope.open = function(){ + return true; + } + $scope.$apply(); + + var deck = $famous.find('fa-deck')[0].renderNode; + + expect(deck.isOpen()).toEqual(true); + + common.destroyApp(app); + }); + + it("using a function name - fa-open=\"openFunc\"", function() { + var app = common.createApp( + '' + + '' + + '' + + '' + + '' + ); + $scope.views = [0, 1, 2]; + $scope.openFunc = function(){ + return true; + } + $scope.$apply(); + + var deck = $famous.find('fa-deck')[0].renderNode; + + expect(deck.isOpen()).toEqual(true); + + common.destroyApp(app); + }); + + it("using a $scope variable - fa-open=\"openVal\"", function() { + var app = common.createApp( + '' + + '' + + '' + + '' + + '' + ); + $scope.views = [0, 1, 2]; + $scope.openVal = true; + $scope.$apply(); + + var deck = $famous.find('fa-deck')[0].renderNode; + + expect(deck.isOpen()).toEqual(true); + + common.destroyApp(app); + }); + }); + + describe("close", function(){ + it("using a direct value - fa-open=\"false\"", function() { + var app = common.createApp( + '' + + '' + + '' + + '' + + '' + ); + $scope.views = [0, 1, 2]; + $scope.$apply(); + + var deck = $famous.find('fa-deck')[0].renderNode; + + expect(deck.isOpen()).toEqual(false); + + common.destroyApp(app); + }); + + it("using the return value of a function - fa-open=\"open()\"", function() { + var app = common.createApp( + '' + + '' + + '' + + '' + + '' + ); + $scope.views = [0, 1, 2]; + $scope.open = function(){ + return false; + } + $scope.$apply(); + + var deck = $famous.find('fa-deck')[0].renderNode; + + expect(deck.isOpen()).toEqual(false); + + common.destroyApp(app); + }); + + it("using a function name - fa-open=\"openFunc\"", function() { + var app = common.createApp( + '' + + '' + + '' + + '' + + '' + ); + $scope.views = [0, 1, 2]; + $scope.openFunc = function(){ + return false; + } + $scope.$apply(); + + var deck = $famous.find('fa-deck')[0].renderNode; + + expect(deck.isOpen()).toEqual(false); + + common.destroyApp(app); + }); + + it("using a $scope variable - fa-open=\"openVal\"", function() { + var app = common.createApp( + '' + + '' + + '' + + '' + + '' + ); + $scope.views = [0, 1, 2]; + $scope.openVal = false; + $scope.$apply(); + + var deck = $famous.find('fa-deck')[0].renderNode; + + expect(deck.isOpen()).toEqual(false); + + common.destroyApp(app); + }); + }); + }); +}); From a057ba1987560408526b384c32b874c6545e09d3 Mon Sep 17 00:00:00 2001 From: Samir Bhatt Date: Wed, 22 Oct 2014 19:23:15 -0700 Subject: [PATCH 3/6] added faDeck example in the header comment --- src/scripts/directives/fa-deck.js | 75 ++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 27 deletions(-) diff --git a/src/scripts/directives/fa-deck.js b/src/scripts/directives/fa-deck.js index 6b081978..41bd8c4e 100644 --- a/src/scripts/directives/fa-deck.js +++ b/src/scripts/directives/fa-deck.js @@ -11,41 +11,62 @@ * * @usage * ```html - * + * * - * + * * ``` * @example - * `Fa-sequential-layout` is a Famous View that arranges a collection of renderables sequentially in a specified direction. Pass options (such as `direction`) by binding an object with the property to `fa-options`. + * `Fa-deck` is a Famous SequentialLayout View that can be opened and closed with a transition. Pass options (such as `direction` and `transition`) by binding an object with the property to `fa-options`. * - * In the example below, an ng-repeat is used on an `fa-view` and the elements nested below it. The size of each `fa-surface` is `[undefined, 100]`, specifying that the width will fill the parent container, and the height will be 100 pixels. + * In the example below, an `fa-surface` and an `fa-view` are added to the deck. ng-repeat is used on an `fa-view` and the elements nested below it. The size of each `fa-surface` is `[250, 50]`. * - * There are no positioning properties (such as `fa-translate`) specified on the `fa-modifier`, but these `fa-surface`s will translate automatically in the specified direction as not to overlap each other. + * The modifier applied to the `fa-deck` positions it 0.5 across in the X direction and 0.25 down in the Y direction. + * + * A clickHandler() is bound to ng-click on the first surface, which toggles the `fa-deck` open and close actions. * - - - - - - - - - - - + + + + + Click Me + + + + {{ $index }} + + + + + + + fa-app { From 279dff9ec6ef9b4b452b7f9885227d6348fe0c94 Mon Sep 17 00:00:00 2001 From: Samir Bhatt Date: Wed, 22 Oct 2014 16:31:21 -0700 Subject: [PATCH 4/6] feat: add faDeck directive --- src/scripts/directives/fa-deck.js | 143 ++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 src/scripts/directives/fa-deck.js diff --git a/src/scripts/directives/fa-deck.js b/src/scripts/directives/fa-deck.js new file mode 100644 index 00000000..6b081978 --- /dev/null +++ b/src/scripts/directives/fa-deck.js @@ -0,0 +1,143 @@ +/** + * @ngdoc directive + * @name faDeck + * @module famous.angular + * @restrict EA + * @description + * This directive will create a Famo.us Deck containing the + * specified child elements. The provided `options` object + * will pass directly through to the Famo.us faDeck's + * constructor. See [https://famo.us/docs/views/Deck] + * + * @usage + * ```html + * + * + * + * ``` + * @example + * `Fa-sequential-layout` is a Famous View that arranges a collection of renderables sequentially in a specified direction. Pass options (such as `direction`) by binding an object with the property to `fa-options`. + * + * In the example below, an ng-repeat is used on an `fa-view` and the elements nested below it. The size of each `fa-surface` is `[undefined, 100]`, specifying that the width will fill the parent container, and the height will be 100 pixels. + * + * There are no positioning properties (such as `fa-translate`) specified on the `fa-modifier`, but these `fa-surface`s will translate automatically in the specified direction as not to overlap each other. + * + + + + + + + + + + + + + + + + fa-app { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + } + + + */ + +angular.module('famous.angular') + .directive('faDeck', ["$famous", "$famousDecorator", function ($famous, $famousDecorator) { + return { + template: '
', + restrict: 'E', + transclude: true, + scope: true, + compile: function (tElem, tAttrs, transclude) { + window.$f = $famous; + return { + pre: function (scope, element, attrs) { + var isolate = $famousDecorator.ensureIsolate(scope); + + var Deck = $famous["famous/views/Deck"]; + + var _children = []; + + var options = scope.$eval(attrs.faOptions) || {}; + + isolate.renderNode = new Deck(options); + + $famousDecorator.addRole('renderable',isolate); + isolate.show(); + + var _updateDeck = function() { + _children.sort(function(a, b) { + return a.index - b.index; + }); + isolate.renderNode.sequenceFrom(function(_children) { + var _ch = []; + angular.forEach(_children, function(c, i) { + _ch[i] = c.renderGate; + }); + return _ch; + }(_children)); + }; + + $famousDecorator.sequenceWith( + scope, + function(data) { + _children.push(data); + _updateDeck(); + }, + function(childScopeId) { + _children = function (_children) { + var _ch = []; + angular.forEach(_children, function (c) { + if (c.id !== childScopeId) { + _ch.push(c); + } + }); + return _ch; + }(_children); + _updateDeck(); + } + ); + + }, + post: function (scope, element, attrs) { + var isolate = $famousDecorator.ensureIsolate(scope); + var deckOpen = scope.$eval(attrs.faOpen); + + scope.$watch(function(){ + return scope.$eval(attrs.faOpen); + }, function () { + deckOpen = scope.$eval(attrs.faOpen); + (scope.$eval(deckOpen) + || attrs.faOpen == "true" + || deckOpen === true + || scope[attrs.faOpen] === true) ? isolate.renderNode.open() : isolate.renderNode.close(); + }); + + transclude(scope, function (clone) { + element.find('div').append(clone); + }); + + $famousDecorator.registerChild(scope, element, isolate); + } + }; + } + }; + }]); From 16c017ac47c4ceb5e7e512815d5615f41f1f4226 Mon Sep 17 00:00:00 2001 From: Samir Bhatt Date: Wed, 22 Oct 2014 18:43:39 -0700 Subject: [PATCH 5/6] test: added tests for faDeck directive --- test/directives/faDeckSpec.js | 279 ++++++++++++++++++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 test/directives/faDeckSpec.js diff --git a/test/directives/faDeckSpec.js b/test/directives/faDeckSpec.js new file mode 100644 index 00000000..bfdf0c36 --- /dev/null +++ b/test/directives/faDeckSpec.js @@ -0,0 +1,279 @@ +'use strict'; + +describe('faDeck', function() { + var eventHandler, common, $compile, $timeout, $scope, $famous; + + beforeEach(module('famous.angular')); + + beforeEach(inject(function(_$compile_, _$timeout_, _$rootScope_, _$famous_) { + $compile = _$compile_; + $timeout = _$timeout_; + $scope = _$rootScope_.$new(); + $famous = _$famous_; + + eventHandler = new $famous['famous/core/EventHandler'](); + + common = window.famousAngularCommon($scope, $compile); + })); + + + it('should work with ng-repeated views', function() { + var app = common.createApp( + '' + + '' + + '' + + '' + + '' + ); + + $scope.eventHandler = eventHandler; + $scope.views = [0, 1]; + $scope.$apply(); + + var deck = $famous.find('fa-deck')[0].renderNode; + + expect(deck._items).not.toBeNull(); + + common.destroyApp(app); + }); + + it('should allow specification of a start index with fa-start-index', function() { + //not yet implemented in lib + pending(); + + var app = common.createApp( + '' + + '' + + '' + + '' + + '' + ); + + $scope.eventHandler = eventHandler; + $scope.views = [0, 1]; + $scope.$apply(); + + var deck = $famous.find('fa-deck')[0].renderNode; + expect(deck._items.getIndex()).toBe(1); + common.destroyApp(app); + }); + + it('should unregister children when their scopes are destroyed', function() { + var app = common.createApp( + '' + + '' + + '' + + '' + + '' + ); + + $scope.eventHandler = eventHandler; + $scope.views = [0, 1, 2]; + $scope.$apply(); + + + var deck = $famous.find('fa-deck')[0].renderNode; + + expect(deck._items._.array.length).toBe(3); + + // Pop out the current index + $scope.views.splice(1, 1); + $scope.$apply(); + + expect(deck._items._.array.length).toBe(2); + + common.destroyApp(app); + }); + describe("hide and show", function() { + + it("hide and show properties on the Deck", function() { + var deck = $compile( + '' + + '' + + '' + + '' + + '' + )($scope); + $scope.eventHandler = eventHandler; + $scope.views = [0, 1, 2]; + $scope.$apply(); + + var scope = deck.scope(); + var isolate = 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); + }); + }); + + describe("open and close Deck", function(){ + + describe("open", function(){ + it("using a direct value - fa-open=\"true\"", function() { + var app = common.createApp( + '' + + '' + + '' + + '' + + '' + ); + $scope.views = [0, 1, 2]; + $scope.$apply(); + + var deck = $famous.find('fa-deck')[0].renderNode; + + expect(deck.isOpen()).toEqual(true); + + common.destroyApp(app); + }); + + it("using the return value of a function - fa-open=\"open()\"", function() { + var app = common.createApp( + '' + + '' + + '' + + '' + + '' + ); + $scope.views = [0, 1, 2]; + $scope.open = function(){ + return true; + } + $scope.$apply(); + + var deck = $famous.find('fa-deck')[0].renderNode; + + expect(deck.isOpen()).toEqual(true); + + common.destroyApp(app); + }); + + it("using a function name - fa-open=\"openFunc\"", function() { + var app = common.createApp( + '' + + '' + + '' + + '' + + '' + ); + $scope.views = [0, 1, 2]; + $scope.openFunc = function(){ + return true; + } + $scope.$apply(); + + var deck = $famous.find('fa-deck')[0].renderNode; + + expect(deck.isOpen()).toEqual(true); + + common.destroyApp(app); + }); + + it("using a $scope variable - fa-open=\"openVal\"", function() { + var app = common.createApp( + '' + + '' + + '' + + '' + + '' + ); + $scope.views = [0, 1, 2]; + $scope.openVal = true; + $scope.$apply(); + + var deck = $famous.find('fa-deck')[0].renderNode; + + expect(deck.isOpen()).toEqual(true); + + common.destroyApp(app); + }); + }); + + describe("close", function(){ + it("using a direct value - fa-open=\"false\"", function() { + var app = common.createApp( + '' + + '' + + '' + + '' + + '' + ); + $scope.views = [0, 1, 2]; + $scope.$apply(); + + var deck = $famous.find('fa-deck')[0].renderNode; + + expect(deck.isOpen()).toEqual(false); + + common.destroyApp(app); + }); + + it("using the return value of a function - fa-open=\"open()\"", function() { + var app = common.createApp( + '' + + '' + + '' + + '' + + '' + ); + $scope.views = [0, 1, 2]; + $scope.open = function(){ + return false; + } + $scope.$apply(); + + var deck = $famous.find('fa-deck')[0].renderNode; + + expect(deck.isOpen()).toEqual(false); + + common.destroyApp(app); + }); + + it("using a function name - fa-open=\"openFunc\"", function() { + var app = common.createApp( + '' + + '' + + '' + + '' + + '' + ); + $scope.views = [0, 1, 2]; + $scope.openFunc = function(){ + return false; + } + $scope.$apply(); + + var deck = $famous.find('fa-deck')[0].renderNode; + + expect(deck.isOpen()).toEqual(false); + + common.destroyApp(app); + }); + + it("using a $scope variable - fa-open=\"openVal\"", function() { + var app = common.createApp( + '' + + '' + + '' + + '' + + '' + ); + $scope.views = [0, 1, 2]; + $scope.openVal = false; + $scope.$apply(); + + var deck = $famous.find('fa-deck')[0].renderNode; + + expect(deck.isOpen()).toEqual(false); + + common.destroyApp(app); + }); + }); + }); +}); From 56caca5fb0b9af3cead62a14d3bea226ed0aeb2f Mon Sep 17 00:00:00 2001 From: Samir Bhatt Date: Wed, 22 Oct 2014 19:23:15 -0700 Subject: [PATCH 6/6] chore: added faDeck example in the header comment --- src/scripts/directives/fa-deck.js | 75 ++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 27 deletions(-) diff --git a/src/scripts/directives/fa-deck.js b/src/scripts/directives/fa-deck.js index 6b081978..41bd8c4e 100644 --- a/src/scripts/directives/fa-deck.js +++ b/src/scripts/directives/fa-deck.js @@ -11,41 +11,62 @@ * * @usage * ```html - * + * * - * + * * ``` * @example - * `Fa-sequential-layout` is a Famous View that arranges a collection of renderables sequentially in a specified direction. Pass options (such as `direction`) by binding an object with the property to `fa-options`. + * `Fa-deck` is a Famous SequentialLayout View that can be opened and closed with a transition. Pass options (such as `direction` and `transition`) by binding an object with the property to `fa-options`. * - * In the example below, an ng-repeat is used on an `fa-view` and the elements nested below it. The size of each `fa-surface` is `[undefined, 100]`, specifying that the width will fill the parent container, and the height will be 100 pixels. + * In the example below, an `fa-surface` and an `fa-view` are added to the deck. ng-repeat is used on an `fa-view` and the elements nested below it. The size of each `fa-surface` is `[250, 50]`. * - * There are no positioning properties (such as `fa-translate`) specified on the `fa-modifier`, but these `fa-surface`s will translate automatically in the specified direction as not to overlap each other. + * The modifier applied to the `fa-deck` positions it 0.5 across in the X direction and 0.25 down in the Y direction. + * + * A clickHandler() is bound to ng-click on the first surface, which toggles the `fa-deck` open and close actions. * - - - - - - - - - - - + + + + + Click Me + + + + {{ $index }} + + + + + + + fa-app {