Skip to content

Commit

Permalink
feat(ngrid-bootstrap): selection column
Browse files Browse the repository at this point in the history
  • Loading branch information
shlomiassaf committed Dec 29, 2020
1 parent 0494678 commit ab09209
Show file tree
Hide file tree
Showing 9 changed files with 325 additions and 0 deletions.
1 change: 1 addition & 0 deletions libs/ngrid-bootstrap/selection-column/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Selection implementation using bootstrap
8 changes: 8 additions & 0 deletions libs/ngrid-bootstrap/selection-column/ng-package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../../dist/@pebula/ngrid-bootstrap/selection-column",
"deleteDestPath": false,
"lib": {
"entryFile": "src/index.ts"
}
}
3 changes: 3 additions & 0 deletions libs/ngrid-bootstrap/selection-column/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "@pebula/ngrid-bootstrap/selection-column"
}
1 change: 1 addition & 0 deletions libs/ngrid-bootstrap/selection-column/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { PblNgridBsSelectionModule } from './lib/bs-selection.module';
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { Directive, Injector, Input, OnDestroy, ComponentFactoryResolver, ComponentRef } from '@angular/core';

import { PblNgridComponent, PblNgridPluginController } from '@pebula/ngrid';

import { PblNgridBsSelectionComponent } from './bs-selection.component';

declare module '@pebula/ngrid/lib/ext/types' {
interface PblNgridPluginExtension {
bsSelectionColumn?: PblNgridBsSelectionPlugin;
}
}

export const PLUGIN_KEY: 'bsSelectionColumn' = 'bsSelectionColumn';

