diff --git a/aurelia-slickgrid/src/aurelia-slickgrid/aurelia-slickgrid.ts b/aurelia-slickgrid/src/aurelia-slickgrid/aurelia-slickgrid.ts index 5af614a86..8532c38f9 100644 --- a/aurelia-slickgrid/src/aurelia-slickgrid/aurelia-slickgrid.ts +++ b/aurelia-slickgrid/src/aurelia-slickgrid/aurelia-slickgrid.ts @@ -44,6 +44,7 @@ import { GridEventService, GridExtraService, GridStateService, + GroupingAndColspanService, ResizerService, SortService, toKebabCase @@ -57,7 +58,22 @@ const aureliaEventPrefix = 'asg'; const eventPrefix = 'sg'; // Aurelia doesn't support well TypeScript @autoinject in a Plugin so we'll do it the old fashion way -@inject(ControlAndPluginService, ExportService, Element, EventAggregator, FilterService, GraphqlService, GridEventService, GridExtraService, GridStateService, I18N, ResizerService, SortService, Container) +@inject( + ControlAndPluginService, + ExportService, + Element, + EventAggregator, + FilterService, + GraphqlService, + GridEventService, + GridExtraService, + GridStateService, + GroupingAndColspanService, + I18N, + ResizerService, + SortService, + Container +) export class AureliaSlickgridCustomElement { private _dataset: any[]; private _eventHandler: any = new Slick.EventHandler(); @@ -88,6 +104,7 @@ export class AureliaSlickgridCustomElement { private gridEventService: GridEventService, private gridExtraService: GridExtraService, private gridStateService: GridStateService, + private groupingAndColspanService: GroupingAndColspanService, private i18n: I18N, private resizer: ResizerService, private sortService: SortService, @@ -140,6 +157,11 @@ export class AureliaSlickgridCustomElement { // attach resize ONLY after the dataView is ready this.attachResizeHook(this.grid, this.gridOptions); + // attach grouping and header grouping colspan service + if (this.gridOptions.createPreHeaderPanel) { + this.groupingAndColspanService.init(this.grid, this.dataview); + } + // attach grid extra service this.gridExtraService.init(this.grid, this.columnDefinitions, this.gridOptions, this.dataview); @@ -174,6 +196,7 @@ export class AureliaSlickgridCustomElement { this.filterService.dispose(); this.gridEventService.dispose(); this.gridStateService.dispose(); + this.groupingAndColspanService.dispose(); this.resizer.dispose(); this.sortService.dispose(); this.grid.destroy(); @@ -392,7 +415,7 @@ export class AureliaSlickgridCustomElement { } // auto-resize grid on browser resize - this.resizer.init(grid, options); + this.resizer.init(grid); if (grid && options.enableAutoResize) { this.resizer.attachAutoResizeDataGrid(); if (options.autoFitColumnsOnFirstLoad && typeof grid.autosizeColumns === 'function') { diff --git a/aurelia-slickgrid/src/aurelia-slickgrid/models/column.interface.ts b/aurelia-slickgrid/src/aurelia-slickgrid/models/column.interface.ts index ce9a53353..284ec907d 100644 --- a/aurelia-slickgrid/src/aurelia-slickgrid/models/column.interface.ts +++ b/aurelia-slickgrid/src/aurelia-slickgrid/models/column.interface.ts @@ -15,12 +15,15 @@ export interface Column { /** Block event triggering of an insert? */ cannotTriggerInsert?: boolean; - /** CSS class to add to the column cell */ - cssClass?: string; + /** Column group name for grouping of column headers spanning accross multiple columns */ + columnGroup?: string; /** Column span in pixels or `*`, only input the number value */ colspan?: number | '*'; + /** CSS class to add to the column cell */ + cssClass?: string; + /** Do we want default sort to be ascending? True by default */ defaultSortAsc?: boolean; diff --git a/aurelia-slickgrid/src/aurelia-slickgrid/models/gridOption.interface.ts b/aurelia-slickgrid/src/aurelia-slickgrid/models/gridOption.interface.ts index 4b0558425..32919d8bb 100644 --- a/aurelia-slickgrid/src/aurelia-slickgrid/models/gridOption.interface.ts +++ b/aurelia-slickgrid/src/aurelia-slickgrid/models/gridOption.interface.ts @@ -68,6 +68,9 @@ export interface GridOption { /** Checkbox Select Plugin options (columnTitle, forceFitTitle, syncResizeTitle) */ columnPicker?: ColumnPicker; + /** Default to false, which leads to create an extra pre-header panel (on top of column header) for column grouping purposes */ + createPreHeaderPanel?: boolean; + /** Defaults to false, which leads to create the footer row of the grid */ createFooterRow?: boolean; @@ -245,6 +248,9 @@ export interface GridOption { /** "params" is a generic property and can be used to pass custom paramaters to your Formatter/Editor or anything else */ params?: any | any[]; + /** Extra pre-header panel height (on top of column header) */ + preHeaderPanelHeight?: number; + /** Do we want to preserve copied selection on paste? */ preserveCopiedSelectionOnPaste?: boolean; @@ -275,6 +281,9 @@ export interface GridOption { /** Do we want to show header row? */ showHeaderRow?: boolean; + /** Do we want to show the extra pre-header panel (on top of column header) for column grouping purposes */ + showPreHeaderPanel?: boolean; + /** Do we want to show top panel row? */ showTopPanel?: boolean; diff --git a/aurelia-slickgrid/src/aurelia-slickgrid/services/groupingAndColspan.service.ts b/aurelia-slickgrid/src/aurelia-slickgrid/services/groupingAndColspan.service.ts new file mode 100644 index 000000000..c8377193b --- /dev/null +++ b/aurelia-slickgrid/src/aurelia-slickgrid/services/groupingAndColspan.service.ts @@ -0,0 +1,87 @@ +import { EventAggregator, Subscription } from 'aurelia-event-aggregator'; +import { inject } from 'aurelia-framework'; +import { + Column, + GridOption +} from './../models/index'; +import * as $ from 'jquery'; + +// using external non-typed js libraries +declare var Slick: any; + +@inject(EventAggregator) +export class GroupingAndColspanService { + private _eventHandler = new Slick.EventHandler(); + private _dataView: any; + private _grid: any; + private _gridOptions: GridOption; + private _columnDefinitions: Column[]; + aureliaEventPrefix: string; + + constructor(private ea: EventAggregator) { } + + init(grid: any, dataView: any) { + this._grid = grid; + this._dataView = dataView; + if (grid) { + this._gridOptions = grid.getOptions(); + this._columnDefinitions = grid.getColumns(); + } + + this.aureliaEventPrefix = (this._gridOptions && this._gridOptions.defaultAureliaEventPrefix) ? this._gridOptions.defaultAureliaEventPrefix : 'asg'; + + // When dealing with Pre-Header Grouping colspan, we need to re-create the pre-header in multiple occasions + // for all these occasions, we have to trigger a re-create + if (this._gridOptions.createPreHeaderPanel) { + this._eventHandler.subscribe(grid.onSort, (e: Event, args: any) => { + this.createPreHeaderRowGroupingTitle(); + }); + this._eventHandler.subscribe(grid.onColumnsResized, (e: Event, args: any) => { + this.createPreHeaderRowGroupingTitle(); + }); + this._eventHandler.subscribe(dataView.onRowCountChanged, (e: Event, args: any) => { + this.createPreHeaderRowGroupingTitle(); + }); + + // also not sure why at this point, but it seems that I need to call the 1st create in a delayed execution + // probably some kind of timing issues and delaying it until the grid is fully ready does help + setTimeout(() => { + this.createPreHeaderRowGroupingTitle(); + }, 50); + } + } + + dispose() { + // unsubscribe all SlickGrid events + this._eventHandler.unsubscribeAll(); + } + + createPreHeaderRowGroupingTitle() { + const $preHeaderPanel = $(this._grid.getPreHeaderPanel()) + .empty() + .addClass('slick-header-columns') + .css('left', '-1000px') + .width(this._grid.getHeadersWidth()); + $preHeaderPanel.parent().addClass('slick-header'); + const headerColumnWidthDiff = this._grid.getHeaderColumnWidthDiff(); + let m; + let header; + let lastColumnGroup = ''; + let widthTotal = 0; + + for (let i = 0; i < this._columnDefinitions.length; i++) { + m = this._columnDefinitions[i]; + if (lastColumnGroup === m.columnGroup && i > 0) { + widthTotal += m.width; + header.width(widthTotal - headerColumnWidthDiff); + } else { + widthTotal = m.width; + header = $(`
`) + .html(`${m.columnGroup || ''}`) + .width(m.width - headerColumnWidthDiff) + .appendTo($preHeaderPanel); + } + lastColumnGroup = m.columnGroup; + } + } +} diff --git a/aurelia-slickgrid/src/aurelia-slickgrid/services/index.ts b/aurelia-slickgrid/src/aurelia-slickgrid/services/index.ts index 808370646..73e9c9489 100644 --- a/aurelia-slickgrid/src/aurelia-slickgrid/services/index.ts +++ b/aurelia-slickgrid/src/aurelia-slickgrid/services/index.ts @@ -8,6 +8,7 @@ export * from './gridExtra.service'; export * from './gridExtraUtils'; export * from './gridState.service'; export * from './grid-odata.service'; +export * from './groupingAndColspan.service'; export * from './odata.service'; export * from './resizer.service'; export * from './sort.service'; diff --git a/aurelia-slickgrid/src/aurelia-slickgrid/services/resizer.service.ts b/aurelia-slickgrid/src/aurelia-slickgrid/services/resizer.service.ts index fed6e3750..ecaab4c21 100644 --- a/aurelia-slickgrid/src/aurelia-slickgrid/services/resizer.service.ts +++ b/aurelia-slickgrid/src/aurelia-slickgrid/services/resizer.service.ts @@ -1,6 +1,7 @@ import { inject } from 'aurelia-framework'; import { GridOption } from './../models/index'; import * as $ from 'jquery'; +import { EventAggregator } from 'aurelia-event-aggregator'; // global constants, height/width are in pixels const DATAGRID_MIN_HEIGHT = 180; @@ -15,14 +16,21 @@ export interface GridDimension { heightWithPagination?: number; } +@inject(EventAggregator) export class ResizerService { private _grid: any; private _gridOptions: GridOption; private _lastDimensions: GridDimension; + aureliaEventPrefix: string; - init(grid: any, gridOptions: GridOption): void { + constructor(private ea: EventAggregator) { } + + init(grid: any): void { this._grid = grid; - this._gridOptions = gridOptions; + if (grid) { + this._gridOptions = grid.getOptions(); + } + this.aureliaEventPrefix = (this._gridOptions && this._gridOptions.defaultAureliaEventPrefix) ? this._gridOptions.defaultAureliaEventPrefix : 'asg'; } /** @@ -42,6 +50,7 @@ export class ResizerService { // -- 2nd attach a trigger on the Window DOM element, so that it happens also when resizing after first load // -- attach auto-resize to Window object only if it exist $(window).on('resize.grid', () => { + this.ea.publish(`${this.aureliaEventPrefix}:onBeforeResize`, true); // for some yet unknown reason, calling the resize twice removes any stuttering/flickering when changing the height and makes it much smoother this.resizeGrid(); this.resizeGrid(); diff --git a/aurelia-slickgrid/src/aurelia-slickgrid/styles/_variables.scss b/aurelia-slickgrid/src/aurelia-slickgrid/styles/_variables.scss index 99278fa59..566b959c6 100644 --- a/aurelia-slickgrid/src/aurelia-slickgrid/styles/_variables.scss +++ b/aurelia-slickgrid/src/aurelia-slickgrid/styles/_variables.scss @@ -46,6 +46,13 @@ $row-selected-color: #e2e2c5 !default; $row-checkbox-selector-background: inherit !default; $row-checkbox-selector-border: none !default; +/* Pre-Header - Header Grouping colspan */ +$preheader-border-left: 1px solid #c0c0c0 !default; +$preheader-border-right: none !default; +$preheader-border-bottom: none !default; +$preheader-border-top: none !default; +$preheader-font-size: $font-size-base + 2px !default; + /* header */ $header-padding-top: 4px !default; $header-padding-right: 4px !default; diff --git a/aurelia-slickgrid/src/aurelia-slickgrid/styles/slick-bootstrap.scss b/aurelia-slickgrid/src/aurelia-slickgrid/styles/slick-bootstrap.scss index 3a4e03127..45343855a 100644 --- a/aurelia-slickgrid/src/aurelia-slickgrid/styles/slick-bootstrap.scss +++ b/aurelia-slickgrid/src/aurelia-slickgrid/styles/slick-bootstrap.scss @@ -214,6 +214,13 @@ } } + .slick-preheader-panel.ui-state-default { + .slick-header-column { + border-left: $preheader-border-left !important; + font-size: $preheader-font-size !important; + } + } + /* .slick-header-columns:last-child .slick-header-column:last-child { diff --git a/aurelia-slickgrid/src/examples/slickgrid/example14.html b/aurelia-slickgrid/src/examples/slickgrid/example14.html index 44e47ae51..df0b1d0bb 100644 --- a/aurelia-slickgrid/src/examples/slickgrid/example14.html +++ b/aurelia-slickgrid/src/examples/slickgrid/example14.html @@ -3,6 +3,6 @@