Skip to content

Commit

Permalink
feat(ngrid-material/context-menu): new plugin for context menus
Browse files Browse the repository at this point in the history
  • Loading branch information
shlomiassaf committed Jun 26, 2019
1 parent cedd949 commit 4826c72
Show file tree
Hide file tree
Showing 21 changed files with 406 additions and 1 deletion.
Empty file.
16 changes: 16 additions & 0 deletions libs/ngrid-material/context-menu/karma.conf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html

const { join } = require('path');
const getBaseKarmaConfig = require('../../../karma.conf');

module.exports = function(config) {
const baseConfig = getBaseKarmaConfig();
config.set({
...baseConfig,
coverageIstanbulReporter: {
...baseConfig.coverageIstanbulReporter,
dir: join(__dirname, '../../../coverage/libs/ngrid-material/context-menu')
}
});
};
8 changes: 8 additions & 0 deletions libs/ngrid-material/context-menu/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-material/context-menu",
"deleteDestPath": false,
"lib": {
"entryFile": "src/index.ts"
}
}
7 changes: 7 additions & 0 deletions libs/ngrid-material/context-menu/ng-package.prod.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../../dist/@pebula/ngrid-material/context-menu",
"lib": {
"entryFile": "src/index.ts"
}
}
3 changes: 3 additions & 0 deletions libs/ngrid-material/context-menu/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "@pebula/ngrid-material/context-menu"
}
1 change: 1 addition & 0 deletions libs/ngrid-material/context-menu/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { PblNgridContextMenuModule } from './lib/context-menu.module';
49 changes: 49 additions & 0 deletions libs/ngrid-material/context-menu/src/lib/context-menu.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { NgModule, Optional, SkipSelf, ComponentFactoryResolver } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { MatMenuModule } from '@angular/material/menu';

import { PblNgridModule, PblNgridConfigService, PblNgridRegistryService } from '@pebula/ngrid';
import { PblNgridOverlayPanelModule, PblNgridOverlayPanelComponentExtension } from '@pebula/ngrid/overlay-panel';
import { PblNgridTargetEventsModule } from '@pebula/ngrid/target-events';

import { MatHeaderContextMenuTrigger } from './header-context/header-context-menu-trigger';
import { MatHeaderContextMenuExtension } from './header-context/header-context-menu-extension';
import { PblNgridMatHeaderContextMenuPlugin } from './header-context/header-context-menu.directive';
import { MatExcelStyleHeaderMenu } from './header-context/styles/excel-style-header-menu';

