Compare commits

...

40 Commits

Author SHA1 Message Date
263f7cad77 Update README.md
All checks were successful
Build / Build NPM Project (push) Successful in 29s
Build / Tag Version (push) Successful in 3s
2023-12-16 00:06:45 +00:00
eea71c930e Update .github/workflows/build.yaml
All checks were successful
Build / Tag Version (push) Successful in 7s
Build / Build NPM Project (push) Successful in 1m24s
2023-12-15 04:36:43 +00:00
f630fa677f Update package.json
All checks were successful
Build / Build NPM Project (push) Successful in 1m18s
Build / Tag Version (push) Successful in 5s
2023-12-15 04:31:44 +00:00
3dbb712260 Update package.json
Some checks failed
Build / Build NPM Project (push) Failing after 9s
Build / Tag Version (push) Has been skipped
2023-12-15 04:28:49 +00:00
7830d08332 Update .github/workflows/build.yaml
Some checks failed
Build / Tag Version (push) Successful in 9s
Build / Build NPM Project (push) Failing after 1m26s
2023-12-15 03:25:38 +00:00
acbaff4428 Update .github/workflows/build.yaml
Some checks failed
Build / Tag Version (push) Successful in 5s
Build / Build NPM Project (push) Failing after 1m18s
2023-12-15 03:22:01 +00:00
c983cd9ef6 Update .github/workflows/build.yaml
Some checks failed
Build / Build NPM Project (push) Failing after 27s
Build / Tag Version (push) Has been skipped
2023-12-15 03:18:19 +00:00
ca75517a53 Update .github/workflows/build.yaml
Some checks failed
Build / Build NPM Project (push) Failing after 26s
Build / Tag Version (push) Has been skipped
2023-12-15 03:16:26 +00:00
16f1cc996a Update package.json
Some checks failed
Build / Build NPM Project (push) Failing after 27s
Build / Tag Version (push) Has been skipped
2023-12-15 03:15:43 +00:00
6108cf9398 Update .github/workflows/build.yaml
Some checks failed
Build / Build NPM Project (push) Failing after 27s
Build / Tag Version (push) Has been skipped
2023-12-15 03:06:45 +00:00
1dd363ce37 Update .github/workflows/build.yaml
Some checks failed
Build / Build NPM Project (push) Failing after 27s
Build / Tag Version (push) Has been skipped
2023-12-15 03:04:23 +00:00
71e39dd9d8 Update README.md
Some checks failed
Build / Tag Version (push) Successful in 3s
Build / Build NPM Project (push) Failing after 28s
2023-12-14 04:28:48 +00:00
2523cdacbf Updated icon
Some checks failed
Build / Build NPM Project (push) Failing after 25s
Build / Tag Version (push) Has been skipped
2023-12-13 23:20:24 -05:00
8a51ebf22d fixed build
Some checks failed
Build / Build NPM Project (push) Failing after 26s
Build / Tag Version (push) Has been skipped
2023-12-13 23:19:01 -05:00
e1bd8398b6 fixed build
Some checks failed
Build / Tag Version (push) Successful in 3s
Build / Build NPM Project (push) Failing after 27s
2023-12-13 23:13:41 -05:00
7d2f00cf4e fixed build
Some checks failed
Build / Build NPM Project (push) Failing after 24s
Build / Tag Version (push) Has been skipped
2023-12-13 23:11:30 -05:00
3e544ee2ef Updated readme
Some checks failed
Build / Build NPM Project (push) Failing after 2s
Build / Tag Version (push) Has been skipped
2023-12-13 23:09:34 -05:00
8d3c235758 Updated readme
Some checks failed
Build / Build NPM Project (push) Failing after 2s
Build / Tag Version (push) Has been skipped
2023-12-13 23:08:56 -05:00
2ab53cacaa Trimmed package down 2023-12-13 22:56:23 -05:00
d92d0dc7a5
Update README.md 2019-05-09 14:36:30 -04:00
e110382ce5
Update README.md 2019-05-09 14:29:09 -04:00
e85c9e52fc Pass object key to template (Fixes #11) 2018-09-17 11:23:29 -04:00
7345e2ed27 Configure if columns can select row (Fixes #7) 2018-09-17 11:21:14 -04:00
b8960b0eb2 Added aggregator (Fixes #13) 2018-09-17 11:11:14 -04:00
a1418306fa version bump 2018-08-21 10:03:56 -04:00
fd552d1ea8 Added restrictions to paginator (Fixes #9) 2018-08-21 10:01:10 -04:00
f21e120e00 Default to first page 2018-07-03 14:42:10 -04:00
2258c12b56 Added grid refresh 2018-06-28 19:53:50 -04:00
fe566961ce Version bump 2018-06-28 19:29:12 -04:00
76bb80d159 Fixed changing pages and stuff 2018-06-28 19:27:34 -04:00
9430642b5d Fixed readme 2018-06-28 19:22:29 -04:00
da9e3960e2 Added events to tie into beginning/ending of processing 2018-06-28 19:22:01 -04:00
7ee1eda2f0 Reset page number on reprocess 2018-06-28 19:16:48 -04:00
d544581b0e default sorter will now use dotNotation 2018-06-28 18:59:36 -04:00
feda2b6c53 Prevent pagination until there is data 2018-06-28 16:44:19 -04:00
0fb93f723e Fixed initial sort happening too soon 2018-06-27 14:05:14 -04:00
dc6ee49467 Default processed data to prevent count calling length on null 2018-06-27 13:54:48 -04:00
5f5abe7fc2 Default processed data to prevent count calling length on null 2018-06-27 13:47:56 -04:00
bbe41ef0b5 Fixed expand icons 2018-06-27 12:38:02 -04:00
d96ff93076 Added a bunch of classes 2018-06-26 21:51:56 -04:00
31 changed files with 506 additions and 8479 deletions

View File

@ -1,46 +0,0 @@
version: 2
jobs:
build:
docker:
- image: circleci/node:10.4-browsers
working_directory: ~/repo
steps:
- checkout
- restore_cache:
keys:
- v1-dependencies-{{ checksum "package.json" }}
- v1-dependencies-
- run:
name: Install
command: yarn
- save_cache:
paths:
- node_modules
key: v1-dependencies-{{ checksum "package.json" }}
- run:
name: Build
command: yarn build-datatable
- run:
name: Copy README and LICENSE
command: |
cp LICENSE dist/ng-datatable/
cp README.md dist/ng-datatable/
- store_artifacts:
path: dist/ng-datatable
- deploy:
command: |
echo "${CIRCLE_BRANCH}"
if [ "${CIRCLE_BRANCH}" == "production" ]; then
yarn ci-publish
else
echo "Only the production branch is published"
fi

View File

@ -4,7 +4,7 @@ root = true
[*] [*]
charset = utf-8 charset = utf-8
indent_style = space indent_style = space
indent_size = 2 indent_size = 4
insert_final_newline = true insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true

41
.github/workflows/build.yaml vendored Normal file
View File

@ -0,0 +1,41 @@
name: Build
run-name: Build
on:
push:
jobs:
build:
name: Build NPM Project
runs-on: ubuntu-latest
container: node:8
steps:
- name: Clone Repository
uses: ztimson/actions/clone@develop
- name: Install Dependencies
run: npm i
- name: Build Project
run: npm run build
- name: Upload to Registry
uses: ztimson/actions/npm/publish@develop
tag:
name: Tag Version
needs: build
if: ${{github.ref_name}} == develop
runs-on: ubuntu-latest
container: node:8
steps:
- name: Clone Repository
uses: ztimson/actions/clone@develop
- name: Get Version Number
run: echo "VERSION=$(cat package.json | grep version | grep -Eo ':.+' | grep -Eo '[[:alnum:]\.\/\-]+')" >> $GITHUB_ENV
- name: Tag Version
uses: ztimson/actions/tag@develop
with:
tag: ${{env.VERSION}}

102
README.md
View File

@ -1,8 +1,52 @@
# ng-datatable <!-- Header -->
<div id="top" align="center">
<br />
[![CircleCI](https://circleci.com/gh/ztimson/ng-datatable/tree/master.svg?style=svg)](https://circleci.com/gh/ztimson/ng-datatable/tree/master) <!-- Logo -->
<img src="https://git.zakscode.com/repo-avatars/56e7adede041749f5df1d8b88b334e8c4cac38d7a4a574ccb42a624c52a35624" alt="Logo" width="200" height="200">
<!-- Title -->
### ng-datatable
<!-- Description -->
**Deprecated:** Angular Table Building Library
<!-- Repo badges -->
[![Version](https://img.shields.io/badge/dynamic/json.svg?label=Version&style=for-the-badge&url=https://git.zakscode.com/api/v1/repos/ztimson/ng-datatable/tags&query=$[0].name)](https://git.zakscode.com/ztimson/ng-datatable/tags)
[![Pull Requests](https://img.shields.io/badge/dynamic/json.svg?label=Pull%20Requests&style=for-the-badge&url=https://git.zakscode.com/api/v1/repos/ztimson/ng-datatable&query=open_pr_counter)](https://git.zakscode.com/ztimson/ng-datatable/pulls)
[![Issues](https://img.shields.io/badge/dynamic/json.svg?label=Issues&style=for-the-badge&url=https://git.zakscode.com/api/v1/repos/ztimson/ng-datatable&query=open_issues_count)](https://git.zakscode.com/ztimson/ng-datatable/issues)
<!-- Links -->
---
<div>
<a href="https://git.zakscode.com/ztimson/ng-datatable/releases" target="_blank">Release Notes</a>
<a href="https://git.zakscode.com/ztimson/ng-datatable/issues/new?template=.github%2fissue_template%2fbug.md" target="_blank">Report a Bug</a>
<a href="https://git.zakscode.com/ztimson/ng-datatable/issues/new?template=.github%2fissue_template%2fenhancement.md" target="_blank">Request a Feature</a>
</div>
---
</div>
## Table of Contents
- [ng-datatable](#top)
- [About](#about)
- [Built With](#built-with)
- [Setup](#setup)
- [Production](#production)
- [Development](#development)
- [Documentation](#documentation)
- [NgDatatableComponent](#ngdatatablecomponent)
- [Column](#column)
- [License](#license)
## About
**Deprecated**
A lightweight, feature rich table to display data. It is built with twitter bootstrap in mind so it will be automatically styled if you have the css included but its simple table structure makes it easy to style.
A lightweight, feature rich table to display data. It is built with twitter bootstrap in mind but its simple table structor makes it easy to style.
Features: Features:
@ -16,13 +60,25 @@ Features:
- Mobile column hiding - Mobile column hiding
- Checkboxes - Checkboxes
[View on NPM](https://www.npmjs.com/package/@ztimson/ng-datatable) [View on NPM](https://www.npmjs.com/package/@ztimson/ng-datatable)
[View the demo here](https://stackblitz.com/edit/angular-rzq6xm) ### Built With
[![Angular](https://img.shields.io/badge/Angular-DD0031?style=for-the-badge&logo=angular)](https://angular.io/)
[![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge&logo=typescript&logoColor=white)](https://typescriptlang.org/)
## Installing ## Setup
<details>
<summary>
<h3 id="production" style="display: inline">
Production
</h3>
</summary>
#### Prerequisites
- [Node.js](https://nodejs.org/en/download)
#### Instructions
1. Install package `npm install @ztimson/ng-datatable --save` 1. Install package `npm install @ztimson/ng-datatable --save`
2. Import into module 2. Import into module
@ -44,8 +100,26 @@ export class AppModule { }
```HTML ```HTML
<ng-datatable [columns]="columns" [data]="data"></ng-datatable> <ng-datatable [columns]="columns" [data]="data"></ng-datatable>
``` ```
</details>
## API <details>
<summary>
<h3 id="development" style="display: inline">
Development
</h3>
</summary>
#### Prerequisites
- [Node.js](https://nodejs.org/en/download)
#### Instructions
1. Install the dependencies: `npm install`
2. Start the Angular server: `npm run start`
3. Open [http://localhost:4200](http://localhost:4200)
</details>
## Documentation
### NgDatatableComponent ### NgDatatableComponent
@ -64,12 +138,14 @@ Selector: `ng-datatable`
| @Input() pageLength: number | Number of rows per page. Default 20 | | @Input() pageLength: number | Number of rows per page. Default 20 |
| @Input() page: number | Current page | | @Input() page: number | Current page |
| @Input() paginate: boolean | Paginate rows or display all at once. Default true | | @Input() paginate: boolean | Paginate rows or display all at once. Default true |
| @Input() paginateCssClass: string | Class added to the paginator | @Input() paginateCssClass: string | Class added to the paginator |
| @Input() selectionMode: null/'single'/'multi' | Allow selecting none, single or multiple rows at once | | @Input() selectionMode: null/'single'/'multi' | Allow selecting none, single or multiple rows at once |
| @Input() showCheckbox: boolean | Show checkbox' for mass selecting | | @Input() showCheckbox: boolean | Show checkbox' for mass selecting |
| @Input() tableLayout: 'auto'/'fixed' | CSS table layout. Defaults to 'auto' | | @Input() tableLayout: 'auto'/'fixed' | CSS table layout. Defaults to 'auto' |
| @Output() filterChanged: EventEmitter<(a, b) => 1/0/-1[]> | Applied filters | | @Output() filterChanged: EventEmitter<(a, b) => 1/0/-1[]> | Applied filters |
| @Output() finished: EventEmitter<any[]> | Emits when finished processing data |
| @Output() pageChanged: EventEmitter<number> | New page | | @Output() pageChanged: EventEmitter<number> | New page |
| @Output() processing: EventEmitter<any[]> | Emits when processing begins |
| @Output() selectionChanged: EventEmitter<any[]> | Selected rows | | @Output() selectionChanged: EventEmitter<any[]> | Selected rows |
| pagedData: any[] | Array of rows on current page after sorting and filtering | | pagedData: any[] | Array of rows on current page after sorting and filtering |
| processedData: any[] | Array of remaining rows after sorting and filtering | | processedData: any[] | Array of remaining rows after sorting and filtering |
@ -105,6 +181,10 @@ Clear filters being applied to. Update can be set to false to prevent instant fi
Clear all selected rows. Clear all selected rows.
#### refresh()
Refreshes the grid
##### selectAll() ##### selectAll()
Select all rows. Ignores pagination but not filtering. Select all rows. Ignores pagination but not filtering.
@ -144,3 +224,9 @@ Exported As: `Column`
| sortFn: (a, b) => 1/0/-1 | Custom function to sort rows by | | sortFn: (a, b) => 1/0/-1 | Custom function to sort rows by |
| template: TemplateRef<any> | Template to render row with | | template: TemplateRef<any> | Template to render row with |
| width: number/string | CSS width property | | width: number/string | CSS width property |
## License
Copyright © 2023 Zakary Timson | Available under the Apache 2.0 License
See the [license](./LICENSE) for more information.

View File

@ -17,9 +17,8 @@
"index": "src/index.html", "index": "src/index.html",
"main": "src/main.ts", "main": "src/main.ts",
"polyfills": "src/polyfills.ts", "polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.app.json", "tsConfig": "tsconfig.json",
"assets": [ "assets": [
"src/favicon.ico",
"src/assets" "src/assets"
], ],
"styles": [ "styles": [
@ -57,63 +56,6 @@
"browserTarget": "Sandbox:build:production" "browserTarget": "Sandbox:build:production"
} }
} }
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "Sandbox:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json",
"karmaConfig": "src/karma.conf.js",
"styles": [
"styles.css"
],
"scripts": [],
"assets": [
"src/favicon.ico",
"src/assets"
]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"src/tsconfig.app.json",
"src/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
},
"Sandbox-e2e": {
"root": "e2e/",
"projectType": "application",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "Sandbox:serve"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": "e2e/tsconfig.e2e.json",
"exclude": [
"**/node_modules/**"
]
}
} }
} }
}, },
@ -134,26 +76,6 @@
"project": "projects/ng-datatable/ng-package.prod.json" "project": "projects/ng-datatable/ng-package.prod.json"
} }
} }
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/ng-datatable/src/test.ts",
"tsConfig": "projects/ng-datatable/tsconfig.spec.json",
"karmaConfig": "projects/ng-datatable/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"projects/ng-datatable/tsconfig.lib.json",
"projects/ng-datatable/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
} }
} }
} }

View File

@ -1,28 +0,0 @@
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.e2e.json')
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};

View File

@ -1,14 +0,0 @@
import { AppPage } from './app.po';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getParagraphText()).toEqual('Welcome to app!');
});
});

View File

@ -1,11 +0,0 @@
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get('/');
}
getParagraphText() {
return element(by.css('app-root h1')).getText();
}
}

View File

@ -1,13 +0,0 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}

View File

@ -1,17 +1,11 @@
{ {
"name": "sandbox", "name": "ng-datatable",
"version": "0.0.0", "version": "1.0.0",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "ng serve", "start": "ng serve",
"build": "ng build", "build": "ng build ng-datatable --prod"
"build-datatable": "ng build ng-datatable --prod",
"ci-publish": "cd dist/ng-datatable && ci-publish",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
}, },
"private": true,
"dependencies": { "dependencies": {
"@angular/animations": "^6.0.0", "@angular/animations": "^6.0.0",
"@angular/common": "^6.0.0", "@angular/common": "^6.0.0",

View File

@ -1,6 +1,6 @@
{ {
"name": "@ztimson/ng-datatable", "name": "@ztimson/ng-datatable",
"version": "1.5.2", "version": "1.11.7",
"homepage": "https://github.com/ztimson/ng-datatable", "homepage": "https://github.com/ztimson/ng-datatable",
"license": "Apache-2.0", "license": "Apache-2.0",
"author": { "author": {

View File

@ -1,7 +1,9 @@
import {TemplateRef} from "@angular/core"; import {TemplateRef} from "@angular/core";
export interface Column { export interface Column {
aggregate?: (rows: any[]) => any;
cssClass?: string; // CSS to add to column cssClass?: string; // CSS to add to column
canSelect?: boolean;
hide?: boolean; // Hide column hide?: boolean; // Hide column
hideMobile?: boolean; // Hide column on mobile hideMobile?: boolean; // Hide column on mobile
initialSort?: 'asc' | 'desc'; // Sort this column initially initialSort?: 'asc' | 'desc'; // Sort this column initially

View File

@ -1,4 +1,4 @@
<table [class]="cssClass" [style.table-layout]="tableLayout"> <table [class]="cssClass + ' ngdt'" [style.table-layout]="tableLayout">
<colgroup> <colgroup>
<col *ngIf="showCheckbox && selectionMode !== null" width="30px"> <col *ngIf="showCheckbox && selectionMode !== null" width="30px">
<col *ngIf="expandedTemplate" width="30px"> <col *ngIf="expandedTemplate" width="30px">
@ -6,15 +6,15 @@
<col *ngIf="c.hide !== true && !(c.hideMobile === true && width < mobileBreakpoint)" span="1" [width]="_convertWidth(c.width)"> <col *ngIf="c.hide !== true && !(c.hideMobile === true && width < mobileBreakpoint)" span="1" [width]="_convertWidth(c.width)">
</ng-container> </ng-container>
</colgroup> </colgroup>
<thead> <thead class="ngdt-header">
<tr> <tr>
<th *ngIf="showCheckbox && selectionMode !== null"> <th *ngIf="showCheckbox && selectionMode !== null" class="ngdt-checkall">
<input *ngIf="selectionMode == 'multi'" type="checkbox" [checked]="processedData.length == selectedRows.size" <input *ngIf="selectionMode == 'multi'" type="checkbox" [checked]="processedData.length == selectedRows.size"
(change)="$event.target.checked ? selectAll() : clearSelected()"/> (change)="$event.target.checked ? selectAll() : clearSelected()"/>
</th> </th>
<th *ngIf="expandedTemplate"></th> <th *ngIf="expandedTemplate"></th>
<ng-container *ngFor="let c of columns; let i = index"> <ng-container *ngFor="let c of columns; let i = index">
<th *ngIf="c.hide !== true && !(c.hideMobile === true && width < mobileBreakpoint)" [class]="c.cssClass" <th *ngIf="c.hide !== true && !(c.hideMobile === true && width < mobileBreakpoint)" [class]="c.cssClass + ' ngdt-column'"
style="cursor: pointer" (click)="sort(i)"> style="cursor: pointer" (click)="sort(i)">
<span *ngIf="sortedColumn == i && !sortedDesc">&uarr;</span> <span *ngIf="sortedColumn == i && !sortedDesc">&uarr;</span>
<span *ngIf="sortedColumn == i && sortedDesc">&darr;</span> <span *ngIf="sortedColumn == i && sortedDesc">&darr;</span>
@ -23,35 +23,54 @@
</ng-container> </ng-container>
</tr> </tr>
</thead> </thead>
<tbody> <tbody class="ngdt-body">
<ng-container *ngFor="let row of pagedData; let i = index"> <ng-container *ngFor="let row of pagedData; let i = index">
<tr [ngClass]="{'active': selectedRows.has(i)}" (click)="updateSelected(i)"> <tr class="ngdt-row" [ngClass]="{'active': selectedRows.has(i)}">
<td *ngIf="showCheckbox && selectionMode !== null"><input type="checkbox" [checked]="selectedRows.has(i)"/></td> <td *ngIf="showCheckbox && selectionMode !== null" class="ngdt-checkbox" (click)="updateSelected(i)">
<td *ngIf="expandedTemplate"> <input type="checkbox" [checked]="selectedRows.has(i)"/>
<span *ngIf="!selectedRows.has(i)">&#9698;</span> </td>
<td *ngIf="expandedTemplate" class="ngdt-expand" (click)="updateSelected(i)">
<span *ngIf="!selectedRows.has(i)">&#9658;</span>
<span *ngIf="selectedRows.has(i)">&#9660;</span> <span *ngIf="selectedRows.has(i)">&#9660;</span>
</td> </td>
<ng-container *ngFor="let c of columns"> <ng-container *ngFor="let c of columns">
<td *ngIf="c.hide !== true && !(c.hideMobile === true && width < mobileBreakpoint)"> <td *ngIf="c.hide !== true && !(c.hideMobile === true && width < mobileBreakpoint)" class="ngdt-cell" (click)="updateSelected(i, c)">
<ng-template #defaultTemplate let-value="value">{{value}}</ng-template> <ng-template #defaultTemplate let-value="value">{{value}}</ng-template>
<ng-template [ngTemplateOutlet]="c.template || defaultTemplate" <ng-template [ngTemplateOutlet]="c.template || defaultTemplate"
[ngTemplateOutletContext]="{object: row, value: _dotNotation(row, c.property)}"> [ngTemplateOutletContext]="{object: row, key: c.property, value: _dotNotation(row, c.property)}">
</ng-template> </ng-template>
</td> </td>
</ng-container> </ng-container>
</tr> </tr>
<tr *ngIf="expandedTemplate && selectedRows.has(i)"> <tr *ngIf="expandedTemplate && selectedRows.has(i)" class="ngdt-detail">
<td [attr.colspan]="columns.length + (showCheckbox ? 1 : 0) + (expandedTemplate ? 1 : 0)"> <td [attr.colspan]="columns.length + (showCheckbox ? 1 : 0) + (expandedTemplate ? 1 : 0)">
<ng-template [ngTemplateOutlet]="expandedTemplate" [ngTemplateOutletContext]="{object: row}"></ng-template> <ng-template [ngTemplateOutlet]="expandedTemplate" [ngTemplateOutletContext]="{object: row}"></ng-template>
</td> </td>
</tr> </tr>
</ng-container> </ng-container>
<tr class="ngdt-total">
<td *ngIf="showCheckbox && selectionMode !== null" class="ngdt-checkbox"></td>
<td *ngIf="expandedTemplate" class="ngdt-expand"></td>
<ng-container *ngFor="let c of columns">
<td class="ngdt-cell">{{aggregate(c)}}</td>
</ng-container>
</tr>
</tbody> </tbody>
</table> </table>
<nav *ngIf="paginate" [class]="paginateCssClass" aria-label="Page navigation"> <nav *ngIf="paginate" [class]="paginateCssClass + 'ngdt-paginator'" aria-label="Page navigation">
<ul class="pagination"> <ul class="pagination">
<li class="page-item" [ngClass]="{'disabled': page <= 1}" (click)="changePage(page - 1)"><a class="page-link">Previous</a></li> <li class="page-item ngdt-first" [ngClass]="{'disabled': page <= 1}" (click)="changePage(1)"><a class="page-link">First</a></li>
<li *ngFor="let i of pages" class="page-item" [ngClass]="{'active': page == i}"><a class="page-link" (click)="changePage(i)">{{i}}</a></li> <li class="page-item ngdt-next" [ngClass]="{'disabled': page <= 1}" (click)="changePage(page - 1)"><a class="page-link">Previous</a></li>
<li class="page-item" [ngClass]="{'disabled': page >= pages.length}" (click)="changePage(page + 1)"><a class="page-link">Next</a></li> <ng-container *ngFor="let i of pages;">
<li *ngIf="i > page - 3 && i < page + 3" class="page-item ngdt-page" [ngClass]="{'active': page == i}">
<a class="page-link" (click)="changePage(i)">
<span *ngIf="i == page - 2 && i > 1">...</span>
{{i}}
<span *ngIf="i == page + 2 && i != pages.length">...</span>
</a>
</li>
</ng-container>
<li class="page-item ngdt-previous" [ngClass]="{'disabled': page >= pages.length}" (click)="changePage(page + 1)"><a class="page-link">Next</a></li>
<li class="page-item ngdt-last" [ngClass]="{'disabled': page == pages.length}" (click)="changePage(pages.length)"><a class="page-link">Last</a></li>
</ul> </ul>
</nav> </nav>

View File

@ -3,7 +3,8 @@ import {Column} from './column';
@Component({ @Component({
selector: 'ng-datatable', selector: 'ng-datatable',
templateUrl: 'ng-datatable.component.html' templateUrl: 'ng-datatable.component.html',
styles: ['.ngdt-expand {font-family: sans-serif;}']
}) })
export class NgDatatableComponent implements OnInit { export class NgDatatableComponent implements OnInit {
// Inputs ============================================================================================================ // Inputs ============================================================================================================
@ -21,30 +22,37 @@ export class NgDatatableComponent implements OnInit {
// Outputs =========================================================================================================== // Outputs ===========================================================================================================
@Output() filterChanged = new EventEmitter<any[]>(); // Output when filters change @Output() filterChanged = new EventEmitter<any[]>(); // Output when filters change
@Output() finished = new EventEmitter<any[]>(); // Fired after processing is finished
@Output() pageChanged = new EventEmitter<number>(); // Output when page is changed @Output() pageChanged = new EventEmitter<number>(); // Output when page is changed
@Output() processing = new EventEmitter<any[]>(); // Fires when grid begins to process
@Output() selectionChanged = new EventEmitter<any[]>(); // Output when selected rows changes @Output() selectionChanged = new EventEmitter<any[]>(); // Output when selected rows changes
// Properties ======================================================================================================== // Properties ========================================================================================================
filters: ((el?: any, i?: number, arr?: any[]) => boolean)[] = []; // Array of process functions to apply to data filters: ((el?: any, i?: number, arr?: any[]) => boolean)[] = []; // Array of process functions to apply to data
pages: number[] = []; // Array of possible pages pages: number[] = []; // Array of possible pages
pagedData: any[] = []; // The data for the current pagedData: any[] = []; // The data for the current
processedData: any[]; // rows left after filtering processedData: any[] = []; // rows left after filtering
selectedRows = new Set<number>(); // Keep track of selected rows selectedRows = new Set<number>(); // Keep track of selected rows
sortedColumn: number; // Column currently being sorted sortedColumn: number; // Column currently being sorted
sortedDesc = false; // Is the sorted column being sorted in ascending or descending order sortedDesc = false; // Is the sorted column being sorted in ascending or descending order
width = window.innerWidth; // Width of the screen. Used for hiding mobile columns width = window.innerWidth; // Width of the screen. Used for hiding mobile columns
// Fields ============================================================================================================ // Fields ============================================================================================================
get count(): number { return this.processedData.length; } // Number of rows after filtering get count(): number {
return this.processedData ? this.processedData.length : 0;
} // Number of rows after filtering
private _data: any[] = []; // Original data entered into table private _data: any[] = []; // Original data entered into table
get data(): any[] { return this.processedData; } // Return the processed data get data(): any[] {
return this.processedData;
} // Return the processed data
@Input() set data(data: any[]) { @Input() set data(data: any[]) {
this._data = data; this._data = data;
this._process(); this.refresh();
} }
// =================================================================================================================== // ===================================================================================================================
constructor() { } constructor() {
}
ngOnInit() { ngOnInit() {
// Look through columns for an initial sort // Look through columns for an initial sort
@ -57,31 +65,6 @@ export class NgDatatableComponent implements OnInit {
} }
// Helpers =========================================================================================================== // Helpers ===========================================================================================================
private _process() {
this.clearSelected();
this.processedData = this._data;
this.filters.forEach(f => this.processedData = this.processedData.filter(f));
if(this.sortedColumn != null) {
if (this.columns[this.sortedColumn].sortFn) {
this.processedData = this.processedData.sort(this.columns[this.sortedColumn].sortFn);
} else {
this.processedData = this.processedData.sort((a: any, b: any) => {
if (a[this.columns[this.sortedColumn].property] > b[this.columns[this.sortedColumn].property]) return 1;
if (a[this.columns[this.sortedColumn].property] < b[this.columns[this.sortedColumn].property]) return -1;
return 0;
});
}
if (this.sortedDesc) this.processedData = this.processedData.reverse();
}
if(this.paginate) {
this.pages = Array(Math.ceil(this.processedData.length / this.pageLength)).fill(0).map((ignore, i) => i + 1);
this.pagedData = this.processedData.filter((ignore, i) => i >= (this.page - 1) * this.pageLength && i < this.page * this.pageLength);
} else {
this.pagedData = this.processedData;
}
}
_convertWidth(width) { _convertWidth(width) {
if (typeof width == 'number') return `${width}px`; if (typeof width == 'number') return `${width}px`;
return width; return width;
@ -93,20 +76,25 @@ export class NgDatatableComponent implements OnInit {
addFilter(...filters: ((row?: any, index?: number, arr?: any[]) => boolean)[]) { addFilter(...filters: ((row?: any, index?: number, arr?: any[]) => boolean)[]) {
this.filters = this.filters.concat(filters); this.filters = this.filters.concat(filters);
this._process(); this.refresh();
this.filterChanged.emit(this.filters); this.filterChanged.emit(this.filters);
} }
aggregate(col: Column) {
if (!col.aggregate) return '';
return col.aggregate(this.processedData.map(row => this._dotNotation(row, col.property)));
}
changePage(page: number) { changePage(page: number) {
if (!this.paginate || page < 1 || page > this.pages.length) return; if (!this.paginate || page < 1 || page > this.pages.length) return;
this.page = page; this.page = page;
this._process(); this.refresh();
this.pageChanged.emit(this.page); this.pageChanged.emit(this.page);
} }
clearFilters(update = true) { clearFilters(update = true) {
this.filters = []; this.filters = [];
if(update) this._process(); if (update) this.refresh();
this.filterChanged.emit(this.filters); this.filterChanged.emit(this.filters);
} }
@ -117,7 +105,38 @@ export class NgDatatableComponent implements OnInit {
} }
@HostListener('window:resize', ['$event']) @HostListener('window:resize', ['$event'])
onResize(event) { this.width = event.target.innerWidth; } onResize(event) {
this.width = event.target.innerWidth;
}
refresh() {
this.processing.emit(this.processedData);
this.clearSelected();
this.processedData = this._data;
this.filters.forEach(f => this.processedData = this.processedData.filter(f));
if (this.sortedColumn != null && this.processedData) {
if (this.columns[this.sortedColumn].sortFn) {
this.processedData = this.processedData.sort(this.columns[this.sortedColumn].sortFn);
} else {
this.processedData = this.processedData.sort((a: any, b: any) => {
if (this._dotNotation(a, this.columns[this.sortedColumn].property) > this._dotNotation(b, this.columns[this.sortedColumn].property)) return 1;
if (this._dotNotation(a, this.columns[this.sortedColumn].property) < this._dotNotation(b, this.columns[this.sortedColumn].property)) return -1;
return 0;
});
}
if (this.sortedDesc) this.processedData = this.processedData.reverse();
}
if (this.paginate && this.processedData) {
this.pages = Array(Math.ceil(this.processedData.length / this.pageLength)).fill(0).map((ignore, i) => i + 1);
if (!this.page) this.page = 1;
if (this.page > this.pages.length) this.page = this.pages.length;
this.pagedData = this.processedData.filter((ignore, i) => i >= (this.page - 1) * this.pageLength && i < this.page * this.pageLength);
} else {
this.pagedData = this.processedData;
}
this.finished.emit(this.processedData);
}
selectAll() { selectAll() {
this.processedData.forEach((ignore, i) => this.selectedRows.add(i)); this.processedData.forEach((ignore, i) => this.selectedRows.add(i));
@ -137,11 +156,12 @@ export class NgDatatableComponent implements OnInit {
this.sortedDesc = desc; this.sortedDesc = desc;
// Preform sort // Preform sort
this._process(); this.refresh();
} }
updateSelected(index: number) { updateSelected(index: number, column?: Column) {
if (this.selectionMode == null) return; if (this.selectionMode == null) return;
if (column && column.canSelect === false) return;
if (this.selectionMode == 'single') { if (this.selectionMode == 'single') {
let alreadySelected = this.selectedRows.has(index); let alreadySelected = this.selectedRows.has(index);

View File

@ -25,8 +25,5 @@
"strictInjectionParameters": true, "strictInjectionParameters": true,
"flatModuleId": "AUTOGENERATED", "flatModuleId": "AUTOGENERATED",
"flatModuleOutFile": "AUTOGENERATED" "flatModuleOutFile": "AUTOGENERATED"
}, }
"exclude": [
"**/*.spec.ts"
]
} }

View File

@ -13,7 +13,7 @@ Checkbox
<input placeholder="Search" (keyup)="search.next($event.target.value)"> <input placeholder="Search" (keyup)="search.next($event.target.value)">
<br> Selected: {{table.selectedRows.size}}/{{table.processedData.length}} <br> Selected: {{table.selectedRows.size}}/{{table.processedData.length}}
<ng-datatable #table [cssClass]="tableCSS" [columns]="columns" [data]="data" [expandedTemplate]="expandable ? expanded : null" <ng-datatable #table [cssClass]="tableCSS" [columns]="columns" [data]="data" [expandedTemplate]="expandable ? expanded : null"
[showCheckbox]="checkbox" [paginate]="false" [selectionMode]="selectionMode == 'None' ? null : selectionMode" (rowSelected)="log($event)"> [showCheckbox]="checkbox" [paginate]="true" [selectionMode]="selectionMode == 'None' ? null : selectionMode" (rowSelected)="log($event)">
<ng-template #expanded let-object="object"> <ng-template #expanded let-object="object">
Hello {{object.firstName}} {{object.lastName}}, How are you today? Hello {{object.firstName}} {{object.lastName}}, How are you today?
<span *ngIf="object.age < 18">I can see that you are under age.</span> <span *ngIf="object.age < 18">I can see that you are under age.</span>

View File

@ -1,27 +0,0 @@
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
],
}).compileComponents();
}));
it('should create the app', async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
}));
it(`should have as title 'app'`, async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('app');
}));
it('should render title in a h1 tag', async(() => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!');
}));
});

File diff suppressed because one or more lines are too long

View File

@ -1,9 +1,9 @@
import { BrowserModule } from '@angular/platform-browser';
import {NgModule} from '@angular/core'; import {NgModule} from '@angular/core';
import {FormsModule} from "@angular/forms";
import {BrowserModule} from '@angular/platform-browser';
import {NgDatatableModule} from "../../projects/ng-datatable/src/lib/ng-datatable.module";
import {AppComponent} from './app.component'; import {AppComponent} from './app.component';
import {FormsModule} from "@angular/forms";
import {NgDatatableModule} from "../../projects/ng-datatable/src/lib/ng-datatable.module";
@NgModule({ @NgModule({
declarations: [ declarations: [

View File

View File

@ -1,15 +1,3 @@
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = { export const environment = {
production: false production: false
}; };
/*
* In development mode, to ignore zone related error stack frames such as
* `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can
* import the following file, but please comment it out in production mode
* because it will have performance impact when throw error
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -1,31 +0,0 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../coverage'),
reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};

View File

@ -1,20 +0,0 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

View File

@ -1,12 +0,0 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"module": "es2015",
"types": []
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

View File

@ -1,19 +0,0 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/spec",
"module": "commonjs",
"types": [
"jasmine",
"node"
]
},
"files": [
"test.ts",
"polyfills.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}

View File

@ -1,17 +0,0 @@
{
"extends": "../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
]
}
}

View File

@ -2,7 +2,8 @@
"compileOnSave": false, "compileOnSave": false,
"compilerOptions": { "compilerOptions": {
"baseUrl": "./", "baseUrl": "./",
"outDir": "./dist/out-tsc", "outDir": "../out-tsc/app",
"module": "es2015",
"sourceMap": true, "sourceMap": true,
"declaration": false, "declaration": false,
"moduleResolution": "node", "moduleResolution": "node",

View File

@ -1,130 +0,0 @@
{
"rulesDirectory": [
"node_modules/codelyzer"
],
"rules": {
"arrow-return-shorthand": true,
"callable-types": true,
"class-name": true,
"comment-format": [
true,
"check-space"
],
"curly": true,
"deprecation": {
"severity": "warn"
},
"eofline": true,
"forin": true,
"import-blacklist": [
true,
"rxjs/Rx"
],
"import-spacing": true,
"indent": [
true,
"spaces"
],
"interface-over-type-literal": true,
"label-position": true,
"max-line-length": [
true,
140
],
"member-access": false,
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-arg": true,
"no-bitwise": true,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-construct": true,
"no-debugger": true,
"no-duplicate-super": true,
"no-empty": false,
"no-empty-interface": true,
"no-eval": true,
"no-inferrable-types": [
true,
"ignore-params"
],
"no-misused-new": true,
"no-non-null-assertion": true,
"no-shadowed-variable": true,
"no-string-literal": false,
"no-string-throw": true,
"no-switch-case-fall-through": true,
"no-trailing-whitespace": true,
"no-unnecessary-initializer": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"one-line": [
true,
"check-open-brace",
"check-catch",
"check-else",
"check-whitespace"
],
"prefer-const": true,
"quotemark": [
true,
"single"
],
"radix": true,
"semicolon": [
true,
"always"
],
"triple-equals": [
true,
"allow-null-check"
],
"typedef-whitespace": [
true,
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}
],
"unified-signatures": true,
"variable-name": false,
"whitespace": [
true,
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type"
],
"no-output-on-prefix": true,
"use-input-property-decorator": true,
"use-output-property-decorator": true,
"use-host-property-decorator": true,
"no-input-rename": true,
"no-output-rename": true,
"use-life-cycle-interface": true,
"use-pipe-transform-interface": true,
"component-class-suffix": true,
"directive-class-suffix": true
}
}

7689
yarn.lock

File diff suppressed because it is too large Load Diff