Compare commits
No commits in common. "develop" and "production" have entirely different histories.
develop
...
production
46
.circleci/config.yml
Normal file
46
.circleci/config.yml
Normal file
@ -0,0 +1,46 @@
|
||||
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
|
@ -4,7 +4,7 @@ root = true
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
|
41
.github/workflows/build.yaml
vendored
41
.github/workflows/build.yaml
vendored
@ -1,41 +0,0 @@
|
||||
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}}
|
108
README.md
108
README.md
@ -1,52 +1,8 @@
|
||||
<!-- Header -->
|
||||
<div id="top" align="center">
|
||||
<br />
|
||||
# ng-datatable
|
||||
|
||||
<!-- 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.
|
||||
[![CircleCI](https://circleci.com/gh/ztimson/ng-datatable/tree/master.svg?style=svg)](https://circleci.com/gh/ztimson/ng-datatable/tree/master)
|
||||
|
||||
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:
|
||||
|
||||
@ -60,27 +16,15 @@ Features:
|
||||
- Mobile column hiding
|
||||
- Checkboxes
|
||||
|
||||
|
||||
[View on NPM](https://www.npmjs.com/package/@ztimson/ng-datatable)
|
||||
|
||||
### 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/)
|
||||
[View the demo here](https://stackblitz.com/edit/angular-rzq6xm)
|
||||
|
||||
## Setup
|
||||
## Installing
|
||||
|
||||
<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`
|
||||
2. Import into module
|
||||
1. Install package `npm install @ztimson/ng-datatable --save`
|
||||
2. Import into module
|
||||
|
||||
```Typescript
|
||||
import {NgDatatableModule} from '@ztimsonm/ng-datatable'
|
||||
@ -95,31 +39,13 @@ import {NgDatatableModule} from '@ztimsonm/ng-datatable'
|
||||
export class AppModule { }
|
||||
```
|
||||
|
||||
3. Add to template
|
||||
3. Add to template
|
||||
|
||||
```HTML
|
||||
<ng-datatable [columns]="columns" [data]="data"></ng-datatable>
|
||||
```
|
||||
</details>
|
||||
|
||||
<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
|
||||
## API
|
||||
|
||||
### NgDatatableComponent
|
||||
|
||||
@ -138,14 +64,12 @@ Selector: `ng-datatable`
|
||||
| @Input() pageLength: number | Number of rows per page. Default 20 |
|
||||
| @Input() page: number | Current page |
|
||||
| @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() showCheckbox: boolean | Show checkbox' for mass selecting |
|
||||
| @Input() tableLayout: 'auto'/'fixed' | CSS table layout. Defaults to 'auto' |
|
||||
| @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() processing: EventEmitter<any[]> | Emits when processing begins |
|
||||
| @Output() selectionChanged: EventEmitter<any[]> | Selected rows |
|
||||
| pagedData: any[] | Array of rows on current page after sorting and filtering |
|
||||
| processedData: any[] | Array of remaining rows after sorting and filtering |
|
||||
@ -181,10 +105,6 @@ Clear filters being applied to. Update can be set to false to prevent instant fi
|
||||
|
||||
Clear all selected rows.
|
||||
|
||||
#### refresh()
|
||||
|
||||
Refreshes the grid
|
||||
|
||||
##### selectAll()
|
||||
|
||||
Select all rows. Ignores pagination but not filtering.
|
||||
@ -224,9 +144,3 @@ Exported As: `Column`
|
||||
| sortFn: (a, b) => 1/0/-1 | Custom function to sort rows by |
|
||||
| template: TemplateRef<any> | Template to render row with |
|
||||
| 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.
|
||||
|
80
angular.json
80
angular.json
@ -17,8 +17,9 @@
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.json",
|
||||
"tsConfig": "src/tsconfig.app.json",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
@ -56,6 +57,63 @@
|
||||
"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/**"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -76,6 +134,26 @@
|
||||
"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/**"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
28
e2e/protractor.conf.js
Normal file
28
e2e/protractor.conf.js
Normal file
@ -0,0 +1,28 @@
|
||||
// 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 } }));
|
||||
}
|
||||
};
|
14
e2e/src/app.e2e-spec.ts
Normal file
14
e2e/src/app.e2e-spec.ts
Normal file
@ -0,0 +1,14 @@
|
||||
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!');
|
||||
});
|
||||
});
|
11
e2e/src/app.po.ts
Normal file
11
e2e/src/app.po.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { browser, by, element } from 'protractor';
|
||||
|
||||
export class AppPage {
|
||||
navigateTo() {
|
||||
return browser.get('/');
|
||||
}
|
||||
|
||||
getParagraphText() {
|
||||
return element(by.css('app-root h1')).getText();
|
||||
}
|
||||
}
|
13
e2e/tsconfig.e2e.json
Normal file
13
e2e/tsconfig.e2e.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/app",
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"jasminewd2",
|
||||
"node"
|
||||
]
|
||||
}
|
||||
}
|
12
package.json
12
package.json
@ -1,11 +1,17 @@
|
||||
{
|
||||
"name": "ng-datatable",
|
||||
"version": "1.0.0",
|
||||
"name": "sandbox",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build ng-datatable --prod"
|
||||
"build": "ng build",
|
||||
"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": {
|
||||
"@angular/animations": "^6.0.0",
|
||||
"@angular/common": "^6.0.0",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ztimson/ng-datatable",
|
||||
"version": "1.11.7",
|
||||
"version": "1.5.2",
|
||||
"homepage": "https://github.com/ztimson/ng-datatable",
|
||||
"license": "Apache-2.0",
|
||||
"author": {
|
||||
|
@ -1,9 +1,7 @@
|
||||
import {TemplateRef} from "@angular/core";
|
||||
|
||||
export interface Column {
|
||||
aggregate?: (rows: any[]) => any;
|
||||
cssClass?: string; // CSS to add to column
|
||||
canSelect?: boolean;
|
||||
hide?: boolean; // Hide column
|
||||
hideMobile?: boolean; // Hide column on mobile
|
||||
initialSort?: 'asc' | 'desc'; // Sort this column initially
|
||||
|
@ -1,4 +1,4 @@
|
||||
<table [class]="cssClass + ' ngdt'" [style.table-layout]="tableLayout">
|
||||
<table [class]="cssClass" [style.table-layout]="tableLayout">
|
||||
<colgroup>
|
||||
<col *ngIf="showCheckbox && selectionMode !== null" 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)">
|
||||
</ng-container>
|
||||
</colgroup>
|
||||
<thead class="ngdt-header">
|
||||
<thead>
|
||||
<tr>
|
||||
<th *ngIf="showCheckbox && selectionMode !== null" class="ngdt-checkall">
|
||||
<th *ngIf="showCheckbox && selectionMode !== null">
|
||||
<input *ngIf="selectionMode == 'multi'" type="checkbox" [checked]="processedData.length == selectedRows.size"
|
||||
(change)="$event.target.checked ? selectAll() : clearSelected()"/>
|
||||
</th>
|
||||
<th *ngIf="expandedTemplate"></th>
|
||||
<ng-container *ngFor="let c of columns; let i = index">
|
||||
<th *ngIf="c.hide !== true && !(c.hideMobile === true && width < mobileBreakpoint)" [class]="c.cssClass + ' ngdt-column'"
|
||||
<th *ngIf="c.hide !== true && !(c.hideMobile === true && width < mobileBreakpoint)" [class]="c.cssClass"
|
||||
style="cursor: pointer" (click)="sort(i)">
|
||||
<span *ngIf="sortedColumn == i && !sortedDesc">↑</span>
|
||||
<span *ngIf="sortedColumn == i && sortedDesc">↓</span>
|
||||
@ -23,54 +23,35 @@
|
||||
</ng-container>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="ngdt-body">
|
||||
<tbody>
|
||||
<ng-container *ngFor="let row of pagedData; let i = index">
|
||||
<tr class="ngdt-row" [ngClass]="{'active': selectedRows.has(i)}">
|
||||
<td *ngIf="showCheckbox && selectionMode !== null" class="ngdt-checkbox" (click)="updateSelected(i)">
|
||||
<input type="checkbox" [checked]="selectedRows.has(i)"/>
|
||||
</td>
|
||||
<td *ngIf="expandedTemplate" class="ngdt-expand" (click)="updateSelected(i)">
|
||||
<span *ngIf="!selectedRows.has(i)">►</span>
|
||||
<tr [ngClass]="{'active': selectedRows.has(i)}" (click)="updateSelected(i)">
|
||||
<td *ngIf="showCheckbox && selectionMode !== null"><input type="checkbox" [checked]="selectedRows.has(i)"/></td>
|
||||
<td *ngIf="expandedTemplate">
|
||||
<span *ngIf="!selectedRows.has(i)">◢</span>
|
||||
<span *ngIf="selectedRows.has(i)">▼</span>
|
||||
</td>
|
||||
<ng-container *ngFor="let c of columns">
|
||||
<td *ngIf="c.hide !== true && !(c.hideMobile === true && width < mobileBreakpoint)" class="ngdt-cell" (click)="updateSelected(i, c)">
|
||||
<td *ngIf="c.hide !== true && !(c.hideMobile === true && width < mobileBreakpoint)">
|
||||
<ng-template #defaultTemplate let-value="value">{{value}}</ng-template>
|
||||
<ng-template [ngTemplateOutlet]="c.template || defaultTemplate"
|
||||
[ngTemplateOutletContext]="{object: row, key: c.property, value: _dotNotation(row, c.property)}">
|
||||
[ngTemplateOutletContext]="{object: row, value: _dotNotation(row, c.property)}">
|
||||
</ng-template>
|
||||
</td>
|
||||
</ng-container>
|
||||
</tr>
|
||||
<tr *ngIf="expandedTemplate && selectedRows.has(i)" class="ngdt-detail">
|
||||
<tr *ngIf="expandedTemplate && selectedRows.has(i)">
|
||||
<td [attr.colspan]="columns.length + (showCheckbox ? 1 : 0) + (expandedTemplate ? 1 : 0)">
|
||||
<ng-template [ngTemplateOutlet]="expandedTemplate" [ngTemplateOutletContext]="{object: row}"></ng-template>
|
||||
</td>
|
||||
</tr>
|
||||
</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>
|
||||
</table>
|
||||
<nav *ngIf="paginate" [class]="paginateCssClass + 'ngdt-paginator'" aria-label="Page navigation">
|
||||
<nav *ngIf="paginate" [class]="paginateCssClass" aria-label="Page navigation">
|
||||
<ul class="pagination">
|
||||
<li class="page-item ngdt-first" [ngClass]="{'disabled': page <= 1}" (click)="changePage(1)"><a class="page-link">First</a></li>
|
||||
<li class="page-item ngdt-next" [ngClass]="{'disabled': page <= 1}" (click)="changePage(page - 1)"><a class="page-link">Previous</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>
|
||||
<li class="page-item" [ngClass]="{'disabled': page <= 1}" (click)="changePage(page - 1)"><a class="page-link">Previous</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" [ngClass]="{'disabled': page >= pages.length}" (click)="changePage(page + 1)"><a class="page-link">Next</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
@ -3,8 +3,7 @@ import {Column} from './column';
|
||||
|
||||
@Component({
|
||||
selector: 'ng-datatable',
|
||||
templateUrl: 'ng-datatable.component.html',
|
||||
styles: ['.ngdt-expand {font-family: sans-serif;}']
|
||||
templateUrl: 'ng-datatable.component.html'
|
||||
})
|
||||
export class NgDatatableComponent implements OnInit {
|
||||
// Inputs ============================================================================================================
|
||||
@ -22,42 +21,35 @@ export class NgDatatableComponent implements OnInit {
|
||||
|
||||
// Outputs ===========================================================================================================
|
||||
@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() processing = new EventEmitter<any[]>(); // Fires when grid begins to process
|
||||
@Output() selectionChanged = new EventEmitter<any[]>(); // Output when selected rows changes
|
||||
|
||||
// Properties ========================================================================================================
|
||||
filters: ((el?: any, i?: number, arr?: any[]) => boolean)[] = []; // Array of process functions to apply to data
|
||||
pages: number[] = []; // Array of possible pages
|
||||
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
|
||||
sortedColumn: number; // Column currently being sorted
|
||||
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
|
||||
|
||||
// Fields ============================================================================================================
|
||||
get count(): number {
|
||||
return this.processedData ? this.processedData.length : 0;
|
||||
} // Number of rows after filtering
|
||||
get count(): number { return this.processedData.length; } // Number of rows after filtering
|
||||
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[]) {
|
||||
this._data = data;
|
||||
this.refresh();
|
||||
this._process();
|
||||
}
|
||||
|
||||
// ===================================================================================================================
|
||||
constructor() {
|
||||
}
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() {
|
||||
// Look through columns for an initial sort
|
||||
for (let i = 0; i < this.columns.length; i++) {
|
||||
if (this.columns[i].initialSort) {
|
||||
for(let i = 0; i < this.columns.length; i++) {
|
||||
if(this.columns[i].initialSort) {
|
||||
this.sort(i, (this.columns[i].initialSort == 'desc'));
|
||||
break;
|
||||
}
|
||||
@ -65,8 +57,33 @@ export class NgDatatableComponent implements OnInit {
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if (typeof width == 'number') return `${width}px`;
|
||||
if(typeof width == 'number') return `${width}px`;
|
||||
return width;
|
||||
}
|
||||
|
||||
@ -76,67 +93,31 @@ export class NgDatatableComponent implements OnInit {
|
||||
|
||||
addFilter(...filters: ((row?: any, index?: number, arr?: any[]) => boolean)[]) {
|
||||
this.filters = this.filters.concat(filters);
|
||||
this.refresh();
|
||||
this._process();
|
||||
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) {
|
||||
if (!this.paginate || page < 1 || page > this.pages.length) return;
|
||||
if(!this.paginate || page < 1 || page > this.pages.length) return;
|
||||
this.page = page;
|
||||
this.refresh();
|
||||
this._process();
|
||||
this.pageChanged.emit(this.page);
|
||||
}
|
||||
|
||||
clearFilters(update = true) {
|
||||
clearFilters(update=true) {
|
||||
this.filters = [];
|
||||
if (update) this.refresh();
|
||||
if(update) this._process();
|
||||
this.filterChanged.emit(this.filters);
|
||||
}
|
||||
|
||||
clearSelected() {
|
||||
let emit = this.selectedRows.size > 0;
|
||||
this.selectedRows.clear();
|
||||
if (emit) this.selectionChanged.emit([]);
|
||||
if(emit) this.selectionChanged.emit([]);
|
||||
}
|
||||
|
||||
@HostListener('window:resize', ['$event'])
|
||||
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);
|
||||
}
|
||||
onResize(event) { this.width = event.target.innerWidth; }
|
||||
|
||||
selectAll() {
|
||||
this.processedData.forEach((ignore, i) => this.selectedRows.add(i));
|
||||
@ -148,25 +129,24 @@ export class NgDatatableComponent implements OnInit {
|
||||
if (!column || column.sort === false) return; // If column is un-sortable return
|
||||
|
||||
// Figure out sorting direction if not supplied
|
||||
if (desc === undefined) {
|
||||
if(desc === undefined) {
|
||||
desc = false;
|
||||
if (columnIndex == this.sortedColumn) desc = !this.sortedDesc;
|
||||
if(columnIndex == this.sortedColumn) desc = !this.sortedDesc;
|
||||
}
|
||||
this.sortedColumn = columnIndex;
|
||||
this.sortedDesc = desc;
|
||||
|
||||
// Preform sort
|
||||
this.refresh();
|
||||
this._process();
|
||||
}
|
||||
|
||||
updateSelected(index: number, column?: Column) {
|
||||
updateSelected(index: number) {
|
||||
if (this.selectionMode == null) return;
|
||||
if (column && column.canSelect === false) return;
|
||||
|
||||
if (this.selectionMode == 'single') {
|
||||
let alreadySelected = this.selectedRows.has(index);
|
||||
this.selectedRows.clear();
|
||||
if (!alreadySelected) this.selectedRows.add(index);
|
||||
if(!alreadySelected) this.selectedRows.add(index);
|
||||
} else {
|
||||
if (this.selectedRows.has(index)) {
|
||||
this.selectedRows.delete(index);
|
||||
|
@ -25,5 +25,8 @@
|
||||
"strictInjectionParameters": true,
|
||||
"flatModuleId": "AUTOGENERATED",
|
||||
"flatModuleOutFile": "AUTOGENERATED"
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
"**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
|
0
src/app/app.component.css
Normal file
0
src/app/app.component.css
Normal file
@ -13,7 +13,7 @@ Checkbox
|
||||
<input placeholder="Search" (keyup)="search.next($event.target.value)">
|
||||
<br> Selected: {{table.selectedRows.size}}/{{table.processedData.length}}
|
||||
<ng-datatable #table [cssClass]="tableCSS" [columns]="columns" [data]="data" [expandedTemplate]="expandable ? expanded : null"
|
||||
[showCheckbox]="checkbox" [paginate]="true" [selectionMode]="selectionMode == 'None' ? null : selectionMode" (rowSelected)="log($event)">
|
||||
[showCheckbox]="checkbox" [paginate]="false" [selectionMode]="selectionMode == 'None' ? null : selectionMode" (rowSelected)="log($event)">
|
||||
<ng-template #expanded let-object="object">
|
||||
Hello {{object.firstName}} {{object.lastName}}, How are you today?
|
||||
<span *ngIf="object.age < 18">I can see that you are under age.</span>
|
||||
|
27
src/app/app.component.spec.ts
Normal file
27
src/app/app.component.spec.ts
Normal file
@ -0,0 +1,27 @@
|
||||
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
@ -1,9 +1,9 @@
|
||||
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 { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
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({
|
||||
declarations: [
|
||||
@ -17,4 +17,4 @@ import {AppComponent} from './app.component';
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule {}
|
||||
export class AppModule { }
|
||||
|
0
src/assets/.gitkeep
Normal file
0
src/assets/.gitkeep
Normal file
@ -1,3 +1,15 @@
|
||||
// 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 = {
|
||||
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.
|
||||
|
BIN
src/favicon.ico
Normal file
BIN
src/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.3 KiB |
31
src/karma.conf.js
Normal file
31
src/karma.conf.js
Normal file
@ -0,0 +1,31 @@
|
||||
// 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
|
||||
});
|
||||
};
|
20
src/test.ts
Normal file
20
src/test.ts
Normal file
@ -0,0 +1,20 @@
|
||||
// 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);
|
12
src/tsconfig.app.json
Normal file
12
src/tsconfig.app.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/app",
|
||||
"module": "es2015",
|
||||
"types": []
|
||||
},
|
||||
"exclude": [
|
||||
"src/test.ts",
|
||||
"**/*.spec.ts"
|
||||
]
|
||||
}
|
19
src/tsconfig.spec.json
Normal file
19
src/tsconfig.spec.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/spec",
|
||||
"module": "commonjs",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"test.ts",
|
||||
"polyfills.ts"
|
||||
],
|
||||
"include": [
|
||||
"**/*.spec.ts",
|
||||
"**/*.d.ts"
|
||||
]
|
||||
}
|
17
src/tslint.json
Normal file
17
src/tslint.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "../tslint.json",
|
||||
"rules": {
|
||||
"directive-selector": [
|
||||
true,
|
||||
"attribute",
|
||||
"app",
|
||||
"camelCase"
|
||||
],
|
||||
"component-selector": [
|
||||
true,
|
||||
"element",
|
||||
"app",
|
||||
"kebab-case"
|
||||
]
|
||||
}
|
||||
}
|
@ -2,8 +2,7 @@
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"outDir": "../out-tsc/app",
|
||||
"module": "es2015",
|
||||
"outDir": "./dist/out-tsc",
|
||||
"sourceMap": true,
|
||||
"declaration": false,
|
||||
"moduleResolution": "node",
|
||||
|
130
tslint.json
Normal file
130
tslint.json
Normal file
@ -0,0 +1,130 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user