Skip to content

Commit

Permalink
fix(comp): switch to reusable Slickgrid-Universal Pagination
Browse files Browse the repository at this point in the history
- Pagination is now a reusable Slickgrid-Universal pagination, used by 2 libs, (all written in plain TypeScript) is well tested and handles all scenarios
  • Loading branch information
ghiscoding committed Nov 17, 2021
1 parent 1322305 commit 549862f
Show file tree
Hide file tree
Showing 6 changed files with 15,799 additions and 267 deletions.
29 changes: 15 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,17 +82,18 @@
"@babel/preset-env": "^7.15.8",
"@babel/preset-react": "^7.14.5",
"@babel/preset-typescript": "^7.15.0",
"@slickgrid-universal/common": "^0.17.0",
"@slickgrid-universal/custom-footer-component": "^0.17.0",
"@slickgrid-universal/empty-warning-component": "^0.17.0",
"@slickgrid-universal/event-pub-sub": "^0.17.0",
"@slickgrid-universal/rxjs-observable": "^0.17.0",
"@slickgrid-universal/common": "^0.19.1",
"@slickgrid-universal/custom-footer-component": "^0.19.1",
"@slickgrid-universal/empty-warning-component": "^0.19.1",
"@slickgrid-universal/event-pub-sub": "^0.19.1",
"@slickgrid-universal/pagination-component": "^0.19.1",
"@slickgrid-universal/rxjs-observable": "^0.19.1",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^11.2.7",
"@testing-library/user-event": "^12.8.3",
"babel-loader": "^8.2.2",
"dequal": "^2.0.2",
"dompurify": "^2.3.1",
"dompurify": "^2.3.3",
"font-awesome": "^4.7.0",
"i18next": ">= 14.1.1",
"jquery": "~3.5.1",
Expand All @@ -107,15 +108,15 @@
"web-vitals": "^1.1.2"
},
"devDependencies": {
"@popperjs/core": "^2.10.1",
"@slickgrid-universal/composite-editor-component": "^0.17.0",
"@slickgrid-universal/excel-export": "^0.11.2",
"@slickgrid-universal/graphql": "^0.17.0",
"@slickgrid-universal/odata": "^0.17.0",
"@slickgrid-universal/rxjs-observable": "^0.17.0",
"@slickgrid-universal/text-export": "^0.17.0",
"@popperjs/core": "^2.10.2",
"@slickgrid-universal/composite-editor-component": "^0.19.1",
"@slickgrid-universal/excel-export": "^0.19.1",
"@slickgrid-universal/graphql": "^0.19.1",
"@slickgrid-universal/odata": "^0.19.1",
"@slickgrid-universal/rxjs-observable": "^0.19.1",
"@slickgrid-universal/text-export": "^0.19.1",
"@types/bluebird": "^3.5.36",
"@types/dompurify": "^2.2.3",
"@types/dompurify": "^2.3.1",
"@types/flatpickr": "^3.1.2",
"@types/i18next-xhr-backend": "^1.4.2",
"@types/jest": "^27.0.1",
Expand Down
45 changes: 33 additions & 12 deletions src/examples/slickgrid/example2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ interface DataItem {
completed: number;
}

interface State {
gridOptions: GridOption;
columnDefinitions: Column[];
dataset: any[];
}

// create my custom Formatter with the Formatter type
const myCustomCheckmarkFormatter: Formatter<DataItem> = (_row, _cell, value) => {
// you can return a string of a object (of type FormatterResultObject), the 2 types are shown below
Expand All @@ -38,7 +44,7 @@ const customEnableButtonFormatter: Formatter<DataItem> = (_row: number, _cell: n

interface Props { }

export default class Example2 extends React.Component {
export default class Example2 extends React.Component<Props, State> {
title = 'Example 2: Grid with Formatters';
subTitle = `
Grid with Custom and/or included Slickgrid Formatters (<a href="https://github.com/ghiscoding/aurelia-slickgrid/wiki/Formatters" target="_blank">Wiki docs</a>).
Expand All @@ -54,27 +60,35 @@ export default class Example2 extends React.Component {
`;

reactGrid!: ReactGridInstance;
gridOptions!: GridOption;
columnDefinitions: Column<DataItem>[] = [];
dataset: any[] = [];
resizerPaused = false;

constructor(public readonly props: Props) {
super(props);

this.state = {
gridOptions: undefined,
columnDefinitions: [],
dataset: [],
};

// define the grid options & columns and then create the grid itself
this.defineGrid();
}

componentDidMount() {
document.title = this.title;
// populate the dataset once the grid is ready
this.getData();
this.setState((state: any, props: Props) => {
return {
dataset: this.getData(),
};
});
}

/* Define grid Options and Columns */
defineGrid() {
// the columns field property is type-safe, try to add a different string not representing one of DataItems properties
this.columnDefinitions = [
const columns = [
{ id: 'title', name: 'Title', field: 'title', sortable: true, type: FieldType.string, width: 70 },
{ id: 'phone', name: 'Phone Number using mask', field: 'phone', sortable: true, type: FieldType.number, minWidth: 100, formatter: Formatters.mask, params: { mask: '(000) 000-0000' } },
{ id: 'duration', name: 'Duration (days)', field: 'duration', formatter: Formatters.decimal, params: { minDecimal: 1, maxDecimal: 2 }, sortable: true, type: FieldType.number, minWidth: 90, exportWithFormatter: true },
Expand All @@ -92,7 +106,7 @@ export default class Example2 extends React.Component {
}
];

this.gridOptions = {
const gridOptions = {
autoResize: {
container: '#demo-container',
rightPadding: 10
Expand Down Expand Up @@ -127,18 +141,24 @@ export default class Example2 extends React.Component {
// onCopyCancelled: (e, args: { ranges: SelectedRange[] }) => console.log('onCopyCancelled', args.ranges),
// }
};

this.state = {
...this.state,
columnDefinitions : columns,
gridOptions,
};
}

getData() {
// mock a dataset
this.dataset = [];
const mockDataset = [];
for (let i = 0; i < 500; i++) {
const randomYear = 2000 + Math.floor(Math.random() * 10);
const randomMonth = Math.floor(Math.random() * 11);
const randomDay = Math.floor((Math.random() * 29));
const randomPercent = Math.round(Math.random() * 100);

this.dataset[i] = {
mockDataset[i] = {
id: i,
title: 'Task ' + i,
phone: this.generatePhoneNumber(),
Expand All @@ -151,6 +171,7 @@ export default class Example2 extends React.Component {
effortDriven: (i % 5 === 0)
};
}
return mockDataset;
}

generatePhoneNumber() {
Expand Down Expand Up @@ -198,9 +219,9 @@ export default class Example2 extends React.Component {
</button>

<ReactSlickgridCustomElement gridId="grid2"
columnDefinitions={this.columnDefinitions}
gridOptions={this.gridOptions}
dataset={this.dataset} />
columnDefinitions={this.state.columnDefinitions}
gridOptions={this.state.gridOptions}
dataset={this.state.dataset} />
{/* instances={this.reactGrid} */}
</div>
);
Expand Down
51 changes: 32 additions & 19 deletions src/react-slickgrid/custom-elements/react-slickgrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ import {
import { EventPubSubService } from '@slickgrid-universal/event-pub-sub';
import { SlickFooterComponent } from '@slickgrid-universal/custom-footer-component';
import { SlickEmptyWarningComponent } from '@slickgrid-universal/empty-warning-component';
import { SlickPaginationComponent } from '@slickgrid-universal/pagination-component';

import { dequal } from 'dequal/lite';
import { Constants } from '../constants';
Expand All @@ -92,7 +93,6 @@ import { Subscription } from 'rxjs';
declare const Slick: SlickNamespace;

import { SlickgridEventAggregator } from '../custom-elements/slickgridEventAggregator';
import { SlickPaginationCustomElement } from './slick-pagination';

import { GlobalContainerService, GlobalEventPubSubService } from '../services/singletons';

Expand Down Expand Up @@ -199,6 +199,7 @@ export class ReactSlickgridCustomElement extends React.Component<Props, State> {
// components
slickEmptyWarning: SlickEmptyWarningComponent | undefined;
slickFooter: SlickFooterComponent | undefined;
slickPagination: SlickPaginationComponent | undefined;

// extensions
extensionUtility: ExtensionUtility;
Expand Down Expand Up @@ -275,7 +276,7 @@ export class ReactSlickgridCustomElement extends React.Component<Props, State> {
const columnPickerExtension = new ColumnPickerExtension(this.extensionUtility, this.sharedService);
const checkboxExtension = new CheckboxSelectorExtension(this.sharedService);
const draggableGroupingExtension = new DraggableGroupingExtension(this.extensionUtility, this._eventPubSubService, this.sharedService);
const gridMenuExtension = new GridMenuExtension(this.extensionUtility, this.filterService, this.sharedService, this.sortService, this.backendUtilityService, this.props.translaterService);
const gridMenuExtension = new GridMenuExtension(this.extensionUtility, this.filterService,this._eventPubSubService, this.sharedService, this.sortService, this.backendUtilityService, this.props.translaterService);
const groupItemMetaProviderExtension = new GroupItemMetaProviderExtension(this.sharedService);
const headerButtonExtension = new HeaderButtonExtension(this.extensionUtility, this.sharedService);
const headerMenuExtension = new HeaderMenuExtension(this.extensionUtility, this.filterService, this._eventPubSubService, this.sharedService, this.sortService, this.props.translaterService);
Expand Down Expand Up @@ -465,7 +466,7 @@ export class ReactSlickgridCustomElement extends React.Component<Props, State> {

// user could show a custom footer with the data metrics (dataset length and last updated timestamp)
if (!this._gridOptions.enablePagination && this._gridOptions.showCustomFooter && this._gridOptions.customFooterOptions && gridContainerElm) {
this.slickFooter = new SlickFooterComponent(this.grid, this._gridOptions.customFooterOptions, this.props.translaterService);
this.slickFooter = new SlickFooterComponent(this.grid, this._gridOptions.customFooterOptions,this._eventPubSubService, this.props.translaterService);
this.slickFooter.renderFooter(gridContainerElm as HTMLDivElement);
}

Expand Down Expand Up @@ -597,7 +598,9 @@ export class ReactSlickgridCustomElement extends React.Component<Props, State> {
}

// dispose the Components
this.slickEmptyWarning?.dispose();
this.slickFooter?.dispose();
this.slickPagination?.dispose();

if (this.dataview) {
if (this.dataview.setItems) {
Expand Down Expand Up @@ -773,20 +776,21 @@ export class ReactSlickgridCustomElement extends React.Component<Props, State> {
if (gridOptions.enableTranslate) {
this.translateColumnHeaderTitleKeys();
this.translateColumnGroupKeys();
this.translateCustomFooterTexts();
}

// on locale change, we have to manually translate the Headers, GridMenu
this.subscriptions.push(
this.props.globalEa.subscribe('i18n:locale:changed', () => {
// publish event of the same name that Slickgrid-Universal uses on a language change event
this._eventPubSubService.publish('onLanguageChange');

if (gridOptions.enableTranslate) {
this.extensionService.translateCellMenu();
this.extensionService.translateColumnHeaders();
this.extensionService.translateColumnPicker();
this.extensionService.translateContextMenu();
this.extensionService.translateGridMenu();
this.extensionService.translateHeaderMenu();
this.translateCustomFooterTexts();
this.translateColumnHeaderTitleKeys();
this.translateColumnGroupKeys();
if (gridOptions.createPreHeaderPanel && !gridOptions.enableDraggableGrouping) {
Expand Down Expand Up @@ -1213,8 +1217,12 @@ export class ReactSlickgridCustomElement extends React.Component<Props, State> {
if (this._gridOptions?.backendServiceApi) {
this.backendUtilityService?.refreshBackendDataset(this._gridOptions);
}
this.renderPagination(this.showPagination);
})
);

// also initialize (render) the pagination component
this.renderPagination();
this._isPaginationInitialized = true;
}
}
Expand Down Expand Up @@ -1440,6 +1448,25 @@ export class ReactSlickgridCustomElement extends React.Component<Props, State> {
this.props.containerService.registerInstance('RxJsResource', this.rxjs);
}

/**
* Render (or dispose) the Pagination Component, user can optionally provide False (to not show it) which will in term dispose of the Pagination,
* also while disposing we can choose to omit the disposable of the Pagination Service (if we are simply toggling the Pagination, we want to keep the Service alive)
* @param {Boolean} showPagination - show (new render) or not (dispose) the Pagination
* @param {Boolean} shouldDisposePaginationService - when disposing the Pagination, do we also want to dispose of the Pagination Service? (defaults to True)
*/
private renderPagination(showPagination = true) {
if (this._gridOptions?.enablePagination && !this._isPaginationInitialized && showPagination) {
this.slickPagination = new SlickPaginationComponent(this.paginationService, this._eventPubSubService, this.sharedService, this.props.translaterService);
this.slickPagination.renderPagination(this.elm.current as HTMLDivElement);
this._isPaginationInitialized = true;
} else if (!showPagination) {
if (this.slickPagination) {
this.slickPagination.dispose();
}
this._isPaginationInitialized = false;
}
}

/**
* Takes a flat dataset with parent/child relationship, sort it (via its tree structure) and return the sorted flat array
* @param {Array<Object>} flatDatasetInput - flat dataset input
Expand Down Expand Up @@ -1502,13 +1529,6 @@ export class ReactSlickgridCustomElement extends React.Component<Props, State> {
});
}

/** Translate all Custom Footer Texts (footer with metrics) */
private translateCustomFooterTexts() {
if (this.slickFooter && this.props.translaterService?.translate) {
this.slickFooter?.translateCustomFooterTexts();
}
}

/** translate all columns (including hidden columns) */
private translateColumnHeaderTitleKeys() {
this.extensionUtility.translateItems(this.sharedService.allColumns, 'nameKey', 'name');
Expand Down Expand Up @@ -1555,13 +1575,6 @@ export class ReactSlickgridCustomElement extends React.Component<Props, State> {
<div id={`${this.props.gridId}`} className="slickgrid-container" style={{ width: '100%' }} onBlur={$event => this.commitEdit($event.target)}>
</div>

{/* <!-- Pagination section under the grid-- > */}
{this.showPagination &&
<div id={`slickPagingContainer-${this.props.gridId}`}>
<SlickPaginationCustomElement gridOptions={this._gridOptions!} paginationService={this.paginationService} />
</div>
}

{/* <!--Footer slot if you need to create a complex custom footer-- > */}
<slot name="slickgrid-footer"></slot>
</div >
Expand Down
Loading

0 comments on commit 549862f

Please sign in to comment.