@NgModule({
imports: [
CommonModule,
MatIconModule,
MatButtonModule,
MatMenuModule,
PblNgridModule,
PblNgridOverlayPanelModule
],
declarations: [
MatHeaderContextMenuTrigger,
PblNgridMatHeaderContextMenuPlugin,
MatExcelStyleHeaderMenu,
],
exports: [
PblNgridMatHeaderContextMenuPlugin,
],
entryComponents: [
MatHeaderContextMenuTrigger,
MatExcelStyleHeaderMenu,
],
})
export class PblNgridContextMenuModule {
constructor(@Optional() @SkipSelf() parentModule: PblNgridContextMenuModule,
registry: PblNgridRegistryService,
cfr: ComponentFactoryResolver,
configService: PblNgridConfigService) {
if (parentModule) {
return;
}
registry.addMulti('dataHeaderExtensions', new MatHeaderContextMenuExtension(cfr));
registry.addMulti('overlayPanels', new PblNgridOverlayPanelComponentExtension('excelMenu', MatExcelStyleHeaderMenu, cfr));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { ComponentFactory, ComponentRef, ComponentFactoryResolver } from '@angular/core';
import { PblNgridMultiComponentRegistry, PblNgridDataHeaderExtensionContext } from '@pebula/ngrid';

import { PblNgridMatHeaderContextMenuPlugin } from './header-context-menu.directive';
import { MatHeaderContextMenuTrigger } from './header-context-menu-trigger';

export class MatHeaderContextMenuExtension extends PblNgridMultiComponentRegistry<MatHeaderContextMenuTrigger, 'dataHeaderExtensions'> {
readonly name: 'matHeaderContextMenuTrigger' = 'matHeaderContextMenuTrigger';
readonly kind: 'dataHeaderExtensions' = 'dataHeaderExtensions';
readonly projectContent = false;

constructor(private cfr: ComponentFactoryResolver) { super(); }

shouldRender(context: PblNgridDataHeaderExtensionContext): boolean {
return !!context.injector.get(PblNgridMatHeaderContextMenuPlugin, false);
}

getFactory(context: PblNgridDataHeaderExtensionContext): ComponentFactory<MatHeaderContextMenuTrigger> {
return this.cfr.resolveComponentFactory(MatHeaderContextMenuTrigger);
}

onCreated(context: PblNgridDataHeaderExtensionContext, cmpRef: ComponentRef<MatHeaderContextMenuTrigger>): void {
cmpRef.instance.context = context;
cmpRef.changeDetectorRef.markForCheck();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<mat-icon style="height: 16px; width: 16px; font-size: 16px; line-height: 16px;">more_vert</mat-icon>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
div[mat-header-context-menu-trigger] {
position: absolute;
display: flex;
align-items: center;
right: 0;
height: 100%;
cursor: pointer;
margin-right: 12px;
z-index: 50000
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Component, ElementRef, ViewEncapsulation } from '@angular/core';
import { PblNgridDataHeaderExtensionContext } from '@pebula/ngrid';
import { PblNgridOverlayPanelConfig } from '@pebula/ngrid/overlay-panel';

import { PblNgridMatHeaderContextMenuPlugin } from './header-context-menu.directive';

const DEFAULT_CONFIG: PblNgridOverlayPanelConfig = { hasBackdrop: true, xPos: 'after', yPos: 'below' };

@Component({
selector: 'div[mat-header-context-menu-trigger]',
host: {
'(click)': 'openOverlayPanel()',
},
templateUrl: `./header-context-menu-trigger.html`,
styleUrls: [ `./header-context-menu-trigger.scss` ],
encapsulation: ViewEncapsulation.None,
})
export class MatHeaderContextMenuTrigger {

context: PblNgridDataHeaderExtensionContext;

constructor(private plugin: PblNgridMatHeaderContextMenuPlugin, private elRef: ElementRef<HTMLElement>) { }

openOverlayPanel() {
const config = this.plugin.config || DEFAULT_CONFIG;
this.plugin.overlayPanel.open(this.plugin.style, this.elRef, config, this.context);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Directive, Input } from '@angular/core';
import { PblNgridPluginController, TablePlugin } from '@pebula/ngrid';
import { PblNgridOverlayPanelFactory, PblNgridOverlayPanel, PblNgridOverlayPanelConfig } from '@pebula/ngrid/overlay-panel';

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

const PLUGIN_KEY: 'matHeaderContextMenu' = 'matHeaderContextMenu';

@Directive({ selector: 'pbl-ngrid[matHeaderContextMenu]' })
@TablePlugin({ id: PLUGIN_KEY })
export class PblNgridMatHeaderContextMenuPlugin {

@Input('matHeaderContextMenu') style: any;
@Input() config: PblNgridOverlayPanelConfig;

readonly overlayPanel: PblNgridOverlayPanel;

constructor(overlayPanelFactory: PblNgridOverlayPanelFactory,
public readonly pluginCtrl: PblNgridPluginController) {
this.overlayPanel = overlayPanelFactory.create(pluginCtrl.extApi.table);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<mat-menu #columnMenu="matMenu" class="pbl-mat-menu-panel">

<button *ngIf="column.sort" mat-menu-item [matMenuTriggerFor]="sortMenu">
<mat-icon>sort</mat-icon>Sort
</button>
<button mat-menu-item [matMenuTriggerFor]="pinMenu">
<mat-icon>place</mat-icon>Pin
</button>
<button mat-menu-item (click)="grid.columnApi.autoSizeColumn(column)">
<mat-icon>keyboard_tab</mat-icon>Auto Fit
</button>
<button mat-menu-item (click)="hide()">
<mat-icon>visibility_off</mat-icon>Hide Column
</button>

<mat-menu #sortMenu="matMenu">
<button mat-menu-item (click)="onSortToggle('asc')" [class.menu-item-selected]="currentSort === 'asc' ? 'primary' : ''">
<mat-icon>arrow_upward</mat-icon>
<span>Ascending</span>
</button>
<button mat-menu-item (click)="onSortToggle('desc')" [class.menu-item-selected]="currentSort === 'desc' ? 'primary' : ''">
<mat-icon>arrow_downward</mat-icon>
<span>Descending</span>
</button>
</mat-menu>

<mat-menu #pinMenu="matMenu">
<button mat-menu-item (click)="onPinToggle('start')" [class.menu-item-selected]="currentPin === 'start' ? 'primary' : ''">
<span>Start</span>
</button>
<button mat-menu-item (click)="onPinToggle('end')" [class.menu-item-selected]="currentPin === 'end' ? 'primary' : ''">
<span>End</span>
</button>
</mat-menu>
</mat-menu>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.mat-menu-panel.pbl-mat-menu-panel {
max-width: 400px;
}

.mat-menu-item.pbl-mat-menu-row {
width: 100%;
box-sizing: border-box;
line-height: inherit;
height: auto;
margin: 6px 0;
cursor: inherit;

&:hover {
background: inherit;
}
}

.menu-item-selected {
font-weight: 500;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { Component, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core';
import { MatMenu } from '@angular/material/menu';

import { PblNgridComponent, PblColumn, PblNgridDataHeaderExtensionContext } from '@pebula/ngrid';
import { PblNgridOverlayPanelRef } from '@pebula/ngrid/overlay-panel';

@Component({
selector: 'mat-excel-style-header-menu',
templateUrl: `./excel-style-header-menu.html`,
styleUrls: [ `./excel-style-header-menu.scss` ],
encapsulation: ViewEncapsulation.None,
})
export class MatExcelStyleHeaderMenu {
column: PblColumn;
grid: PblNgridComponent

@ViewChild('columnMenu', { read: MatMenu, static: true }) matMenu: MatMenu;

currentSort: 'asc' | 'desc' | undefined;
currentPin: 'start' | 'end' | undefined;

constructor(private ref: PblNgridOverlayPanelRef<PblNgridDataHeaderExtensionContext>, private vcRef: ViewContainerRef) {
this.column = ref.data.col;
this.grid = ref.data.table;

if (this.grid.ds.sort.column === this.column) {
this.currentSort = this.grid.ds.sort.sort.order;
}
this.currentPin = this.column.columnDef.sticky ? 'start' : this.column.columnDef.stickyEnd ? 'end' : undefined;
}

ngAfterViewInit() {
this.matMenu.closed.subscribe( reason => {
this.ref.close();
});

const view = this.vcRef.createEmbeddedView(this.matMenu.templateRef);
this.matMenu.setElevation(0);
this.matMenu.focusFirstItem('program');
this.matMenu._resetAnimation();
view.markForCheck();
view.detectChanges();
this.matMenu._startAnimation();
}

hide(): void {
const hidden: string[] = [this.column.id];

for (const col of this.grid.columnApi.columns) {
if (col.hidden) {
hidden.push(col.id);
}
}

this.grid.hideColumns = hidden;
}

onSortToggle(sort: 'asc' | 'desc'): void {
if (this.currentSort === sort) {
this.grid.ds.setSort();
} else {
this.grid.ds.setSort(this.column, { order: sort });
}
}

onPinToggle(pin: 'start' | 'end'): void {
if (this.currentPin === pin) {
this.column.columnDef.updatePin()
} else {
this.column.columnDef.updatePin(pin)
}
}
}
22 changes: 22 additions & 0 deletions libs/ngrid-material/context-menu/src/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files

import 'core-js/es7/reflect';
import 'zone.js/dist/zone';
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);
9 changes: 9 additions & 0 deletions libs/ngrid-material/context-menu/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"types": [
"jasmine",
"node"
]
}
}
34 changes: 34 additions & 0 deletions libs/ngrid-material/context-menu/tsconfig.lib.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "../../../dist/out-tsc/libs/ngrid-material/context-menu",
"target": "es2015",
"module": "es2015",
"moduleResolution": "node",
"declaration": true,
"sourceMap": true,
"inlineSources": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": true,
"types": [],
"lib": [
"dom",
"es2015"
]
},
"angularCompilerOptions": {
"annotateForClosureCompiler": true,
"skipTemplateCodegen": true,
"strictMetadataEmit": true,
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true,
"flatModuleId": "AUTOGENERATED",
"flatModuleOutFile": "AUTOGENERATED"
},
"exclude": [
"src/test.ts",
"**/*.spec.ts",
"karma.conf.ts"
]
}
Loading

0 comments on commit 4826c72

Please sign in to comment.