├── .gitignore
├── src
├── components
│ ├── menu
│ │ ├── context
│ │ │ ├── ContextMenuController.js
│ │ │ ├── ContextMenuDirective.js
│ │ │ └── contextMenu.js
│ │ ├── dropdown
│ │ │ ├── DropdownController.js
│ │ │ ├── DropdownMenuDirective.js
│ │ │ ├── dropdown.js
│ │ │ ├── DropdownToggleDirective.js
│ │ │ └── DropdownDirective.js
│ │ ├── menu.js
│ │ ├── menu.less
│ │ ├── MenuController.js
│ │ └── MenuDirective.js
│ ├── body
│ │ ├── SelectionDirective.js
│ │ ├── GroupRowController.js
│ │ ├── StyleTranslator.js
│ │ ├── GroupRowDirective.js
│ │ ├── RowController.js
│ │ ├── CellController.js
│ │ ├── ScrollerDirective.js
│ │ ├── BodyDirective.js
│ │ ├── CellDirective.js
│ │ ├── RowDirective.js
│ │ └── SelectionController.js
│ ├── popover
│ │ ├── popover.js
│ │ ├── PopoverRegistry.js
│ │ ├── PositionHelper.js
│ │ └── popover.less
│ ├── footer
│ │ ├── FooterController.js
│ │ ├── FooterDirective.js
│ │ ├── PagerDirective.js
│ │ └── PagerController.js
│ ├── DataTableService.spec.js
│ ├── header
│ │ ├── HeaderCellController.js
│ │ ├── ResizableDirective.js
│ │ ├── SortableDirective.js
│ │ ├── HeaderController.js
│ │ ├── HeaderCellDirective.js
│ │ └── HeaderDirective.js
│ ├── DataTableService.js
│ └── DataTableDirective.js
├── utils
│ ├── utils.spec.js
│ ├── keys.js
│ ├── translate.js
│ ├── polyfill.js
│ ├── vendorPrefixes.js
│ ├── throttle.js
│ ├── utils.js
│ └── math.js
├── tests
│ └── frameworks.js
├── dataTable.js
├── dataTable.less
└── defaults.js
├── jsconfig.json
├── .editorconfig
├── .eslintrc
├── .travis.yml
├── bower.json
├── karma.conf.js
├── config.js
├── LICENSE
├── CONTRIBUTING.md
├── demos
├── cell-templates.html
├── virtual.html
├── basic.html
├── slow.html
├── empty.html
├── scroll.html
├── greed.html
├── action-links.html
├── grouping.html
├── expressive.html
├── paging.html
├── tabs.html
├── virtual-paging.html
├── force.html
├── updating.html
├── perf.html
├── sort.html
├── inline-editing.html
├── columnadd.html
├── single-select.html
├── perf-horzscroll.html
├── checkboxes.html
├── transclude.html
├── tall.html
├── filters.html
├── pins.html
└── tooltip.html
├── package.json
├── release
└── dataTable.css
├── index.html
└── gulpfile.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | jspm_packages
3 | npm-debug.log
4 | dist
5 | .idea
6 |
--------------------------------------------------------------------------------
/src/components/menu/context/ContextMenuController.js:
--------------------------------------------------------------------------------
1 | export class ContextMenuController{
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES6",
4 | "module": "commonjs",
5 | "diagnostics": true,
6 | "emitDecoratorMetadata": true,
7 | "experimentalDecorators":true
8 | }
9 | }
--------------------------------------------------------------------------------
/src/components/menu/context/ContextMenuDirective.js:
--------------------------------------------------------------------------------
1 | export function ContextMenuDirective(){
2 | return {
3 | restrict: 'C',
4 | controller: 'ContextMenuController',
5 | link: function($scope, $elm, $attrs, ctrl) {
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/src/components/menu/dropdown/DropdownController.js:
--------------------------------------------------------------------------------
1 | export class DropdownController{
2 | /*@ngInject*/
3 | constructor($scope){
4 | $scope.open = false;
5 | }
6 |
7 | toggle(scope){
8 | scope.open = !scope.open;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/components/menu/context/contextMenu.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 | import dropdown from '../dropdown/dropdown';
3 |
4 | export default angular
5 | .module('contextmenu', [ dropdown.name ])
6 | .directive('contextMenu', ContextMenuDirective)
7 |
--------------------------------------------------------------------------------
/src/utils/utils.spec.js:
--------------------------------------------------------------------------------
1 | import '../tests/frameworks';
2 |
3 | import {ObjectId} from './utils';
4 |
5 | describe('utils', function () {
6 | it('should generate unique ID', () => {
7 | var id1 = ObjectId();
8 | var id2 = ObjectId();
9 |
10 | id1.should.not.eq(id2);
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/src/components/body/SelectionDirective.js:
--------------------------------------------------------------------------------
1 | import { SelectionController } from './SelectionController';
2 |
3 | export function SelectionDirective(){
4 | return {
5 | controller: SelectionController,
6 | restrict: 'A',
7 | require:'^dtBody',
8 | controllerAs: 'selCtrl'
9 | };
10 | };
11 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: http://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | # Unix-style newlines with a newline ending every file
7 | [*]
8 | end_of_line = lf
9 | insert_final_newline = true
10 |
11 | # 2 space indentation
12 | [**.*]
13 | indent_style = space
14 | indent_size = 2
--------------------------------------------------------------------------------
/src/components/menu/menu.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 | import { MenuController } from './MenuController';
3 | import { MenuDirective } from './MenuDirective';
4 | import dropdown from './dropdown/dropdown';
5 |
6 | export default angular
7 | .module('dt.menu', [ dropdown.name ])
8 | .controller('MenuController', MenuController)
9 | .directive('dtm', MenuDirective);
10 |
--------------------------------------------------------------------------------
/src/components/menu/dropdown/DropdownMenuDirective.js:
--------------------------------------------------------------------------------
1 | export function DropdownMenuDirective($animate){
2 | return {
3 | restrict: 'C',
4 | require: '?^dropdown',
5 | link: function($scope, $elm, $attrs, ctrl) {
6 | $scope.$watch('open', () => {
7 | $animate[$scope.open ? 'addClass' : 'removeClass']($elm, 'ddm-open');
8 | });
9 | }
10 | };
11 | };
12 |
--------------------------------------------------------------------------------
/src/tests/frameworks.js:
--------------------------------------------------------------------------------
1 | import 'babel-core/polyfill';
2 | import chai from 'chai';
3 | import sinonChai from 'sinon-chai';
4 | import sinon from 'sinon';
5 | import chaiAsPromised from 'chai-as-promised';
6 |
7 | chai.use(chaiAsPromised);
8 | chai.use(sinonChai);
9 |
10 | chai.should();
11 |
12 | let expect = chai.expect;
13 |
14 | export default chai;
15 | export {expect, sinon};
16 |
--------------------------------------------------------------------------------
/src/components/menu/menu.less:
--------------------------------------------------------------------------------
1 | .dt-menu{
2 | text-align:left;
3 |
4 | ul, li{
5 | padding:0;
6 | margin:0;
7 | list-style:none;
8 | }
9 |
10 | .dropdown-menu{
11 | width:200px;
12 | display: none;
13 |
14 | &.ddm-open{
15 | display: inline-block;
16 | }
17 | }
18 |
19 | ul{
20 | max-height: 300px;
21 | overflow-y: auto;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/body/GroupRowController.js:
--------------------------------------------------------------------------------
1 | export class GroupRowController {
2 |
3 | onGroupToggled(evt){
4 | evt.stopPropagation();
5 | this.onGroupToggle({
6 | group: this.row
7 | });
8 | }
9 |
10 | treeClass(){
11 | return {
12 | 'dt-tree-toggle': true,
13 | 'icon-right': !this.expanded,
14 | 'icon-down': this.expanded
15 | };
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "env": {
4 | "browser": true
5 | },
6 | "rules": {
7 | "strict": 0,
8 | "no-multi-spaces": false,
9 | "quotes": "single",
10 | "no-shadow": false,
11 | "no-empty": false,
12 | "no-cond-assign": false,
13 | "no-loop-func": false,
14 | "no-underscore-dangle": false,
15 | "no-use-before-define": false
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/popover/popover.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 | import { PopoverDirective } from './PopoverDirective';
3 | import { PopoverRegistry } from './PopoverRegistry';
4 | import { PositionHelper } from './PositionHelper';
5 |
6 | export default angular
7 | .module('popover', [])
8 | .service('PopoverRegistry', PopoverRegistry)
9 | .factory('PositionHelper', PositionHelper)
10 | .directive('popover', PopoverDirective);
11 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '0.12'
4 |
5 | branches:
6 | only:
7 | - master
8 |
9 | before_install:
10 | - export CHROME_BIN=chromium-browser
11 | - "export DISPLAY=:99.0"
12 | - "sh -e /etc/init.d/xvfb start"
13 | - npm install -g gulp jspm # this is done on before install due to npm install script in `package.json`
14 |
15 | script:
16 | - gulp test
17 |
18 | after_success:
19 | - test $TRAVIS_TEST_RESULT = 0
20 | && gulp release
--------------------------------------------------------------------------------
/src/utils/keys.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Shortcut for key handlers
3 | * @type {Object}
4 | */
5 | export var KEYS = {
6 | BACKSPACE: 8,
7 | TAB: 9,
8 | RETURN: 13,
9 | ALT: 18,
10 | ESC: 27,
11 | SPACE: 32,
12 | PAGE_UP: 33,
13 | PAGE_DOWN: 34,
14 | END: 35,
15 | HOME: 36,
16 | LEFT: 37,
17 | UP: 38,
18 | RIGHT: 39,
19 | DOWN: 40,
20 | DELETE: 46,
21 | COMMA: 188,
22 | PERIOD: 190,
23 | A: 65,
24 | Z: 90,
25 | ZERO: 48,
26 | NUMPAD_0: 96,
27 | NUMPAD_9: 105
28 | };
29 |
--------------------------------------------------------------------------------
/src/components/menu/dropdown/dropdown.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 | import { DropdownController } from './DropdownController';
3 | import { DropdownDirective } from './DropdownDirective';
4 | import { DropdownToggleDirective } from './DropdownToggleDirective';
5 | import { DropdownMenuDirective } from './DropdownMenuDirective';
6 |
7 | export default angular
8 | .module('dt.dropdown', [])
9 | .controller('DropdownController', DropdownController)
10 | .directive('dropdown', DropdownDirective)
11 | .directive('dropdownToggle', DropdownToggleDirective)
12 | .directive('dropdownMenu', DropdownMenuDirective);
13 |
--------------------------------------------------------------------------------
/src/components/menu/MenuController.js:
--------------------------------------------------------------------------------
1 | export class MenuController{
2 |
3 | /*@ngInject*/
4 | constructor($scope, $timeout){
5 | this.$scope = $scope;
6 | }
7 |
8 | getColumnIndex(model){
9 | return this.$scope.current.findIndex((col) => {
10 | return model.name == col.name;
11 | });
12 | }
13 |
14 | isChecked(model){
15 | return this.getColumnIndex(model) > -1;
16 | }
17 |
18 | onCheck(model){
19 | var idx = this.getColumnIndex(model);
20 | if(idx === -1){
21 | this.$scope.current.push(model);
22 | } else {
23 | this.$scope.current.splice(idx, 1);
24 | }
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/components/menu/dropdown/DropdownToggleDirective.js:
--------------------------------------------------------------------------------
1 | export function DropdownToggleDirective($timeout){
2 | return {
3 | restrict: 'C',
4 | controller: 'DropdownController',
5 | require: '?^dropdown',
6 | link: function($scope, $elm, $attrs, ctrl) {
7 |
8 | function toggleClick(event) {
9 | event.preventDefault();
10 | $timeout(() => {
11 | ctrl.toggle($scope);
12 | });
13 | };
14 |
15 | function toggleDestroy(){
16 | $elm.unbind('click', toggleClick);
17 | };
18 |
19 | $elm.bind('click', toggleClick);
20 | $scope.$on('$destroy', toggleDestroy);
21 | }
22 | };
23 | };
24 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-data-table",
3 | "version": "0.7.0",
4 | "homepage": "https://github.com/Swimlane/angular-data-table",
5 | "author": {
6 | "name": "Swimlane",
7 | "email": "austin@swimlane.com",
8 | "web": "http://swimlane.com/"
9 | },
10 | "main": [
11 | "release/dataTable.js",
12 | "release/dataTable.css"
13 | ],
14 | "description": "A feature-rich but lightweight ES6 AngularJS Data Table crafted for large data sets!",
15 | "keywords": [
16 | "data-table",
17 | "material-design",
18 | "angular",
19 | "table",
20 | "grid"
21 | ],
22 | "license": "MIT",
23 | "ignore": [
24 | "**/.*",
25 | "jspm_packages",
26 | "node_modules",
27 | "bower_components",
28 | "test",
29 | "tests"
30 | ]
31 | }
32 |
--------------------------------------------------------------------------------
/src/components/popover/PopoverRegistry.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Registering to deal with popovers
3 | * @param {function} $animate
4 | */
5 | export function PopoverRegistry($animate){
6 | var popovers = {};
7 | this.add = function(id, object){
8 | popovers[id] = object;
9 | }
10 | this.find = function(id){
11 | popovers[id];
12 | }
13 | this.remove = function(id){
14 | delete popovers[id];
15 | }
16 | this.removeGroup = function(group, currentId){
17 | angular.forEach(popovers, function(popoverOb, id){
18 | if (id === currentId) return;
19 |
20 | if (popoverOb.group && popoverOb.group === group){
21 | $animate.removeClass(popoverOb.popover, 'sw-popover-animate').then(() => {
22 | popoverOb.popover.remove();
23 | delete popovers[id];
24 | });
25 | }
26 | });
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/src/components/footer/FooterController.js:
--------------------------------------------------------------------------------
1 | export class FooterController {
2 |
3 | /**
4 | * Creates an instance of the Footer Controller
5 | * @param {scope}
6 | * @return {[type]}
7 | */
8 | /*@ngInject*/
9 | constructor($scope){
10 | this.page = this.paging.offset + 1;
11 | $scope.$watch('footer.paging.offset', (newVal) => {
12 | this.offsetChanged(newVal)
13 | });
14 | }
15 |
16 | /**
17 | * The offset ( page ) changed externally, update the page
18 | * @param {new offset}
19 | */
20 | offsetChanged(newVal){
21 | this.page = newVal + 1;
22 | }
23 |
24 | /**
25 | * The pager was invoked
26 | * @param {scope}
27 | */
28 | onPaged(page){
29 | this.paging.offset = page - 1;
30 | this.onPage({
31 | offset: this.paging.offset,
32 | size: this.paging.size
33 | });
34 | }
35 |
36 | };
37 |
--------------------------------------------------------------------------------
/src/components/footer/FooterDirective.js:
--------------------------------------------------------------------------------
1 | import { FooterController } from './FooterController';
2 |
3 | export function FooterDirective(){
4 | return {
5 | restrict: 'E',
6 | controller: FooterController,
7 | controllerAs: 'footer',
8 | scope: true,
9 | bindToController: {
10 | paging: '=',
11 | onPage: '&'
12 | },
13 | template:
14 | `
`,
23 | replace: true
24 | };
25 | };
26 |
--------------------------------------------------------------------------------
/src/components/body/StyleTranslator.js:
--------------------------------------------------------------------------------
1 | import { TranslateXY } from '../../utils/translate';
2 |
3 | /**
4 | * This translates the dom position based on the model row index.
5 | * This only exists because Angular's binding process is too slow.
6 | */
7 | export class StyleTranslator{
8 |
9 | constructor(height){
10 | this.height = height;
11 | this.map = new Map();
12 | }
13 |
14 | /**
15 | * Update the rows
16 | * @param {Array} rows
17 | */
18 | update(rows){
19 | let n = 0;
20 | while (n <= this.map.size) {
21 | let dom = this.map.get(n);
22 | let model = rows[n];
23 | if(dom && model){
24 | TranslateXY(dom[0].style, 0, model.$$index * this.height);
25 | }
26 | n++;
27 | }
28 | }
29 |
30 | /**
31 | * Register the row
32 | * @param {int} idx
33 | * @param {dom} dom
34 | */
35 | register(idx, dom){
36 | this.map.set(idx, dom);
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/src/utils/translate.js:
--------------------------------------------------------------------------------
1 | import { GetVendorPrefixedName } from './vendorPrefixes';
2 | import { CamelCase } from './utils';
3 |
4 | // browser detection and prefixing tools
5 | var transform = GetVendorPrefixedName('transform'),
6 | backfaceVisibility = GetVendorPrefixedName('backfaceVisibility'),
7 | hasCSSTransforms = !!GetVendorPrefixedName('transform'),
8 | hasCSS3DTransforms = !!GetVendorPrefixedName('perspective'),
9 | ua = window.navigator.userAgent,
10 | isSafari = (/Safari\//).test(ua) && !(/Chrome\//).test(ua);
11 |
12 | export function TranslateXY(styles, x,y){
13 | if (hasCSSTransforms) {
14 | if (!isSafari && hasCSS3DTransforms) {
15 | styles[transform] = `translate3d(${x}px, ${y}px, 0)`;
16 | styles[backfaceVisibility] = 'hidden';
17 | } else {
18 | styles[CamelCase(transform)] = `translate(${x}px, ${y}px)`;
19 | }
20 | } else {
21 | styles.top = y + 'px';
22 | styles.left = x + 'px';
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function (config) {
2 | var configuration = {
3 | basePath: './src',
4 | browsers: ['Chrome'],
5 | frameworks: ['angular', 'mocha', 'sinon-chai', 'browserify', 'phantomjs-shim'],
6 | reporters: ['mocha'],
7 | angular: ['mocks'],
8 | files: [
9 | { pattern: './**/*.spec.js', watched: false }
10 | ],
11 | preprocessors: {
12 | './**/*.js': ['browserify']
13 | },
14 | browserify: {
15 | debug: true,
16 | transform: [
17 | ['babelify', { stage: 0 }]
18 | ]
19 | },
20 | customLaunchers: {
21 | ChromeTravis: {
22 | base: 'Chrome',
23 | flags: ['--no-sandbox']
24 | }
25 | },
26 | client: {
27 | mocha: {
28 | timeout: 20000
29 | }
30 | },
31 | browserDisconnectTimeout: 20000
32 | };
33 |
34 | if(process.env.TRAVIS){
35 | configuration.browsers = ['ChromeTravis'];
36 | configuration.reporters = ['dots'];
37 | }
38 | config.set(configuration);
39 | };
--------------------------------------------------------------------------------
/config.js:
--------------------------------------------------------------------------------
1 | System.config({
2 | baseURL: "",
3 | defaultJSExtensions: true,
4 | transpiler: "babel",
5 | babelOptions: {
6 | "optional": [
7 | "runtime"
8 | ]
9 | },
10 | paths: {
11 | "*": "dist/*.js",
12 | "github:*": "jspm_packages/github/*",
13 | "npm:*": "jspm_packages/npm/*"
14 | },
15 |
16 | map: {
17 | "angular": "npm:angular@1.4.0",
18 | "babel": "npm:babel-core@5.8.22",
19 | "babel-runtime": "npm:babel-runtime@5.8.20",
20 | "core-js": "npm:core-js@1.1.1",
21 | "github:jspm/nodelibs-process@0.1.1": {
22 | "process": "npm:process@0.10.1"
23 | },
24 | "npm:angular@1.4.0": {
25 | "process": "github:jspm/nodelibs-process@0.1.1"
26 | },
27 | "npm:babel-runtime@5.8.20": {
28 | "process": "github:jspm/nodelibs-process@0.1.1"
29 | },
30 | "npm:core-js@1.1.1": {
31 | "fs": "github:jspm/nodelibs-fs@0.1.2",
32 | "process": "github:jspm/nodelibs-process@0.1.1",
33 | "systemjs-json": "github:systemjs/plugin-json@0.1.0"
34 | }
35 | }
36 | });
37 |
--------------------------------------------------------------------------------
/src/utils/polyfill.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Array.prototype.find()
3 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
4 | */
5 | (function() {
6 | function polyfill(fnName) {
7 | if (!Array.prototype[fnName]) {
8 | Array.prototype[fnName] = function(predicate /*, thisArg */ ) {
9 | var i, len, test, thisArg = arguments[1];
10 |
11 | if (typeof predicate !== "function") {
12 | throw new TypeError();
13 | }
14 |
15 | test = !thisArg ? predicate : function() {
16 | return predicate.apply(thisArg, arguments);
17 | };
18 |
19 | for (i = 0, len = this.length; i < len; i++) {
20 | if (test(this[i], i, this) === true) {
21 | return fnName === "find" ? this[i] : i;
22 | }
23 | }
24 |
25 | if (fnName !== "find") {
26 | return -1;
27 | }
28 | };
29 | }
30 | }
31 |
32 | for (var i in {
33 | find: 1,
34 | findIndex: 1
35 | }) {
36 | polyfill(i);
37 | }
38 | }());
39 |
--------------------------------------------------------------------------------
/src/components/menu/dropdown/DropdownDirective.js:
--------------------------------------------------------------------------------
1 | export function DropdownDirective($document, $timeout){
2 | return {
3 | restrict: 'C',
4 | controller: 'DropdownController',
5 | link: function($scope, $elm, $attrs) {
6 |
7 | function closeDropdown(ev){
8 | if($elm[0].contains(ev.target) ) {
9 | return;
10 | }
11 |
12 | $timeout(() => {
13 | $scope.open = false;
14 | off();
15 | });
16 | };
17 |
18 | function keydown(ev){
19 | if (ev.which === 27) {
20 | $timeout(() => {
21 | $scope.open = false;
22 | off();
23 | });
24 | }
25 | };
26 |
27 | function off(){
28 | $document.unbind('click', closeDropdown);
29 | $document.unbind('keydown', keydown);
30 | };
31 |
32 | $scope.$watch('open', (newVal) => {
33 | if(newVal){
34 | $document.bind('click', closeDropdown);
35 | $document.bind('keydown', keydown);
36 | }
37 | });
38 |
39 | }
40 | };
41 | };
42 |
--------------------------------------------------------------------------------
/src/components/body/GroupRowDirective.js:
--------------------------------------------------------------------------------
1 | import { GroupRowController } from './GroupRowController';
2 | import { TranslateXY } from '../../utils/translate';
3 |
4 | export function GroupRowDirective(){
5 | return {
6 | restrict: 'E',
7 | controller: GroupRowController,
8 | controllerAs: 'group',
9 | bindToController: {
10 | row: '=',
11 | onGroupToggle: '&',
12 | expanded: '=',
13 | options: '='
14 | },
15 | scope: true,
16 | replace:true,
17 | template: `
18 |
19 |
21 |
22 |
23 |
24 |
`,
25 | link: function($scope, $elm, $attrs, ctrl){
26 | // inital render position
27 | TranslateXY($elm[0].style, 0, ctrl.row.$$index * ctrl.options.rowHeight);
28 |
29 | // register w/ the style translator
30 | ctrl.options.internal.styleTranslator.register($scope.$index, $elm);
31 | }
32 | };
33 | };
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Swimlane
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/components/DataTableService.spec.js:
--------------------------------------------------------------------------------
1 | import '../tests/frameworks';
2 | import {ObjectId} from '../utils/utils';
3 | import {DataTableService} from './DataTableService';
4 |
5 | var mock = angular.mock;
6 |
7 | describe('DataTableService', function () {
8 | before(() => {
9 | angular.module('DataTables.Mock', []);
10 | })
11 | beforeEach(mock.module('DataTables.Mock'));
12 |
13 | beforeEach(mock.module(($provide) => {
14 | $provide.constant('DataTableService', DataTableService);
15 | }));
16 |
17 | beforeEach(mock.inject((DataTableService, $parse) => {
18 | this.DataTableService = DataTableService;
19 | this.$parse = $parse;
20 | }));
21 |
22 | it('should build and save columns', () => {
23 | let id = ObjectId();
24 | let columnElements = [
25 | ` `,
26 | `{{monkey}} ---- {{$cell}} `
27 | ].map((el) => angular.element(el)[0]);
28 |
29 | this.DataTableService.saveColumns(id, columnElements);
30 | this.DataTableService.buildColumns({}, this.$parse);
31 |
32 | this.DataTableService.columns.should.have.property(id).and.have.length(2);
33 | });
34 |
35 | });
36 |
--------------------------------------------------------------------------------
/src/components/menu/MenuDirective.js:
--------------------------------------------------------------------------------
1 | export function MenuDirective(){
2 | return {
3 | restrict: 'E',
4 | controller: 'MenuController',
5 | controllerAs: 'dtm',
6 | scope: {
7 | current: '=',
8 | available: '='
9 | },
10 | template:
11 | ``
35 | };
36 | };
37 |
--------------------------------------------------------------------------------
/src/components/footer/PagerDirective.js:
--------------------------------------------------------------------------------
1 | import { PagerController } from './PagerController';
2 |
3 | export function PagerDirective(){
4 | return {
5 | restrict: 'E',
6 | controller: PagerController,
7 | controllerAs: 'pager',
8 | scope: true,
9 | bindToController: {
10 | page: '=',
11 | size: '=',
12 | count: '=',
13 | onPage: '&'
14 | },
15 | template:
16 | ``,
35 | replace: true
36 | };
37 | };
38 |
--------------------------------------------------------------------------------
/src/utils/vendorPrefixes.js:
--------------------------------------------------------------------------------
1 | import { CamelCase } from './utils';
2 |
3 | var cache = {},
4 | testStyle = document.createElement('div').style;
5 |
6 | function getWithPrefix(name) {
7 | for (var i = 0; i < prefixes.length; i++) {
8 | var prefixedName = prefixes[i] + name;
9 | if (prefixedName in testStyle) {
10 | return prefixedName;
11 | }
12 | }
13 | return null;
14 | }
15 |
16 | // Get Prefix
17 | // http://davidwalsh.name/vendor-prefix
18 | var prefix = (function () {
19 | var styles = window.getComputedStyle(document.documentElement, ''),
20 | pre = (Array.prototype.slice
21 | .call(styles)
22 | .join('')
23 | .match(/-(moz|webkit|ms)-/) || (styles.OLink === '' && ['', 'o'])
24 | )[1],
25 | dom = ('WebKit|Moz|MS|O').match(new RegExp('(' + pre + ')', 'i'))[1];
26 | return {
27 | dom: dom,
28 | lowercase: pre,
29 | css: '-' + pre + '-',
30 | js: pre[0].toUpperCase() + pre.substr(1)
31 | };
32 | })();
33 |
34 | /**
35 | * @param {string} property Name of a css property to check for.
36 | * @return {?string} property name supported in the browser, or null if not
37 | * supported.
38 | */
39 | export function GetVendorPrefixedName(property) {
40 | var name = CamelCase(property)
41 | if(!cache[name]){
42 | if(testStyle[prefix.css + property] !== undefined) {
43 | cache[name] = prefix.css + property;
44 | } else if(testStyle[property] !== undefined){
45 | cache[name] = property;
46 | }
47 | }
48 | return cache[name];
49 | }
50 |
--------------------------------------------------------------------------------
/src/components/body/RowController.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 | import { DeepValueGetter } from '../../utils/utils';
3 | import { TranslateXY } from '../../utils/translate';
4 |
5 | export class RowController {
6 |
7 | /**
8 | * Returns the value for a given column
9 | * @param {col}
10 | * @return {value}
11 | */
12 | getValue(col){
13 | if(!col.prop) return '';
14 | return DeepValueGetter(this.row, col.prop);
15 | }
16 |
17 | /**
18 | * Invoked when a cell triggers the tree toggle
19 | * @param {cell}
20 | */
21 | onTreeToggled(cell){
22 | this.onTreeToggle({
23 | cell: cell,
24 | row: this.row
25 | });
26 | }
27 |
28 | /**
29 | * Calculates the styles for a pin group
30 | * @param {group}
31 | * @return {styles object}
32 | */
33 | stylesByGroup( group){
34 | var styles = {
35 | width: this.columnWidths[group] + 'px'
36 | };
37 |
38 | if(group === 'left'){
39 | TranslateXY(styles, this.options.internal.offsetX, 0);
40 | } else if(group === 'right'){
41 | var offset = (((this.columnWidths.total - this.options.internal.innerWidth) -
42 | this.options.internal.offsetX) + this.options.internal.scrollBarWidth) * -1;
43 | TranslateXY(styles, offset, 0);
44 | }
45 |
46 | return styles;
47 | }
48 |
49 | /**
50 | * Invoked when the cell directive's checkbox changed state
51 | */
52 | onCheckboxChanged(ev){
53 | this.onCheckboxChange({
54 | $event: ev,
55 | row: this.row
56 | });
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/src/dataTable.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 | import './utils/polyfill';
3 |
4 | import { ResizableDirective } from './components/header/ResizableDirective';
5 | import { SortableDirective } from './components/header/SortableDirective';
6 | import { DataTableDirective } from './components/DataTableDirective';
7 | import { HeaderDirective } from './components/header/HeaderDirective';
8 | import { HeaderCellController } from './components/header/HeaderCellController';
9 | import { HeaderCellDirective } from './components/header/HeaderCellDirective';
10 | import { BodyDirective } from './components/body/BodyDirective';
11 | import { ScrollerDirective } from './components/body/ScrollerDirective';
12 | import { SelectionDirective } from './components/body/SelectionDirective';
13 | import { RowDirective } from './components/body/RowDirective';
14 | import { GroupRowDirective } from './components/body/GroupRowDirective';
15 | import { CellDirective } from './components/body/CellDirective';
16 | import { FooterDirective } from './components/footer/FooterDirective';
17 | import { PagerDirective } from './components/footer/PagerDirective';
18 |
19 | export default angular
20 | .module('data-table', [])
21 | .directive('dtable', DataTableDirective)
22 | .directive('resizable', ResizableDirective)
23 | .directive('sortable', SortableDirective)
24 | .directive('dtHeader', HeaderDirective)
25 | .directive('dtHeaderCell', HeaderCellDirective)
26 | .directive('dtBody', BodyDirective)
27 | .directive('dtScroller', ScrollerDirective)
28 | .directive('dtSeletion', SelectionDirective)
29 | .directive('dtRow', RowDirective)
30 | .directive('dtGroupRow', GroupRowDirective)
31 | .directive('dtCell', CellDirective)
32 | .directive('dtFooter', FooterDirective)
33 | .directive('dtPager', PagerDirective);
34 |
--------------------------------------------------------------------------------
/src/components/body/CellController.js:
--------------------------------------------------------------------------------
1 | export class CellController {
2 |
3 | /**
4 | * Calculates the styles for the Cell Directive
5 | * @return {styles object}
6 | */
7 | styles(){
8 | return {
9 | width: this.column.width + 'px',
10 | 'min-width': this.column.width + 'px'
11 | };
12 | }
13 |
14 | /**
15 | * Calculates the css classes for the cell directive
16 | * @param {column}
17 | * @return {class object}
18 | */
19 | cellClass(){
20 | var style = {
21 | 'dt-tree-col': this.column.isTreeColumn
22 | };
23 |
24 | if(this.column.className){
25 | style[this.column.className] = true;
26 | }
27 |
28 | return style;
29 | }
30 |
31 | /**
32 | * Calculates the tree class styles.
33 | * @return {css classes object}
34 | */
35 | treeClass(){
36 | return {
37 | 'dt-tree-toggle': true,
38 | 'icon-right': !this.expanded,
39 | 'icon-down': this.expanded
40 | }
41 | }
42 |
43 | /**
44 | * Invoked when the tree toggle button was clicked.
45 | * @param {event}
46 | */
47 | onTreeToggled(evt){
48 | evt.stopPropagation();
49 | this.expanded = !this.expanded;
50 | this.onTreeToggle({
51 | cell: {
52 | value: this.value,
53 | column: this.column,
54 | expanded: this.expanded
55 | }
56 | });
57 | }
58 |
59 | /**
60 | * Invoked when the checkbox was changed
61 | * @param {object} event
62 | */
63 | onCheckboxChanged(event){
64 | event.stopPropagation();
65 | this.onCheckboxChange({ $event: event });
66 | }
67 |
68 | /**
69 | * Returns the value in its fomatted form
70 | * @return {string} value
71 | */
72 | getValue(){
73 | var val = this.column.cellDataGetter ?
74 | this.column.cellDataGetter(this.value) : this.value;
75 |
76 | if(val === undefined || val === null) val = '';
77 | return val;
78 | }
79 |
80 | };
81 |
--------------------------------------------------------------------------------
/src/utils/throttle.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 |
3 | /**
4 | * Debounce helper
5 | * @param {function}
6 | * @param {int}
7 | * @param {boolean}
8 | */
9 | export function debounce(func, wait, immediate) {
10 | var timeout, args, context, timestamp, result;
11 | return function() {
12 | context = this;
13 | args = arguments;
14 | timestamp = new Date();
15 | var later = function() {
16 | var last = new Date() - timestamp;
17 | if (last < wait) {
18 | timeout = setTimeout(later, wait - last);
19 | } else {
20 | timeout = null;
21 | if (!immediate)
22 | result = func.apply(context, args);
23 | }
24 | };
25 | var callNow = immediate && !timeout;
26 | if (!timeout) {
27 | timeout = setTimeout(later, wait);
28 | }
29 | if (callNow)
30 | result = func.apply(context, args);
31 | return result;
32 | };
33 | };
34 |
35 | /**
36 | * Throttle helper
37 | * @param {function}
38 | * @param {boolean}
39 | * @param {object}
40 | */
41 | export function throttle(func, wait, options) {
42 | var context, args, result;
43 | var timeout = null;
44 | var previous = 0;
45 | options || (options = {});
46 | var later = function() {
47 | previous = options.leading === false ? 0 : new Date();
48 | timeout = null;
49 | result = func.apply(context, args);
50 | };
51 | return function() {
52 | var now = new Date();
53 | if (!previous && options.leading === false)
54 | previous = now;
55 | var remaining = wait - (now - previous);
56 | context = this;
57 | args = arguments;
58 | if (remaining <= 0) {
59 | clearTimeout(timeout);
60 | timeout = null;
61 | previous = now;
62 | result = func.apply(context, args);
63 | } else if (!timeout && options.trailing !== false) {
64 | timeout = setTimeout(later, remaining);
65 | }
66 | return result;
67 | };
68 | };
69 |
--------------------------------------------------------------------------------
/src/components/header/HeaderCellController.js:
--------------------------------------------------------------------------------
1 | import { NextSortDirection } from '../../utils/utils';
2 |
3 | export class HeaderCellController{
4 | /**
5 | * Calculates the styles for the header cell directive
6 | * @return {styles}
7 | */
8 | styles(){
9 | return {
10 | width: this.column.width + 'px',
11 | minWidth: this.column.minWidth + 'px',
12 | maxWidth: this.column.maxWidth + 'px',
13 | height: this.column.height + 'px'
14 | };
15 | }
16 |
17 | /**
18 | * Calculates the css classes for the header cell directive
19 | */
20 | cellClass(){
21 | var cls = {
22 | 'sortable': this.column.sortable,
23 | 'resizable': this.column.resizable
24 | };
25 |
26 | if(this.column.headerClassName){
27 | cls[this.column.headerClassName] = true;
28 | }
29 |
30 | return cls;
31 | }
32 |
33 | /**
34 | * Toggles the sorting on the column
35 | */
36 | onSorted(){
37 | if(this.column.sortable){
38 | this.column.sort = NextSortDirection(this.sortType, this.column.sort);
39 |
40 | if (this.column.sort === undefined){
41 | this.column.sortPriority = undefined;
42 | }
43 |
44 | this.onSort({
45 | column: this.column
46 | });
47 | }
48 | }
49 |
50 | /**
51 | * Toggles the css class for the sort button
52 | */
53 | sortClass(){
54 | return {
55 | 'sort-btn': true,
56 | 'sort-asc icon-down': this.column.sort === 'asc',
57 | 'sort-desc icon-up': this.column.sort === 'desc'
58 | };
59 | }
60 |
61 | /**
62 | * Updates the column width on resize
63 | * @param {width}
64 | * @param {column}
65 | */
66 | onResized(width, column){
67 | this.onResize({
68 | column: column,
69 | width: width
70 | });
71 | }
72 |
73 | /**
74 | * Invoked when the header cell directive checkbox was changed
75 | */
76 | onCheckboxChange(){
77 | this.onCheckboxChanged();
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/src/components/body/ScrollerDirective.js:
--------------------------------------------------------------------------------
1 | import { requestAnimFrame } from '../../utils/utils';
2 | import { StyleTranslator } from './StyleTranslator';
3 | import { TranslateXY } from '../../utils/translate';
4 |
5 | export function ScrollerDirective($timeout, $rootScope){
6 | return {
7 | restrict: 'E',
8 | require:'^dtBody',
9 | transclude: true,
10 | replace: true,
11 | template: `
`,
12 | link: function($scope, $elm, $attrs, ctrl){
13 | var ticking = false,
14 | lastScrollY = 0,
15 | lastScrollX = 0,
16 | parent = $elm.parent();
17 |
18 | ctrl.options.internal.styleTranslator =
19 | new StyleTranslator(ctrl.options.rowHeight);
20 |
21 | ctrl.options.internal.setYOffset = function(offsetY){
22 | parent[0].scrollTop = offsetY;
23 | };
24 |
25 | function update(){
26 | ctrl.options.internal.offsetY = lastScrollY;
27 | ctrl.options.internal.offsetX = lastScrollX;
28 | ctrl.updatePage();
29 |
30 | if(ctrl.options.scrollbarV){
31 | ctrl.getRows();
32 | }
33 |
34 | // https://github.com/Swimlane/angular-data-table/pull/74
35 | ctrl.options.$outer.$digest();
36 |
37 | ticking = false;
38 | };
39 |
40 | function requestTick() {
41 | if(!ticking) {
42 | requestAnimFrame(update);
43 | ticking = true;
44 | }
45 | };
46 |
47 | parent.on('scroll', function(ev) {
48 | lastScrollY = this.scrollTop;
49 | lastScrollX = this.scrollLeft;
50 | requestTick();
51 | });
52 |
53 | $scope.$on('$destroy', () => {
54 | parent.off('scroll');
55 | });
56 |
57 | $scope.scrollerStyles = function(){
58 | if(ctrl.options.scrollbarV){
59 | return {
60 | height: ctrl.count * ctrl.options.rowHeight + 'px'
61 | }
62 | }
63 | };
64 |
65 | }
66 | };
67 | };
68 |
--------------------------------------------------------------------------------
/src/components/popover/PositionHelper.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Position helper for the popover directive.
3 | */
4 | export function PositionHelper(){
5 | return {
6 |
7 | calculateVerticalAlignment: function(elDimensions, popoverDimensions, alignment){
8 | if (alignment === 'top'){
9 | return elDimensions.top;
10 | }
11 | if (alignment === 'bottom'){
12 | return elDimensions.top + elDimensions.height - popoverDimensions.height;
13 | }
14 | if (alignment === 'center'){
15 | return elDimensions.top + elDimensions.height/2 - popoverDimensions.height/2;
16 | }
17 | },
18 |
19 | calculateVerticalCaret: function(elDimensions, popoverDimensions, caretDimensions, alignment){
20 | if (alignment === 'top'){
21 | return elDimensions.height/2 - caretDimensions.height/2 - 1;
22 | }
23 | if (alignment === 'bottom'){
24 | return popoverDimensions.height - elDimensions.height/2 - caretDimensions.height/2 - 1;
25 | }
26 | if (alignment === 'center'){
27 | return popoverDimensions.height/2 - caretDimensions.height/2 - 1;
28 | }
29 | },
30 |
31 | calculateHorizontalCaret: function(elDimensions, popoverDimensions, caretDimensions, alignment){
32 | if (alignment === 'left'){
33 | return elDimensions.width/2 - caretDimensions.height/2 - 1;
34 | }
35 | if (alignment === 'right'){
36 | return popoverDimensions.width - elDimensions.width/2 - caretDimensions.height/2 - 1;
37 | }
38 | if (alignment === 'center'){
39 | return popoverDimensions.width/2 - caretDimensions.height/2 - 1;
40 | }
41 | },
42 |
43 | calculateHorizontalAlignment: function(elDimensions, popoverDimensions, alignment){
44 | if (alignment === 'left'){
45 | return elDimensions.left;
46 | }
47 | if (alignment === 'right'){
48 | return elDimensions.left + elDimensions.width - popoverDimensions.width;
49 | }
50 | if (alignment === 'center'){
51 | return elDimensions.left + elDimensions.width/2 - popoverDimensions.width/2;
52 | }
53 | }
54 |
55 | }
56 | };
57 |
--------------------------------------------------------------------------------
/src/components/header/ResizableDirective.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Resizable directive
3 | * http://stackoverflow.com/questions/18368485/angular-js-resizable-div-directive
4 | * @param {object}
5 | * @param {function}
6 | * @param {function}
7 | */
8 | export function ResizableDirective($document, $timeout){
9 | return {
10 | restrict: 'A',
11 | scope:{
12 | isResizable: '=resizable',
13 | minWidth: '=',
14 | maxWidth: '=',
15 | onResize: '&'
16 | },
17 | link: function($scope, $element, $attrs){
18 | if($scope.isResizable){
19 | $element.addClass('resizable');
20 | }
21 |
22 | var handle = angular.element(` `),
23 | parent = $element.parent(),
24 | prevScreenX;
25 |
26 | handle.on('mousedown', function(event) {
27 | if(!$element[0].classList.contains('resizable')) {
28 | return false;
29 | }
30 |
31 | event.stopPropagation();
32 | event.preventDefault();
33 |
34 | $document.on('mousemove', mousemove);
35 | $document.on('mouseup', mouseup);
36 | });
37 |
38 | function mousemove(event) {
39 | event = event.originalEvent || event;
40 |
41 | var width = parent[0].clientWidth,
42 | movementX = event.movementX || event.mozMovementX || (event.screenX - prevScreenX),
43 | newWidth = width + (movementX || 0);
44 |
45 | prevScreenX = event.screenX;
46 |
47 | if((!$scope.minWidth || newWidth >= $scope.minWidth) && (!$scope.maxWidth || newWidth <= $scope.maxWidth)){
48 | parent.css({
49 | width: newWidth + 'px'
50 | });
51 | }
52 | }
53 |
54 | function mouseup() {
55 | if ($scope.onResize) {
56 | $timeout(function () {
57 | let width = parent[0].clientWidth;
58 | if (width < $scope.minWidth){
59 | width = $scope.minWidth;
60 | }
61 | $scope.onResize({ width: width });
62 | });
63 | }
64 |
65 | $document.unbind('mousemove', mousemove);
66 | $document.unbind('mouseup', mouseup);
67 | }
68 |
69 | $element.append(handle);
70 | }
71 | };
72 | };
73 |
--------------------------------------------------------------------------------
/src/components/header/SortableDirective.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 |
3 | /**
4 | * Sortable Directive
5 | * http://jsfiddle.net/RubaXa/zLq5J/3/
6 | * https://jsfiddle.net/hrohxze0/6/
7 | * @param {function}
8 | */
9 | export function SortableDirective($timeout) {
10 | return {
11 | restrict: 'A',
12 | scope: {
13 | isSortable: '=sortable',
14 | onSortableSort: '&'
15 | },
16 | link: function($scope, $element, $attrs){
17 | var rootEl = $element[0], dragEl, nextEl, dropEl;
18 |
19 | function isbefore(a, b) {
20 | if (a.parentNode == b.parentNode) {
21 | for (var cur = a; cur; cur = cur.previousSibling) {
22 | if (cur === b) {
23 | return true;
24 | }
25 | }
26 | }
27 | return false;
28 | };
29 |
30 | function onDragEnter(e) {
31 | var target = e.target;
32 | if (isbefore(dragEl, target)) {
33 | target.parentNode.insertBefore(dragEl, target);
34 | } else if(target.nextSibling && target.hasAttribute('draggable')) {
35 | target.parentNode.insertBefore(dragEl, target.nextSibling.nextSibling);
36 | }
37 | };
38 |
39 | function onDragEnd(evt) {
40 | evt.preventDefault();
41 |
42 | dragEl.classList.remove('dt-clone');
43 |
44 | $element.off('dragend', onDragEnd);
45 | $element.off('dragenter', onDragEnter);
46 |
47 | if (nextEl !== dragEl.nextSibling) {
48 | $scope.onSortableSort({
49 | event: evt,
50 | columnId: angular.element(dragEl).attr('data-id')
51 | });
52 | }
53 | };
54 |
55 | function onDragStart(evt){
56 | if(!$scope.isSortable) return false;
57 | evt = evt.originalEvent || evt;
58 |
59 | dragEl = evt.target;
60 | nextEl = dragEl.nextSibling;
61 | dragEl.classList.add('dt-clone');
62 |
63 | evt.dataTransfer.effectAllowed = 'move';
64 | evt.dataTransfer.setData('Text', dragEl.textContent);
65 |
66 | $element.on('dragenter', onDragEnter);
67 | $element.on('dragend', onDragEnd);
68 | };
69 |
70 | $element.on('dragstart', onDragStart);
71 |
72 | $scope.$on('$destroy', () => {
73 | $element.off('dragstart', onDragStart);
74 | });
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/components/DataTableService.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 | import { ColumnDefaults } from '../defaults';
3 | import { CamelCase } from '../utils/utils';
4 |
5 | export let DataTableService = {
6 |
7 | // id: [ column defs ]
8 | columns: {},
9 | dTables: {},
10 |
11 | saveColumns(id, columnElms) {
12 | if (columnElms && columnElms.length) {
13 | let columnsArray = [].slice.call(columnElms);
14 | this.dTables[id] = columnsArray;
15 | }
16 | },
17 |
18 | /**
19 | * Create columns from elements
20 | * @param {array} columnElms
21 | */
22 | buildColumns(scope, parse) {
23 | //FIXME: Too many nested for loops. O(n3)
24 |
25 | // Iterate through each dTable
26 | angular.forEach(this.dTables, (columnElms, id) => {
27 | this.columns[id] = [];
28 |
29 | // Iterate through each column
30 | angular.forEach(columnElms, (c) => {
31 | let column = {};
32 |
33 | var visible = true;
34 | // Iterate through each attribute
35 | angular.forEach(c.attributes, (attr) => {
36 | let attrName = CamelCase(attr.name);
37 |
38 | // cuz putting className vs class on
39 | // a element feels weird
40 | switch (attrName) {
41 | case 'class':
42 | column.className = attr.value;
43 | break;
44 | case 'name':
45 | case 'prop':
46 | column[attrName] = attr.value;
47 | break;
48 | case 'headerRenderer':
49 | case 'cellRenderer':
50 | case 'cellDataGetter':
51 | column[attrName] = parse(attr.value);
52 | break;
53 | case 'visible':
54 | visible = parse(attr.value)(scope);
55 | break;
56 | default:
57 | column[attrName] = parse(attr.value)(scope);
58 | break;
59 | }
60 | });
61 |
62 | let header = c.getElementsByTagName('column-header');
63 | if(header.length){
64 | column.headerTemplate = header[0].innerHTML;
65 | c.removeChild(header[0])
66 | }
67 |
68 | if (c.innerHTML !== '') {
69 | column.template = c.innerHTML;
70 | }
71 |
72 | if (visible)
73 | this.columns[id].push(column);
74 | });
75 | });
76 |
77 | this.dTables = {};
78 | }
79 | };
80 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 | We would love for you to contribute to our project and help make it ever better! As a contributor, here are the guidelines we would like you to follow.
3 |
4 | ### Found an Issue?
5 | If you find a bug in the source code or a mistake in the documentation, you can help us by submitting an issue to our GitHub Repository. Including an issue reproduction (via CodePen, JsBin, Plunkr, etc.) is the absolute best way to help the team quickly diagnose the problem. Screenshots are also helpful.
6 |
7 | You can help the team even more and submit a Pull Request with a fix.
8 |
9 | ### Want a Feature?
10 | You can request a new feature by submitting an issue to our GitHub Repository. If you would like to implement a new feature, please submit an issue with a proposal for your work first, to be sure that we can use it. Please consider what kind of change it is:
11 |
12 | - For a Major Feature, first open an issue and outline your proposal so that it can be discussed. This will also allow us to better coordinate our efforts, prevent duplication of work, and help you to craft the change so that it is successfully accepted into the project.
13 | - Small Features can be crafted and directly submitted as a Pull Request.
14 |
15 | ### Issue Etiquette
16 | Before you submit an issue, search the archive, maybe your question was already answered.
17 |
18 | If your issue appears to be a bug, and hasn't been reported, open a new issue. Help us to maximize the effort we can spend fixing issues and adding new features by not reporting duplicate issues. Providing the following information will increase the chances of your issue being dealt with quickly:
19 |
20 | - Overview of the Issue - if an error is being thrown a non-minified stack trace helps
21 | - Angular and angular-data-table Versions - which versions of Angular and angular-data-table are affected
22 | - Motivation for or Use Case - explain what are you trying to do and why the current behavior is a bug for you
23 | - Browsers and Operating System - is this a problem with all browsers?
24 | - Reproduce the Error - provide a live example (using CodePen, JsBin, Plunker, etc.) or a unambiguous set of steps
25 | - Screenshots - Due to the visual nature of angular-data-table, screenshots can help the team triage issues far more quickly than a text descrption.
26 | - Related Issues - has a similar issue been reported before?
27 | - Suggest a Fix - if you can't fix the bug yourself, perhaps you can point to what might be causing the problem (line of code or commit)
28 |
--------------------------------------------------------------------------------
/src/components/header/HeaderController.js:
--------------------------------------------------------------------------------
1 | import { TranslateXY } from '../../utils/translate';
2 |
3 | export class HeaderController {
4 |
5 | /**
6 | * Returns the styles for the header directive.
7 | * @param {object} scope
8 | * @return {object} styles
9 | */
10 | styles() {
11 | return {
12 | width: this.options.internal.innerWidth + 'px',
13 | height: this.options.headerHeight + 'px'
14 | }
15 | }
16 |
17 | /**
18 | * Returns the inner styles for the header directive
19 | * @param {object} scope
20 | * @return {object} styles
21 | */
22 | innerStyles(){
23 | return {
24 | width: this.columnWidths.total + 'px'
25 | };
26 | }
27 |
28 | /**
29 | * Invoked when a column sort direction has changed
30 | * @param {object} scope
31 | * @param {object} column
32 | */
33 | onSorted(sortedColumn){
34 | if (this.options.sortType === 'single') {
35 | // if sort type is single, then only one column can be sorted at once,
36 | // so we set the sort to undefined for the other columns
37 | function unsortColumn(column) {
38 | if (column !== sortedColumn) {
39 | column.sort = undefined;
40 | }
41 | }
42 |
43 | this.columns.left.forEach(unsortColumn);
44 | this.columns.center.forEach(unsortColumn);
45 | this.columns.right.forEach(unsortColumn);
46 | }
47 |
48 | this.onSort({
49 | column: sortedColumn
50 | });
51 | }
52 |
53 | /**
54 | * Returns the styles by group for the headers.
55 | * @param {scope}
56 | * @param {group}
57 | * @return {styles object}
58 | */
59 | stylesByGroup(group){
60 | var styles = {
61 | width: this.columnWidths[group] + 'px'
62 | };
63 |
64 | if(group === 'center'){
65 | TranslateXY(styles, this.options.internal.offsetX * -1, 0);
66 | } else if(group === 'right'){
67 | var offset = (this.columnWidths.total - this.options.internal.innerWidth) *-1;
68 | TranslateXY(styles, offset, 0);
69 | }
70 |
71 | return styles;
72 | }
73 |
74 | /**
75 | * Invoked when the header cell directive's checkbox has changed.
76 | * @param {scope}
77 | */
78 | onCheckboxChanged(){
79 | this.onCheckboxChange();
80 | }
81 |
82 | /**
83 | * Occurs when a header cell directive triggered a resize
84 | * @param {object} scope
85 | * @param {object} column
86 | * @param {int} width
87 | */
88 | onResized(column, width){
89 | this.onResize({
90 | column: column,
91 | width: width
92 | });
93 | }
94 |
95 | };
96 |
--------------------------------------------------------------------------------
/src/components/header/HeaderCellDirective.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 | import { HeaderCellController } from './HeaderCellController';
3 |
4 | export function HeaderCellDirective($compile){
5 | return {
6 | restrict: 'E',
7 | controller: HeaderCellController,
8 | controllerAs: 'hcell',
9 | scope: true,
10 | bindToController: {
11 | options: '=',
12 | column: '=',
13 | onCheckboxChange: '&',
14 | onSort: '&',
15 | sortType: '=',
16 | onResize: '&',
17 | selected: '='
18 | },
19 | replace: true,
20 | template:
21 | ``,
42 | compile: function() {
43 | return {
44 | pre: function($scope, $elm, $attrs, ctrl) {
45 | let label = $elm[0].querySelector('.dt-header-cell-label'), cellScope;
46 |
47 | if(ctrl.column.headerTemplate || ctrl.column.headerRenderer){
48 | cellScope = ctrl.options.$outer.$new(false);
49 |
50 | // copy some props
51 | cellScope.$header = ctrl.column.name;
52 | cellScope.$index = $scope.$index;
53 | }
54 |
55 | if(ctrl.column.headerTemplate){
56 | let elm = angular.element(`${ctrl.column.headerTemplate.trim()} `);
57 | angular.element(label).append($compile(elm)(cellScope));
58 | } else if(ctrl.column.headerRenderer){
59 | let elm = angular.element(ctrl.column.headerRenderer($elm));
60 | angular.element(label).append($compile(elm)(cellScope)[0]);
61 | } else {
62 | let val = ctrl.column.name;
63 | if(val === undefined || val === null) val = '';
64 | label.textContent = val;
65 | }
66 | }
67 | }
68 | }
69 | };
70 | };
71 |
--------------------------------------------------------------------------------
/demos/cell-templates.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - Templates
11 |
12 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/demos/virtual.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - Fixed Virtual
11 |
12 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/src/components/body/BodyDirective.js:
--------------------------------------------------------------------------------
1 | import { BodyController } from './BodyController';
2 |
3 | export function BodyDirective($timeout){
4 | return {
5 | restrict: 'E',
6 | controller: BodyController,
7 | controllerAs: 'body',
8 | bindToController: {
9 | columns: '=',
10 | columnWidths: '=',
11 | rows: '=',
12 | options: '=',
13 | selected: '=?',
14 | expanded: '=?',
15 | onPage: '&',
16 | onTreeToggle: '&',
17 | onSelect: '&',
18 | onRowClick: '&',
19 | onRowDblClick: '&'
20 | },
21 | scope: true,
22 | template: `
23 |
31 |
32 |
33 |
41 |
42 |
60 |
61 |
62 |
65 |
66 |
69 |
70 |
`
71 | };
72 | };
73 |
--------------------------------------------------------------------------------
/src/components/body/CellDirective.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 | import { CellController } from './CellController';
3 |
4 | export function CellDirective($rootScope, $compile, $log, $timeout){
5 | return {
6 | restrict: 'E',
7 | controller: CellController,
8 | scope: true,
9 | controllerAs: 'cell',
10 | bindToController: {
11 | options: '=',
12 | value: '=',
13 | selected: '=',
14 | column: '=',
15 | row: '=',
16 | expanded: '=',
17 | hasChildren: '=',
18 | onTreeToggle: '&',
19 | onCheckboxChange: '&'
20 | },
21 | template:
22 | `
26 |
27 |
30 |
31 |
34 |
35 |
`,
36 | replace: true,
37 | compile: function() {
38 | return {
39 | pre: function($scope, $elm, $attrs, ctrl) {
40 | var content = angular.element($elm[0].querySelector('.dt-cell-content')), cellScope;
41 |
42 | // extend the outer scope onto our new cell scope
43 | if(ctrl.column.template || ctrl.column.cellRenderer){
44 | createCellScope();
45 | }
46 |
47 | $scope.$watch('cell.row', () => {
48 | if(cellScope){
49 | cellScope.$destroy();
50 |
51 | createCellScope();
52 |
53 | cellScope.$cell = ctrl.value;
54 | cellScope.$row = ctrl.row;
55 | cellScope.$column = ctrl.column;
56 | cellScope.$$watchers = null;
57 | }
58 |
59 | if(ctrl.column.template){
60 | content.empty();
61 | var elm = angular.element(`${ctrl.column.template.trim()} `);
62 | content.append($compile(elm)(cellScope));
63 | } else if(ctrl.column.cellRenderer){
64 | content.empty();
65 | var elm = angular.element(ctrl.column.cellRenderer(cellScope, content));
66 | content.append($compile(elm)(cellScope));
67 | } else {
68 | content[0].innerHTML = ctrl.getValue();
69 | }
70 |
71 | }, true);
72 |
73 | function createCellScope(){
74 | cellScope = ctrl.options.$outer.$new(false);
75 | cellScope.getValue = ctrl.getValue;
76 | }
77 | }
78 | }
79 | }
80 | };
81 | };
82 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-data-table",
3 | "title": "AngularJS Data-Table",
4 | "description": "A feature-rich but lightweight ES6 AngularJS Data Table crafted for large data sets!",
5 | "version": "0.8.1",
6 | "repository": {
7 | "type": "git",
8 | "url": "git@github.com:Swimlane/angular-data-table.git",
9 | "github": "http://github.com/swimlane/angular-data-table.git"
10 | },
11 | "main": "./release/dataTable.js",
12 | "homepage": "http://swimlane.com/",
13 | "author": {
14 | "name": "Swimlane",
15 | "email": "austin@swimlane.com",
16 | "web": "http://swimlane.com/"
17 | },
18 | "licenses": [
19 | {
20 | "type": "MIT",
21 | "url": "http://opensource.org/licenses/mit-license.php"
22 | }
23 | ],
24 | "devDependencies": {
25 | "angular": "^1.4.4",
26 | "angular-mocks": "^1.4.3",
27 | "babel-core": "^6.9.0",
28 | "babel-eslint": "^6.0.2",
29 | "babel-plugin-external-helpers": "^6.5.0",
30 | "babel-plugin-transform-es2015-modules-amd": "^6.8.0",
31 | "babel-plugin-transform-es2015-modules-commonjs": "^6.8.0",
32 | "babel-plugin-transform-es2015-modules-umd": "^6.8.0",
33 | "babel-preset-es2015": "^6.9.0",
34 | "babel-preset-stage-0": "^6.5.0",
35 | "babelify": "7.3.0",
36 | "browser-sync": "^2.7.2",
37 | "chai": "^4.2.0",
38 | "chai-as-promised": "^5.3.0",
39 | "del": "^1.2.0",
40 | "gulp": "^3.8.11",
41 | "gulp-babel": "6.1.2",
42 | "gulp-changed": "^1.2.1",
43 | "gulp-header": "^1.2.2",
44 | "gulp-less": "^3.0.3",
45 | "gulp-ng-annotate": "^1.0.0",
46 | "gulp-plumber": "^1.0.1",
47 | "gulp-rename": "^1.2.2",
48 | "gulp-replace": "^0.5.3",
49 | "gulp-uglify": "^1.2.0",
50 | "jspm": "^0.16.1",
51 | "karma": "^0.13.3",
52 | "karma-angular": "0.0.6",
53 | "karma-babel-preprocessor": "^5.2.1",
54 | "karma-browserify": "^4.2.1",
55 | "karma-chrome-launcher": "^0.2.0",
56 | "karma-mocha": "^0.2.0",
57 | "karma-mocha-reporter": "^1.0.3",
58 | "karma-phantomjs-launcher": "^0.2.0",
59 | "karma-phantomjs-shim": "^1.0.0",
60 | "karma-sinon-chai": "^1.0.0",
61 | "mocha": "^2.2.1",
62 | "natives": "^1.1.6",
63 | "phantomjs": "^1.9.17",
64 | "rollup": "^0.7.8",
65 | "run-sequence": "^1.1.0",
66 | "sinon": "^1.15.4",
67 | "sinon-chai": "^2.7.0",
68 | "systemjs-builder": "^0.11.3",
69 | "vinyl-paths": "^1.0.0"
70 | },
71 | "jspm": {
72 | "dependencies": {
73 | "angular": "npm:angular@^1.4.0"
74 | },
75 | "devDependencies": {
76 | "babel": "npm:babel-core@^5.8.22",
77 | "babel-runtime": "npm:babel-runtime@^5.6.4",
78 | "core-js": "npm:core-js@^1.1.1"
79 | }
80 | },
81 | "babel": {
82 | "sourceMaps": false,
83 | "presets": [
84 | "es2015",
85 | "stage-0"
86 | ],
87 | "moduleIds": false,
88 | "comments": false,
89 | "compact": false
90 | },
91 | "dependencies": {}
92 | }
93 |
--------------------------------------------------------------------------------
/demos/basic.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - Basic
11 |
12 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/demos/slow.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - Empty
11 |
12 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/demos/empty.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - Empty
11 |
12 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | Load Data
47 | Clear Data
48 |
49 |
50 |
51 |
52 |
53 |
54 |
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/src/components/footer/PagerController.js:
--------------------------------------------------------------------------------
1 | export class PagerController {
2 |
3 | /**
4 | * Creates an instance of the Pager Controller
5 | * @param {object} $scope
6 | */
7 | /*@ngInject*/
8 | constructor($scope){
9 | $scope.$watch('pager.count', (newVal) => {
10 | this.calcTotalPages(this.size, this.count);
11 | this.getPages(this.page || 1);
12 | });
13 |
14 | $scope.$watch('pager.size', (newVal) => {
15 | this.calcTotalPages(this.size, this.count);
16 | this.getPages(this.page || 1);
17 | });
18 |
19 | $scope.$watch('pager.page', (newVal) => {
20 | if (newVal !== 0 && newVal <= this.totalPages) {
21 | this.getPages(newVal);
22 | }
23 | });
24 |
25 | this.getPages(this.page || 1);
26 | }
27 |
28 | /**
29 | * Calculates the total number of pages given the count.
30 | * @return {int} page count
31 | */
32 | calcTotalPages(size, count) {
33 | var count = size < 1 ? 1 : Math.ceil(count / size);
34 | this.totalPages = Math.max(count || 0, 1);
35 | }
36 |
37 | /**
38 | * Select a page
39 | * @param {int} num
40 | */
41 | selectPage(num){
42 | if (num > 0 && num <= this.totalPages) {
43 | this.page = num;
44 | this.onPage({
45 | page: num
46 | });
47 | }
48 | }
49 |
50 | /**
51 | * Selects the previous pager
52 | */
53 | prevPage(){
54 | if (this.page > 1) {
55 | this.selectPage(--this.page);
56 | }
57 | }
58 |
59 | /**
60 | * Selects the next page
61 | */
62 | nextPage(){
63 | this.selectPage(++this.page);
64 | }
65 |
66 | /**
67 | * Determines if the pager can go previous
68 | * @return {boolean}
69 | */
70 | canPrevious(){
71 | return this.page > 1;
72 | }
73 |
74 | /**
75 | * Determines if the pager can go forward
76 | * @return {boolean}
77 | */
78 | canNext(){
79 | return this.page < this.totalPages;
80 | }
81 |
82 | /**
83 | * Gets the page set given the current page
84 | * @param {int} page
85 | */
86 | getPages(page) {
87 | var pages = [],
88 | startPage = 1,
89 | endPage = this.totalPages,
90 | maxSize = 5,
91 | isMaxSized = maxSize < this.totalPages;
92 |
93 | if (isMaxSized) {
94 | startPage = ((Math.ceil(page / maxSize) - 1) * maxSize) + 1;
95 | endPage = Math.min(startPage + maxSize - 1, this.totalPages);
96 | }
97 |
98 | for (var number = startPage; number <= endPage; number++) {
99 | pages.push({
100 | number: number,
101 | text: number,
102 | active: number === page
103 | });
104 | }
105 |
106 | /*
107 | if (isMaxSized) {
108 | if (startPage > 1) {
109 | pages.unshift({
110 | number: startPage - 1,
111 | text: '...'
112 | });
113 | }
114 |
115 | if (endPage < this.totalPages) {
116 | pages.push({
117 | number: endPage + 1,
118 | text: '...'
119 | });
120 | }
121 | }
122 | */
123 |
124 | this.pages = pages;
125 | }
126 |
127 | };
128 |
--------------------------------------------------------------------------------
/demos/scroll.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - H/V Scrolling
11 |
12 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/src/components/body/RowDirective.js:
--------------------------------------------------------------------------------
1 | import { RowController } from './RowController';
2 | import { TranslateXY } from '../../utils/translate';
3 |
4 | export function RowDirective(){
5 | return {
6 | restrict: 'E',
7 | controller: RowController,
8 | controllerAs: 'rowCtrl',
9 | scope: true,
10 | bindToController: {
11 | row: '=',
12 | columns: '=',
13 | columnWidths: '=',
14 | expanded: '=',
15 | selected: '=',
16 | hasChildren: '=',
17 | options: '=',
18 | onCheckboxChange: '&',
19 | onTreeToggle: '&'
20 | },
21 | link: function($scope, $elm, $attrs, ctrl){
22 | if(ctrl.row){
23 | // inital render position
24 | TranslateXY($elm[0].style, 0, ctrl.row.$$index * ctrl.options.rowHeight);
25 | }
26 |
27 | // register w/ the style translator
28 | ctrl.options.internal.styleTranslator.register($scope.$index, $elm);
29 | },
30 | template: `
31 |
32 |
35 |
45 |
46 |
47 |
49 |
59 |
60 |
61 |
64 |
74 |
75 |
76 |
`,
77 | replace:true
78 | };
79 | };
80 |
--------------------------------------------------------------------------------
/demos/greed.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - Greedy Columns
11 |
12 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
Native flexbox behavior
58 |
59 |
Name
60 |
Gender
61 |
Company
62 |
63 |
64 |
65 |
66 |
67 |
68 |
94 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/demos/action-links.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - Action Links
11 |
12 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | {{$cell}}
54 |
55 |
56 |
57 | DELETE |
59 | LOG
61 |
62 |
63 |
64 |
65 |
66 |
67 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/demos/grouping.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - Grouping
11 |
12 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | Group on Country
47 | Group on Year
48 |
49 |
50 |
51 |
52 |
53 |
54 |
97 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/demos/expressive.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - Expressive
11 |
12 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | {{monkey}} ---- {{$cell}}
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | {{v}} : {{$row.name}} : {{$cell}}
58 |
59 |
60 |
61 |
62 | Change External Value
63 |
64 |
65 |
66 |
67 |
105 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/demos/paging.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - Paging
11 |
12 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/src/components/popover/popover.less:
--------------------------------------------------------------------------------
1 | .popover{
2 | position: absolute;
3 | background: #fff;
4 | color: #999;
5 | z-index: 5000;
6 | opacity: 0;
7 | display: block;
8 | max-width:250px;
9 | max-height: 300px;
10 | overflow-y:auto;
11 |
12 | // border: 1px solid rgba(0,0,0,0.19);
13 | // -webkit-border-radius: 7px;
14 | // -moz-border-radius: 7px;
15 | // border-radius: 7px;
16 |
17 | -webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 2px 0 rgba(0, 0, 0, 0.24);
18 | -moz-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 2px 0 rgba(0, 0, 0, 0.24);
19 | box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 2px 0 rgba(0, 0, 0, 0.24);
20 |
21 | h1, h2, h3, h4, h5, h6{
22 | color: #272e33 !important;
23 | }
24 |
25 | .popover-caret{
26 | //background: url(images/caret.png);
27 | position: absolute;
28 | z-index: 5001;
29 | min-width: 6px;
30 | height: 11px;
31 |
32 | &.caret-right{
33 | }
34 |
35 | &.caret-left{
36 | -webkit-transform: rotate(180deg);
37 | -moz-transform: rotate(180deg);
38 | -ms-transform: rotate(180deg);
39 | -o-transform: rotate(180deg);
40 | transform: rotate(180deg);
41 | }
42 |
43 | &.caret-top{
44 | -webkit-transform: rotate(-90deg);
45 | -moz-transform: rotate(-90deg);
46 | -ms-transform: rotate(-90deg);
47 | -o-transform: rotate(-90deg);
48 | transform: rotate(-90deg);
49 | }
50 |
51 | &.caret-bottom{
52 | -webkit-transform: rotate(90deg);
53 | -moz-transform: rotate(90deg);
54 | -ms-transform: rotate(90deg);
55 | -o-transform: rotate(90deg);
56 | transform: rotate(90deg);
57 | }
58 | }
59 |
60 | header{
61 | padding: 15px 20px;
62 | h1{
63 | font-size: 16px;
64 | font-weight: 600;
65 | margin: 0;
66 | }
67 |
68 | a, a:hover, a:focus {
69 | color: #5ea1df;
70 | }
71 | }
72 |
73 | p {
74 | color: #999 !important;
75 | }
76 |
77 | .list{
78 | list-style-type: none;
79 | margin: 0px;
80 | padding: 0px;
81 |
82 | .main-text{
83 | display: block;
84 | color: #73777b;
85 | }
86 |
87 | .sub-text{
88 | color: #afafaf;
89 | font-size: 12px;
90 | }
91 |
92 | li{
93 | border-top: 1px solid #f1f1f1;
94 | margin:0;
95 | background: #fff;
96 |
97 | .list-link{
98 | padding: 10px 20px;
99 | display: block;
100 | cursor: pointer;
101 | }
102 |
103 | &:hover{
104 | background: #eff5fc;
105 | }
106 | }
107 |
108 | .inactive, .inactive:hover{
109 | padding: 10px 20px;
110 | background: none;
111 | }
112 | }
113 | }
114 |
115 | .popover-text{
116 | padding: 10px;
117 | color: #000;
118 | font-weight: 600;
119 | font-size: 12px;
120 | }
121 |
122 |
123 | .popover-right{
124 | -webkit-transform: translate3d(10px,0,0);
125 | transform: translate3d(10,0,0);
126 | }
127 |
128 | .popover-left{
129 | -webkit-transform: translate3d(-10px,0,0);
130 | transform: translate3d(-10px,0,0);
131 | }
132 |
133 | .popover-top{
134 | -webkit-transform: translate3d(0,-10px,0);
135 | transform: translate3d(0,-10px,0);
136 | }
137 |
138 | .popover-bottom{
139 | -webkit-transform: translate3d(0,10px,0);
140 | transform: translate3d(0,10px,0);
141 | }
142 |
143 | .popover-animation{
144 | opacity: 1;
145 | -webkit-transition: opacity 0.3s, -webkit-transform 0.3s;
146 | transition: opacity 0.3s, transform 0.3s;
147 | -webkit-transform: translate3d(0,0,0);
148 | transform: translate3d(0,0,0);
149 | }
150 |
--------------------------------------------------------------------------------
/demos/tabs.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - Basic
11 |
12 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | Tab 1
67 | Tab 2
68 |
69 |
70 |
71 | some content here
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
117 |
118 |
119 |
120 |
--------------------------------------------------------------------------------
/demos/virtual-paging.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - Paging
11 |
12 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
55 |
56 |
57 |
58 |
59 |
60 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/release/dataTable.css:
--------------------------------------------------------------------------------
1 | .dt {
2 | visibility: hidden;
3 | overflow: hidden;
4 | justify-content: center;
5 | position: relative;
6 | }
7 | .dt.dt-loaded {
8 | visibility: visible !important;
9 | }
10 | .dt *,
11 | .dt *:before,
12 | .dt *:after {
13 | -moz-box-sizing: border-box;
14 | -webkit-box-sizing: border-box;
15 | box-sizing: border-box;
16 | }
17 | .dt .dt-clone {
18 | opacity: .4;
19 | }
20 | .dt .dt-clone .dt-resize-handle {
21 | visibility: hidden;
22 | }
23 | .dt .dt-cell,
24 | .dt .dt-header-cell {
25 | vertical-align: top;
26 | overflow: hidden;
27 | white-space: nowrap;
28 | line-height: 1.625;
29 | text-overflow: ellipsis;
30 | touch-callout: none;
31 | -webkit-user-select: none;
32 | -moz-user-select: none;
33 | -ms-user-select: none;
34 | -o-user-select: none;
35 | user-select: none;
36 | }
37 | .dt .dt-row,
38 | .dt .dt-header-inner {
39 | display: -webkit-box;
40 | display: -moz-box;
41 | display: -ms-flexbox;
42 | display: -webkit-flex;
43 | display: flex;
44 | flex-direction: row;
45 | -webkit-flex-flow: row;
46 | -moz-flex-flow: row;
47 | -ms-flex-flow: row;
48 | -o-flex-flow: row;
49 | flex-flow: row;
50 | }
51 | .dt .dt-row-left,
52 | .dt .dt-row-right {
53 | z-index: 9;
54 | }
55 | .dt .dt-row-left,
56 | .dt .dt-row-center,
57 | .dt .dt-row-right {
58 | position: relative;
59 | }
60 | .dt .dt-header {
61 | overflow: hidden;
62 | }
63 | .dt .dt-header .dt-header-inner {
64 | white-space: nowrap;
65 | align-items: stretch;
66 | -webkit-align-items: stretch;
67 | }
68 | .dt .dt-header .dt-header-cell {
69 | position: relative;
70 | white-space: nowrap;
71 | display: inline-block;
72 | }
73 | .dt .dt-header .dt-header-cell.dt-drag-over {
74 | background: #EEE;
75 | }
76 | .dt .dt-header .dt-header-cell.sortable .dt-header-cell-label {
77 | cursor: pointer;
78 | }
79 | .dt .dt-header .dt-header-cell .sort-btn {
80 | visibility: hidden;
81 | display: inline-block;
82 | }
83 | .dt .dt-header .dt-header-cell .sort-btn.sort-desc,
84 | .dt .dt-header .dt-header-cell .sort-btn.sort-asc {
85 | visibility: visible;
86 | }
87 | .dt .dt-header .dt-header-cell .dt-resize-handle {
88 | display: inline-block;
89 | position: absolute;
90 | right: 0;
91 | top: 0;
92 | bottom: 0;
93 | width: 5px;
94 | padding: 0 5px;
95 | visibility: hidden;
96 | cursor: ew-resize;
97 | }
98 | .dt .dt-header .dt-header-cell.resizable:hover .dt-resize-handle {
99 | visibility: visible;
100 | }
101 | .dt .dt-header .dt-header-cell:last-child .dt-resize-handle {
102 | visibility: hidden !important;
103 | }
104 | .dt .dt-body {
105 | overflow: auto;
106 | position: relative;
107 | z-index: 10;
108 | }
109 | .dt .dt-body .dt-body-scroller {
110 | white-space: nowrap;
111 | }
112 | .dt .dt-body .dt-group-row {
113 | outline: none;
114 | }
115 | .dt .dt-body .dt-row {
116 | backface-visibility: hidden;
117 | outline: none;
118 | white-space: nowrap;
119 | }
120 | .dt .dt-body .dt-row > div {
121 | display: -webkit-box;
122 | display: -moz-box;
123 | display: -ms-flexbox;
124 | display: -webkit-flex;
125 | display: flex;
126 | }
127 | .dt .dt-body .dt-tree-toggle {
128 | cursor: pointer;
129 | }
130 | .dt .dt-footer .page-count {
131 | display: inline-block;
132 | }
133 | .dt .dt-footer .dt-pager {
134 | display: inline-block;
135 | float: right;
136 | }
137 | .dt .dt-footer .dt-pager .pager,
138 | .dt .dt-footer .dt-pager .pager li {
139 | padding: 0;
140 | margin: 0;
141 | list-style: none;
142 | }
143 | .dt .dt-footer .dt-pager .pager li,
144 | .dt .dt-footer .dt-pager .pager li a {
145 | display: inline-block;
146 | outline: none;
147 | }
148 | .dt.fixed .dt-body .dt-row,
149 | .dt.fixed .dt-body .dt-group-row {
150 | position: absolute;
151 | }
152 |
--------------------------------------------------------------------------------
/demos/force.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - Column Add / Remove
11 |
12 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | 3 Cols
53 | 7 Cols
54 |
55 |
56 |
57 |
58 |
59 |
60 |
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/demos/updating.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - Basic
11 |
12 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | Change data
49 |
50 |
51 |
52 |
53 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/demos/perf.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - Fixed Virtual
11 |
12 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/demos/sort.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - Sorting
11 |
12 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/demos/inline-editing.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - Templates
11 |
12 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | Double click a name to edit
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/demos/columnadd.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - Column Add / Remove
11 |
12 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/src/dataTable.less:
--------------------------------------------------------------------------------
1 | .dt{
2 |
3 | visibility: hidden;
4 | overflow:hidden;
5 |
6 | &.dt-loaded{
7 | visibility: visible !important;
8 | }
9 |
10 | *, *:before, *:after {
11 | -moz-box-sizing: border-box;
12 | -webkit-box-sizing: border-box;
13 | box-sizing: border-box;
14 | }
15 |
16 | justify-content: center;
17 | position: relative;
18 |
19 | .dt-clone{
20 | opacity: .4;
21 |
22 | .dt-resize-handle{
23 | visibility: hidden;
24 | }
25 | }
26 |
27 | .dt-cell,
28 | .dt-header-cell{
29 | vertical-align: top;
30 | overflow: hidden;
31 |
32 | //white-space: normal;
33 | white-space: nowrap;
34 | line-height: 1.625;
35 | text-overflow: ellipsis;
36 |
37 | touch-callout: none;
38 | -webkit-user-select: none;
39 | -moz-user-select: none;
40 | -ms-user-select: none;
41 | -o-user-select: none;
42 | user-select: none;
43 | }
44 |
45 | .dt-row,
46 | .dt-header-inner{
47 | display: -webkit-box;
48 | display: -moz-box;
49 | display: -ms-flexbox;
50 | display: -webkit-flex;
51 | display: flex;
52 |
53 | flex-direction: row;
54 | -webkit-flex-flow: row;
55 | -moz-flex-flow: row;
56 | -ms-flex-flow: row;
57 | -o-flex-flow: row;
58 | flex-flow: row;
59 | flex-flow: row;
60 | }
61 |
62 | .dt-row-left,
63 | .dt-row-right{
64 | z-index: 9;
65 | }
66 |
67 | .dt-row-left,
68 | .dt-row-center,
69 | .dt-row-right{
70 | position:relative;
71 | }
72 |
73 | .dt-header{
74 | overflow:hidden;
75 |
76 | .dt-header-inner{
77 | white-space: nowrap;
78 | align-items: stretch;
79 | -webkit-align-items: stretch;
80 | }
81 |
82 | .dt-header-cell{
83 | position:relative;
84 | white-space: nowrap;
85 | display:inline-block;
86 |
87 | &.dt-drag-over{
88 | background:#EEE;
89 | }
90 |
91 | &.sortable{
92 | .dt-header-cell-label{
93 | cursor: pointer;
94 | }
95 | }
96 |
97 | .sort-btn{
98 | visibility: hidden;
99 | display: inline-block;
100 |
101 | &.sort-desc,
102 | &.sort-asc{
103 | visibility: visible;
104 | }
105 | }
106 |
107 | .dt-resize-handle{
108 | display: inline-block;
109 | position: absolute;
110 | right:0;
111 | top:0;
112 | bottom: 0;
113 | width:5px;
114 | padding:0 5px;
115 | visibility: hidden;
116 | cursor: ew-resize;
117 | }
118 |
119 | &.resizable:hover {
120 | .dt-resize-handle{
121 | visibility: visible;
122 | }
123 | }
124 |
125 | &:last-child {
126 | .dt-resize-handle{
127 | visibility: hidden !important;
128 | }
129 | }
130 | }
131 | }
132 |
133 | .dt-body{
134 | overflow:auto;
135 | position:relative;
136 | z-index:10;
137 |
138 | .dt-body-scroller{
139 | white-space: nowrap;
140 | }
141 |
142 | .dt-group-row{
143 | outline:none;
144 | }
145 |
146 | .dt-row{
147 | backface-visibility: hidden;
148 | outline:none;
149 | white-space: nowrap;
150 |
151 | > div {
152 | display: -webkit-box;
153 | display: -moz-box;
154 | display: -ms-flexbox;
155 | display: -webkit-flex;
156 | display: flex;
157 | }
158 | }
159 |
160 | .dt-tree-toggle{
161 | cursor:pointer;
162 | }
163 | }
164 |
165 | .dt-footer{
166 | .page-count{
167 | display:inline-block;
168 | }
169 |
170 | .dt-pager{
171 | display: inline-block;
172 | float:right;
173 |
174 | .pager,
175 | .pager li {
176 | padding:0;
177 | margin:0;
178 | list-style:none;
179 | }
180 |
181 | .pager{
182 | li,
183 | li a{
184 | display:inline-block;
185 | outline:none;
186 | }
187 | }
188 | }
189 | }
190 |
191 | &.fixed {
192 | .dt-body .dt-row,
193 | .dt-body .dt-group-row{
194 | position: absolute;
195 | }
196 | }
197 | }
198 |
199 |
--------------------------------------------------------------------------------
/demos/single-select.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - Single Select
11 |
12 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
77 |
78 |
79 |
Selected item
80 | {{selectedz}}
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
125 |
126 |
127 |
128 |
--------------------------------------------------------------------------------
/demos/perf-horzscroll.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - Fixed Virtual
11 |
12 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
123 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/demos/checkboxes.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - Checkboxes
11 |
12 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
74 |
75 |
76 |
Selected list
77 | {{selected}}
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
123 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/src/defaults.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Default Table Options
3 | * @type {object}
4 | */
5 | export const TableDefaults = {
6 |
7 | // Enable vertical scrollbars
8 | scrollbarV: true,
9 |
10 | // Enable horz scrollbars
11 | // scrollbarH: true,
12 |
13 | // The row height, which is necessary
14 | // to calculate the height for the lazy rendering.
15 | rowHeight: 30,
16 |
17 | // flex
18 | // force
19 | // standard
20 | columnMode: 'standard',
21 |
22 | // Loading message presented when the array is undefined
23 | loadingMessage: 'Loading...',
24 |
25 | // Message to show when array is presented
26 | // but contains no values
27 | emptyMessage: 'No data to display',
28 |
29 | // The minimum header height in pixels.
30 | // pass falsey for no header
31 | headerHeight: 30,
32 |
33 | // The minimum footer height in pixels.
34 | // pass falsey for no footer
35 | footerHeight: 0,
36 |
37 | paging: {
38 | // if external paging is turned on
39 | externalPaging: false,
40 |
41 | // Page size
42 | size: undefined,
43 |
44 | // Total count
45 | count: 0,
46 |
47 | // Page offset
48 | offset: 0,
49 |
50 | // Loading indicator
51 | loadingIndicator: false,
52 |
53 | // template for the footer count text
54 | countText: function(count) {
55 | return `${count} total`;
56 | }
57 | },
58 |
59 | // if users can select itmes
60 | selectable: false,
61 |
62 | // if users can select mutliple items
63 | multiSelect: false,
64 |
65 | // checkbox selection vs row click
66 | checkboxSelection: false,
67 |
68 | // if you can reorder columns
69 | reorderable: true,
70 |
71 | internal: {
72 | offsetX: 0,
73 | offsetY: 0,
74 | innerWidth: 0,
75 | bodyHeight: 300
76 | },
77 |
78 | // flag if sorting shuld be handeled externally
79 | externalSorting: false
80 | };
81 |
82 | /**
83 | * Default Column Options
84 | * @type {object}
85 | */
86 | export const ColumnDefaults = {
87 |
88 | // pinned to the left
89 | frozenLeft: false,
90 |
91 | // pinned to the right
92 | frozenRight: false,
93 |
94 | // body cell css class name
95 | className: undefined,
96 |
97 | // header cell css class name
98 | headerClassName: undefined,
99 |
100 | // The grow factor relative to other columns. Same as the flex-grow
101 | // API from http://www.w3.org/TR/css3-flexbox/. Basically,
102 | // take any available extra width and distribute it proportionally
103 | // according to all columns' flexGrow values.
104 | flexGrow: 0,
105 |
106 | // Minimum width of the column.
107 | minWidth: 100,
108 |
109 | //Maximum width of the column.
110 | maxWidth: undefined,
111 |
112 | // The width of the column, by default (in pixels).
113 | width: 150,
114 |
115 | // If yes then the column can be resized, otherwise it cannot.
116 | resizable: true,
117 |
118 | // Custom sort comparator
119 | // pass false if you want to server sort
120 | comparator: undefined,
121 |
122 | // If yes then the column can be sorted.
123 | sortable: true,
124 |
125 | // Default sort asecending/descending for the column
126 | sort: undefined,
127 |
128 | // If you want to sort a column by a special property
129 | // See an example in demos/sort.html
130 | sortBy: undefined,
131 |
132 | // The cell renderer that returns content for table column header
133 | headerRenderer: undefined,
134 |
135 | // The cell renderer function(scope, elm) that returns React-renderable content for table cell.
136 | cellRenderer: undefined,
137 |
138 | // The getter function(value) that returns the cell data for the cellRenderer.
139 | // If not provided, the cell data will be collected from row data instead.
140 | cellDataGetter: undefined,
141 |
142 | // Adds +/- button and makes a secondary call to load nested data
143 | isTreeColumn: false,
144 |
145 | // Adds the checkbox selection to the column
146 | isCheckboxColumn: false,
147 |
148 | // Toggles the checkbox column in the header
149 | // for selecting all values given to the grid
150 | headerCheckbox: false,
151 |
152 | // Whether the column can automatically resize to fill space in the table.
153 | canAutoResize: true
154 |
155 | };
156 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid
11 |
12 |
50 |
51 |
52 |
53 | Table Demos
54 |
55 |
56 | Basic
57 |
71 |
72 | Templates
73 |
79 |
80 | Selection
81 |
86 |
87 | Width Options
88 |
92 |
93 | Grouping
94 |
99 |
100 | Performance
101 |
106 |
107 | Plugins
108 |
112 |
113 |
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/src/utils/utils.js:
--------------------------------------------------------------------------------
1 | import { ColumnTotalWidth } from './math';
2 |
3 | /**
4 | * Shim layer with setTimeout fallback
5 | * http://www.html5rocks.com/en/tutorials/speed/animations/
6 | */
7 | export var requestAnimFrame = (function(){
8 | return window.requestAnimationFrame ||
9 | window.webkitRequestAnimationFrame ||
10 | window.mozRequestAnimationFrame ||
11 | window.oRequestAnimationFrame ||
12 | window.msRequestAnimationFrame ||
13 | function( callback ){
14 | window.setTimeout(callback, 1000 / 60);
15 | };
16 | })();
17 |
18 | /**
19 | * Creates a unique object id.
20 | */
21 | export function ObjectId() {
22 | var timestamp = (new Date().getTime() / 1000 | 0).toString(16);
23 | return timestamp + 'xxxxxxxxxxxxxxxx'.replace(/[x]/g, function () {
24 | return (Math.random() * 16 | 0).toString(16);
25 | }).toLowerCase();
26 | };
27 |
28 | /**
29 | * Returns the columns by pin.
30 | * @param {array} colsumns
31 | */
32 | export function ColumnsByPin(cols){
33 | var ret = {
34 | left: [],
35 | center: [],
36 | right: []
37 | };
38 |
39 | for(var i=0, len=cols.length; i < len; i++) {
40 | var c = cols[i];
41 | if(c.frozenLeft){
42 | ret.left.push(c)
43 | } else if(c.frozenRight){
44 | ret.right.push(c);
45 | } else {
46 | ret.center.push(c);
47 | }
48 | }
49 |
50 | return ret;
51 | };
52 |
53 | /**
54 | * Returns the widths of all group sets of a column
55 | * @param {object} groups
56 | * @param {array} all
57 | */
58 | export function ColumnGroupWidths(groups, all){
59 | return {
60 | left: ColumnTotalWidth(groups.left),
61 | center: ColumnTotalWidth(groups.center),
62 | right: ColumnTotalWidth(groups.right),
63 | total: ColumnTotalWidth(all)
64 | };
65 | }
66 |
67 | /**
68 | * Returns a deep object given a string. zoo['animal.type']
69 | * @param {object} obj
70 | * @param {string} path
71 | */
72 | export function DeepValueGetter(obj, path) {
73 | if(!obj || !path) return obj;
74 |
75 | var current = obj,
76 | split = path.split('.');
77 |
78 | if(split.length){
79 | for(var i=0, len=split.length; i < len; i++) {
80 | current = current[split[i]];
81 | }
82 | }
83 |
84 | return current;
85 | };
86 |
87 | /**
88 | * Converts strings from something to camel case
89 | * http://stackoverflow.com/questions/10425287/convert-dash-separated-string-to-camelcase
90 | * @param {string} str
91 | * @return {string} camel case string
92 | */
93 | export function CamelCase(str) {
94 | // Replace special characters with a space
95 | str = str.replace(/[^a-zA-Z0-9 ]/g, " ");
96 | // put a space before an uppercase letter
97 | str = str.replace(/([a-z](?=[A-Z]))/g, '$1 ');
98 | // Lower case first character and some other stuff
99 | str = str.replace(/([^a-zA-Z0-9 ])|^[0-9]+/g, '').trim().toLowerCase();
100 | // uppercase characters preceded by a space or number
101 | str = str.replace(/([ 0-9]+)([a-zA-Z])/g, function(a,b,c) {
102 | return b.trim()+c.toUpperCase();
103 | });
104 | return str;
105 | };
106 |
107 |
108 | /**
109 | * Gets the width of the scrollbar. Nesc for windows
110 | * http://stackoverflow.com/a/13382873/888165
111 | * @return {int} width
112 | */
113 | export function ScrollbarWidth() {
114 | var outer = document.createElement("div");
115 | outer.style.visibility = "hidden";
116 | outer.style.width = "100px";
117 | outer.style.msOverflowStyle = "scrollbar";
118 | document.body.appendChild(outer);
119 |
120 | var widthNoScroll = outer.offsetWidth;
121 | outer.style.overflow = "scroll";
122 |
123 | var inner = document.createElement("div");
124 | inner.style.width = "100%";
125 | outer.appendChild(inner);
126 |
127 | var widthWithScroll = inner.offsetWidth;
128 | outer.parentNode.removeChild(outer);
129 |
130 | return widthNoScroll - widthWithScroll;
131 | };
132 |
133 | export function NextSortDirection(sortType, currentSort) {
134 | if (sortType === 'single') {
135 | if(currentSort === 'asc'){
136 | return 'desc';
137 | } else {
138 | return 'asc';
139 | }
140 | } else {
141 | if(!currentSort){
142 | return 'asc';
143 | } else if(currentSort === 'asc'){
144 | return 'desc';
145 | } else if(currentSort === 'desc'){
146 | return undefined;
147 | }
148 | }
149 | };
150 |
--------------------------------------------------------------------------------
/demos/transclude.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - Transclude
11 |
12 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | {{value}}
50 |
51 |
52 | {{value}} outerwrapCtrl.bar: {{ outerwrapCtrl.bar }} foo: {{ foo }}
53 | Set "New Bar"
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
125 |
126 |
127 |
128 |
--------------------------------------------------------------------------------
/src/components/header/HeaderDirective.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 | import { HeaderController } from './HeaderController';
3 |
4 | export function HeaderDirective($timeout){
5 | return {
6 | restrict: 'E',
7 | controller: HeaderController,
8 | controllerAs: 'header',
9 | scope: true,
10 | bindToController: {
11 | options: '=',
12 | columns: '=',
13 | columnWidths: '=',
14 | onSort: '&',
15 | onResize: '&',
16 | onCheckboxChange: '&'
17 | },
18 | template: `
19 | `,
71 | replace:true,
72 | link: function($scope, $elm, $attrs, ctrl){
73 |
74 | $scope.columnsResorted = function(event, columnId){
75 | var col = findColumnById(columnId),
76 | parent = angular.element(event.currentTarget),
77 | newIdx = -1;
78 |
79 | angular.forEach(parent.children(), (c, i) => {
80 | if (columnId === angular.element(c).attr('data-id')) {
81 | newIdx = i;
82 | }
83 | });
84 |
85 | $timeout(() => {
86 | angular.forEach(ctrl.columns, (group) => {
87 | var idx = group.indexOf(col);
88 | if(idx > -1){
89 |
90 | // this is tricky because we want to update the index
91 | // in the orig columns array instead of the grouped one
92 | var curColAtIdx = group[newIdx],
93 | siblingIdx = ctrl.options.columns.indexOf(curColAtIdx),
94 | curIdx = ctrl.options.columns.indexOf(col);
95 |
96 | ctrl.options.columns.splice(curIdx, 1);
97 | ctrl.options.columns.splice(siblingIdx, 0, col);
98 |
99 | return false;
100 | }
101 | });
102 |
103 | });
104 | }
105 |
106 | var findColumnById = function(columnId){
107 | var columns = ctrl.columns.left.concat(ctrl.columns.center).concat(ctrl.columns.right)
108 | return columns.find(function(c){
109 | return c.$id === columnId;
110 | })
111 | }
112 | }
113 | };
114 | };
115 |
--------------------------------------------------------------------------------
/src/components/body/SelectionController.js:
--------------------------------------------------------------------------------
1 | import { KEYS } from '../../utils/keys';
2 |
3 | export class SelectionController {
4 |
5 | /*@ngInject*/
6 | constructor($scope){
7 | this.body = $scope.body;
8 | this.options = $scope.body.options;
9 | this.selected = $scope.body.selected;
10 | }
11 |
12 | /**
13 | * Handler for the keydown on a row
14 | * @param {event}
15 | * @param {index}
16 | * @param {row}
17 | */
18 | keyDown(ev, index, row){
19 | if(KEYS[ev.keyCode]){
20 | ev.preventDefault();
21 | }
22 |
23 | if (ev.keyCode === KEYS.DOWN) {
24 | var next = ev.target.nextElementSibling;
25 | if(next){
26 | next.focus();
27 | }
28 | } else if (ev.keyCode === KEYS.UP) {
29 | var prev = ev.target.previousElementSibling;
30 | if(prev){
31 | prev.focus();
32 | }
33 | } else if(ev.keyCode === KEYS.RETURN){
34 | this.selectRow(index, row);
35 | }
36 | }
37 |
38 | /**
39 | * Handler for the row click event
40 | * @param {object} event
41 | * @param {int} index
42 | * @param {object} row
43 | */
44 | rowClicked(event, index, row){
45 | if(!this.options.checkboxSelection){
46 | // event.preventDefault();
47 | this.selectRow(event, index, row);
48 | }
49 |
50 | this.body.onRowClick({ row: row });
51 | }
52 |
53 | /**
54 | * Handler for the row double click event
55 | * @param {object} event
56 | * @param {int} index
57 | * @param {object} row
58 | */
59 | rowDblClicked(event, index, row){
60 | if(!this.options.checkboxSelection){
61 | event.preventDefault();
62 | this.selectRow(event, index, row);
63 | }
64 |
65 | this.body.onRowDblClick({ row: row });
66 | }
67 |
68 | /**
69 | * Invoked when a row directive's checkbox was changed.
70 | * @param {index}
71 | * @param {row}
72 | */
73 | onCheckboxChange(event, index, row){
74 | this.selectRow(event, index, row);
75 | }
76 |
77 | /**
78 | * Selects a row and places in the selection collection
79 | * @param {index}
80 | * @param {row}
81 | */
82 | selectRow(event, index, row){
83 | if(this.options.selectable){
84 | if(this.options.multiSelect){
85 | var isCtrlKeyDown = event.ctrlKey || event.metaKey,
86 | isShiftKeyDown = event.shiftKey;
87 |
88 | if(isShiftKeyDown){
89 | this.selectRowsBetween(index, row);
90 | } else {
91 | var idx = this.selected.indexOf(row);
92 | if(idx > -1){
93 | this.selected.splice(idx, 1);
94 | } else {
95 | if(this.options.multiSelectOnShift && this.selected.length === 1) {
96 | this.selected.splice(0, 1);
97 | }
98 | this.selected.push(row);
99 | this.body.onSelect({ rows: [ row ] });
100 | }
101 | }
102 | this.prevIndex = index;
103 | } else {
104 | this.selected = row;
105 | this.body.onSelect({ rows: [ row ] });
106 | }
107 | }
108 | }
109 |
110 | /**
111 | * Selects the rows between a index. Used for shift click selection.
112 | * @param {index}
113 | */
114 | selectRowsBetween(index){
115 | var reverse = index < this.prevIndex,
116 | selecteds = [];
117 |
118 | for(var i=0, len=this.body.rows.length; i < len; i++) {
119 | var row = this.body.rows[i],
120 | greater = i >= this.prevIndex && i <= index,
121 | lesser = i <= this.prevIndex && i >= index;
122 |
123 | var range = {};
124 | if ( reverse ) {
125 | range = {
126 | start: index,
127 | end: ( this.prevIndex - index )
128 | }
129 | } else {
130 | range = {
131 | start: this.prevIndex,
132 | end: index + 1
133 | }
134 | }
135 |
136 | if((reverse && lesser) || (!reverse && greater)){
137 | var idx = this.selected.indexOf(row);
138 | // if reverse shift selection (unselect) and the
139 | // row is already selected, remove it from selected
140 | if ( reverse && idx > -1 ) {
141 | this.selected.splice(idx, 1);
142 | continue;
143 | }
144 | // if in the positive range to be added to `selected`, and
145 | // not already in the selected array, add it
146 | if( i >= range.start && i < range.end ){
147 | if ( idx === -1 ) {
148 | this.selected.push(row);
149 | selecteds.push(row);
150 | }
151 | }
152 | }
153 | }
154 |
155 | this.body.onSelect({ rows: selecteds });
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/demos/tall.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - Tall
11 |
12 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/src/components/DataTableDirective.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 | import { DataTableController } from './DataTableController';
3 | import { ScrollbarWidth, ObjectId } from '../utils/utils';
4 | import { throttle } from '../utils/throttle';
5 | import { DataTableService } from './DataTableService';
6 |
7 | export function DataTableDirective($window, $timeout, $parse){
8 | return {
9 | restrict: 'E',
10 | replace: true,
11 | controller: DataTableController,
12 | scope: true,
13 | bindToController: {
14 | options: '=',
15 | rows: '=',
16 | selected: '=?',
17 | expanded: '=?',
18 | onSelect: '&',
19 | onSort: '&',
20 | onTreeToggle: '&',
21 | onPage: '&',
22 | onRowClick: '&',
23 | onRowDblClick: '&',
24 | onColumnResize: '&'
25 | },
26 | controllerAs: 'dt',
27 | template: function(element){
28 | // Gets the column nodes to transposes to column objects
29 | // http://stackoverflow.com/questions/30845397/angular-expressive-directive-design/30847609#30847609
30 | var columns = element[0].getElementsByTagName('column'),
31 | id = ObjectId();
32 | DataTableService.saveColumns(id, columns);
33 |
34 | return `
35 |
43 |
44 |
55 |
56 |
60 |
61 |
`
62 | },
63 | compile: function(tElem, tAttrs){
64 | return {
65 | pre: function($scope, $elm, $attrs, ctrl){
66 | DataTableService.buildColumns($scope, $parse);
67 |
68 | // Check and see if we had expressive columns
69 | // and if so, lets use those
70 | var id = $elm.attr('data-column-id'),
71 | columns = DataTableService.columns[id];
72 | if (columns) {
73 | ctrl.options.columns = columns;
74 | }
75 |
76 | ctrl.transposeColumnDefaults();
77 | ctrl.options.internal.scrollBarWidth = ScrollbarWidth();
78 |
79 | /**
80 | * Invoked on init of control or when the window is resized;
81 | */
82 | function resize() {
83 | var rect = $elm[0].getBoundingClientRect();
84 |
85 | ctrl.options.internal.innerWidth = Math.floor(rect.width);
86 |
87 | if (ctrl.options.scrollbarV) {
88 | var height = rect.height;
89 |
90 | if (ctrl.options.headerHeight) {
91 | height = height - ctrl.options.headerHeight;
92 | }
93 |
94 | if (ctrl.options.footerHeight) {
95 | height = height - ctrl.options.footerHeight;
96 | }
97 |
98 | ctrl.options.internal.bodyHeight = height;
99 | ctrl.calculatePageSize();
100 | }
101 |
102 | ctrl.adjustColumns();
103 | };
104 |
105 | angular.element($window).on('resize', throttle(() => {
106 | $timeout(resize);
107 | }));
108 |
109 | // When an item is hidden for example
110 | // in a tab with display none, the height
111 | // is not calculated correrctly. We need to watch
112 | // the visible attribute and resize if this occurs
113 | var checkVisibility = function() {
114 | var bounds = $elm[0].getBoundingClientRect(),
115 | visible = bounds.width && bounds.height;
116 | if (visible) resize();
117 | else $timeout(checkVisibility, 100);
118 | };
119 | checkVisibility();
120 |
121 | // add a loaded class to avoid flickering
122 | $elm.addClass('dt-loaded');
123 |
124 | // prevent memory leaks
125 | $scope.$on('$destroy', () => {
126 | angular.element($window).off('resize');
127 | });
128 | }
129 | };
130 | }
131 | };
132 | };
133 |
--------------------------------------------------------------------------------
/demos/filters.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - Basic
11 |
12 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
47 |
48 |
52 |
53 |
54 | {{$header}}
55 |
61 |
62 | {{$cell}}
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
106 |
107 |
145 |
146 |
147 |
148 |
--------------------------------------------------------------------------------
/src/utils/math.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 | import { ColumnsByPin, ColumnGroupWidths } from './utils';
3 |
4 | /**
5 | * Calculates the total width of all columns and their groups
6 | * @param {array} columns
7 | * @param {string} property width to get
8 | */
9 | export function ColumnTotalWidth(columns, prop) {
10 | var totalWidth = 0;
11 |
12 | columns.forEach((c) => {
13 | var has = prop && c[prop];
14 | totalWidth = totalWidth + (has ? c[prop] : c.width);
15 | });
16 |
17 | return totalWidth;
18 | }
19 |
20 | /**
21 | * Calculates the Total Flex Grow
22 | * @param {array}
23 | */
24 | export function GetTotalFlexGrow(columns){
25 | var totalFlexGrow = 0;
26 |
27 | for (let c of columns) {
28 | totalFlexGrow += c.flexGrow || 0;
29 | }
30 |
31 | return totalFlexGrow;
32 | }
33 |
34 | /**
35 | * Adjusts the column widths.
36 | * Inspired by: https://github.com/facebook/fixed-data-table/blob/master/src/FixedDataTableWidthHelper.js
37 | * @param {array} all columns
38 | * @param {int} width
39 | */
40 | export function AdjustColumnWidths(allColumns, expectedWidth){
41 | var columnsWidth = ColumnTotalWidth(allColumns),
42 | totalFlexGrow = GetTotalFlexGrow(allColumns),
43 | colsByGroup = ColumnsByPin(allColumns);
44 |
45 | if (columnsWidth !== expectedWidth){
46 | ScaleColumns(colsByGroup, expectedWidth, totalFlexGrow);
47 | }
48 | }
49 |
50 | /**
51 | * Resizes columns based on the flexGrow property, while respecting manually set widths
52 | * @param {array} colsByGroup
53 | * @param {int} maxWidth
54 | * @param {int} totalFlexGrow
55 | */
56 | function ScaleColumns(colsByGroup, maxWidth, totalFlexGrow) {
57 | // calculate total width and flexgrow points for coulumns that can be resized
58 | angular.forEach(colsByGroup, (cols) => {
59 | cols.forEach((column) => {
60 | if (!column.canAutoResize){
61 | maxWidth -= column.width;
62 | totalFlexGrow -= column.flexGrow;
63 | } else {
64 | column.width = 0;
65 | }
66 | });
67 | });
68 |
69 | var hasMinWidth = {}
70 | var remainingWidth = maxWidth;
71 |
72 | // resize columns until no width is left to be distributed
73 | do {
74 | let widthPerFlexPoint = remainingWidth / totalFlexGrow;
75 | remainingWidth = 0;
76 | angular.forEach(colsByGroup, (cols) => {
77 | cols.forEach((column, i) => {
78 | // if the column can be resize and it hasn't reached its minimum width yet
79 | if (column.canAutoResize && !hasMinWidth[i]){
80 | let newWidth = column.width + column.flexGrow * widthPerFlexPoint;
81 | if (column.minWidth !== undefined && newWidth < column.minWidth){
82 | remainingWidth += newWidth - column.minWidth;
83 | column.width = column.minWidth;
84 | hasMinWidth[i] = true;
85 | } else {
86 | column.width = newWidth;
87 | }
88 | }
89 | });
90 | });
91 | } while (remainingWidth !== 0);
92 |
93 | }
94 |
95 | /**
96 | * Forces the width of the columns to
97 | * distribute equally but overflowing when nesc.
98 | *
99 | * Rules:
100 | *
101 | * - If combined withs are less than the total width of the grid,
102 | * proporation the widths given the min / max / noraml widths to fill the width.
103 | *
104 | * - If the combined widths, exceed the total width of the grid,
105 | * use the standard widths.
106 | *
107 | * - If a column is resized, it should always use that width
108 | *
109 | * - The proporational widths should never fall below min size if specified.
110 | *
111 | * - If the grid starts off small but then becomes greater than the size ( + / - )
112 | * the width should use the orginial width; not the newly proporatied widths.
113 | *
114 | * @param {array} allColumns
115 | * @param {int} expectedWidth
116 | */
117 | export function ForceFillColumnWidths(allColumns, expectedWidth, startIdx){
118 | var contentWidth = 0,
119 | columnsToResize = startIdx > -1 ?
120 | allColumns.slice(startIdx, allColumns.length).filter((c) => { return c.canAutoResize }) :
121 | allColumns.filter((c) => { return c.canAutoResize });
122 |
123 | allColumns.forEach((c) => {
124 | if(!c.canAutoResize){
125 | contentWidth += c.width;
126 | } else {
127 | contentWidth += (c.$$oldWidth || c.width);
128 | }
129 | });
130 |
131 | var remainingWidth = expectedWidth - contentWidth,
132 | additionWidthPerColumn = remainingWidth / columnsToResize.length,
133 | exceedsWindow = contentWidth > expectedWidth;
134 |
135 | columnsToResize.forEach((column) => {
136 | if(exceedsWindow){
137 | column.width = column.$$oldWidth || column.width;
138 | } else {
139 | if(!column.$$oldWidth){
140 | column.$$oldWidth = column.width;
141 | }
142 |
143 | var newSize = column.$$oldWidth + additionWidthPerColumn;
144 | if(column.minWith && newSize < column.minWidth){
145 | column.width = column.minWidth;
146 | } else if(column.maxWidth && newSize > column.maxWidth){
147 | column.width = column.maxWidth;
148 | } else {
149 | column.width = newSize;
150 | }
151 | }
152 | });
153 | }
154 |
--------------------------------------------------------------------------------
/demos/pins.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - Column Pinning
11 |
12 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
Pinning Options
63 |
64 |
67 | Pin 'Gender'
68 |
69 |
70 |
71 |
72 |
73 |
74 |
148 |
149 |
150 |
151 |
--------------------------------------------------------------------------------
/demos/tooltip.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Datagrid - Tooltips
11 |
12 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var nPath = require('path');
2 | var gulp = require('gulp');
3 | var plumber = require('gulp-plumber');
4 | var babel = require('gulp-babel');
5 | var browserSync = require('browser-sync');
6 | var runSequence = require('run-sequence');
7 | var less = require('gulp-less');
8 | var changed = require('gulp-changed');
9 | var Builder = require('systemjs-builder');
10 | var vinylPaths = require('vinyl-paths');
11 | var del = require('del');
12 | var ngAnnotate = require('gulp-ng-annotate');
13 | var rollup = require('rollup');
14 | var rename = require('gulp-rename');
15 | var uglify = require('gulp-uglify');
16 | var header = require('gulp-header');
17 |
18 | var KarmaServer = require('karma').Server;
19 |
20 | var path = {
21 | source: 'src/**/*.js',
22 | less: 'src/**/*.less',
23 | output: 'dist/',
24 | release: 'release/',
25 | outputCss: 'dist/**/*.css'
26 | };
27 |
28 | var pkg = require('./package.json');
29 |
30 | var banner = ['/**',
31 | ' * <%= pkg.name %> - <%= pkg.description %>',
32 | ' * @version v<%= pkg.version %>',
33 | ' * @link <%= pkg.homepage %>',
34 | ' * @license <%= pkg.license %>',
35 | ' */',
36 | ''].join('\n');
37 |
38 | //
39 | // Compile Tasks
40 | // ------------------------------------------------------------
41 | gulp.task('es6', function () {
42 | return gulp.src(path.source)
43 | .pipe(plumber())
44 | .pipe(changed(path.output, { extension: '.js' }))
45 | .pipe(babel())
46 | .pipe(ngAnnotate({
47 | gulpWarnings: false
48 | }))
49 | .pipe(gulp.dest(path.output))
50 | .pipe(browserSync.reload({ stream: true }));
51 | });
52 |
53 | gulp.task('less', function () {
54 | return gulp.src(path.less)
55 | .pipe(changed(path.output, { extension: '.css' }))
56 | .pipe(plumber())
57 | .pipe(less())
58 | .pipe(gulp.dest(path.output))
59 | .pipe(browserSync.reload({ stream: true }));
60 | });
61 |
62 | gulp.task('clean', function () {
63 | return gulp.src([path.output, path.release])
64 | .pipe(vinylPaths(del));
65 | });
66 |
67 | gulp.task('compile', function (callback) {
68 | return runSequence(
69 | ['less', 'es6'],
70 | callback
71 | );
72 | });
73 |
74 | //
75 | // Dev Mode Tasks
76 | // ------------------------------------------------------------
77 | gulp.task('serve', ['compile'], function (done) {
78 | browserSync({
79 | open: false,
80 | port: 9000,
81 | server: {
82 | baseDir: ['.'],
83 | middleware: function (req, res, next) {
84 | res.setHeader('Access-Control-Allow-Origin', '*');
85 | next();
86 | }
87 | }
88 | }, done);
89 | });
90 |
91 | gulp.task('watch', ['serve'], function () {
92 | var watcher = gulp.watch([path.source, path.less, '*.html'], ['compile']);
93 | watcher.on('change', function (event) {
94 | console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
95 | });
96 | });
97 |
98 | //
99 | // Release Tasks
100 | // ------------------------------------------------------------
101 |
102 | gulp.task('release', function (callback) {
103 | return runSequence(
104 | 'clean',
105 | ['release-less', 'release-build'],
106 | 'release-umd',
107 | 'release-common',
108 | 'release-es6-min',
109 | callback
110 | );
111 | });
112 |
113 | gulp.task('release-less', function () {
114 | return gulp.src(['src/themes/*.less', 'src/dataTable.less'])
115 | .pipe(less())
116 | .pipe(gulp.dest(path.release));
117 | });
118 |
119 | gulp.task('release-build', function () {
120 | return rollup.rollup({
121 | entry: 'src/dataTable.js',
122 | external: ['angular']
123 | }).then(function (bundle) {
124 | return bundle.write({
125 | dest: 'release/dataTable.es6.js',
126 | format: 'es6',
127 | moduleName: 'DataTable'
128 | });
129 | });
130 | });
131 |
132 | gulp.task('release-umd', function () {
133 | return gulp.src('release/dataTable.es6.js')
134 | .pipe(babel({
135 | plugins: [
136 | "transform-es2015-modules-umd"
137 | ],
138 | moduleId: 'DataTable'
139 | }))
140 | .pipe(ngAnnotate({
141 | gulpWarnings: false
142 | }))
143 | .pipe(header(banner, { pkg: pkg }))
144 | .pipe(rename('dataTable.js'))
145 | .pipe(gulp.dest("release/"))
146 | });
147 |
148 | gulp.task('release-common', function () {
149 | return gulp.src('release/dataTable.es6.js')
150 | .pipe(babel({
151 | plugins: [
152 | "transform-es2015-modules-commonjs"
153 | ],
154 | moduleId: 'DataTable'
155 | }))
156 | .pipe(ngAnnotate({
157 | gulpWarnings: false
158 | }))
159 | .pipe(header(banner, { pkg: pkg }))
160 | .pipe(rename('dataTable.cjs.js'))
161 | .pipe(gulp.dest("release/"))
162 | });
163 |
164 | gulp.task('release-es6-min', function () {
165 | return gulp.src('release/dataTable.es6.js')
166 | .pipe(babel({
167 | plugins: [
168 | "transform-es2015-modules-umd"
169 | ],
170 | moduleId: 'DataTable'
171 | }))
172 | .pipe(ngAnnotate({
173 | gulpWarnings: false
174 | }))
175 | .pipe(uglify())
176 | .pipe(header(banner, { pkg: pkg }))
177 | .pipe(rename('dataTable.min.js'))
178 | .pipe(gulp.dest("release/"))
179 | });
180 |
181 |
182 | //
183 | // Test Tasks
184 | // ------------------------------------------------------------
185 |
186 | gulp.task('test', ['compile'], function (done) {
187 | var server = new KarmaServer({
188 | configFile: nPath.join(__dirname, 'karma.conf.js'),
189 | singleRun: true
190 | }, function () {
191 | done();
192 | });
193 |
194 | server.start();
195 | });
196 |
197 | gulp.task('test-watch', ['compile'], function (done) {
198 | var server = new KarmaServer({
199 | configFile: nPath.join(__dirname, 'karma.conf.js'),
200 | singleRun: false
201 | }, function () {
202 | done();
203 | });
204 |
205 | server.start();
206 | });
207 |
--------------------------------------------------------------------------------