├── .gitignore
├── .travis.yml
├── Gruntfile.js
├── README.md
├── bower.json
├── demo
├── demo.css
├── demo.js
└── index.html
├── dist
├── fixed-header.css
├── fixed-header.js
└── fixed-header.min.js
├── karma.conf.js
├── package.json
└── test
└── fixed-header-spec.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .idea
3 | FixedHeader.iml
4 | bower_components
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '0.10'
4 | before_script:
5 | - 'npm install'
6 | - 'node_modules/bower/bin/bower install'
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function (grunt) {
2 |
3 | // Project configuration.
4 | grunt.initConfig({
5 | pkg: grunt.file.readJSON('package.json'),
6 | uglify: {
7 | options: {
8 | banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
9 | },
10 | build: {
11 | src: 'dist/<%= pkg.name %>.js',
12 | dest: 'dist/<%= pkg.name %>.min.js'
13 | }
14 | }
15 | });
16 |
17 | // Load the plugin that provides the "uglify" task.
18 | grunt.loadNpmTasks('grunt-contrib-uglify');
19 |
20 | // Default task(s).
21 | grunt.registerTask('default', ['uglify']);
22 |
23 | };
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FixedHeader [](http://travis-ci.org/objectcomputing/FixedHeader)
2 |
3 | AngularJS Directive and CSS for creating scrollable tables with fixed
4 | headers.
5 |
6 | Creating scrollable tables with fixed headers that do not scroll off
7 | the screen is tricky due to the way tables work in HTML. We use the
8 | the techniques described by Miriam Salzer here:
9 | [Don’t Mess With Tables – Pure CSS Fixed-Header Left-Aligned Tables](http://salzerdesign.com/blog/?p=191),
10 | and wrap them up in a reusable AngularJS directive.
11 |
12 | ## Usage
13 |
14 | Add `dist/fixed-header.js` and `dist/fixed-header.css` to your
15 | index.html.
16 |
17 | Add `oci.fixedHeader` as a module dependency on your module:
18 |
19 | ```js
20 | angular.module('app', ['oci.fixedHeader']);
21 | ```
22 |
23 | Add `oci.fixed-header` as an attribute to your table:
24 |
25 | ```html
26 |
31 | ```
32 |
33 | Note the wrapper div with the class `my-table`. The `oci.fixed-header`
34 | directive adds wrapper divs around the table, so we need
35 | `div.my-table` for our custom CSS below:
36 |
37 | Add custom css to control the height of the table and the height of
38 | the header row:
39 |
40 | ```css
41 | div.my-table div.fixed-table-container-inner {
42 | /* the maximum height of the table: */
43 | max-height: 150px;
44 |
45 | border: 1px solid #aaaaaa;
46 | width: 150px;
47 | }
48 |
49 | div.my-table div.th-inner {
50 | /* the height of the header row */
51 | line-height: 30px;
52 | }
53 |
54 | div.my-table div.fixed-table-container {
55 | /* the height of the header row - this needs to match line-height above */
56 | padding-top: 30px;
57 | }
58 |
59 | div.my-table tr.hidden-header .th-inner {
60 | padding-right: 5px;
61 | }
62 |
63 | ```
64 |
65 | ## Demo
66 |
67 | [oci.fixed-header demo](http://objectcomputing.github.io/FixedHeader/demo/index.html)
68 |
69 | ## Running Tests
70 |
71 | * Install global dependencies
72 | `npm install -g karma phantomjs bower`
73 |
74 | * Install local dependencies
75 | `npm install`
76 |
77 | * Install Bower dependencies
78 | `bower install`
79 |
80 | * Run tests
81 | `karma start`
82 |
83 | ## Authors
84 |
85 | - Lance Finney finneyl@ociweb.com
86 | - Steve Molitor molitors@ociweb.com
87 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "oci.FixedHeader",
3 | "version": "0.1.1",
4 | "homepage": "https://github.com/objectcomputing/FixedHeader",
5 | "authors": [
6 | "Object Computing Inc."
7 | ],
8 | "description": "AngularJS Directive and CSS for creating scrollable tables with fixed headers",
9 | "main": "dist/fixed-header.js",
10 | "keywords": [
11 | "Angular",
12 | "AngularJS",
13 | "angular.js",
14 | "table",
15 | "header",
16 | "fixed",
17 | "static"
18 | ],
19 | "license": "MIT",
20 | "ignore": [
21 | "**/.*",
22 | "node_modules",
23 | "bower_components",
24 | "test",
25 | "lib"
26 | ],
27 | "dependencies": {
28 | "angular": "1.2.x"
29 | },
30 | "devDependencies": {
31 | "jquery": "1.11.x",
32 | "angular-mocks": "1.2.x"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/demo/demo.css:
--------------------------------------------------------------------------------
1 | div.my-table div.fixed-table-container-inner {
2 | /* the maximum height of the table: */
3 | max-height: 150px;
4 |
5 | border: 1px solid #aaaaaa;
6 | width: 150px;
7 | }
8 |
9 | div.my-table div.th-inner {
10 | /* the height of the header row */
11 | line-height: 30px;
12 | }
13 |
14 | div.my-table div.fixed-table-container {
15 | /* the height of the header row - this needs to match line-height above */
16 | padding-top: 30px;
17 | }
18 |
19 | div.my-table tr.hidden-header .th-inner {
20 | padding-right: 5px;
21 | }
22 |
--------------------------------------------------------------------------------
/demo/demo.js:
--------------------------------------------------------------------------------
1 | /*
2 | @license OCI Fixed Header version 0.1.0
3 | ⓒ 2014 OCI https://github.com/objectcomputing/FixedHeader
4 | License: MIT
5 | */
6 |
7 | (function() {
8 | 'use strict';
9 |
10 | var app = angular.module('app', ['oci.fixedHeader']);
11 |
12 | app.controller('ctrl', function($scope) {
13 | $scope.headers = ['First', 'Middle', 'Last'];
14 | });
15 | })();
16 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Fixed Header Demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | First |
17 | Middle |
18 | Last |
19 |
20 |
21 |
22 | Bob | L | Smith |
23 | Bob | L | Smith |
24 | Bob | L | Smith |
25 | Bob | L | Smith |
26 | Bob | L | Smith |
27 | Bob | L | Smith |
28 | Bob | L | Smith |
29 | Bob | L | Smith |
30 | Bob | L | Smith |
31 | Bob | L | Smith |
32 | Bob | L | Smith |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | {{header}}
44 |
45 | |
46 |
47 |
54 |
55 |
56 | Bob | L | Smith |
57 | Bob | L | Smith |
58 | Bob | L | Smith |
59 | Bob | L | Smith |
60 | Bob | L | Smith |
61 | Bob | L | Smith |
62 | Bob | L | Smith |
63 | Bob | L | Smith |
64 | Bob | L | Smith |
65 | Bob | L | Smith |
66 | Bob | L | Smith |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/dist/fixed-header.css:
--------------------------------------------------------------------------------
1 | .fixed-table-container {
2 | position: relative;
3 | }
4 |
5 | .fixed-table-container-inner {
6 | overflow-y: auto;
7 | height: 100%;
8 | }
9 |
10 | div.fixed-table-container div table thead tr th {
11 | padding: 0;
12 | border: 0;
13 | }
14 |
15 | .th-inner {
16 | position: absolute;
17 | top: 0;
18 | text-align: left;
19 | }
20 |
21 | .hidden-header .th-inner {
22 | position: static;
23 | overflow-y: hidden;
24 | height: 0;
25 | }
26 |
--------------------------------------------------------------------------------
/dist/fixed-header.js:
--------------------------------------------------------------------------------
1 | /*
2 | @license OCI Fixed Header version 0.1.0
3 | ⓒ 2014 OCI https://github.com/objectcomputing/FixedHeader
4 | License: MIT
5 | */
6 |
7 | (function () {
8 | 'use strict';
9 |
10 | angular.module('oci.fixedHeader', [])
11 | .directive('oci.fixedHeader', function () {
12 | return function link(scope, elem) {
13 | // Wrap the contents of every header cell with div.th-inner so that the
14 | // CSS can relocate it.
15 | var header = elem.find('thead').find('tr');
16 | angular.forEach(header.find('th'), function (th) {
17 | angular.element(th).contents().wrap('');
18 | });
19 |
20 | // Make a clone of the header that we hide using css. The purpose of this
21 | // is to allow the width of the contents of the header to be included
22 | // in the calculation of the widths of the columns
23 | var hiddenHeader = header.clone();
24 | hiddenHeader.addClass('hidden-header');
25 | header.after(hiddenHeader);
26 |
27 | // wrap the table in a couple of divs that bring in important css modifications
28 | elem.wrap('');
29 | elem.wrap('');
30 | };
31 | });
32 | })();
33 |
--------------------------------------------------------------------------------
/dist/fixed-header.min.js:
--------------------------------------------------------------------------------
1 | /*! fixed-header 2014-03-04 */
2 | !function(){"use strict";angular.module("oci.fixedHeader",[]).directive("oci.fixedHeader",function(){return function(a,b){var c=b.find("thead").find("tr");angular.forEach(c.find("th"),function(a){angular.element(a).contents().wrap('')});var d=c.clone();d.addClass("hidden-header"),c.after(d),b.wrap(''),b.wrap('')}})}();
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function (config) {
2 | "use strict";
3 |
4 | config.set({
5 |
6 | // base path, that will be used to resolve files and exclude
7 | basePath: '.',
8 |
9 |
10 | // frameworks to use
11 | frameworks: ['jasmine'],
12 |
13 |
14 | // list of files / patterns to load in the browser
15 | files: [
16 | 'bower_components/jquery/dist/jquery.js',
17 | 'bower_components/angular/angular.js',
18 | 'bower_components/angular-mocks/angular-mocks.js',
19 | 'dist/fixed-header.js',
20 | 'test/fixed-header-spec.js'
21 | ],
22 |
23 | // list of files to exclude
24 | exclude: [
25 | ],
26 |
27 | singleRun: true,
28 |
29 | // test results reporter to use
30 | // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
31 | reporters: ['dots'],
32 |
33 |
34 | // level of logging
35 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
36 | logLevel: config.LOG_INFO,
37 |
38 |
39 | // enable / disable watching file and executing tests whenever any file changes
40 | autoWatch: false,
41 |
42 |
43 | // Start these browsers, currently available:
44 | // - Chrome
45 | // - ChromeCanary
46 | // - Firefox
47 | // - Opera (has to be installed with `npm install karma-opera-launcher`)
48 | // - Safari (only Mac; has to be installed with `npm install karma-safari-launcher`)
49 | // - PhantomJS
50 | // - IE (only Windows; has to be installed with `npm install karma-ie-launcher`)
51 | //browsers: ['Chrome', 'PhantomJS', 'Firefox'],
52 | browsers: ['PhantomJS'],
53 |
54 | plugins: [
55 | //'karma-chrome-launcher',
56 | //'karma-firefox-launcher',
57 | 'karma-phantomjs-launcher',
58 | 'karma-jasmine'
59 | //'karma-junit-reporter'
60 | ],
61 |
62 | // junitReporter: {
63 | // outputFile: 'test-results.xml'
64 | // // suite: 'unit'
65 | // },
66 | });
67 | };
68 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fixed-header",
3 | "repository": "https://github.com/objectcomputing/FixedHeader",
4 | "scripts": {
5 | "test": "./node_modules/.bin/karma start"
6 | },
7 | "devDependencies": {
8 | "bower": "^1.3.12",
9 | "grunt": "~0.4.2",
10 | "grunt-contrib-jshint": "~0.6.3",
11 | "grunt-contrib-nodeunit": "~0.2.0",
12 | "grunt-contrib-uglify": "~0.4.0",
13 | "karma": "~0.10.9",
14 | "karma-chrome-launcher": "~0.1.2",
15 | "karma-coffee-preprocessor": "~0.1.3",
16 | "karma-firefox-launcher": "~0.1.3",
17 | "karma-html2js-preprocessor": "~0.1.0",
18 | "karma-jasmine": "~0.1.5",
19 | "karma-phantomjs-launcher": "~0.1.2",
20 | "karma-requirejs": "~0.2.1",
21 | "karma-script-launcher": "~0.1.0",
22 | "phantomjs": "~1.9.7-1",
23 | "requirejs": "~2.1.11"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/test/fixed-header-spec.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | describe('fixed-header directive', function () {
5 | beforeEach(module('oci.fixedHeader'));
6 |
7 | var compile;
8 | var scope;
9 |
10 | beforeEach(inject(function ($compile, $rootScope) {
11 | compile = $compile;
12 | scope = $rootScope.$new();
13 | }));
14 |
15 | it('should render html for fixed header', function () {
16 | var html =
17 | '';
18 | var elem = angular.element(html);
19 | compile(elem)(scope);
20 | scope.$digest();
21 |
22 | expect(elem.find('>div.fixed-table-container').length).toBe(1);
23 | expect(elem.find('>div.fixed-table-container>div.fixed-table-container-inner').length).toBe(1);
24 | expect(elem.find('>div.fixed-table-container>div.fixed-table-container-inner>table').length).toBe(1);
25 |
26 | expect(elem.find('thead > tr').length).toBe(2);
27 | expect(elem.find('thead > tr').eq(0).hasClass('hidden-header')).toBeFalsy();
28 | expect(elem.find('thead > tr').eq(1).hasClass('hidden-header')).toBeTruthy();
29 | expect(elem.find('thead > tr').eq(0).find('th').eq(0).html()).toBe('one
');
30 | expect(elem.find('thead > tr').eq(0).find('th').eq(1).html()).toBe('two
');
31 | expect(elem.find('thead tr').eq(1).find('th').eq(0).html()).toBe('one
');
32 | expect(elem.find('thead > tr').eq(1).find('th').eq(1).html()).toBe('two
');
33 | });
34 | });
35 | })();
36 |
--------------------------------------------------------------------------------