angular-sticky-table-header
Advanced tools
Comparing version 0.1.3 to 0.2.0
{ | ||
"name": "angular-sticky-table-header", | ||
"version": "0.1.3", | ||
"version": "0.2.0", | ||
"description": "Sticky headers for tables", | ||
@@ -14,3 +14,3 @@ "main": "dist/angular-sticky-table-header.js", | ||
"jquery": "^1.11", | ||
"watch-dom": "^0.0" | ||
"watch-dom": "~0.0" | ||
}, | ||
@@ -17,0 +17,0 @@ "keywords": [ |
@@ -20,3 +20,3 @@ angular.module('watchDom', []).constant('watchDomOptions', { | ||
mutationObserver.observe(element, angular.extend({}, watchDomOptions, options)); | ||
return mutationObserver.disconnect; | ||
return mutationObserver.disconnect.bind(mutationObserver); | ||
}; | ||
@@ -46,5 +46,11 @@ } | ||
restrict: 'A', | ||
link: function (scope, element, attrs) { | ||
scope: { | ||
disabled: '=', | ||
rows: '=' | ||
}, | ||
template: '<div ng-transclude></div>', | ||
transclude: true, | ||
link: function (scope, element) { | ||
angular.extend(scope, { | ||
isStuck: false, | ||
stuck: false, | ||
mutationObserver: null, | ||
@@ -63,3 +69,3 @@ offset: {}, | ||
removeClones: function () { | ||
scope.isStuck = false; | ||
scope.stuck = false; | ||
element.find('.' + options.cloneClassName).remove(); | ||
@@ -80,7 +86,7 @@ }, | ||
setOffset: function () { | ||
scope.offset = scope.tr.getBoundingClientRect(); | ||
scope.offset = angular.element(scope.tr).offset(); | ||
}, | ||
setStuck: function (bool) { | ||
scope.$apply(function () { | ||
scope.isStuck = !!bool; | ||
scope.stuck = !!bool; | ||
}); | ||
@@ -98,6 +104,6 @@ }, | ||
var scroll = $window.scrollY; | ||
if (!scope.isStuck && scroll >= scope.offset.top) { | ||
if (!scope.stuck && scroll >= scope.offset.top) { | ||
scope.setClonedCellWidths(); | ||
scope.setStuck(true); | ||
} else if (scope.isStuck && scroll < scope.offset.top) { | ||
} else if (scope.stuck && scroll < scope.offset.top) { | ||
scope.setStuck(false); | ||
@@ -114,24 +120,43 @@ } | ||
}); | ||
}, | ||
on: function () { | ||
scope.observeTr(); | ||
scope.addEvents(); | ||
}, | ||
off: function () { | ||
scope.mutationObserver(); | ||
scope.removeEvents(); | ||
scope.removeClones(); | ||
}, | ||
addEvents: function () { | ||
angular.element($window).on({ | ||
'resize.angularStickyTableHeader': _.debounce(scope.setClonedCellWidths.bind(scope), options.interval), | ||
'scroll.angularStickyTableHeader': _.debounce(scope.checkScroll.bind(scope), options.interval) | ||
}); | ||
}, | ||
removeEvents: function () { | ||
angular.element($window).off('.angularStickyTableHeader'); | ||
}, | ||
changeDisabled: function (disabled, old) { | ||
if (disabled === old) { | ||
return; | ||
} | ||
if (disabled) { | ||
scope.off(); | ||
} else { | ||
scope.on(); | ||
scope.resetClone(); | ||
} | ||
} | ||
}); | ||
// enable/disable api | ||
scope.$watch('disabled', scope.changeDisabled); | ||
// watch rows, and re-measure column widths when they change | ||
if (attrs.rows) { | ||
scope.$watch(function () { | ||
return scope[attrs.rows]; | ||
}, scope.rowsChanged); | ||
} | ||
scope.$watch('rows', scope.rowsChanged); | ||
// fired when stuck state changes | ||
scope.$watch('isStuck', scope.toggleClone); | ||
// start observing header for DOM changes | ||
scope.observeTr(); | ||
// listen on window resize event | ||
angular.element($window).on({ | ||
'resize.angularStickyTableHeader': _.debounce(scope.setClonedCellWidths.bind(scope), options.interval), | ||
'scroll.angularStickyTableHeader': _.debounce(scope.checkScroll.bind(scope), options.interval) | ||
}); | ||
scope.$watch('stuck', scope.toggleClone); | ||
// teardown | ||
scope.$on('$destroy', function () { | ||
angular.element($window).off('.angularStickyTableHeader'); | ||
scope.mutationObserver(); | ||
}); | ||
scope.$on('$destroy', scope.off); | ||
// init | ||
scope.on(); | ||
// helpers | ||
@@ -138,0 +163,0 @@ function ifClone(fn) { |
{ | ||
"name": "angular-sticky-table-header", | ||
"version": "0.1.3", | ||
"version": "0.2.0", | ||
"description": "Sticky headers for tables", | ||
@@ -5,0 +5,0 @@ "main": "dist/angular-sticky-table-header.js", |
@@ -23,3 +23,7 @@ angular-sticky-table-header [![Build Status](https://travis-ci.org/turn/angular-sticky-table-header.svg?branch=master)](https://travis-ci.org/turn/angular-sticky-table-header) | ||
<div ng-controller="fooCtrl"> | ||
<div sticky-table-header rows="rowCollection"> | ||
<div | ||
sticky-table-header | ||
rows="rowCollection" | ||
disabled="expression" | ||
> | ||
<table> | ||
@@ -46,2 +50,4 @@ ... | ||
$scope.expression = false; | ||
}); | ||
@@ -48,0 +54,0 @@ ``` |
@@ -25,3 +25,9 @@ angular | ||
restrict: 'A', | ||
link: function (scope, element, attrs) { | ||
scope: { | ||
disabled: '=', | ||
rows: '=' | ||
}, | ||
template: '<div ng-transclude></div>', | ||
transclude: true, | ||
link: function (scope, element) { | ||
@@ -31,3 +37,3 @@ angular.extend(scope, { | ||
// show the cloned <tr>? | ||
isStuck: false, | ||
stuck: false, | ||
@@ -66,3 +72,3 @@ // MutationObserver bound to the original <tr> | ||
scope.isStuck = false; | ||
scope.stuck = false; | ||
@@ -96,3 +102,3 @@ element | ||
scope.offset = scope.tr.getBoundingClientRect(); | ||
scope.offset = angular.element(scope.tr).offset(); | ||
@@ -104,3 +110,3 @@ }, | ||
scope.$apply(function(){ | ||
scope.isStuck = !!bool; | ||
scope.stuck = !!bool; | ||
}); | ||
@@ -128,6 +134,6 @@ | ||
if (!scope.isStuck && scroll >= scope.offset.top) { | ||
if (!scope.stuck && scroll >= scope.offset.top) { | ||
scope.setClonedCellWidths(); | ||
scope.setStuck(true); | ||
} else if (scope.isStuck && scroll < scope.offset.top) { | ||
} else if (scope.stuck && scroll < scope.offset.top) { | ||
scope.setStuck(false); | ||
@@ -155,34 +161,67 @@ } | ||
}, | ||
on: function () { | ||
scope.observeTr(); | ||
scope.addEvents(); | ||
}, | ||
off: function () { | ||
scope.mutationObserver(); | ||
scope.removeEvents(); | ||
scope.removeClones(); | ||
}, | ||
addEvents: function () { | ||
angular.element($window).on({ | ||
'resize.angularStickyTableHeader': _.debounce(scope.setClonedCellWidths.bind(scope), options.interval), | ||
'scroll.angularStickyTableHeader': _.debounce(scope.checkScroll.bind(scope), options.interval) | ||
}); | ||
}, | ||
removeEvents: function () { | ||
angular.element($window).off('.angularStickyTableHeader'); | ||
}, | ||
changeDisabled: function (disabled, old) { | ||
if (disabled === old) { | ||
return; | ||
} | ||
if (disabled) { | ||
scope.off(); | ||
} else { | ||
scope.on(); | ||
scope.resetClone(); | ||
} | ||
} | ||
}); | ||
// enable/disable api | ||
scope.$watch('disabled', scope.changeDisabled); | ||
// watch rows, and re-measure column widths when they change | ||
if (attrs.rows) { | ||
scope.$watch(function(){ | ||
return scope[attrs.rows]; | ||
}, scope.rowsChanged); | ||
} | ||
scope.$watch('rows', scope.rowsChanged); | ||
// fired when stuck state changes | ||
scope.$watch('isStuck', scope.toggleClone); | ||
scope.$watch('stuck', scope.toggleClone); | ||
// start observing header for DOM changes | ||
scope.observeTr(); | ||
// listen on window resize event | ||
angular.element($window).on({ | ||
'resize.angularStickyTableHeader': _.debounce(scope.setClonedCellWidths.bind(scope), options.interval), | ||
'scroll.angularStickyTableHeader': _.debounce(scope.checkScroll.bind(scope), options.interval) | ||
}); | ||
// teardown | ||
scope.$on('$destroy', function() { | ||
angular.element($window).off('.angularStickyTableHeader'); | ||
scope.mutationObserver(); | ||
}); | ||
scope.$on('$destroy', scope.off); | ||
// init | ||
scope.on(); | ||
// helpers | ||
function ifClone (fn) { | ||
@@ -189,0 +228,0 @@ return util.guard(fn, cloneExists); |
121
test/test.js
@@ -40,2 +40,3 @@ // Generated by CoffeeScript 1.7.1 | ||
_this.scope.$digest(); | ||
_this.scope = _this.element.scope(); | ||
return $window = { | ||
@@ -57,3 +58,3 @@ scrollY: 0, | ||
this.scope.createClone(); | ||
return expect(($((this.element.find('thead tr'))[1]).find('th')).length).toBe(this.scope.columnCollection.length); | ||
return expect(($((this.element.find('thead tr'))[1]).find('th')).length).toBe(this.scope.$parent.columnCollection.length); | ||
}); | ||
@@ -103,6 +104,6 @@ it('should clone the <tr>\'s events', function() { | ||
describe('#removeClones', function() { | ||
it('should set scope.isStuck to false', function() { | ||
it('should set scope.stuck to false', function() { | ||
this.scope.createClone(); | ||
this.scope.removeClones(); | ||
return expect(this.scope.isStuck).toBe(false); | ||
return expect(this.scope.stuck).toBe(false); | ||
}); | ||
@@ -133,10 +134,10 @@ return it('should remove all <tr> clones', function() { | ||
describe('#setOffset', function() { | ||
it('should call getBoundingClientRect on the first <tr>', function() { | ||
spyOn((this.element.find('tr'))[0], 'getBoundingClientRect'); | ||
it('should call getOffset on the first <tr>', function() { | ||
spyOn(($()).__proto__, 'offset'); | ||
this.scope.setOffset(); | ||
return expect((this.element.find('tr'))[0].getBoundingClientRect).toHaveBeenCalled(); | ||
return expect(($()).__proto__.offset).toHaveBeenCalled(); | ||
}); | ||
return it('should set scope.offset equal to the value returned by getBoundingClientRect', function() { | ||
this.scope.offset = null; | ||
spyOn((this.element.find('tr'))[0], 'getBoundingClientRect').andReturn('foo'); | ||
spyOn(($()).__proto__, 'offset').andReturn('foo'); | ||
this.scope.setOffset(); | ||
@@ -147,20 +148,20 @@ return expect(this.scope.offset).toEqual('foo'); | ||
describe('#setStuck', function() { | ||
it('should set scope.isStuck equal to the boolean passed into it', function() { | ||
this.scope.isStuck = null; | ||
it('should set scope.stuck equal to the boolean passed into it', function() { | ||
this.scope.stuck = null; | ||
this.scope.setStuck(true); | ||
return expect(this.scope.isStuck).toBe(true); | ||
return expect(this.scope.stuck).toBe(true); | ||
}); | ||
return it('should coerce non-boolean values into booleans', function() { | ||
this.scope.setStuck(true); | ||
expect(this.scope.isStuck).toBe(true); | ||
expect(this.scope.stuck).toBe(true); | ||
this.scope.setStuck('foo'); | ||
expect(this.scope.isStuck).toBe(true); | ||
expect(this.scope.stuck).toBe(true); | ||
this.scope.setStuck(42); | ||
expect(this.scope.isStuck).toBe(true); | ||
expect(this.scope.stuck).toBe(true); | ||
this.scope.setStuck(null); | ||
expect(this.scope.isStuck).toBe(false); | ||
expect(this.scope.stuck).toBe(false); | ||
this.scope.setStuck(0); | ||
expect(this.scope.isStuck).toBe(false); | ||
expect(this.scope.stuck).toBe(false); | ||
this.scope.setStuck(false); | ||
return expect(this.scope.isStuck).toBe(false); | ||
return expect(this.scope.stuck).toBe(false); | ||
}); | ||
@@ -214,5 +215,5 @@ }); | ||
}); | ||
it('should call #setStuck with true and #setClonedCellWidths with no arguments when scope.isStuck is false and scrollY is >= offset.top', function() { | ||
it('should call #setStuck with true and #setClonedCellWidths with no arguments when scope.stuck is false and scrollY is >= offset.top', function() { | ||
this.scope.clone = true; | ||
this.scope.isStuck = false; | ||
this.scope.stuck = false; | ||
this.scope.offset = { | ||
@@ -226,5 +227,5 @@ top: 0 | ||
}); | ||
it('should call #setStuck with false when scope.isStuck is true and scrollY is < offset.top', function() { | ||
it('should call #setStuck with false when scope.stuck is true and scrollY is < offset.top', function() { | ||
this.scope.clone = true; | ||
this.scope.isStuck = true; | ||
this.scope.stuck = true; | ||
this.scope.offset = { | ||
@@ -239,3 +240,3 @@ top: 1 | ||
this.scope.clone = true; | ||
this.scope.isStuck = true; | ||
this.scope.stuck = true; | ||
this.scope.offset = { | ||
@@ -246,3 +247,3 @@ top: 0 | ||
this.scope.checkScroll(); | ||
this.scope.isStuck = false; | ||
this.scope.stuck = false; | ||
this.scope.offset = { | ||
@@ -266,10 +267,80 @@ top: 1 | ||
}); | ||
return describe('$destroy', function() { | ||
return it('should remove the mutation observer', function() { | ||
describe('#on', function() { | ||
return it('should call #observeTr and #addEvents with no arguments', function() { | ||
spyOn(this.scope, 'observeTr'); | ||
spyOn(this.scope, 'addEvents'); | ||
this.scope.on(); | ||
expect(this.scope.observeTr).toHaveBeenCalledWith; | ||
return expect(this.scope.addEvents).toHaveBeenCalledWith; | ||
}); | ||
}); | ||
describe('#off', function() { | ||
return it('should call #mutationObserver, #removeEvents, and #removeClones with no arguments', function() { | ||
this.scope.mutationObserver = function() {}; | ||
spyOn(this.scope, 'mutationObserver'); | ||
spyOn(this.scope, 'removeEvents'); | ||
spyOn(this.scope, 'removeClones'); | ||
this.scope.off(); | ||
expect(this.scope.mutationObserver).toHaveBeenCalledWith; | ||
expect(this.scope.removeEvents).toHaveBeenCalledWith; | ||
return expect(this.scope.removeClones).toHaveBeenCalledWith; | ||
}); | ||
}); | ||
describe('#changeDisabled', function() { | ||
it('shouldn\'t call anything if the 1st argument is identical to the 2nd argument', function() { | ||
spyOn(this.scope, 'on'); | ||
spyOn(this.scope, 'off'); | ||
spyOn(this.scope, 'resetClone'); | ||
this.scope.changeDisabled(true, true); | ||
expect(this.scope.on).not.toHaveBeenCalled(); | ||
expect(this.scope.off).not.toHaveBeenCalled(); | ||
return expect(this.scope.resetClone).not.toHaveBeenCalled(); | ||
}); | ||
it('should call #off with no arguments if the 1st argument is truthy', function() { | ||
spyOn(this.scope, 'off'); | ||
this.scope.changeDisabled(true); | ||
return expect(this.scope.off).toHaveBeenCalledWith; | ||
}); | ||
return it('should call #on and #resetClone with no arguments if the 1st argument is truthy', function() { | ||
spyOn(this.scope, 'on'); | ||
spyOn(this.scope, 'resetClone'); | ||
this.scope.changeDisabled(false); | ||
expect(this.scope.on).toHaveBeenCalledWith; | ||
return expect(this.scope.resetClone).toHaveBeenCalledWith; | ||
}); | ||
}); | ||
describe('$destroy', function() { | ||
return it('should call #off with no arguments', function() { | ||
this.scope.mutationObserver = function() {}; | ||
spyOn(this.scope, 'off'); | ||
this.scope.$destroy(); | ||
return expect(this.scope.mutationObserver).toHaveBeenCalled(); | ||
return expect(this.scope.off).toHaveBeenCalledWith; | ||
}); | ||
}); | ||
return describe('$watches', function() { | ||
it('should call #changeDisabled when scope.disabled changes', inject(function($timeout) { | ||
spyOn(this.scope, 'changeDisabled'); | ||
this.element.attr('disabled', 'foo'); | ||
this.scope.$apply(); | ||
return $timeout(function() { | ||
return expect(this.scope.changeDisabled).toHaveBeenCalled(); | ||
}); | ||
})); | ||
it('should call #rowsChanged when scope.rows changes', inject(function($timeout) { | ||
spyOn(this.scope, 'rowsChanged'); | ||
this.element.attr('rows', 'foo'); | ||
this.scope.$apply(); | ||
return $timeout(function() { | ||
return expect(this.scope.rowsChanged).toHaveBeenCalled(); | ||
}); | ||
})); | ||
return it('should call #toggleClone when scope.stuck changes', inject(function($timeout) { | ||
spyOn(this.scope, 'toggleClone'); | ||
this.element.attr('stuck', 'foo'); | ||
this.scope.$apply(); | ||
return $timeout(function() { | ||
return expect(this.scope.toggleClone).toHaveBeenCalled(); | ||
}); | ||
})); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
51635
694
90