@Directive({ selector: 'pbl-ngrid[bsSelectionColumn]' })
export class PblNgridBsSelectionPlugin implements OnDestroy {

@Input() get isCheckboxDisabled() { return this._isCheckboxDisabled; }
set isCheckboxDisabled(value: (row: any) => boolean ) {
if (value !== this._isCheckboxDisabled) {
this._isCheckboxDisabled = value;
if (this.cmpRef && value) {
this.cmpRef.instance.isCheckboxDisabled = value;
this.cmpRef.changeDetectorRef.detectChanges();
}
}
}

/**
* Add's a selection column using material's `mat-checkbox` in the column specified.
*/
@Input() get bsSelectionColumn(): string { return this._name; }
set bsSelectionColumn(value: string ) {
if (value !== this._name) {
this._name = value;
if (!value) {
if (this.cmpRef) {
this.cmpRef.destroy();
this.cmpRef = undefined;
}
} else {
if (!this.cmpRef) {
this.cmpRef = this.cfr.resolveComponentFactory(PblNgridBsSelectionComponent).create(this.injector);
this.cmpRef.instance.table = this.table;
if (this._bulkSelectMode) {
this.cmpRef.instance.bulkSelectMode = this._bulkSelectMode;
}
this.cmpRef.instance.selectionClass = this._selectionClass;
}
if (this.isCheckboxDisabled) {
this.cmpRef.instance.isCheckboxDisabled = this.isCheckboxDisabled;
}
this.cmpRef.instance.name = value;
this.cmpRef.changeDetectorRef.detectChanges();
}
}
}

/**
* Defines the behavior when clicking on the bulk select checkbox (header).
* There are 2 options:
*
* - all: Will select all items in the current collection
* - view: Will select only the rendered items in the view
*
* The default value is `all`
*/
@Input() get bulkSelectMode(): 'all' | 'view' | 'none' { return this._bulkSelectMode; }
set bulkSelectMode(value: 'all' | 'view' | 'none') {
if (value !== this._bulkSelectMode) {
this._bulkSelectMode = value;
if (this.cmpRef) {
this.cmpRef.instance.bulkSelectMode = value;
}
}
}

@Input() get bsSelectionClass(): string { return this._selectionClass; }
set matCheckboxSelectionColor(value: string) {
if (value !== this._selectionClass) {
this._selectionClass = value;
if (this.cmpRef) {
this.cmpRef.instance.selectionClass = value;
}
}
}

private _name: string;
private _bulkSelectMode: 'all' | 'view' | 'none';
private _selectionClass: string = '';
private cmpRef: ComponentRef<PblNgridBsSelectionComponent>;
private _removePlugin: (table: PblNgridComponent<any>) => void;
private _isCheckboxDisabled: (row: any) => boolean;

constructor(private table: PblNgridComponent<any>,
private cfr: ComponentFactoryResolver,
private injector: Injector,
pluginCtrl: PblNgridPluginController) {
this._removePlugin = pluginCtrl.setPlugin(PLUGIN_KEY, this);
}

ngOnDestroy() {
if (this.cmpRef) {
this.cmpRef.destroy();
}
this._removePlugin(this.table);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<ng-container *pblNgridHeaderCellDef="name; col as col;">
<label *ngIf="bulkSelectMode !== 'none'"
[class]="selectionClass">
<input type="checkbox"
[checked]="allSelected"
(input)="masterToggle()">
</label>
</ng-container>

<label *pblNgridCellDef="name; row as row;">
<input type="checkbox"
[class]="selectionClass"
[checked]="selection.isSelected(row)"
[disabled]="isCheckboxDisabled(row)"
(input)="rowItemChange(row)">
</label>

<span *pblNgridFooterCellDef="name; col as col;">{{ length ? length : '' }}</span>
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import { Component, Input, ViewChild, ViewEncapsulation, AfterViewInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
import { SelectionModel } from '@angular/cdk/collections';

import { unrx } from '@pebula/ngrid/core';
import {
PblNgridComponent,
PblNgridHeaderCellDefDirective,
PblNgridCellDefDirective,
PblNgridFooterCellDefDirective,
PblNgridPluginController,
} from '@pebula/ngrid';

const ALWAYS_FALSE_FN = () => false;

@Component({
selector: 'pbl-ngrid-bs-checkbox',
templateUrl: './bs-selection.component.html',
styleUrls: ['./bs-selection.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
})
export class PblNgridBsSelectionComponent implements AfterViewInit, OnDestroy {
/**
* Unique name for the checkbox column.
* When not set, the name 'checkbox' is used.
*
**/
@Input() name: string;

/**
* Defines the behavior when clicking on the bulk select checkbox (header).
* There are 2 options:
*
* - all: Will select all items in the current collection
* - view: Will select only the rendered items in the view
*
* The default value is `all`
*/
@Input() get bulkSelectMode(): 'all' | 'view' | 'none' { return this._bulkSelectMode; }
set bulkSelectMode(value: 'all' | 'view' | 'none') {
if (value !== this._bulkSelectMode) {
this._bulkSelectMode = value;
this.setupSelection();
}
}
/**
* A Custom selection model, optional.
* If not set, the selection model from the DataSource is used.
*/
@Input() get selection(): SelectionModel<any> {
return this._selection;
}
set selection(value: SelectionModel<any>) {
if (value !== this._selection) {
this._selection = value;
this.setupSelection();
}
}

@Input() get isCheckboxDisabled() { return this._isCheckboxDisabled; }
set isCheckboxDisabled(value: (row: any) => boolean) {
if (value !== this._isCheckboxDisabled) {
this._isCheckboxDisabled = value;
if (!this._isCheckboxDisabled || typeof this._isCheckboxDisabled !== 'function') {
this._isCheckboxDisabled = ALWAYS_FALSE_FN;
}
}
}

@Input() get selectionClass(): string { return this._selectionClass; }
set selectionClass(value: string) {
if (value !== this._selectionClass) {
this._selectionClass = value;
if (this.table.isInit) {
this.markAndDetect();
}
}
}

@ViewChild(PblNgridHeaderCellDefDirective, { static: true }) headerDef: PblNgridHeaderCellDefDirective<any>;
@ViewChild(PblNgridCellDefDirective, { static: true }) cellDef: PblNgridCellDefDirective<any>;
@ViewChild(PblNgridFooterCellDefDirective, { static: true }) footerDef: PblNgridFooterCellDefDirective<any>;

allSelected = false;
length: number;

private _selection: SelectionModel<any>;
private _bulkSelectMode: 'all' | 'view' | 'none';
private _isCheckboxDisabled: (row: any) => boolean = ALWAYS_FALSE_FN;
private _selectionClass: string;

constructor(public table: PblNgridComponent<any>, private cdr: ChangeDetectorRef) {
const pluginCtrl = PblNgridPluginController.find(table);
pluginCtrl.events
.pipe(unrx(this))
.subscribe( e => {
if (e.kind === 'onDataSource') {
this.selection = e.curr.selection;
}
});

}

ngAfterViewInit(): void {
if (!this.selection && this.table.ds) {
this.selection = this.table.ds.selection;
}

const registry = this.table.registry;
registry.addMulti('headerCell', this.headerDef);
registry.addMulti('tableCell', this.cellDef);
registry.addMulti('footerCell', this.footerDef);
}

ngOnDestroy(): void {
unrx.kill(this);
}

masterToggle(): void {
if (this.allSelected) {
this.selection.clear();
} else {
const selected = this.getCollection().filter(data => !this._isCheckboxDisabled(data));
this.selection.select(...selected);
}
}

rowItemChange(row: any): void {
this.selection.toggle(row);
this.markAndDetect();
}

onInput(a,b){
console.log(a,b)
}
private getCollection() {
const { ds } = this.table;
return this.bulkSelectMode === 'view' ? ds.renderedData : ds.source;
}

private setupSelection(): void {
unrx.kill(this, this.table);
if (this._selection) {
this.length = this.selection.selected.length;
this.selection.changed
.pipe(unrx(this, this.table))
.subscribe(() => this.handleSelectionChanged());
const changeSource = this.bulkSelectMode === 'view' ? this.table.ds.onRenderedDataChanged : this.table.ds.onSourceChanged;
changeSource
.pipe(unrx(this, this.table))
.subscribe(() => this.handleSelectionChanged());
} else {
this.length = 0;
}
}

private handleSelectionChanged() {
const { length } = this.getCollection().filter(data => !this._isCheckboxDisabled(data));
this.allSelected = !this.selection.isEmpty() && this.selection.selected.length === length;
this.length = this.selection.selected.length;
this.markAndDetect();
}

private markAndDetect() {
this.cdr.markForCheck();
this.cdr.detectChanges();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatCheckboxModule } from '@angular/material/checkbox';

import { PblNgridModule, ngridPlugin } from '@pebula/ngrid';
import { PblNgridBsSelectionPlugin, PLUGIN_KEY } from './bs-selection-plugin.directive';
import { PblNgridBsSelectionComponent } from './bs-selection.component';

@NgModule({
imports: [ CommonModule, MatCheckboxModule, PblNgridModule ],
declarations: [ PblNgridBsSelectionPlugin, PblNgridBsSelectionComponent ],
exports: [ PblNgridBsSelectionPlugin, PblNgridBsSelectionComponent ],
// TODO(REFACTOR_REF 2): remove when ViewEngine is no longer supported by angular (V12 ???)
entryComponents: [ PblNgridBsSelectionComponent ]
})
export class PblNgridBsSelectionModule {
static readonly NGRID_PLUGIN = ngridPlugin({ id: PLUGIN_KEY }, PblNgridBsSelectionPlugin);
}

0 comments on commit ab09209

Please sign in to comment.