diff --git a/src/examples/slickgrid/app.tsx b/src/examples/slickgrid/app.tsx index 091ee1ce..1d4799c3 100644 --- a/src/examples/slickgrid/app.tsx +++ b/src/examples/slickgrid/app.tsx @@ -1,4 +1,7 @@ import React from 'react'; +import { BrowserRouter, Route, Routes } from 'react-router-dom'; + +import { NavBar } from '../../NavBar'; import Example1 from './example1'; import Example2 from './example2'; import Example3 from './example3'; @@ -30,43 +33,44 @@ import Example29 from './example29'; import Example30 from './example30'; import Example31 from './example31'; import Example32 from './example32'; -import Router from './Router'; -import { Redirect, Route, Switch } from 'react-router-dom'; -import { NavBar } from '../../NavBar'; +// import Example33 from './example33'; -const routes = [ - { route: '/example1', component: Example1, title: '1- Basic Grid / 2 Grids' }, - { route: '/example2', component: Example2, title: '2- Formatters' }, - { route: '/example3', component: Example3, title: '3- Editors / Delete' }, - { route: '/example4', component: Example4, title: '4- Client Side Sort/Filter' }, - { route: '/example5', component: Example5, title: '5- Backend OData Service' }, - { route: '/example6', component: Example6, title: '6- Backend GraphQL Service' }, - { route: '/example7', component: Example7, title: '7- Header Button Plugin' }, - { route: '/example8', component: Example8, title: '8- Header Menu Plugin' }, - { route: '/example9', component: Example9, title: '9- Grid Menu Control' }, - { route: '/example10', component: Example10, title: '10- Row Selection / 2 Grids' }, - { route: '/example11', component: Example11, title: '11- Add/Update Grid Item' }, - { route: '/example12', component: Example12, title: '12- Localization (i18n)' }, - { route: '/example13', component: Example13, title: '13- Grouping & Aggregators' }, - { route: '/example14', component: Example14, title: '14- Column Span & Header Grouping' }, - { route: '/example15', component: Example15, title: '15- Grid State & Local Storage' }, - { route: '/example16', component: Example16, title: '16- Row Move Plugin' }, - { route: '/example17', component: Example17, title: '17- Remote Model' }, - { route: '/example18', component: Example18, title: '18- Draggable Grouping' }, - { route: '/example19', component: Example19, title: '19- Row Detail View' }, - { route: '/example20', component: Example20, title: '20- Pinned Columns/Rows' }, - { route: '/example21', component: Example21, title: '21- Grid AutoHeight (full height)' }, - { route: '/example22', component: Example22, title: '22- with Bootstrap Tabs' }, - { route: '/example23', component: Example23, title: '23- Filter by Range of Values' }, - { route: '/example24', component: Example24, title: '24- Cell & Context Menu' }, - { route: '/example25', component: Example25, title: '25- GraphQL without Pagination' }, - // { route: '/example26', component: Example26, title: '26- Use of Aurelia Components' }, - { route: '/example27', component: Example27, title: '27- Tree Data (Parent/Child)' }, - { route: '/example28', component: Example28, title: '28- Tree Data (Hierarchical set)' }, - { route: '/example29', component: Example29, title: '29- Grid with header and footer slots' }, - { route: '/example30', component: Example30, title: '30- Composite Editor Modal' }, - { route: '/example31', component: Example31, title: '31- Backend OData with RxJS' }, - { route: '/example32', component: Example32, title: '32- Columns Resize by Content' }, +const routes: Array<{ route: string; component: any; title: string; }> = [ + // { route: '/example1', component: React.lazy(() => import('./example1')), title: '1- Basic Grid / 2 Grids' }, + { route: '/example1', component: , title: '1- Basic Grid / 2 Grids' }, + { route: '/example2', component: , title: '2- Formatters' }, + { route: '/example3', component: , title: '3- Editors / Delete' }, + { route: '/example4', component: , title: '4- Client Side Sort/Filter' }, + { route: '/example5', component: , title: '5- Backend OData Service' }, + { route: '/example6', component: , title: '6- Backend GraphQL Service' }, + { route: '/example7', component: , title: '7- Header Button Plugin' }, + { route: '/example8', component: , title: '8- Header Menu Plugin' }, + { route: '/example9', component: , title: '9- Grid Menu Control' }, + { route: '/example10', component: , title: '10- Row Selection / 2 Grids' }, + { route: '/example11', component: , title: '11- Add/Update Grid Item' }, + { route: '/example12', component: , title: '12- Localization (i18n)' }, + { route: '/example13', component: , title: '13- Grouping & Aggregators' }, + { route: '/example14', component: , title: '14- Column Span & Header Grouping' }, + { route: '/example15', component: , title: '15- Grid State & Local Storage' }, + { route: '/example16', component: , title: '16- Row Move Plugin' }, + { route: '/example17', component: , title: '17- Remote Model' }, + { route: '/example18', component: , title: '18- Draggable Grouping' }, + { route: '/example19', component: , title: '19- Row Detail View' }, + { route: '/example20', component: , title: '20- Pinned Columns/Rows' }, + { route: '/example21', component: , title: '21- Grid AutoHeight (full height)' }, + { route: '/example22', component: , title: '22- with Bootstrap Tabs' }, + { route: '/example23', component: , title: '23- Filter by Range of Values' }, + { route: '/example24', component: , title: '24- Cell & Context Menu' }, + { route: '/example25', component: , title: '25- GraphQL without Pagination' }, + // { route: '/example26', component: , title: '26- Use of React Components' }, + { route: '/example27', component: , title: '27- Tree Data (Parent/Child)' }, + { route: '/example28', component: , title: '28- Tree Data (Hierarchical set)' }, + { route: '/example29', component: , title: '29- Grid with header and footer slots' }, + { route: '/example30', component: , title: '30- Composite Editor Modal' }, + { route: '/example31', component: , title: '31- Backend OData with RxJS' }, + { route: '/example32', component: , title: '32- Columns Resize by Content' }, + // { route: '/example33', component: , title: '33- Regular & Custom Tooltip' }, + // { route: '/example34', component: , title: '34- Real-Time Trading Platform' }, ]; @@ -91,16 +95,15 @@ export class App extends React.Component {
- - + + { routes.map(row => - + ) } - - - + +
diff --git a/src/examples/slickgrid/example1.tsx b/src/examples/slickgrid/example1.tsx index 044cd7f6..74581702 100644 --- a/src/examples/slickgrid/example1.tsx +++ b/src/examples/slickgrid/example1.tsx @@ -1,7 +1,7 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -import { Column, Formatters, GridOption, ReactSlickgridCustomElement } from '../../slickgrid-react'; import React from 'react'; +import { Column, Formatters, GridOption, ReactSlickgridComponent } from '../../slickgrid-react'; + const NB_ITEMS = 995; // eslint-disable-next-line @typescript-eslint/no-empty-interface @@ -10,8 +10,8 @@ interface Props { } interface State { title: string; subTitle: string; - gridOptions1: GridOption; - gridOptions2: GridOption; + gridOptions1?: GridOption; + gridOptions2?: GridOption; columnDefinitions1: Column[]; columnDefinitions2: Column[]; dataset1: any[]; @@ -32,26 +32,24 @@ export default class Example1 extends React.Component { dataset1: [], dataset2: [] }; - - // define the grid options & columns and then create the grid itself - this.defineGrids(); } componentDidMount() { document.title = this.state.title; + + // define the grid options & columns and then create the grid itself + this.defineGrids(); + // mock some data (different in each dataset) - // eslint-disable-next-line @typescript-eslint/no-unused-vars - this.setState((state: any, props: Props) => { - return { + this.setState((state: State, props: Props) => ({ dataset1: this.mockData(NB_ITEMS), dataset2: this.mockData(NB_ITEMS) - }; - }); + })); } /* Define grid Options and Columns */ defineGrids() { - const columns = [ + const columns: Column[] = [ { id: 'title', name: 'Title', field: 'title', sortable: true, minWidth: 100 }, { id: 'duration', name: 'Duration (days)', field: 'duration', sortable: true, minWidth: 100 }, { id: '%', name: '% Complete', field: 'percentComplete', sortable: true, minWidth: 100 }, @@ -59,7 +57,7 @@ export default class Example1 extends React.Component { { id: 'finish', name: 'Finish', field: 'finish', formatter: Formatters.dateIso }, { id: 'effort-driven', name: 'Effort Driven', field: 'effortDriven', sortable: true, minWidth: 100 } ]; - const gridOptions1 = { + const gridOptions1: GridOption = { gridHeight: 225, gridWidth: 800, enableAutoResize: false, @@ -68,7 +66,7 @@ export default class Example1 extends React.Component { // copy the same Grid Options and Column Definitions to 2nd grid // but also add Pagination in this grid - const gridOptions2 = { + const gridOptions2: GridOption = { ...gridOptions1, ...{ enablePagination: true, @@ -79,18 +77,18 @@ export default class Example1 extends React.Component { } }; - this.state = { - ...this.state, + this.setState((state: State) => ({ + ...state, columnDefinitions1: columns, columnDefinitions2: columns, gridOptions1, gridOptions2 - }; + })); } mockData(count: number) { // mock a dataset - const mockDataset = []; + const mockDataset: any[] = []; for (let i = 0; i < count; i++) { const randomYear = 2000 + Math.floor(Math.random() * 10); const randomMonth = Math.floor(Math.random() * 11); @@ -112,7 +110,7 @@ export default class Example1 extends React.Component { } render() { - return ( + return !this.state.gridOptions1 ? '' : (

{this.state.title} @@ -127,17 +125,17 @@ export default class Example1 extends React.Component {
{this.state.subTitle}

Grid 1

-

Grid 2 (with local Pagination)

-

); diff --git a/src/examples/slickgrid/example10.tsx b/src/examples/slickgrid/example10.tsx index f3428912..f24289db 100644 --- a/src/examples/slickgrid/example10.tsx +++ b/src/examples/slickgrid/example10.tsx @@ -1,10 +1,19 @@ -import { ReactGridInstance, Column, FieldType, Filters, Formatters, GridOption, GridStateChange, ReactSlickgridCustomElement } from '../../slickgrid-react'; +import { ReactGridInstance, Column, FieldType, Filters, Formatters, GridOption, GridStateChange, ReactSlickgridComponent } from '../../slickgrid-react'; import React from 'react'; import './example10.scss'; // provide custom CSS/SASS styling interface Props { } - -export default class Example10 extends React.Component { +interface State { + dataset1?: any[]; + dataset2?: any[]; + gridOptions1?: GridOption; + gridOptions2?: GridOption; + columnDefinitions1?: Column[]; + columnDefinitions2?: Column[]; + selectedTitle: string; + selectedTitles: any[]; +} +export default class Example10 extends React.Component { title = 'Example 10: Multiple Grids with Row Selection'; subTitle = ` Row selection, single or multi-select (Wiki docs). @@ -14,32 +23,33 @@ export default class Example10 extends React.Component {
  • NOTE: Any Row Selection(s) will be reset when using Pagination and changing Page (you will need to set it back manually if you want it back)
  • `; + isMounted = false; isGrid2WithPagination = true; reactGrid1!: ReactGridInstance; reactGrid2!: ReactGridInstance; - columnDefinitions1: Column[] = []; - columnDefinitions2: Column[] = []; - gridOptions1!: GridOption; - gridOptions2!: GridOption; - dataset1: any[] = []; - dataset2: any[] = []; - selectedTitles: any[] = []; - selectedTitle = ''; selectedGrid2IDs: number[] = []; constructor(public readonly props: Props) { super(props); - this.componentDidMount(); - // define the grid options & columns and then create the grid itself - this.defineGrids(); + + this.state = { + gridOptions1: undefined, + gridOptions2: undefined, + columnDefinitions1: [], + columnDefinitions2: [], + dataset1: [], + dataset2: [], + selectedTitle: '', + selectedTitles: [], + }; } componentDidMount() { document.title = this.title; - // populate the dataset once the grid is ready - this.dataset1 = this.prepareData(495); - this.dataset2 = this.prepareData(525); + + // define the grid options & columns and then create the grid itself + this.defineGrids(); } reactGrid1Ready(reactGrid: ReactGridInstance) { @@ -52,7 +62,7 @@ export default class Example10 extends React.Component { /* Define grid Options and Columns */ defineGrids() { - this.columnDefinitions1 = [ + const columnDefinitions1 = [ { id: 'title', name: 'Title', field: 'title', sortable: true, type: FieldType.string, filterable: true }, { id: 'duration', name: 'Duration (days)', field: 'duration', sortable: true, type: FieldType.number, filterable: true }, { id: 'complete', name: '% Complete', field: 'percentComplete', formatter: Formatters.percentCompleteBar, type: FieldType.number, filterable: true, sortable: true }, @@ -77,7 +87,7 @@ export default class Example10 extends React.Component { } ]; - this.columnDefinitions2 = [ + const columnDefinitions2 = [ { id: 'title', name: 'Title', field: 'title', sortable: true, type: FieldType.string, filterable: true }, { id: 'duration', name: 'Duration (days)', field: 'duration', sortable: true, type: FieldType.number, filterable: true }, { id: 'complete', name: '% Complete', field: 'percentComplete', formatter: Formatters.percentCompleteBar, type: FieldType.number, filterable: true, sortable: true }, @@ -102,7 +112,7 @@ export default class Example10 extends React.Component { } ]; - this.gridOptions1 = { + const gridOptions1: GridOption = { enableAutoResize: false, enableCellNavigation: true, enableRowSelection: true, @@ -140,7 +150,7 @@ export default class Example10 extends React.Component { }, }; - this.gridOptions2 = { + const gridOptions2: GridOption = { enableAutoResize: false, enableCellNavigation: true, enableFiltering: true, @@ -179,11 +189,22 @@ export default class Example10 extends React.Component { } }, }; + this.isMounted = true; + + this.setState((state: State) => ({ + ...state, + gridOptions1, + gridOptions2, + columnDefinitions1, + columnDefinitions2, + dataset1: this.prepareData(495), + dataset2: this.prepareData(525) + })); } prepareData(count: number) { // mock a dataset - const mockDataset = []; + const mockDataset: any[] = []; for (let i = 0; i < count; i++) { const randomYear = 2000 + Math.floor(Math.random() * 10); const randomMonth = Math.floor(Math.random() * 11); @@ -234,29 +255,41 @@ export default class Example10 extends React.Component { if (gridStateChanges.gridState!.rowSelection) { this.selectedGrid2IDs = (gridStateChanges.gridState!.rowSelection.filteredDataContextIds || []) as number[]; this.selectedGrid2IDs = this.selectedGrid2IDs.sort((a, b) => a - b); // sort by ID - this.selectedTitles = this.selectedGrid2IDs.map(dataContextId => `Task ${dataContextId}`); + this.setState((state: State) => ({ + ...state, + selectedTitles: this.selectedGrid2IDs.map(dataContextId => `Task ${dataContextId}`).join(','), + })); } } // Toggle the Pagination of Grid2 // IMPORTANT, the Pagination MUST BE CREATED on initial page load before you can start toggling it // Basically you cannot toggle a Pagination that doesn't exist (must created at the time as the grid) - isGrid2WithPaginationChanged() { - this.reactGrid2.paginationService!.togglePaginationVisibility(this.isGrid2WithPagination); + showGrid2Pagination(showPagination: boolean) { + console.log('toggle grid2 pagination', showPagination) + this.reactGrid2.paginationService!.togglePaginationVisibility(showPagination); } onGrid1SelectedRowsChanged(_e: Event, args: any) { const grid = args && args.grid; if (Array.isArray(args.rows)) { - this.selectedTitle = args.rows.map((idx: number) => { - const item = grid.getDataItem(idx); - return item && item.title || ''; - }); + this.setState((state: State) => ({ + ...state, + selectedTitle: args.rows.map((idx: number) => { + const item = grid.getDataItem(idx); + return item && item.title || ''; + }) + })); } } + onGrid2PaginationCheckChanged() { + this.isGrid2WithPagination = !this.isGrid2WithPagination; + this.showGrid2Pagination(this.isGrid2WithPagination); + } + render() { - return ( + return this.state.gridOptions1 && (

    {this.title} @@ -274,31 +307,30 @@ export default class Example10 extends React.Component {
    Pagination -
    (single select) Selected Row: - {this.selectedTitle} + {this.state.selectedTitle}

    - this.reactGrid1Ready($event), - onGridStateChanged: $event => this.grid1StateChanged($event), - onSelectedRowsChanged: $event => this.onGrid1SelectedRowsChanged($event.detail.eventData, $event.detail.args) - }} /> + this.reactGrid1Ready($event.detail)} + onGridStateChanged={$event => this.grid1StateChanged($event.detail)} + onSelectedRowsChanged={$event => this.onGrid1SelectedRowsChanged($event.detail.eventData, $event.detail.args)} + />

    @@ -308,16 +340,17 @@ export default class Example10 extends React.Component { {this.isGrid2WithPagination && } @@ -325,22 +358,18 @@ export default class Example10 extends React.Component {
    (multi-select) Selected Row(s): - {this.selectedTitles} + {this.state.selectedTitles}
    - - - this.reactGrid2Ready($event.detail), - onGridStateChanged: $event => this.grid2StateChanged($event.detail) - }}/> + this.reactGrid2Ready($event.detail)} + onGridStateChanged={$event => this.grid2StateChanged($event.detail)} />
    ); diff --git a/src/examples/slickgrid/example11.tsx b/src/examples/slickgrid/example11.tsx index 68e4e3d1..e25864f2 100644 --- a/src/examples/slickgrid/example11.tsx +++ b/src/examples/slickgrid/example11.tsx @@ -9,7 +9,7 @@ import { OnEventArgs, SlickDataView, SlickGrid, - ReactSlickgridCustomElement, + ReactSlickgridComponent, } from '../../slickgrid-react'; import React from 'react'; import './example11.scss'; @@ -154,7 +154,7 @@ export default class Example11 extends React.Component { mockData(itemCount: number) { // mock a dataset - const mockedDataset = []; + const mockedDataset: any[] = []; for (let i = 0; i < itemCount; i++) { const randomYear = 2000 + Math.floor(Math.random() * 10); const randomMonth = Math.floor(Math.random() * 11); @@ -296,41 +296,40 @@ export default class Example11 extends React.Component {
    -
    -
    - + - +

    - this.reactGridReady($event.detail), - }} /> + onReactGridCreated={$event => this.reactGridReady($event.detail)} + /> ); } diff --git a/src/examples/slickgrid/example13.tsx b/src/examples/slickgrid/example13.tsx index 9d191130..be6b2133 100644 --- a/src/examples/slickgrid/example13.tsx +++ b/src/examples/slickgrid/example13.tsx @@ -16,13 +16,18 @@ import { SortComparers, SlickDataView, SlickGrid, - ReactSlickgridCustomElement, + ReactSlickgridComponent, } from '../../slickgrid-react'; import React from 'react'; +import BaseSlickGridState from './state-slick-grid-base'; interface Props { } +interface State extends BaseSlickGridState { + dataset: any[], + processing: boolean; +} -export default class Example13 extends React.Component { +export default class Example13 extends React.Component { title = 'Example 13: Grouping & Aggregators'; subTitle = `
      @@ -34,26 +39,39 @@ export default class Example13 extends React.Component { `; reactGrid!: ReactGridInstance; - columnDefinitions: Column[] = []; - gridOptions!: GridOption; - dataset: any[] = []; dataviewObj!: SlickDataView; gridObj!: SlickGrid; - processing = false; excelExportService = new ExcelExportService(); textExportService = new TextExportService(); constructor(public readonly props: Props) { super(props); - this.componentDidMount(); - // define the grid options & columns and then create the grid itself - this.defineGrid(); + + this.state = { + columnDefinitions: [], + gridOptions: undefined, + processing: false, + dataset: this.loadData(500), + } } componentDidMount() { document.title = this.title; + + // define the grid options & columns and then create the grid itself + this.defineGrid(); + + // this.initDataLoad(); + } + + initDataLoad() { // populate the dataset once the grid is ready - this.loadData(500); + this.setState((state: State, props: Props) => { + return { + ...state, + dataset: this.loadData(500) + }; + }); } reactGridReady(reactGrid: ReactGridInstance) { @@ -64,7 +82,7 @@ export default class Example13 extends React.Component { /* Define grid Options and Columns */ defineGrid() { - this.columnDefinitions = [ + const columnDefinitions: Column[] = [ { id: 'sel', name: '#', field: 'num', width: 40, excludeFromExport: true, @@ -154,7 +172,7 @@ export default class Example13 extends React.Component { } ]; - this.gridOptions = { + const gridOptions: GridOption = { autoResize: { container: '#demo-container', rightPadding: 10 @@ -169,18 +187,26 @@ export default class Example13 extends React.Component { textExportOptions: { sanitizeDataExport: true }, registerExternalResources: [this.excelExportService, this.textExportService], }; + + this.setState((state: State, props: Props) => { + return { + ...state, + columnDefinitions, + gridOptions, + } + }); } loadData(rowCount: number) { // mock a dataset - this.dataset = []; + const dataset: any[] = []; for (let i = 0; i < rowCount; 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] = { + dataset[i] = { id: 'id_' + i, num: i, title: 'Task ' + i, @@ -193,6 +219,17 @@ export default class Example13 extends React.Component { effortDriven: (i % 5 === 0) }; } + + return dataset; + } + + updateData(rowCount: number) { + this.setState((state: State, props: Props) => { + return { + ...state, + dataset: this.loadData(rowCount), + } + }); } clearGrouping() { @@ -335,8 +372,17 @@ export default class Example13 extends React.Component { this.gridObj.invalidate(); // invalidate all rows and re-render } + changeProcessing(isProcessing: boolean) { + this.setState((state: State, props: Props) => { + return { + ...state, + processing: isProcessing + } + }); + } + render() { - return ( + return !this.state.gridOptions ? '' : (

      {this.title} @@ -352,23 +398,22 @@ export default class Example13 extends React.Component {
      - - - - - -
      @@ -379,7 +424,7 @@ export default class Example13 extends React.Component {
      - {!this.processing && + {this.state.processing && }
      - this.reactGridReady($event.detail), - }} /> + this.changeProcessing(true)} + onAfterExportToExcel={() => this.changeProcessing(false)} + onReactGridCreated={$event => this.reactGridReady($event.detail)} + />

      ); } diff --git a/src/examples/slickgrid/example14.tsx b/src/examples/slickgrid/example14.tsx index e274040c..5aade662 100644 --- a/src/examples/slickgrid/example14.tsx +++ b/src/examples/slickgrid/example14.tsx @@ -1,5 +1,5 @@ import { ExcelExportService } from '@slickgrid-universal/excel-export'; -import { ReactGridInstance, Column, FieldType, GridOption, ItemMetadata, ReactSlickgridCustomElement } from '../../slickgrid-react'; +import { ReactGridInstance, Column, FieldType, GridOption, ItemMetadata, ReactSlickgridComponent } from '../../slickgrid-react'; import React from 'react'; import './example14.scss'; // provide custom CSS/SASS styling @@ -170,7 +170,7 @@ export default class Example14 extends React.Component {

      Grid 1 (with Header Grouping & Colspan)

      - @@ -190,13 +190,12 @@ export default class Example14 extends React.Component { - this.reactGridReady2($event.detail), - }} /> + onReactGridCreated={$event => this.reactGridReady2($event.detail)} + /> ); } diff --git a/src/examples/slickgrid/example15.tsx b/src/examples/slickgrid/example15.tsx index 287db4a4..ad05bdc3 100644 --- a/src/examples/slickgrid/example15.tsx +++ b/src/examples/slickgrid/example15.tsx @@ -1,4 +1,7 @@ -import i18next from 'i18next'; +import i18next, { TFunction } from 'i18next'; +import React from 'react'; +import { withTranslation } from 'react-i18next'; + import { ReactGridInstance, Column, @@ -9,14 +12,16 @@ import { GridState, GridStateChange, MultipleSelectOption, - ReactSlickgridCustomElement + ReactSlickgridComponent } from '../../slickgrid-react'; -import React from 'react'; -i18next.init({ - lng: 'en', -} -); +import BaseSlickGridState from './state-slick-grid-base'; +interface Props { + t: TFunction; +} +interface State extends BaseSlickGridState { + selectedLanguage: string; +} function randomBetween(min: number, max: number) { return Math.floor(Math.random() * (max - min + 1) + min); @@ -25,9 +30,7 @@ const DEFAULT_PAGE_SIZE = 25; const LOCAL_STORAGE_KEY = 'gridState'; const NB_ITEMS = 500; -interface Props { } - -export default class Example15 extends React.Component { +class Example15 extends React.Component { title = 'Example 15: Grid State & Presets using Local Storage'; subTitle = ` Grid State & Preset (Wiki docs) @@ -42,31 +45,29 @@ export default class Example15 extends React.Component { `; reactGrid!: ReactGridInstance; - columnDefinitions: Column[] = []; - gridOptions!: GridOption; - dataset: any[] = []; - selectedLanguage: string; // private i18n: i18n; constructor(public readonly props: Props) { super(props); - const presets = JSON.parse(localStorage[LOCAL_STORAGE_KEY] || null); - - // use some Grid State preset defaults if you wish or just restore from Locale Storage - // presets = presets || this.useDefaultPresets(); - this.defineGrid(presets); - this.componentDidMount(); // always start with English for Cypress E2E tests to be consistent const defaultLang = 'en'; i18next.changeLanguage(defaultLang); - this.selectedLanguage = defaultLang; + this.state = { + gridOptions: undefined, + columnDefinitions: [], + selectedLanguage: defaultLang, + }; } componentDidMount() { document.title = this.title; - // populate the dataset once the grid is ready - this.dataset = this.getData(NB_ITEMS); + + const presets = JSON.parse(localStorage[LOCAL_STORAGE_KEY] || null); + + // use some Grid State preset defaults if you wish or just restore from Locale Storage + // presets = presets || this.useDefaultPresets(); + this.defineGrid(presets); } componentWillUnmount() { @@ -79,20 +80,63 @@ export default class Example15 extends React.Component { /** Clear the Grid State from Local Storage and reset the grid to it's original state */ clearGridStateFromLocalStorage() { - localStorage[LOCAL_STORAGE_KEY] = null; - this.reactGrid.gridService.resetGrid(this.columnDefinitions); + localStorage.removeItem(LOCAL_STORAGE_KEY); + // this.reactGrid.slickGrid.setColumns(this.reactGrid.gridService.getAllColumnDefinitions()); + // this.reactGrid.slickGrid.autosizeColumns(); + this.reactGrid.gridService.resetGrid(this.getColumnDefinitions()); this.reactGrid.paginationService!.changeItemPerPage(DEFAULT_PAGE_SIZE); } /* Define grid Options and Columns */ defineGrid(gridStatePresets?: GridState) { + const gridOptions: GridOption = { + autoResize: { + container: '#demo-container', + rightPadding: 10 + }, + enableCheckboxSelector: true, + enableFiltering: true, + enableTranslate: true, + // i18n: this.i18n, + columnPicker: { + hideForceFitButton: true + }, + gridMenu: { + hideForceFitButton: true, + hideClearFrozenColumnsCommand: false, + }, + headerMenu: { + hideFreezeColumnsCommand: false, + }, + enablePagination: true, + pagination: { + pageSizes: [5, 10, 15, 20, 25, 30, 40, 50, 75, 100], + pageSize: DEFAULT_PAGE_SIZE + }, + }; + + // reload the Grid State with the grid options presets + // but make sure the colums array is part of the Grid State before using them as presets + if (gridStatePresets) { + gridOptions.presets = gridStatePresets; + } + + this.setState((state: State) => ({ + ...state, + gridOptions, + columnDefinitions: this.getColumnDefinitions(), + dataset: this.getData(NB_ITEMS), + })); + } + + getColumnDefinitions(): Column[] { // prepare a multiple-select array to filter with - const multiSelectFilterArray = []; + const multiSelectFilterArray: any[] = []; for (let i = 0; i < NB_ITEMS; i++) { multiSelectFilterArray.push({ value: i, label: i }); } - this.columnDefinitions = [ + return [ { id: 'title', name: 'Title', @@ -147,43 +191,11 @@ export default class Example15 extends React.Component { } } ]; - - this.gridOptions = { - autoResize: { - container: '#demo-container', - rightPadding: 10 - }, - enableCheckboxSelector: true, - enableFiltering: true, - enableTranslate: true, - i18n: i18next, - columnPicker: { - hideForceFitButton: true - }, - gridMenu: { - hideForceFitButton: true, - hideClearFrozenColumnsCommand: false, - }, - headerMenu: { - hideFreezeColumnsCommand: false, - }, - enablePagination: true, - pagination: { - pageSizes: [5, 10, 15, 20, 25, 30, 40, 50, 75, 100], - pageSize: DEFAULT_PAGE_SIZE - }, - }; - - // reload the Grid State with the grid options presets - // but make sure the colums array is part of the Grid State before using them as presets - if (gridStatePresets) { - this.gridOptions.presets = gridStatePresets; - } } getData(count: number) { // mock a dataset - const tmpData = []; + const tmpData: any[] = []; for (let i = 0; i < count; i++) { const randomDuration = Math.round(Math.random() * 100); const randomYear = randomBetween(2000, 2025); @@ -225,9 +237,9 @@ export default class Example15 extends React.Component { } async switchLanguage() { - const nextLanguage = (this.selectedLanguage === 'en') ? 'fr' : 'en'; + const nextLanguage = (this.state.selectedLanguage === 'en') ? 'fr' : 'en'; await i18next.changeLanguage(nextLanguage); - this.selectedLanguage = nextLanguage; + this.setState((state: State) => ({ ...state, selectedLanguage: nextLanguage })); } useDefaultPresets() { @@ -257,7 +269,7 @@ export default class Example15 extends React.Component { } render() { - return ( + return !this.state.gridOptions ? '' : (

      {this.title} @@ -265,36 +277,37 @@ export default class Example15 extends React.Component { - code + code

      - - Locale: + Locale: - {this.selectedLanguage + '.json'} + {this.state.selectedLanguage + '.json'} - this.reactGridReady($event.detail), - onGridStateChanged: $event => this.gridStateChanged($event.detail), - }} /> + this.reactGridReady($event.detail)} + onGridStateChanged={$event => this.gridStateChanged($event.detail)} + />
      ); } } + +export default withTranslation()(Example15); \ No newline at end of file diff --git a/src/examples/slickgrid/example16.tsx b/src/examples/slickgrid/example16.tsx index d8f09dad..e2175e9d 100644 --- a/src/examples/slickgrid/example16.tsx +++ b/src/examples/slickgrid/example16.tsx @@ -1,9 +1,11 @@ -import { ReactGridInstance, Column, ExtensionName, Filters, Formatters, GridOption, ReactSlickgridCustomElement } from '../../slickgrid-react'; +import { ReactGridInstance, Column, ExtensionName, Filters, Formatters, GridOption, ReactSlickgridComponent } from '../../slickgrid-react'; import React from 'react'; +import BaseSlickGridState from './state-slick-grid-base'; interface Props { } +interface State extends BaseSlickGridState { } -export default class Example16 extends React.Component { +export default class Example16 extends React.Component { title = 'Example 16: Row Move & Checkbox Selector'; subTitle = ` This example demonstrates using the Slick.Plugins.RowMoveManager plugin to easily move a row in the grid.
      @@ -21,15 +23,17 @@ export default class Example16 extends React.Component {
    `; - reactGrid!: ReactGridInstance; - columnDefinitions: Column[] = []; - gridOptions!: GridOption; - dataset: any[] = []; constructor(public readonly props: Props) { super(props); - this.componentDidMount(); + + this.state = { + gridOptions: undefined, + columnDefinitions: [], + dataset: [], + }; + this.defineGrid(); } @@ -38,18 +42,23 @@ export default class Example16 extends React.Component { } get rowMoveInstance(): any { - return this.reactGrid && this.reactGrid.extensionService.getSlickgridAddonInstance(ExtensionName.rowMoveManager) || {}; + return this.reactGrid?.extensionService?.getExtensionInstanceByName(ExtensionName.rowMoveManager) || {}; } componentDidMount() { document.title = this.title; + // populate the dataset once the grid is ready - this.getData(); + this.setState((state: State, props: Props) => { + return { + dataset: this.getData(), + }; + }); } /* Define grid Options and Columns */ defineGrid() { - this.columnDefinitions = [ + const columnDefinitions: Column[] = [ { id: 'title', name: 'Title', field: 'title', filterable: true, }, { id: 'duration', name: 'Duration', field: 'duration', filterable: true, sortable: true }, { id: '%', name: '% Complete', field: 'percentComplete', filterable: true, sortable: true }, @@ -73,7 +82,7 @@ export default class Example16 extends React.Component { } ]; - this.gridOptions = { + const gridOptions: GridOption = { enableAutoResize: true, autoResize: { container: '#demo-container', @@ -126,11 +135,17 @@ export default class Example16 extends React.Component { } }, }; + + this.state = { + ...this.state, + columnDefinitions, + gridOptions, + }; } getData() { // Set up some test columns. - const mockDataset = []; + const mockDataset: any[] = []; for (let i = 0; i < 500; i++) { mockDataset[i] = { id: i, @@ -142,7 +157,7 @@ export default class Example16 extends React.Component { effortDriven: (i % 5 === 0) }; } - this.dataset = mockDataset; + return mockDataset; } onBeforeMoveRow(e: Event, data: { rows: number[]; insertBefore: number; }) { @@ -157,12 +172,13 @@ export default class Example16 extends React.Component { } onMoveRows(_e: Event, args: any) { + console.log('onMoveRows', _e, args) // rows and insertBefore references, // note that these references are assuming that the dataset isn't filtered at all // which is not always the case so we will recalcualte them and we won't use these reference afterward const rows = args.rows as number[]; const insertBefore = args.insertBefore; - const extractedRows = []; + const extractedRows: number[] = []; // when moving rows, we need to cancel any sorting that might happen // we can do this by providing an undefined sort comparer @@ -205,7 +221,12 @@ export default class Example16 extends React.Component { // final updated dataset, we need to overwrite the DataView dataset (and our local one) with this new dataset that has a new order const finalDataset = left.concat(extractedRows.concat(right)); - this.dataset = finalDataset; // update dataset and re-render the grid + + this.setState((state: State, props: Props) => { + return { + dataset: finalDataset, + }; + }); } hideDurationColumnDynamically() { @@ -259,26 +280,26 @@ export default class Example16 extends React.Component {
    - - -
    @@ -286,13 +307,12 @@ export default class Example16 extends React.Component {
    - this.reactGridReady($event.detail) - }} /> + this.reactGridReady($event.detail)} + />
    ); } diff --git a/src/examples/slickgrid/example17.tsx b/src/examples/slickgrid/example17.tsx index 5285df2e..78524399 100644 --- a/src/examples/slickgrid/example17.tsx +++ b/src/examples/slickgrid/example17.tsx @@ -8,7 +8,7 @@ import { Formatter, GridOption, SlickNamespace, - ReactSlickgridCustomElement + ReactSlickgridComponent } from '../../slickgrid-react'; import React from 'react'; @@ -193,16 +193,15 @@ export default class Example17 extends React.Component { Loading... } - this.reactGridReady($event.detail), - onViewportChanged: $event => this.onViewportChanged($event.detail.eventData, $event.detail.args), - onSort: $event => this.onSort($event.detail.eventData, $event.detail.args) - }} /> + onReactGridCreated={$event => this.reactGridReady($event.detail)} + onViewportChanged={$event => this.onViewportChanged()} + onSort={$event => this.onSort($event.detail.eventData, $event.detail.args)} + /> ); } diff --git a/src/examples/slickgrid/example18.tsx b/src/examples/slickgrid/example18.tsx index 6d567b88..f86f2a79 100644 --- a/src/examples/slickgrid/example18.tsx +++ b/src/examples/slickgrid/example18.tsx @@ -17,47 +17,52 @@ import { Grouping, SlickDataView, SlickGrid, - ReactSlickgridCustomElement + ReactSlickgridComponent } from '../../slickgrid-react'; import React from 'react'; +import BaseSlickGridState from './state-slick-grid-base'; interface Props { } +interface State extends BaseSlickGridState { + durationOrderByCount: boolean; + processing: boolean; + selectedGroupingFields: Array; +} -export default class Example18 extends React.Component { +export default class Example18 extends React.Component { title = 'Example 18: Draggable Grouping & Aggregators'; subTitle = `
      -
    • Wiki docs
    • -
    • This example shows 3 ways of grouping
    • -
        -
      1. Drag any Column Header on the top placeholder to group by that column (support moti-columns grouping by adding more columns to the drop area).
      2. -
      3. Use buttons and defined functions to group by whichever field you want
      4. -
      5. Use the Select dropdown to group, the position of the Selects represent the grouping level
      6. -
      -
    • Fully dynamic and interactive multi-level grouping with filtering and aggregates ovor 50'000 items
    • -
    • Each grouping level can have its own aggregates (over child rows, child groups, or all descendant rows)..
    • -
    • Use "Aggregators" and "GroupTotalFormatters" directly from Slickgrid-React
    • +
    • This example shows 3 ways of grouping Wiki docs
    • +
        +
      1. Drag any Column Header on the top placeholder to group by that column (support moti-columns grouping by adding more columns to the drop area).
      2. +
      3. Use buttons and defined functions to group by whichever field you want
      4. +
      5. Use the Select dropdown to group, the position of the Selects represent the grouping level
      6. +
      +
    • Fully dynamic and interactive multi-level grouping with filtering and aggregates ovor 50'000 items
    • +
    • Each grouping level can have its own aggregates (over child rows, child groups, or all descendant rows)..
    • +
    • Use "Aggregators" and "GroupTotalFormatters" directly from Slickgrid-React
    `; reactGrid!: ReactGridInstance; - columnDefinitions: Column[] = []; - dataset: any[] = []; dataviewObj!: SlickDataView; draggableGroupingPlugin: any; - durationOrderByCount = false; gridObj!: SlickGrid; - gridOptions!: GridOption; - processing = false; - selectedGroupingFields: Array = ['', '', '']; excelExportService = new ExcelExportService(); textExportService = new TextExportService(); constructor(public readonly props: Props) { super(props); - // define the grid options & columns and then create the grid itself - this.loadData(500); - this.defineGrid(); + + this.state = { + gridOptions: undefined, + columnDefinitions: [], + dataset: [], + durationOrderByCount: false, + processing: false, + selectedGroupingFields: ['', '', ''], + } } reactGridReady(reactGrid: ReactGridInstance) { @@ -66,9 +71,13 @@ export default class Example18 extends React.Component { this.dataviewObj = reactGrid.dataView; } + componentDidMount() { + this.defineGrid(); + } + /* Define grid Options and Columns */ defineGrid() { - this.columnDefinitions = [ + const columnDefinitions: Column[] = [ { id: 'title', name: 'Title', field: 'title', width: 70, minWidth: 50, @@ -97,7 +106,7 @@ export default class Example18 extends React.Component { getter: 'duration', formatter: (g) => `Duration: ${g.value} (${g.count} items)`, comparer: (a, b) => { - return this.durationOrderByCount ? (a.count - b.count) : SortComparers.numeric(a.value, b.value, SortDirectionNumber.asc); + return this.state.durationOrderByCount ? (a.count - b.count) : SortComparers.numeric(a.value, b.value, SortDirectionNumber.asc); }, aggregators: [ new Aggregators.Sum('cost') @@ -206,7 +215,7 @@ export default class Example18 extends React.Component { } ]; - this.gridOptions = { + const gridOptions: GridOption = { autoResize: { container: '#demo-container', rightPadding: 10 @@ -241,18 +250,25 @@ export default class Example18 extends React.Component { textExportOptions: { sanitizeDataExport: true }, registerExternalResources: [this.excelExportService, this.textExportService], }; + + this.setState((state: State) => ({ + ...state, + gridOptions, + columnDefinitions, + dataset: this.loadData(500) + })); } loadData(rowCount: number) { // mock a dataset - this.dataset = []; + const tmpData: any[] = []; for (let i = 0; i < rowCount; 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] = { + tmpData[i] = { id: 'id_' + i, num: i, title: 'Task ' + i, @@ -265,6 +281,14 @@ export default class Example18 extends React.Component { effortDriven: (i % 5 === 0) }; } + return tmpData; + } + + setData(rowCount: number) { + this.setState((state: State, props: Props) => ({ + ...state, + dataset: this.loadData(rowCount) + })); } clearGroupsAndSelects() { @@ -273,12 +297,42 @@ export default class Example18 extends React.Component { } clearGroupingSelects() { - this.selectedGroupingFields.forEach((_g, i) => this.selectedGroupingFields[i] = ''); - this.selectedGroupingFields = [...this.selectedGroupingFields]; // force dirty checking + this.state.selectedGroupingFields.forEach((_g, i) => this.state.selectedGroupingFields[i] = ''); + this.setState((state: State) => ({ + ...state, + selectedGroupingFields: ['', '', ''], // force dirty checking + })); + + // reset all select dropdown using JS + this.state.selectedGroupingFields.forEach((_val, index) => this.dynamicallyChangeSelectGroupByValue(index, '')); + } + + changeSelectedGroupByField(e: React.ChangeEvent, index: number) { + const val = (e.target as HTMLSelectElement).value; + this.updateSelectGroupFieldsArray(index, val, () => this.groupByFieldName()); + } + + /** Change the select dropdown group using pure JS */ + dynamicallyChangeSelectGroupByValue(selectGroupIndex = 0, val = '') { + const selectElm = document.querySelector(`.select-group-${selectGroupIndex}`); + if (selectElm) { + selectElm.selectedIndex = Array.from(selectElm.options).findIndex(o => o.value === val); + this.updateSelectGroupFieldsArray(selectGroupIndex, val); + } + } + + /** update grouping field array React state */ + updateSelectGroupFieldsArray(index: number, val: string, setStateCallback?: () => void) { + const tmpSelectedGroupingFields = this.state.selectedGroupingFields; + tmpSelectedGroupingFields[index] = val; + this.setState((state: State) => ({ + ...state, + selectedGroupingFields: [...tmpSelectedGroupingFields], // force dirty checking + }), setStateCallback); } clearGrouping() { - if (this.draggableGroupingPlugin && this.draggableGroupingPlugin.setDroppedGroups) { + if (this.draggableGroupingPlugin?.setDroppedGroups) { this.draggableGroupingPlugin.clearDroppedGroups(); } this.gridObj.invalidate(); // invalidate all rows and re-render @@ -309,15 +363,20 @@ export default class Example18 extends React.Component { groupByDuration() { this.clearGrouping(); - if (this.draggableGroupingPlugin && this.draggableGroupingPlugin.setDroppedGroups) { + this.clearGroupingSelects(); + + if (this.draggableGroupingPlugin) { this.showPreHeader(); this.draggableGroupingPlugin.setDroppedGroups('duration'); this.gridObj.invalidate(); // invalidate all rows and re-render } + // use JS to change 1st select dropdown value + this.dynamicallyChangeSelectGroupByValue(0, 'duration'); } groupByDurationOrderByCount(sortedByCount = false) { - this.durationOrderByCount = sortedByCount; + this.setState((state: State) => ({ ...state, durationOrderByCount: sortedByCount })); + this.clearGrouping(); this.groupByDuration(); @@ -329,24 +388,28 @@ export default class Example18 extends React.Component { groupByDurationEffortDriven() { this.clearGrouping(); - if (this.draggableGroupingPlugin && this.draggableGroupingPlugin.setDroppedGroups) { + if (this.draggableGroupingPlugin) { this.showPreHeader(); - this.draggableGroupingPlugin.setDroppedGroups(['duration', 'effortDriven']); + const groupingFields = ['duration', 'effortDriven']; + this.draggableGroupingPlugin.setDroppedGroups(groupingFields); this.gridObj.invalidate(); // invalidate all rows and re-render // you need to manually add the sort icon(s) in UI const sortColumns = [{ columnId: 'duration', sortAsc: true }]; this.reactGrid.filterService.setSortColumnIcons(sortColumns); + + // use JS to change 1st select dropdown value + groupingFields.forEach((groupingVal, index) => this.dynamicallyChangeSelectGroupByValue(index, groupingVal)); } } groupByFieldName() { this.clearGrouping(); - if (this.draggableGroupingPlugin && this.draggableGroupingPlugin.setDroppedGroups) { + if (this.draggableGroupingPlugin?.setDroppedGroups) { this.showPreHeader(); // get the field names from Group By select(s) dropdown, but filter out any empty fields - const groupedFields = this.selectedGroupingFields.filter((g) => g !== ''); + const groupedFields = this.state.selectedGroupingFields.filter((g) => g !== ''); if (groupedFields.length === 0) { this.clearGrouping(); } else { @@ -357,13 +420,15 @@ export default class Example18 extends React.Component { } onGroupChanged(change: { caller?: string; groupColumns: Grouping[] }) { + console.log('onGroupChanged', change); const caller = change && change.caller || []; const groups = change && change.groupColumns || []; + const tmpSelectedGroupingFields = this.state.selectedGroupingFields; - if (Array.isArray(this.selectedGroupingFields) && Array.isArray(groups) && groups.length > 0) { + if (Array.isArray(tmpSelectedGroupingFields) && Array.isArray(groups) && groups.length > 0) { // update all Group By select dropdown - this.selectedGroupingFields.forEach((_g, i) => this.selectedGroupingFields[i] = groups[i] && groups[i].getter || ''); - this.selectedGroupingFields = [...this.selectedGroupingFields]; // force dirty checking + tmpSelectedGroupingFields.forEach((_g, i) => tmpSelectedGroupingFields[i] = groups[i]?.getter ?? ''); + this.setState((state: State) => ({ ...state, selectedGroupingFields: [...tmpSelectedGroupingFields] })); } else if (groups.length === 0 && caller === 'remove-group') { this.clearGroupingSelects(); } @@ -394,7 +459,7 @@ export default class Example18 extends React.Component { } render() { - return ( + return !this.state.gridOptions ? '' : (

    {this.title} @@ -408,30 +473,28 @@ export default class Example18 extends React.Component {

    -
    + e.preventDefault()}>
    - - - - - - -
    @@ -448,15 +511,15 @@ export default class Example18 extends React.Component { Group by duration & sort groups by count
    @@ -468,14 +531,13 @@ export default class Example18 extends React.Component {
    { - this.selectedGroupingFields.map((groupField) => -
    - this.changeSelectedGroupByField($event, index)}> { - this.columnDefinitions.map((column) => - + this.state.columnDefinitions.map((column) => + ) } @@ -492,13 +554,12 @@ export default class Example18 extends React.Component {
    - this.reactGridReady($event.detail) - }} /> + this.reactGridReady($event.detail)} + />
    ); } diff --git a/src/examples/slickgrid/example19.tsx b/src/examples/slickgrid/example19.tsx index 28d20f79..495b9878 100644 --- a/src/examples/slickgrid/example19.tsx +++ b/src/examples/slickgrid/example19.tsx @@ -1,4 +1,3 @@ -import { Subscription } from 'react-event-aggregator'; import { ReactGridInstance, Column, @@ -7,7 +6,7 @@ import { Filters, Formatters, GridOption, - ReactSlickgridCustomElement, + ReactSlickgridComponent, } from '../../slickgrid-react'; import React from 'react'; @@ -29,10 +28,9 @@ export default class Example19 extends React.Component { gridOptions!: GridOption; columnDefinitions: Column[] = []; dataset: any[] = []; - extensions!: ExtensionList; + extensions!: ExtensionList; flashAlertType = 'info'; message = ''; - subscriptions: Subscription[] = []; constructor(public readonly props: Props) { super(props); @@ -48,7 +46,7 @@ export default class Example19 extends React.Component { return this.extensions.rowDetailView.instance || {}; // OR options 2 - // return this.reactGrid && this.reactGrid.extensionService.getSlickgridAddonInstance(ExtensionName.rowDetailView) || {}; + // return this.reactGrid && this.reactGrid.extensionService.getExtensionInstanceByName(ExtensionName.rowDetailView) || {}; } componentDidMount() { @@ -57,6 +55,10 @@ export default class Example19 extends React.Component { this.getData(); } + reactGridReady(reactGrid: ReactGridInstance) { + this.reactGrid = reactGrid; + } + /* Define grid Options and Columns */ defineGrid() { this.columnDefinitions = [ @@ -208,7 +210,7 @@ export default class Example19 extends React.Component {
    -    @@ -216,7 +218,7 @@ export default class Example19 extends React.Component { @@ -225,12 +227,13 @@ export default class Example19 extends React.Component {
    - + onReactGridCreated={$event => this.reactGridReady($event.detail)} + />
    ); } diff --git a/src/examples/slickgrid/example2.tsx b/src/examples/slickgrid/example2.tsx index ce359c49..27f1b71b 100644 --- a/src/examples/slickgrid/example2.tsx +++ b/src/examples/slickgrid/example2.tsx @@ -6,9 +6,10 @@ import { Formatter, Formatters, GridOption, - ReactSlickgridCustomElement, + ReactSlickgridComponent, } from '../../slickgrid-react'; import React from 'react'; +import BaseSlickGridState from './state-slick-grid-base'; interface DataItem { id: number; @@ -22,12 +23,7 @@ interface DataItem { phone: string; completed: number; } - -interface State { - gridOptions: GridOption; - columnDefinitions: Column[]; - dataset: any[]; -} +interface State extends BaseSlickGridState { } // create my custom Formatter with the Formatter type const myCustomCheckmarkFormatter: Formatter = (_row, _cell, value) => { @@ -47,9 +43,10 @@ const customEnableButtonFormatter: Formatter = (_row: number, _cell: n interface Props {} export default class Example2 extends React.Component { + isGridMounted = false; title = 'Example 2: Grid with Formatters'; subTitle = ` - Grid with Custom and/or included Slickgrid Formatters (Wiki docs). + Grid with Custom and/or included Slickgrid Formatters (Wiki docs).
    • The 2 last columns are using Custom Formatters
      • The "Completed" column uses a the "onCellClick" event and a formatter to simulate a toggle action
      @@ -79,19 +76,21 @@ export default class Example2 extends React.Component { componentDidMount() { document.title = this.title; + // populate the dataset once the grid is ready - // eslint-disable-next-line @typescript-eslint/no-unused-vars - this.setState((state: any, props: Props) => { - return { + this.setState((state: State, props: Props) => ({ dataset: this.getData(), - }; - }); + })); + } + + reactGridReady(reactGrid: ReactGridInstance) { + this.reactGrid = reactGrid; } /* 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 - const columns = [ + const columns: Column[] = [ { 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 }, @@ -109,7 +108,7 @@ export default class Example2 extends React.Component { } ]; - const gridOptions = { + const gridOptions: GridOption = { autoResize: { container: '#demo-container', rightPadding: 10 @@ -150,11 +149,13 @@ export default class Example2 extends React.Component { columnDefinitions : columns, gridOptions, }; + this.isGridMounted = true; } + // mock a dataset getData() { - // mock a dataset - const mockDataset = []; + const mockDataset: any[] = []; + for (let i = 0; i < 500; i++) { const randomYear = 2000 + Math.floor(Math.random() * 10); const randomMonth = Math.floor(Math.random() * 11); @@ -177,7 +178,7 @@ export default class Example2 extends React.Component { return mockDataset; } - generatePhoneNumber() { + generatePhoneNumber(): string { let phone = ''; for (let i = 0; i < 10; i++) { phone += Math.round(Math.random() * 9) + ''; @@ -187,7 +188,7 @@ export default class Example2 extends React.Component { togglePauseResizer() { this.resizerPaused = !this.resizerPaused; - this.reactGrid.resizerService.pauseResizer(this.resizerPaused); + this.reactGrid?.resizerService.pauseResizer(this.resizerPaused); } toggleCompletedProperty(item: any) { @@ -197,13 +198,13 @@ export default class Example2 extends React.Component { // simulate a backend http call and refresh the grid row after delay setTimeout(() => { - this.reactGrid.gridService.updateItemById(item.id, item, { highlightRow: false }); + this.reactGrid?.gridService.updateItemById(item.id, item, { highlightRow: false }); }, 250); } } render() { - return ( + return !this.isGridMounted ? '' : (

      {this.title} @@ -217,14 +218,16 @@ export default class Example2 extends React.Component {

      - + dataset={this.state.dataset} + onReactGridCreated={$event => this.reactGridReady($event.detail)} + />
      ); } diff --git a/src/examples/slickgrid/example20.tsx b/src/examples/slickgrid/example20.tsx index 143eb7c3..4ea8f16d 100644 --- a/src/examples/slickgrid/example20.tsx +++ b/src/examples/slickgrid/example20.tsx @@ -10,16 +10,23 @@ import { GridOption, SlickGrid, SlickNamespace, - ReactSlickgridCustomElement + ReactSlickgridComponent, + DOMEvent } from '../../slickgrid-react'; import React from 'react'; import './example20.scss'; // provide custom CSS/SASS styling +import BaseSlickGridState from './state-slick-grid-base'; declare const Slick: SlickNamespace; interface Props { } +interface State extends BaseSlickGridState { + frozenColumnCount: number; + frozenRowCount: number; + isFrozenBottom: boolean; +} -export default class Example20 extends React.Component { +export default class Example20 extends React.Component { title = 'Example 20: Pinned (frozen) Columns/Rows'; subTitle = ` This example demonstrates the use of Pinned (aka frozen) Columns and/or Rows (Wiki docs) @@ -32,20 +39,20 @@ export default class Example20 extends React.Component { `; reactGrid!: ReactGridInstance; - columnDefinitions: Column[] = []; gridObj!: SlickGrid; - gridOptions!: GridOption; - frozenColumnCount = 2; - frozenRowCount = 3; - isFrozenBottom = false; - dataset: any[] = []; slickEventHandler: any; constructor(public readonly props: Props) { super(props); - this.defineGrid(); - this.componentDidMount(); this.slickEventHandler = new Slick.EventHandler(); + + this.state = { + gridOptions: undefined, + columnDefinitions: [], + frozenColumnCount: 2, + frozenRowCount: 3, + isFrozenBottom: false, + } } reactGridReady(reactGrid: ReactGridInstance) { @@ -61,15 +68,15 @@ export default class Example20 extends React.Component { highlightRow(event: Event, isMouseEnter: boolean) { const cell = this.gridObj.getCellFromEvent(event); - const rows = isMouseEnter ? [cell.row] : []; + const rows = isMouseEnter ? [cell?.row ?? 0] : []; this.gridObj.setSelectedRows(rows); // highlight current row event.preventDefault(); } componentDidMount() { document.title = this.title; - // populate the dataset once the grid is ready - this.getData(); + + this.defineGrid(); } componentWillUnmount() { @@ -79,7 +86,7 @@ export default class Example20 extends React.Component { /* Define grid Options and Columns */ defineGrid() { - this.columnDefinitions = [ + const columnDefinitions: Column[] = [ { id: 'sel', name: '#', field: 'id', minWidth: 40, width: 40, maxWidth: 40, @@ -224,7 +231,7 @@ export default class Example20 extends React.Component { } ]; - this.gridOptions = { + const gridOptions: GridOption = { autoResize: { container: '#demo-container', rightPadding: 10 @@ -234,19 +241,26 @@ export default class Example20 extends React.Component { editable: true, autoEdit: true, enableExcelCopyBuffer: true, - frozenColumn: this.frozenColumnCount, - frozenRow: this.frozenRowCount, + frozenColumn: 2, + frozenRow: 3, // frozenBottom: true, // if you want to freeze the bottom instead of the top, you can enable this property // show both Frozen Columns in HeaderMenu & GridMenu, these are opt-in commands so they're disabled by default gridMenu: { hideClearFrozenColumnsCommand: false }, headerMenu: { hideFreezeColumnsCommand: false } }; + + this.setState((state: State) => ({ + ...state, + gridOptions, + columnDefinitions, + dataset: this.getData() + })); } getData() { // Set up some test columns. - const mockDataset = []; + const mockDataset: any[] = []; for (let i = 0; i < 500; i++) { mockDataset[i] = { id: i, @@ -263,25 +277,35 @@ export default class Example20 extends React.Component { title4: `Some Text ${Math.round(Math.random() * 25)}`, }; } - this.dataset = mockDataset; + return mockDataset; } /** change dynamically, through slickgrid "setOptions()" the number of pinned columns */ - changeFrozenColumnCount() { - if (this.gridObj && this.gridObj.setOptions) { - this.gridObj.setOptions({ - frozenColumn: this.frozenColumnCount - }); - } + changeFrozenColumnCount(e: React.FormEvent) { + this.setState((state: State) => ({ + ...state, + frozenColumnCount: +((e.target as HTMLInputElement)?.value ?? 0), + })); + } + + setFrozenColumnCount() { + this.gridObj?.setOptions({ + frozenColumn: this.state.frozenColumnCount + }); } /** change dynamically, through slickgrid "setOptions()" the number of pinned rows */ - changeFrozenRowCount() { - if (this.gridObj && this.gridObj.setOptions) { - this.gridObj.setOptions({ - frozenRow: this.frozenRowCount - }); - } + changeFrozenRowCount(e: React.FormEvent) { + this.setState((state: State) => ({ + ...state, + frozenRowCount: +((e.target as HTMLInputElement)?.value ?? 0), + })); + } + + setFrozenRowCount() { + this.gridObj?.setOptions({ + frozenRow: this.state.frozenRowCount + }); } costDurationFormatter(_row: number, _cell: number, _value: any, _columnDef: Column, dataContext: any) { @@ -303,21 +327,29 @@ export default class Example20 extends React.Component { setFrozenColumns(frozenCols: number) { this.gridObj.setOptions({ frozenColumn: frozenCols }); - this.gridOptions = this.gridObj.getOptions(); + + this.setState((state: State) => ({ + ...state, + gridOptions: this.gridObj.getOptions(), + })); } /** toggle dynamically, through slickgrid "setOptions()" the top/bottom pinned location */ toggleFrozenBottomRows() { if (this.gridObj && this.gridObj.setOptions) { this.gridObj.setOptions({ - frozenBottom: !this.isFrozenBottom + frozenBottom: !this.state.isFrozenBottom }); - this.isFrozenBottom = !this.isFrozenBottom; // toggle the variable + + this.setState((state: State) => ({ + ...state, + isFrozenBottom: !state.isFrozenBottom, + })); } } render() { - return ( + return !this.state.gridOptions ? '' : (

      {this.title} @@ -337,19 +369,15 @@ export default class Example20 extends React.Component {
      - - - - @@ -367,10 +395,10 @@ export default class Example20 extends React.Component { Set 3 Frozen Columns - - : {this.isFrozenBottom ? 'Bottom' : 'Top'} + : {this.state.isFrozenBottom ? 'Bottom' : 'Top'}

      @@ -379,14 +407,12 @@ export default class Example20 extends React.Component {
    - this.onCellValidationError($event.detail.eventData, $event.detail.args), - onReactGridCreated: $event => this.reactGridReady($event.detail) - }} + this.reactGridReady($event.detail)} + onValidationError={$event => this.onCellValidationError($event.detail.eventData, $event.detail.args)} /> ); diff --git a/src/examples/slickgrid/example21.tsx b/src/examples/slickgrid/example21.tsx index a75a651d..447fae1f 100644 --- a/src/examples/slickgrid/example21.tsx +++ b/src/examples/slickgrid/example21.tsx @@ -1,21 +1,24 @@ -import { bindable } from 'react-framework'; import { ReactGridInstance, Column, FieldType, Formatters, - GridOption, OperatorString, - ReactSlickgridCustomElement + ReactSlickgridComponent, + GroupingGetterFunction, + GridOption } from '../../slickgrid-react'; import React from 'react'; +import BaseSlickGridState from './state-slick-grid-base'; interface Props { } - -export default class Example21 extends React.Component { - selectedColumn!: Column; - selectedOperator!: string; - searchValue = ''; +interface State extends BaseSlickGridState { + selectedColumn?: Column; + selectedOperator: string; + searchValue: string; + reactGrid?: ReactGridInstance; +} +export default class Example21 extends React.Component { title = 'Example 21: Grid AutoHeight'; subTitle = ` The SlickGrid option "autoHeight" can be used if you wish to keep the full height of the grid without any scrolling @@ -27,27 +30,35 @@ export default class Example21 extends React.Component { `; selectedGroupingFields: Array = ['', '', '']; reactGrid!: ReactGridInstance; - columnDefinitions: Column[] = []; - gridOptions!: GridOption; - dataset: any[] = []; operatorList: OperatorString[] = ['=', '<', '<=', '>', '>=', '<>', 'StartsWith', 'EndsWith']; constructor(public readonly props: Props) { super(props); - this.componentDidMount(); - // define the grid options & columns and then create the grid itself - this.defineGrid(); + + this.state = { + gridOptions: undefined, + columnDefinitions: [], + dataset: [], + selectedColumn: undefined, + selectedOperator: '', + searchValue: '', + }; } componentDidMount() { document.title = this.title; - // populate the dataset once the grid is ready - this.getData(); + + // define the grid options & columns and then create the grid itself + this.defineGrid(); + } + + reactGridReady(reactGrid: ReactGridInstance) { + this.reactGrid = reactGrid; } /* Define grid Options and Columns */ defineGrid() { - this.columnDefinitions = [ + const columnDefinitions: Column[] = [ { id: 'title', name: 'Title', field: 'title', width: 100, @@ -87,7 +98,7 @@ export default class Example21 extends React.Component { } ]; - this.gridOptions = { + const gridOptions: GridOption = { // if you want to disable autoResize and use a fixed width which requires horizontal scrolling // it's advised to disable the autoFitColumnsOnFirstLoad as well // enableAutoResize: false, @@ -109,11 +120,20 @@ export default class Example21 extends React.Component { enableCellNavigation: true, enableRowSelection: true }; + + this.setState((state: State, props: Props) => { + return { + ...state, + gridOptions, + columnDefinitions, + dataset: this.getData(), + }; + }); } getData() { // mock a dataset - const mockedDataset = []; + const mockedDataset: any[] = []; for (let i = 0; i < 25; i++) { const randomYear = 2000 + Math.floor(Math.random() * 10); const randomMonth = Math.floor(Math.random() * 11); @@ -131,7 +151,7 @@ export default class Example21 extends React.Component { effortDriven: (i % 5 === 0) }; } - this.dataset = mockedDataset; + return mockedDataset; } generatePhoneNumber() { @@ -146,33 +166,51 @@ export default class Example21 extends React.Component { // -- if any of the Search form input changes, we'll call the updateFilter() method // - cleargridSearchInput() { - this.searchValue = ''; - this.updateFilter(); + clearGridSearchInput() { + this.setState((state: State, props: Props) => { + return { + ...state, + searchValue: '', + } + }, () => this.updateFilter()); } - selectedOperatorChanged() { - this.updateFilter(); + selectedOperatorChanged(e: React.FormEvent) { + this.setState((state: State, props: Props) => { + return { + ...state, + selectedOperator: (e.target as HTMLSelectElement)?.value ?? '', + }; + }, () => this.updateFilter()); } - selectedColumnChanged() { - this.updateFilter(); + selectedColumnChanged(e: React.ChangeEvent) { + const selectedVal = (e.target as HTMLSelectElement)?.value ?? ''; + const selectedColumn = this.state.columnDefinitions.find(c => c.id === selectedVal); + + this.setState((state: State) => + ({ ...state, selectedColumn }), + () => this.updateFilter() + ); } - searchValueChanged() { - this.updateFilter(); + searchValueChanged(e: React.FormEvent) { + this.setState((state: State) => + ({ ...state, searchValue: (e.target as HTMLInputElement)?.value ?? '' }), + () => this.updateFilter() + ); } updateFilter() { this.reactGrid?.filterService.updateSingleFilter({ - columnId: `${this.selectedColumn.id || ''}`, - operator: this.selectedOperator as OperatorString, - searchTerms: [this.searchValue || ''] + columnId: `${this.state.selectedColumn?.id ?? ''}`, + operator: this.state.selectedOperator as OperatorString, + searchTerms: [this.state.searchValue || ''] }); } render() { - return ( + return !this.state.gridOptions ? '' : (

    {this.title} @@ -191,23 +229,21 @@ export default class Example21 extends React.Component {

    - this.selectedColumnChanged($event)}> { - this.columnDefinitions.map((column) => - + this.state.columnDefinitions.map((column) => + ) }
    - this.selectedOperatorChanged($event)}> { - this.columnDefinitions.map((column) => - + this.operatorList.map((operator) => + ) } @@ -219,10 +255,10 @@ export default class Example21 extends React.Component { className="form-control" placeholder="search value" data-test="search-value-input" - value="searchValue" /> + onInput={($event) => this.searchValueChanged($event)} />
    @@ -230,11 +266,12 @@ export default class Example21 extends React.Component {
    - + this.reactGridReady($event.detail)} + /> ); } diff --git a/src/examples/slickgrid/example22.tsx b/src/examples/slickgrid/example22.tsx index 47e0b872..1976adda 100644 --- a/src/examples/slickgrid/example22.tsx +++ b/src/examples/slickgrid/example22.tsx @@ -1,13 +1,21 @@ -import { HttpClient } from 'react-fetch-client'; -import { ReactGridInstance, Column, Filters, GridOption, ReactSlickgridCustomElement } from '../../slickgrid-react'; +import { ReactGridInstance, Column, Filters, GridOption, ReactSlickgridComponent } from '../../slickgrid-react'; import React from 'react'; import './example22.scss'; +import BaseSlickGridState from './state-slick-grid-base'; const URL_CUSTOMERS = 'assets/data/customers_100.json'; interface Props { } +interface State { + gridOptions1?: GridOption; + gridOptions2?: GridOption; + columnDefinitions1: Column[]; + columnDefinitions2: Column[]; + dataset1: any[]; + dataset2: any[]; +} -export default class Example22 extends React.Component { +export default class Example22 extends React.Component { title = 'Example 22: Grids in Bootstrap Tabs'; subTitle = `This example demonstrate the creation of multiple grids in Bootstrap Tabs
      @@ -16,20 +24,20 @@ export default class Example22 extends React.Component {
    `; reactGrid2!: ReactGridInstance; - gridOptions1!: GridOption; - gridOptions2!: GridOption; - columnDefinitions1: Column[] = []; - columnDefinitions2: Column[] = []; - dataset1: any[] = []; - dataset2: any[] = []; + isGrid2DataLoaded = false; isGrid2Resize = false; - constructor(public readonly props: Props, private http: HttpClient) { + constructor(public readonly props: Props) { super(props); - // define the grid options & columns and then create the grid itself - this.defineGrid1(); - this.defineGrid2(); - this.componentDidMount(); + + this.state = { + gridOptions1: undefined, + gridOptions2: undefined, + columnDefinitions1: [], + columnDefinitions2: [], + dataset1: [], + dataset2: [], + } } reactGrid2Ready(reactGrid: ReactGridInstance) { @@ -38,17 +46,14 @@ export default class Example22 extends React.Component { async componentDidMount() { document.title = this.title; - // mock some data with JavaScript - this.dataset1 = this.mockData(); - // load data with Fetch-Client - const response2 = await this.http.fetch(URL_CUSTOMERS); - this.dataset2 = await response2['json'](); + this.defineGrids(); } // Grid2 definition - defineGrid1() { - this.columnDefinitions1 = [ + defineGrids() { + // grid 1 + const columnDefinitions1 = [ { id: 'title', name: 'Title', field: 'title', sortable: true, minWidth: 100 }, { id: 'duration', name: 'Duration (days)', field: 'duration', sortable: true, minWidth: 100 }, { id: '%', name: '% Complete', field: 'percentComplete', sortable: true, minWidth: 100 }, @@ -56,7 +61,7 @@ export default class Example22 extends React.Component { { id: 'finish', name: 'Finish', field: 'finish', minWidth: 100 }, { id: 'effort-driven', name: 'Effort Driven', field: 'effortDriven', sortable: true, minWidth: 100 } ]; - this.gridOptions1 = { + const gridOptions1: GridOption = { enableAutoResize: true, autoResize: { container: '#demo-container', @@ -65,11 +70,8 @@ export default class Example22 extends React.Component { enableSorting: true }; - } - - // Grid2 definition - defineGrid2() { - this.columnDefinitions2 = [ + // grid 2 + const columnDefinitions2 = [ { id: 'name', name: 'Name', field: 'name', filterable: true, sortable: true, }, { id: 'gender', name: 'Gender', field: 'gender', filterable: true, sortable: true, @@ -81,7 +83,7 @@ export default class Example22 extends React.Component { { id: 'company', name: 'Company', field: 'company', filterable: true, sortable: true } ]; - this.gridOptions2 = { + const gridOptions2: GridOption = { enableAutoResize: true, autoResize: { container: '#demo-container', @@ -91,11 +93,35 @@ export default class Example22 extends React.Component { enableSorting: true }; + this.setState((state: State, props: Props) => { + return { + ...state, + gridOptions1, + gridOptions2, + columnDefinitions1, + columnDefinitions2, + dataset1: this.mockData(), + }; + }); + } + + async loadGrid2Data() { + // load data with Fetch-Client + const response2 = await fetch(URL_CUSTOMERS); + const dataset2 = await response2['json'](); + + this.setState((state: State, props: Props) => { + return { + ...state, + dataset2, + } + }); + this.isGrid2DataLoaded = true; } mockData() { // mock a dataset - const mockDataset = []; + const mockDataset: any[] = []; for (let i = 0; i < 1000; i++) { const randomYear = 2000 + Math.floor(Math.random() * 10); const randomMonth = Math.floor(Math.random() * 11); @@ -121,14 +147,17 @@ export default class Example22 extends React.Component { * We need to do this (only once) because SlickGrid relies on the grid being visible in the DOM for it to be sized properly * and if it's not (like our use case) we need to resize the grid ourselve and we just need to do that once. */ - resizeGrid2() { + async resizeGrid2() { + if (!this.isGrid2DataLoaded) { + await this.loadGrid2Data(); + } if (!this.isGrid2Resize) { - this.reactGrid2.resizerService.resizeGrid(150); + this.reactGrid2?.resizerService.resizeGrid(150); } } render() { - return ( + return !this.state.gridOptions1 ? '' : (

    {this.title} @@ -163,7 +192,7 @@ export default class Example22 extends React.Component { role="tab" aria-controls="fetch" aria-selected="false" - onClick={this.resizeGrid2}>Fetch-Client + onClick={() => this.resizeGrid2()}>Fetch-Client @@ -174,23 +203,21 @@ export default class Example22 extends React.Component { role="tabpanel" aria-labelledby="javascript-tab">

    Grid 1 - Load Local Data

    - +

    Grid 2 - Load a JSON dataset through Fetch-Client

    - this.reactGrid2Ready($event.detail) - }} + this.reactGrid2Ready($event.detail)} />
    diff --git a/src/examples/slickgrid/example23.tsx b/src/examples/slickgrid/example23.tsx index e1d5bf58..d0b06ebd 100644 --- a/src/examples/slickgrid/example23.tsx +++ b/src/examples/slickgrid/example23.tsx @@ -1,4 +1,4 @@ -import i18next from 'i18next'; +import i18next, { TFunction } from 'i18next'; import * as moment from 'moment-mini'; import { SlickCustomTooltip } from '@slickgrid-universal/custom-tooltip-plugin'; import { ExcelExportService } from '@slickgrid-universal/excel-export'; @@ -19,13 +19,25 @@ import { OperatorType, SlickGrid, SliderRangeOption, - ReactSlickgridCustomElement + ReactSlickgridComponent, + GroupingGetterFunction } from '../../slickgrid-react'; import React from 'react'; -i18next.init({ - lng: 'en', +import BaseSlickGridState from './state-slick-grid-base'; +import { withTranslation } from 'react-i18next'; + +interface Props { + t: TFunction; +} + +interface State extends BaseSlickGridState { + selectedLanguage: string; + metrics?: Metrics; + selectedGroupingFields: Array; + selectedPredefinedFilter: string; + filterList: Array<{ value: string; label: string; }>; } -); + const NB_ITEMS = 1500; function randomBetween(min: number, max: number): number { @@ -39,9 +51,7 @@ const taskTranslateFormatter: Formatter = (_row, _cell, value, _columnDef, _data return gridOptions.i18n?.t('TASK_X', { x: value }) ?? ''; }; -interface Props { } - -export default class Example23 extends React.Component { +class Example23 extends React.Component { title = 'Example 23: Filtering from Range of Search Values'; subTitle = ` This demo shows how to use Filters with Range of Search Values (Wiki docs) @@ -63,44 +73,48 @@ export default class Example23 extends React.Component { `; reactGrid!: ReactGridInstance; - columnDefinitions: Column[] = []; - gridOptions!: GridOption; - dataset: any[] = []; - selectedLanguage: string; - metrics!: Metrics; - selectedGroupingFields: Array = ['', '', '']; - filterList = [ - { value: '', label: '' }, - { value: 'currentYearTasks', label: 'Current Year Completed Tasks' }, - { value: 'nextYearTasks', label: 'Next Year Active Tasks' } - ]; - selectedPredefinedFilter = ''; - private i18n: i18n; + // private i18n: i18n; constructor(public readonly props: Props) { super(props); - // define the grid options & columns and then create the grid itself - this.defineGrid(); - this.componentDidMount(); + // always start with English for Cypress E2E tests to be consistent const defaultLang = 'en'; i18next.changeLanguage(defaultLang); - this.selectedLanguage = defaultLang; + + this.state = { + gridOptions: undefined, + columnDefinitions: [], + selectedLanguage: 'en', + metrics: undefined, + selectedGroupingFields: ['', '', ''], + selectedPredefinedFilter: '', + filterList: [ + { value: '', label: '...' }, + { value: 'currentYearTasks', label: 'Current Year Completed Tasks' }, + { value: 'nextYearTasks', label: 'Next Year Active Tasks' } + ] + } } componentDidMount() { document.title = this.title; - // populate the dataset once the grid is ready - this.dataset = this.mockData(NB_ITEMS); + + // define the grid options & columns and then create the grid itself + this.defineGrid(); } componentWillUnmount() { this.saveCurrentGridState(); } + reactGridReady(reactGrid: ReactGridInstance) { + this.reactGrid = reactGrid; + } + /* Define grid Options and Columns */ defineGrid() { - this.columnDefinitions = [ + const columnDefinitions: Column[] = [ { id: 'title', name: 'Title', field: 'id', nameKey: 'TITLE', minWidth: 100, formatter: taskTranslateFormatter, @@ -170,7 +184,7 @@ export default class Example23 extends React.Component { const presetLowestDay = moment().add(-2, 'days').format('YYYY-MM-DD'); const presetHighestDay = moment().add(20, 'days').format('YYYY-MM-DD'); - this.gridOptions = { + const gridOptions: GridOption = { autoResize: { container: '#demo-container', rightPadding: 10 @@ -179,7 +193,7 @@ export default class Example23 extends React.Component { enableFiltering: true, // enableFilterTrimWhiteSpace: true, enableTranslate: true, - i18n: this.i18n, + // i18n: this.i18n, // use columnDef searchTerms OR use presets as shown below presets: { @@ -201,6 +215,13 @@ export default class Example23 extends React.Component { }, registerExternalResources: [new SlickCustomTooltip(), new ExcelExportService()], }; + + this.setState((state: State, props: Props) => ({ + ...state, + gridOptions, + columnDefinitions, + dataset: this.mockData(NB_ITEMS) + })); } mockData(itemCount: number, startingIndex = 0): any[] { @@ -230,8 +251,12 @@ export default class Example23 extends React.Component { } clearFilters() { - this.selectedPredefinedFilter = ''; - this.reactGrid.filterService.clearFilters(); + this.setState((state: State, props: Props) => { + return { + ...state, + selectedPredefinedFilter: '' + } + }, () => this.reactGrid.filterService.clearFilters()); } /** Dispatched event of a Grid State Changed event */ @@ -247,15 +272,32 @@ export default class Example23 extends React.Component { refreshMetrics(_e: Event, args: any) { if (args && args.current >= 0) { setTimeout(() => { - this.metrics = { - startTime: new Date(), - itemCount: args && args.current || 0, - totalItemCount: this.dataset.length || 0 - }; + this.setState((state: State, props: Props) => { + return { + ...state, + metrics: { + startTime: new Date(), + itemCount: args?.current ?? 0, + totalItemCount: state.dataset?.length || 0 + } + } + }); }); } } + selectedColumnChanged(e: React.ChangeEvent) { + const selectedVal = (e.target as HTMLSelectElement)?.value ?? ''; + const selectedColumn = this.state.columnDefinitions.find(c => c.id === selectedVal); + + this.setState((state: State, props: Props) => { + return { + ...state, + selectedColumn: selectedColumn, + }; + }); + } + setFiltersDynamically() { const presetLowestDay = moment().add(-5, 'days').format('YYYY-MM-DD'); const presetHighestDay = moment().add(25, 'days').format('YYYY-MM-DD'); @@ -277,12 +319,13 @@ export default class Example23 extends React.Component { } async switchLanguage() { - const nextLanguage = (this.selectedLanguage === 'en') ? 'fr' : 'en'; - await this.i18n.changeLanguage(nextLanguage); - this.selectedLanguage = nextLanguage; + const nextLanguage = (this.state.selectedLanguage === 'en') ? 'fr' : 'en'; + await i18next.changeLanguage(nextLanguage); + this.setState((state: State) => ({ ...state, selectedLanguage: nextLanguage })); } - predefinedFilterChanged(newPredefinedFilter: string) { + predefinedFilterChanged(e: React.ChangeEvent) { + const newPredefinedFilter = (e.target as HTMLSelectElement)?.value ?? ''; let filters: CurrentFilter[] = []; const currentYear = moment().year(); @@ -301,7 +344,7 @@ export default class Example23 extends React.Component { } render() { - return ( + return !this.state.gridOptions ? '' : (

    {this.title} @@ -317,12 +360,12 @@ export default class Example23 extends React.Component {
    - {this.metrics && - Metrics: - {this.metrics.endTime} | {this.metrics.itemCount} of {this.metrics.totalItemCount} items + {this.state.metrics && <>Metrics: + {moment(this.state.metrics.endTime).format('YYYY-MM-DD HH:mm:ss')} + | {this.state.metrics.itemCount} of {this.state.metrics.totalItemCount} items } - + e.preventDefault()}>
    @@ -351,12 +394,10 @@ export default class Example23 extends React.Component {

    - this.predefinedFilterChanged($event)}> { - this.columnDefinitions.map((column) => - + this.state.filterList.map((filter) => + ) } @@ -365,25 +406,25 @@ export default class Example23 extends React.Component {
    - - Locale: {this.selectedLanguage + '.json'} + Locale: {this.state.selectedLanguage + '.json'}
    - this.gridStateChanged($event.detail), - onRowCountChanged: $event => this.refreshMetrics($event.detail.eventData, $event.detail.args) - }} + this.reactGridReady($event.detail)} + onGridStateChanged={$event => this.gridStateChanged($event.detail)} + onRowCountChanged={$event => this.refreshMetrics($event.detail.eventData, $event.detail.args)} />
    ); } } + +export default withTranslation()(Example23); \ No newline at end of file diff --git a/src/examples/slickgrid/example24.tsx b/src/examples/slickgrid/example24.tsx index 4bf4ded8..dda6d11b 100644 --- a/src/examples/slickgrid/example24.tsx +++ b/src/examples/slickgrid/example24.tsx @@ -1,5 +1,7 @@ import { ExcelExportService } from '@slickgrid-universal/excel-export'; -import i18next from 'i18next'; +import i18next, { TFunction } from 'i18next'; +import React from 'react'; +import { withTranslation } from 'react-i18next'; import { ReactGridInstance, @@ -12,14 +14,18 @@ import { Formatters, GridOption, SlickGrid, - ReactSlickgridCustomElement, + ReactSlickgridComponent, } from '../../slickgrid-react'; -import React from 'react'; +import BaseSlickGridState from './state-slick-grid-base'; import './example24.scss'; // provide custom CSS/SASS styling -i18next.init({ - lng: 'en', + +interface Props { + t: TFunction; +} +interface State extends BaseSlickGridState { + selectedLanguage: string; } -); + const actionFormatter: Formatter = (_row, _cell, _value, _columnDef, dataContext) => { if (dataContext.priority === 3) { // option 3 is High return ``; @@ -62,9 +68,7 @@ const taskTranslateFormatter: Formatter = (_row, _cell, value, _columnDef, _data return i18n?.t('TASK_X', { x: value }) ?? ''; }; -interface Props { } - -export default class Example24 extends React.Component { +class Example24 extends React.Component { title = 'Example 24: Cell Menu & Context Menu Plugins'; subTitle = `Add Cell Menu and Context Menu
      @@ -93,41 +97,44 @@ export default class Example24 extends React.Component {
    `; reactGrid!: ReactGridInstance; - gridOptions!: GridOption; - columnDefinitions: Column[] = []; - dataset: any[] = []; - selectedLanguage: string; - // private i18n: i18n; constructor(public readonly props: Props) { super(props); - // define the grid options & columns and then create the grid itself - this.defineGrid(); // always start with English for Cypress E2E tests to be consistent const defaultLang = 'en'; i18next.changeLanguage(defaultLang); - this.selectedLanguage = defaultLang; - this.componentDidMount(); + + this.state = { + gridOptions: undefined, + columnDefinitions: [], + dataset: [], + selectedLanguage: 'en', + } } get cellMenuInstance(): any { - return this.reactGrid && this.reactGrid.extensionService.getSlickgridAddonInstance(ExtensionName.cellMenu) || {}; + return this.reactGrid && this.reactGrid.extensionService.getExtensionInstanceByName(ExtensionName.cellMenu) || {}; } get contextMenuInstance(): any { - return this.reactGrid && this.reactGrid.extensionService.getSlickgridAddonInstance(ExtensionName.contextMenu) || {}; + return this.reactGrid && this.reactGrid.extensionService.getExtensionInstanceByName(ExtensionName.contextMenu) || {}; } componentDidMount() { document.title = this.title; - // populate the dataset once the grid is ready - this.dataset = this.getData(1000); + + // define the grid options & columns and then create the grid itself + this.defineGrid(); + } + + reactGridReady(reactGrid: ReactGridInstance) { + this.reactGrid = reactGrid; } /* Define grid Options and Columns */ defineGrid() { - this.columnDefinitions = [ + const columnDefinitions: Column[] = [ { id: 'id', name: '#', field: 'id', maxWidth: 45, sortable: true, filterable: true }, { id: 'title', name: 'Title', field: 'id', nameKey: 'TITLE', minWidth: 100, @@ -256,7 +263,7 @@ export default class Example24 extends React.Component { }, ]; - this.gridOptions = { + const gridOptions: GridOption = { autoResize: { container: '#demo-container', rightPadding: 10 @@ -303,6 +310,13 @@ export default class Example24 extends React.Component { // load Context Menu structure contextMenu: this.getContextMenuOptions(), }; + + this.setState((state: State) => ({ + ...state, + gridOptions, + columnDefinitions, + dataset: this.getData(1000) + })); } executeCommand(_e: Event, args: any) { @@ -320,7 +334,7 @@ export default class Example24 extends React.Component { alert('Please help!'); break; case 'delete-row': - if (confirm(`Do you really want to delete row ${args.row + 1} with ${this.i18n.t('TASK_X', { x: dataContext.id })}`)) { + if (confirm(`Do you really want to delete row ${args.row + 1} with ${i18next?.t('TASK_X', { x: dataContext.id })}`)) { this.reactGrid.dataView.deleteItem(dataContext.id); } break; @@ -329,7 +343,7 @@ export default class Example24 extends React.Component { getData(count: number): any[] { // mock a dataset - const tmpData = []; + const tmpData: any[] = []; for (let i = 0; i < count; i++) { const randomYear = 2000 + Math.floor(Math.random() * 30); const randomMonth = Math.floor(Math.random() * 11); @@ -463,13 +477,13 @@ export default class Example24 extends React.Component { } async switchLanguage() { - const nextLanguage = (this.selectedLanguage === 'en') ? 'fr' : 'en'; + const nextLanguage = (this.state.selectedLanguage === 'en') ? 'fr' : 'en'; await i18next.changeLanguage(nextLanguage); - this.selectedLanguage = nextLanguage; + this.setState((state: State) => ({ ...state, selectedLanguage: nextLanguage })); } render() { - return ( + return !this.state.gridOptions ? '' : (

    {this.title} @@ -511,23 +525,26 @@ export default class Example24 extends React.Component {
    - - + - {this.selectedLanguage + '.json'} + {this.state.selectedLanguage + '.json'}
    - + this.reactGridReady($event.detail)} + />

    ); } } + +export default withTranslation()(Example24); \ No newline at end of file diff --git a/src/examples/slickgrid/example25.tsx b/src/examples/slickgrid/example25.tsx index fa28e765..74d4de88 100644 --- a/src/examples/slickgrid/example25.tsx +++ b/src/examples/slickgrid/example25.tsx @@ -8,10 +8,11 @@ import { Metrics, MultipleSelectOption, OperatorType, - ReactSlickgridCustomElement + ReactSlickgridComponent } from '../../slickgrid-react'; import React from 'react'; import './example25.scss'; // provide custom CSS/SASS styling +import BaseSlickGridState from './state-slick-grid-base'; const COUNTRIES_API = 'https://countries.trevorblades.com/'; @@ -28,10 +29,19 @@ export interface Country { languageName: string; languageNative: string; } +interface Status { text: string, class: string } interface Props { } +interface State extends BaseSlickGridState { + metrics?: Metrics; + graphqlQuery: string; + isWithCursor: boolean; + processing: boolean; + selectedLanguage: string; + status: Status; +} -export default class Example25 extends React.Component { +export default class Example25 extends React.Component { title = 'Example 25: GraphQL Basic API without Pagination'; subTitle = ` Use basic GraphQL query with any external public APIs (Wiki docs). @@ -48,25 +58,29 @@ export default class Example25 extends React.Component { `; reactGrid!: ReactGridInstance; - columnDefinitions: Column[] = []; - gridOptions!: GridOption; - dataset = []; - metrics!: Metrics; - - isWithCursor = false; - graphqlQuery = ''; - processing = false; - selectedLanguage = ''; - status = { text: '', class: '' }; constructor(public readonly props: Props) { super(props); + + this.state = { + gridOptions: undefined, + columnDefinitions: [], + isWithCursor: false, + graphqlQuery: '', + metrics: undefined, + processing: false, + selectedLanguage: '', + status: { text: '', class: '' }, + } + } + + async componentDidMount() { // define the grid options & columns and then create the grid itself this.defineGrid(); } defineGrid() { - this.columnDefinitions = [ + const columnDefinitions: Column[] = [ { id: 'countryCode', field: 'code', name: 'Code', maxWidth: 90, sortable: true, filterable: true, columnGroup: 'Country' }, { id: 'countryName', field: 'name', name: 'Name', width: 60, sortable: true, filterable: true, columnGroup: 'Country' }, { id: 'countryNative', field: 'native', name: 'Native', width: 60, sortable: true, filterable: true, columnGroup: 'Country' }, @@ -169,7 +183,7 @@ export default class Example25 extends React.Component { }, ]; - this.gridOptions = { + const gridOptions: GridOption = { autoResize: { container: '#demo-container', rightPadding: 10 @@ -197,18 +211,29 @@ export default class Example25 extends React.Component { preProcess: () => this.displaySpinner(true), process: (query) => this.getCountries(query), postProcess: (result: GraphqlResult) => { - this.metrics = result.metrics as Metrics; + this.setState((state: State, props: Props) => ({ ...state, metrics: result.metrics })); this.displaySpinner(false); } } as GraphqlServiceApi }; + + this.setState((state: State, props: Props) => ({ + ...state, + gridOptions, + columnDefinitions, + })); } displaySpinner(isProcessing: boolean) { - this.processing = isProcessing; - this.status = (isProcessing) + const newStatus = (isProcessing) ? { text: 'processing...', class: 'alert alert-danger' } : { text: 'finished', class: 'alert alert-success' }; + + this.setState((state: State, props: any) => ({ + ...state, + status: newStatus, + processing: isProcessing, + })); } // -- @@ -222,7 +247,8 @@ export default class Example25 extends React.Component { return new Promise(async resolve => { const response = await fetch(COUNTRIES_API, { method: 'post', - body: json({ query }) + body: JSON.stringify({ query }), + headers: { "Content-type": "application/json; charset=UTF-8" } }); resolve(response.json()); }); @@ -238,7 +264,8 @@ export default class Example25 extends React.Component { return new Promise(async resolve => { const response = await fetch(COUNTRIES_API, { method: 'post', - body: json({ query: continentQuery }) + body: JSON.stringify({ query: continentQuery }), + headers: { "Content-type": "application/json; charset=UTF-8" } }); resolve(response.json()); }); @@ -251,10 +278,12 @@ export default class Example25 extends React.Component { */ getLanguages(): Promise> { const languageQuery = `query { languages { code, name, native }}`; + console.log('GET LANG', JSON.stringify({ query: languageQuery })) return new Promise(async resolve => { const response = await fetch(COUNTRIES_API, { method: 'post', - body: json({ query: languageQuery }) + body: JSON.stringify({ query: languageQuery }), + headers: { "Content-type": "application/json; charset=UTF-8" } }); resolve(response.json()); }); @@ -276,7 +305,7 @@ export default class Example25 extends React.Component { } render() { - return ( + return !this.state.gridOptions ? '' : (

    {this.title} @@ -292,19 +321,19 @@ export default class Example25 extends React.Component {
    -
    - Status: {this.status.text} - {!this.processing && +
    + Status: {this.state.status.text} + {this.state.processing ? - } + : ''}
    - +
    ); } diff --git a/src/examples/slickgrid/example26.tsx b/src/examples/slickgrid/example26.tsx index e72c9809..201b37c8 100644 --- a/src/examples/slickgrid/example26.tsx +++ b/src/examples/slickgrid/example26.tsx @@ -11,11 +11,12 @@ import { GridOption, OnEventArgs, OperatorType, - ReactSlickgridCustomElement + ReactSlickgridComponent } from '../../slickgrid-react'; -import { CustomReactViewModelEditor } from './custom-reactViewModelEditor'; -import { CustomReactViewModelFilter } from './custom-reactViewModelFilter'; +import { CustomInputEditor } from './custom-inputEditor'; +import { CustomInputFilter } from './custom-inputFilter'; import React from 'react'; +import { withTranslation } from 'react-i18next'; // using external non-typed js libraries declare const Slick: any; @@ -24,7 +25,7 @@ const NB_ITEMS = 100; interface Props { } -export default class Example26 extends React.Component { +class Example26 extends React.Component { title = 'Example 26: Use of React Custom Elements'; subTitle = `

    Filters, Editors, AsyncPostRender with React Custom Elements

    @@ -78,6 +79,10 @@ export default class Example26 extends React.Component { this.dataset = this.mockData(NB_ITEMS); } + reactGridReady(reactGrid: ReactGridInstance) { + this.reactGrid = reactGrid; + } + /* Define grid Options and Columns */ defineGrid() { this.columnDefinitions = [ @@ -106,14 +111,14 @@ export default class Example26 extends React.Component { filterable: true, sortable: true, filter: { - model: new CustomReactViewModelFilter(), + model: new CustomAureliaViewModelFilter(), collection: this.assignees, params: { - reactUtilService: this.reactUtilService, // pass the reactUtilService here OR in the grid option params + aureliaUtilService: this.aureliaUtilService, // pass the aureliaUtilService here OR in the grid option params templateUrl: PLATFORM.moduleName('examples/slickgrid/filter-select') // FilterSelect, } }, - queryFieldFilter: 'assignee.id', // for a complex object it's important to tell the Filter which field to query and our CustomReactComponentFilter returns the "id" property + queryFieldFilter: 'assignee.id', // for a complex object it's important to tell the Filter which field to query and our CustomAureliaComponentFilter returns the "id" property queryFieldSorter: 'assignee.name', formatter: Formatters.complexObject, params: { @@ -121,10 +126,10 @@ export default class Example26 extends React.Component { }, exportWithFormatter: true, editor: { - model: CustomReactViewModelEditor, + model: CustomAureliaViewModelEditor, collection: this.assignees, params: { - reactUtilService: this.reactUtilService, // pass the reactUtilService here OR in the grid option params + aureliaUtilService: this.aureliaUtilService, // pass the aureliaUtilService here OR in the grid option params templateUrl: PLATFORM.moduleName('examples/slickgrid/editor-select') // EditorSelect, } }, @@ -134,29 +139,29 @@ export default class Example26 extends React.Component { } }, { id: 'assignee2', - name: 'Assignee with React Component', + name: 'Assignee with Aurelia Component', field: 'assignee', minWidth: 125, filterable: true, sortable: true, filter: { - model: new CustomReactViewModelFilter(), + model: new CustomAureliaViewModelFilter(), collection: this.assignees, params: { - reactUtilService: this.reactUtilService, // pass the reactUtilService here OR in the grid option params + aureliaUtilService: this.aureliaUtilService, // pass the aureliaUtilService here OR in the grid option params templateUrl: PLATFORM.moduleName('examples/slickgrid/filter-select') // FilterSelect, } }, - queryFieldFilter: 'assignee.id', // for a complex object it's important to tell the Filter which field to query and our CustomReactComponentFilter returns the "id" property + queryFieldFilter: 'assignee.id', // for a complex object it's important to tell the Filter which field to query and our CustomAureliaComponentFilter returns the "id" property queryFieldSorter: 'assignee.name', // loading formatter, text to display while Post Render gets processed formatter: () => '...', - // to load an React Custom Element, you cannot use a Formatter since React needs at least 1 cycle to render everything + // to load an Aurelia Custom Element, you cannot use a Formatter since Aurelia needs at least 1 cycle to render everything // you can use a PostRenderer but you will visually see the data appearing, - // which is why it's still better to use regular Formatter (with jQuery if need be) instead of React Custom Element - asyncPostRender: this.renderReactComponent.bind(this), + // which is why it's still better to use regular Formatter (with jQuery if need be) instead of Aurelia Custom Element + asyncPostRender: this.renderAureliaComponent.bind(this), params: { templateUrl: PLATFORM.moduleName('examples/slickgrid/custom-title-formatter'), // CustomTitleFormatterCustomElement, complexFieldLabel: 'assignee.name' // for the exportCustomFormatter @@ -278,7 +283,7 @@ export default class Example26 extends React.Component { mockData(itemCount: number, startingIndex = 0) { // mock a dataset - const tempDataset = []; + const tempDataset: any[] = []; for (let i = startingIndex; i < (startingIndex + itemCount); i++) { const randomYear = 2000 + Math.floor(Math.random() * 10); const randomMonth = Math.floor(Math.random() * 11); @@ -338,9 +343,9 @@ export default class Example26 extends React.Component { } renderReactComponent(cellNode: JQuery, _row: number, dataContext: any, colDef: Column) { - if (colDef.params.templateUrl && cellNode.length) { - this.reactUtilService.createReactViewModelAddToSlot(colDef.params.templateUrl, { model: dataContext }, cellNode[0], true); - } + // if (colDef.params.templateUrl && cellNode.length) { + // this.reactUtilService.createReactViewModelAddToSlot(colDef.params.templateUrl, { model: dataContext }, cellNode[0], true); + // } } setAutoEdit(isAutoEdit: boolean) { @@ -399,13 +404,13 @@ export default class Example26 extends React.Component {
    -
    @@ -431,19 +436,18 @@ export default class Example26 extends React.Component {
    - this.onCellChanged($event.detail.eventData, $event.detail.args), - onClick: $event => this.onCellClicked($event.detail.eventData, $event.detail.args), - onValidationError: $event => this.onCellValidationError($event.detail.eventData, $event.detail.args) - }} + onReactGridCreated={$event => this.reactGridReady($event.detail)} + onCellChange={$event => this.onCellChanged($event.detail.eventData, $event.detail.args)} + onClick={$event => this.onCellClicked($event.detail.eventData, $event.detail.args)} />
    ); } } + +export default withTranslation()(Example26); \ No newline at end of file diff --git a/src/examples/slickgrid/example27.scss b/src/examples/slickgrid/example27.scss index 146545e9..25b5c3de 100644 --- a/src/examples/slickgrid/example27.scss +++ b/src/examples/slickgrid/example27.scss @@ -1,5 +1,5 @@ /* make sure to add the @import the SlickGrid Theme AFTER the variables changes */ -@import '@slickgrid-universal/common/dist/styles/sass/slickgrid-theme-material.lite.scss'; +// @import '@slickgrid-universal/common/dist/styles/sass/slickgrid-theme-material.lite.scss'; .icon { align-items: center; diff --git a/src/examples/slickgrid/example27.tsx b/src/examples/slickgrid/example27.tsx index 1af8516a..a0e9c480 100644 --- a/src/examples/slickgrid/example27.tsx +++ b/src/examples/slickgrid/example27.tsx @@ -10,16 +10,24 @@ import { GridStateType, TreeToggledItem, TreeToggleStateChange, - ReactSlickgridCustomElement, + ReactSlickgridComponent, } from '../../slickgrid-react'; import React from 'react'; import './example27.scss'; // provide custom CSS/SASS styling +import BaseSlickGridState from './state-slick-grid-base'; const NB_ITEMS = 500; interface Props { } +interface State extends BaseSlickGridState { + datasetHierarchical?: any[]; + loadingClass: string; + isLargeDataset: boolean; + hasNoExpandCollapseChanged: boolean; + treeToggleItems: TreeToggledItem[]; +} -export default class Example27 extends React.Component { +export default class Example27 extends React.Component { title = 'Example 27: Tree Data (from a flat dataset with parentId references)'; subTitle = `
    • It is assumed that your dataset will have Parent/Child references AND also Tree Level (indent) property.
    • @@ -35,31 +43,36 @@ export default class Example27 extends React.Component {
    `; reactGrid!: ReactGridInstance; - gridOptions!: GridOption; - columnDefinitions: Column[] = []; - dataset: any[] = []; - datasetHierarchical: any[] = []; - loadingClass = ''; - isLargeDataset = false; - hasNoExpandCollapseChanged = true; - treeToggleItems: TreeToggledItem[] = []; constructor(public readonly props: Props) { super(props); - // define the grid options & columns and then create the grid itself - this.defineGrid(); - this.componentDidMount(); + + this.state = { + gridOptions: undefined, + columnDefinitions: [], + dataset: [], + datasetHierarchical: undefined, + loadingClass: '', + isLargeDataset: false, + hasNoExpandCollapseChanged: true, + treeToggleItems: [], + } } componentDidMount() { document.title = this.title; - // populate the dataset once the grid is ready - this.dataset = this.loadData(NB_ITEMS); + + // define the grid options & columns and then create the grid itself + this.defineGrid(); + } + + reactGridReady(reactGrid: ReactGridInstance) { + this.reactGrid = reactGrid; } /* Define grid Options and Columns */ defineGrid() { - this.columnDefinitions = [ + const columnDefinitions: Column[] = [ { id: 'title', name: 'Title', field: 'title', width: 220, cssClass: 'cell-title', filterable: true, sortable: true, exportWithFormatter: false, @@ -98,7 +111,7 @@ export default class Example27 extends React.Component { } ]; - this.gridOptions = { + const gridOptions: GridOption = { autoResize: { container: '#demo-container', rightPadding: 10 @@ -172,6 +185,15 @@ export default class Example27 extends React.Component { iconColumnHideCommand: 'mdi mdi-close', } }; + + this.setState((state: State, props: Props) => { + return { + ...state, + gridOptions, + columnDefinitions, + dataset: this.loadData(10), + }; + }); } /** @@ -184,7 +206,7 @@ export default class Example27 extends React.Component { const treeLevelPropName = 'treeLevel'; // if undefined in your options, the default prop name is "__treeLevel" const newTreeLevel = 1; // find first parent object and add the new item as a child - const childItemFound = this.dataset.find((item) => item[treeLevelPropName] === newTreeLevel); + const childItemFound = this.state.dataset?.find((item) => item[treeLevelPropName] === newTreeLevel); const parentItemFound = this.reactGrid.dataView.getItemByIdx(childItemFound[parentPropName]); if (childItemFound && parentItemFound) { @@ -252,17 +274,18 @@ export default class Example27 extends React.Component { } hideSpinner() { - setTimeout(() => this.loadingClass = '', 200); // delay the hide spinner a bit to avoid show/hide too quickly + setTimeout(() => { + this.setState((state: State, props: Props) => ({ ...state, loadingClass: '' })); + }, 200); // delay the hide spinner a bit to avoid show/hide too quickly } showSpinner() { - if (this.isLargeDataset) { - this.loadingClass = 'mdi mdi-load mdi-spin-1s mdi-24px color-alt-success'; + if (this.state.isLargeDataset) { + this.setState((state: State, props: Props) => ({ ...state, loadingClass: 'mdi mdi-load mdi-spin-1s mdi-24px color-alt-success' })); } } loadData(rowCount: number) { - this.isLargeDataset = rowCount > 5000; // we'll show a spinner when it's large, else don't show show since it should be fast enough let indent = 0; const parents: any[] = []; const data: any[] = []; @@ -313,10 +336,17 @@ export default class Example27 extends React.Component { item['finish'] = new Date(randomYear, (randomMonth + 1), randomDay); item['effortDriven'] = (i % 5 === 0); } - this.dataset = data; + console.log('data', data) return data; } + setData(rowCount: number) { + this.setState((state: State, props: Props) => ({ + ...state, + dataset: this.loadData(rowCount) + })); + } + handleOnTreeFullToggleEnd(treeToggleExecution: TreeToggleStateChange) { console.log('Tree Data changes', treeToggleExecution); this.hideSpinner(); @@ -324,17 +354,27 @@ export default class Example27 extends React.Component { /** Whenever a parent is being toggled, we'll keep a reference of all of these changes so that we can reapply them whenever we want */ handleOnTreeItemToggled(treeToggleExecution: TreeToggleStateChange) { - this.hasNoExpandCollapseChanged = false; - this.treeToggleItems = treeToggleExecution.toggledItems as TreeToggledItem[]; + console.log('Tree Item Toggled!!!', treeToggleExecution) + this.setState((state: State, props: Props) => ({ + ...state, + hasNoExpandCollapseChanged: false, + treeToggleItems: treeToggleExecution.toggledItems as TreeToggledItem[], + })); console.log('Tree Data changes', treeToggleExecution); } handleOnGridStateChanged(gridStateChange: GridStateChange) { - this.hasNoExpandCollapseChanged = false; + this.setState((state: State, props: Props) => ({ + ...state, + hasNoExpandCollapseChanged: false, + })); if (gridStateChange?.change?.type === GridStateType.treeData) { console.log('Tree Data gridStateChange', gridStateChange?.gridState?.treeData); - this.treeToggleItems = gridStateChange?.gridState?.treeData?.toggledItems as TreeToggledItem[]; + this.setState((state: State, props: Props) => ({ + ...state, + treeToggleItems: gridStateChange?.gridState?.treeData?.toggledItems as TreeToggledItem[], + })); } } @@ -348,7 +388,7 @@ export default class Example27 extends React.Component { const newTreeLevel = 1; // find first parent object and toggle it - const childItemFound = this.dataset.find((item) => item[treeLevelPropName] === newTreeLevel); + const childItemFound = this.state.dataset?.find((item) => item[treeLevelPropName] === newTreeLevel); const parentItemFound = this.reactGrid.dataView.getItemByIdx(childItemFound[parentPropName]); if (childItemFound && parentItemFound) { @@ -357,11 +397,11 @@ export default class Example27 extends React.Component { } reapplyToggledItems() { - this.reactGrid.treeDataService.applyToggledItemStateChanges(this.treeToggleItems); + this.reactGrid.treeDataService.applyToggledItemStateChanges(this.state.treeToggleItems); } render() { - return ( + return !this.state.gridOptions ? '' : (

    {this.title} @@ -377,58 +417,58 @@ export default class Example27 extends React.Component {
    - - - - - - -
    +
    - - - - - -
    @@ -437,11 +477,10 @@ export default class Example27 extends React.Component {
    - this.handleOnTreeFullToggleEnd($event.detail)} - onTreeItemToggled={$event => this.handleOnTreeItemToggled($event.detail)} /> + onTreeItemToggled={$event => this.handleOnTreeItemToggled($event.detail)} + onReactGridCreated={$event => this.reactGridReady($event.detail)} + />
    ); diff --git a/src/examples/slickgrid/example28.scss b/src/examples/slickgrid/example28.scss index 0479c1dc..60f4c62f 100644 --- a/src/examples/slickgrid/example28.scss +++ b/src/examples/slickgrid/example28.scss @@ -1,20 +1,3 @@ -/* make sure to add the @import the SlickGrid Theme AFTER the variables changes */ -@import '@slickgrid-universal/common/dist/styles/sass/slickgrid-theme-salesforce.lite.scss'; - -/* - www.OnlineWebFonts.Com - You must credit the author Copy this link on your web -
    Font made from oNline Web Fontsis licensed by CC BY 3.0
    -*/ -@font-face { - font-family: "SalesforceSans-Regular"; - src: url("https://db.onlinewebfonts.com/t/0fadaa21fcac88ceee0bb8da992c221b.eot"); /* IE9*/ - src: url("https://db.onlinewebfonts.com/t/0fadaa21fcac88ceee0bb8da992c221b.eot?#iefix") format("embedded-opentype"), /* IE6-IE8 */ - url("https://db.onlinewebfonts.com/t/0fadaa21fcac88ceee0bb8da992c221b.woff2") format("woff2"), /* chrome firefox */ - url("https://db.onlinewebfonts.com/t/0fadaa21fcac88ceee0bb8da992c221b.woff") format("woff"), /* chrome firefox */ - url("https://db.onlinewebfonts.com/t/0fadaa21fcac88ceee0bb8da992c221b.ttf") format("truetype"), /* chrome firefox opera Safari, Android, iOS 4.2+*/ - url("https://db.onlinewebfonts.com/t/0fadaa21fcac88ceee0bb8da992c221b.svg#SalesforceSans-Regular") format("svg"); /* iOS 4.1- */ -} .icon { align-items: center; diff --git a/src/examples/slickgrid/example28.tsx b/src/examples/slickgrid/example28.tsx index 5133e681..80d8b084 100644 --- a/src/examples/slickgrid/example28.tsx +++ b/src/examples/slickgrid/example28.tsx @@ -8,7 +8,7 @@ import { GridOption, findItemInTreeStructure, Formatter, - ReactSlickgridCustomElement + ReactSlickgridComponent } from '../../slickgrid-react'; import React from 'react'; import './example28.scss'; // provide custom CSS/SASS styling @@ -45,6 +45,10 @@ export default class Example28 extends React.Component { this.datasetHierarchical = this.mockDataset(); } + reactGridReady(reactGrid: ReactGridInstance) { + this.reactGrid = reactGrid; + } + /* Define grid Options and Columns */ defineGrid() { this.columnDefinitions = [ @@ -273,22 +277,22 @@ export default class Example28 extends React.Component {
    - - - - -
    @@ -297,7 +301,7 @@ export default class Example28 extends React.Component {
    @@ -307,11 +311,12 @@ export default class Example28 extends React.Component {
    - + onReactGridCreated={$event => this.reactGridReady($event.detail)} + />
    ); diff --git a/src/examples/slickgrid/example29.tsx b/src/examples/slickgrid/example29.tsx index 504314d6..07f60c05 100644 --- a/src/examples/slickgrid/example29.tsx +++ b/src/examples/slickgrid/example29.tsx @@ -1,8 +1,10 @@ -import { Column, GridOption, Formatters, ReactSlickgridCustomElement} from '../../slickgrid-react'; +import { Column, GridOption, Formatters, ReactSlickgridComponent } from '../../slickgrid-react'; import React from 'react'; const NB_ITEMS = 995; +interface Props { } + export default class Example29 extends React.Component { title = 'Example 29: Grid with Header and Footer slot'; subTitle = `Simple Grids with a custom header and footer via named slots`; @@ -45,7 +47,7 @@ export default class Example29 extends React.Component { mockData(count: number) { // mock a dataset - const mockDataset = []; + const mockDataset: any[] = []; for (let i = 0; i < count; i++) { const randomYear = 2000 + Math.floor(Math.random() * 10); const randomMonth = Math.floor(Math.random() * 11); @@ -82,7 +84,7 @@ export default class Example29 extends React.Component {
    - diff --git a/src/examples/slickgrid/example3.tsx b/src/examples/slickgrid/example3.tsx index 4ed67cb3..eb467f37 100644 --- a/src/examples/slickgrid/example3.tsx +++ b/src/examples/slickgrid/example3.tsx @@ -1,5 +1,7 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ +import * as $ from 'jquery'; import i18next from 'i18next'; +import React from 'react'; + import { ReactGridInstance, AutocompleterOption, @@ -15,22 +17,16 @@ import { OperatorType, SlickNamespace, SortComparers, - ReactSlickgridCustomElement, + ReactSlickgridComponent, + GridOption, } from '../../slickgrid-react'; -import React from 'react'; import { CustomInputEditor } from './custom-inputEditor'; import { CustomInputFilter } from './custom-inputFilter'; -import * as $ from 'jquery'; import BaseSlickGridState from './state-slick-grid-base'; // using external non-typed js libraries declare const Slick: SlickNamespace; -i18next.init({ - lng: 'en', -}); - -// eslint-disable-next-line @typescript-eslint/no-empty-interface interface Props {} const NB_ITEMS = 100; @@ -81,7 +77,7 @@ interface State extends BaseSlickGridState { export default class Example3 extends React.Component { title = 'Example 3: Editors / Delete'; subTitle = ` - Grid with Inline Editors and onCellClick actions (Wiki docs). + Grid with Inline Editors and onCellClick actions (Wiki docs).
    • Multiple Editors & Filters are available: AutoComplete, Checkbox, Date, Slider, SingleSelect, MultipleSelect, Float, Text, LongText... even Custom Editor
    • When using 'enableCellNavigation: true', clicking on a cell will automatically make it active & selected.
    • @@ -107,7 +103,7 @@ export default class Example3 extends React.Component { super(props); this.selectedLanguage = i18next.language; this.state = { - gridOptions: null, + gridOptions: undefined, columnDefinitions: [], dataset: [], isAutoEdit: true, @@ -122,18 +118,15 @@ export default class Example3 extends React.Component { const options = this.getGridOptions(); const columns = this.getColumns(); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - this.setState((state: any, props: Props) => { - return { + this.setState((state: State) => ({ ...state, dataset: this.mockData(NB_ITEMS), gridOptions: options, columnDefinitions: columns, - }; - }); + })); } - getColumns() { + getColumns(): Column[] { return [ { id: 'edit', @@ -145,16 +138,14 @@ export default class Example3 extends React.Component { minWidth: 30, maxWidth: 30, // use onCellClick OR grid.onClick.subscribe which you can see down below - onCellClick: (_e: JQuery.Event, args: OnEventArgs) => { + onCellClick: (_e: any, args: OnEventArgs) => { console.log(args); // eslint-disable-next-line @typescript-eslint/no-unused-vars - this.setState((state:State, props:Props)=>{ - return{ + this.setState((state: State, props: Props) => ({ ...state, alertWarning: `Editing: ${args.dataContext.title}` - }; - }); + })); this.reactGrid.gridService.highlightRow(args.row, 1500); this.reactGrid.gridService.setSelectedRow(args.row); }, @@ -193,12 +184,10 @@ export default class Example3 extends React.Component { onCellChange: (_e: Event, args: OnEventArgs) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - this.setState((state:State, props:Props)=>{ - return { + this.setState((state: State, props: Props) => ({ ...state, alertWarning: `Updated Title: ${args.dataContext.title}` - }; - }); + })); }, }, { @@ -525,7 +514,7 @@ export default class Example3 extends React.Component { ]; } - getGridOptions() { + getGridOptions(): GridOption { return { autoEdit: this.state.isAutoEdit, autoCommitEdit: false, @@ -547,13 +536,13 @@ export default class Example3 extends React.Component { /** Add a new row to the grid and refresh the Filter collection */ addItem() { - const lastRowIndex = this.state.dataset.length; + const lastRowIndex = this.state.dataset?.length; const newRows = this.mockData(1, lastRowIndex); // wrap into a timer to simulate a backend async call setTimeout(() => { // at any time, we can poke the 'collection' property and modify it - const requisiteColumnDef = this.state.columnDefinitions.find( + const requisiteColumnDef = this.state.columnDefinitions?.find( (column: Column) => column.id === 'prerequisites' ); if (requisiteColumnDef) { @@ -595,7 +584,7 @@ export default class Example3 extends React.Component { /** Delete last inserted row */ deleteItem() { - const requisiteColumnDef = this.state.columnDefinitions.find( + const requisiteColumnDef = this.state.columnDefinitions?.find( (column: Column) => column.id === 'prerequisites' ); if (requisiteColumnDef) { @@ -618,7 +607,7 @@ export default class Example3 extends React.Component { mockData(itemCount: number, startingIndex = 0) { // mock a dataset - const tempDataset = []; + const tempDataset: any[] = []; for (let i = startingIndex; i < startingIndex + itemCount; i++) { const randomYear = 2000 + Math.floor(Math.random() * 10); const randomFinishYear = @@ -656,28 +645,21 @@ export default class Example3 extends React.Component { onCellChanged(_e: JQuery.Event, args: any) { console.log('onCellChange', args); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - this.setState((state: State, props: Props) => { - return { + this.setState((state: State, props: Props) => ({ ...state, updatedObject: { ...args.item }, - }; - }); + })); } onCellClicked(_e: JQuery.Event, args: any) { - const metadata = - this.reactGrid.gridService.getColumnFromEventArguments(args); + const metadata = this.reactGrid.gridService.getColumnFromEventArguments(args); console.log(metadata); if (metadata.columnDef.id === 'edit') { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - this.setState((state: State, props: Props) => { - return { + this.setState((state: State, props: Props) => ({ ...state, alertWarning: `Open a modal window to edit: ${metadata.dataContext.title}`, - }; - }); + })); // highlight the row, to customize the color, you can change the SASS variable $row-highlight-background-color this.reactGrid.gridService.highlightRow(args.row, 1500); @@ -688,12 +670,10 @@ export default class Example3 extends React.Component { if (confirm('Are you sure?')) { this.reactGrid.gridService.deleteItemById(metadata.dataContext.id); // eslint-disable-next-line @typescript-eslint/no-unused-vars - this.setState((state: State, props: Props) => { - return { + this.setState((state: State, props: Props) => ({ ...state, alertWarning: `Deleted: ${metadata.dataContext.title}`, - }; - }); + })); } } } @@ -730,12 +710,10 @@ export default class Example3 extends React.Component { // you can dynamically add your column to your column definitions // and then use the spread operator [...cols] OR slice to force React to review the changes // eslint-disable-next-line @typescript-eslint/no-unused-vars - this.setState((state: any, props: Props) => { - return { + this.setState((state: State, props: Props) => ({ ...state, - columnDefinitions: [...this.state.columnDefinitions, newCol], - }; - }); + columnDefinitions: [...this.state.columnDefinitions!, newCol], + })); // NOTE if you use an Extensions (Checkbox Selector, Row Detail, ...) that modifies the column definitions in any way // you MUST use 'getAllColumnDefinitions()' from the GridService, using this will be ALL columns including the 1st column that is created internally @@ -748,21 +726,16 @@ export default class Example3 extends React.Component { } dynamicallyRemoveLastColumn() { - const lastColumn = - this.state.columnDefinitions[this.state.columnDefinitions.length - 1]; - // eslint-disable-next-line @typescript-eslint/no-unused-vars - this.setState((state: any, props: Props) => { - return { + this.state.columnDefinitions.pop(); + + this.setState((state: State, props: Props) => ({ ...state, - columnDefinitions: this.state.columnDefinitions.filter( - (x) => x.id !== lastColumn.id - ), - }; - }); + columnDefinitions: this.state.columnDefinitions.slice(), + })); // NOTE if you use an Extensions (Checkbox Selector, Row Detail, ...) that modifies the column definitions in any way // you MUST use the code below, first you must reassign the Editor facade (from the internalColumnEditor back to the editor) - // in other words, SlickGrid is not using the same as Slickgrid-React uses (editor with a 'model' and other properties are a facade, SlickGrid only uses what is inside the model) + // in other words, SlickGrid is not using the same as Slickgrid-React uses (editor with a "model" and other properties are a facade, SlickGrid only uses what is inside the model) /* const allColumns = this.reactGrid.gridService.getAllColumnDefinitions(); const allOriginalColumns = allColumns.map((column) => { @@ -777,13 +750,7 @@ export default class Example3 extends React.Component { } setAutoEdit(isAutoEdit: boolean) { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - this.setState((state: any, props: Props) => { - return { - ...state, - isAutoEdit, - }; - }); + this.setState((state: State) => ({ ...state, isAutoEdit })); this.reactGrid.slickGrid.setOptions({ autoEdit: isAutoEdit, @@ -795,12 +762,6 @@ export default class Example3 extends React.Component { this.reactGrid = reactGrid; } - async switchLanguage() { - const nextLanguage = this.selectedLanguage === 'en' ? 'fr' : 'en'; - await i18next.changeLanguage(nextLanguage); - this.selectedLanguage = nextLanguage; - } - undo() { const command = this._commandQueue.pop(); if (command && Slick.GlobalEditorLock.cancelCurrentEdit()) { @@ -810,7 +771,7 @@ export default class Example3 extends React.Component { } render() { - let objectAlert = null; + let objectAlert: any = null; if (this.state.updatedObject) { objectAlert = (
      @@ -819,7 +780,7 @@ export default class Example3 extends React.Component {
      ); } - let alertWarning = null; + let alertWarning: any = null; if (this.state.alertWarning) { alertWarning = (
      @@ -833,9 +794,7 @@ export default class Example3 extends React.Component { const marginTop5px = { marginTop: '5px' }; - return this.state.gridOptions === null ? ( - '' - ) : ( + return !this.state.gridOptions ? '' : (

      {this.title} @@ -852,17 +811,15 @@ export default class Example3 extends React.Component {
      - + -
      - -
      - @@ -949,21 +879,17 @@ export default class Example3 extends React.Component {
      @@ -976,16 +902,15 @@ export default class Example3 extends React.Component {
      - {this.reactGridReady(e.detail.args);}} - onActiveCellChanged={e=>{this.onCellChanged(e.detail.eventData, e.detail.args);}} + onReactGridCreated={e => { this.reactGridReady(e.detail); }} + onCellChange={e => { this.onCellChanged(e.detail.eventData, e.detail.args); }} onClick={e=>{this.onCellClicked(e.detail.eventData, e.detail.args);}} - onValidationError={e=>{this.onCellValidationError(e.detail.eventData, e.detail.args);}} - + onValidationError={e => { this.onCellValidationError(e.detail.eventData, e.detail.args); }} />
      diff --git a/src/examples/slickgrid/example30.tsx b/src/examples/slickgrid/example30.tsx index 398f9471..879947d3 100644 --- a/src/examples/slickgrid/example30.tsx +++ b/src/examples/slickgrid/example30.tsx @@ -21,10 +21,11 @@ import { SlickGrid, SlickNamespace, SortComparers, - ReactSlickgridCustomElement, + ReactSlickgridComponent, } from '../../slickgrid-react'; import React from 'react'; import './example30.scss'; // provide custom CSS/SASS styling +import BaseSlickGridState from './state-slick-grid-base'; const NB_ITEMS = 500; const URL_COUNTRIES_COLLECTION = 'assets/data/countries.json'; @@ -83,48 +84,56 @@ const myCustomTitleValidator = (value: any, args: any) => { }; interface Props { } - -export default class Example30 extends React.Component { +interface State extends BaseSlickGridState { + isGridEditable: boolean; + isCompositeDisabled: boolean; + isMassSelectionDisabled: boolean; + cellCssStyleQueue: string[]; + complexityLevelList: Array<{ value: number; label: string; }>; +} +export default class Example30 extends React.Component { title = 'Example 30: Composite Editor Modal'; subTitle = `Composite Editor allows you to Create, Clone, Edit, Mass Update & Mass Selection Changes inside a nice Modal Window.
      The modal is simply populated by looping through your column definition list and also uses a lot of the same logic as inline editing (see Composite Editor - Wiki.)`; - reactGrid!: ReactGridInstance; compositeEditorInstance: SlickCompositeEditorComponent; - gridOptions!: GridOption; - columnDefinitions: Column[] = []; - dataset: any[] = []; + reactGrid!: ReactGridInstance; editQueue: any[] = []; editedItems: any = {}; - isGridEditable = true; - isCompositeDisabled = false; - isMassSelectionDisabled = true; cellCssStyleQueue: string[] = []; - complexityLevelList = [ - { value: 0, label: 'Very Simple' }, - { value: 1, label: 'Simple' }, - { value: 2, label: 'Straightforward' }, - { value: 3, label: 'Complex' }, - { value: 4, label: 'Very Complex' }, - ]; constructor(public readonly props: Props) { super(props); this.compositeEditorInstance = new SlickCompositeEditorComponent(); - this.componentDidMount(); - // define the grid options & columns and then create the grid itself - this.defineGrids(); + + this.state = { + gridOptions: undefined, + columnDefinitions: [], + dataset: [], + isGridEditable: true, + isCompositeDisabled: false, + isMassSelectionDisabled: true, + cellCssStyleQueue: [], + complexityLevelList: [ + { value: 0, label: 'Very Simple' }, + { value: 1, label: 'Simple' }, + { value: 2, label: 'Straightforward' }, + { value: 3, label: 'Complex' }, + { value: 4, label: 'Very Complex' }, + ], + } } componentDidMount() { document.title = this.title; - // mock some data (different in each dataset) - this.dataset = this.loadData(NB_ITEMS); + + // define the grid options & columns and then create the grid itself + this.defineGrids(); } /* Define grid Options and Columns */ defineGrids() { - this.columnDefinitions = [ + const columnDefinitions: Column[] = [ { id: 'title', name: 'Title', field: 'title', sortable: true, type: FieldType.string, minWidth: 75, filterable: true, columnGroup: 'Common Factor', @@ -200,15 +209,15 @@ export default class Example30 extends React.Component { id: 'complexity', name: 'Complexity', field: 'complexity', minWidth: 100, type: FieldType.number, sortable: true, filterable: true, columnGroup: 'Analysis', - formatter: (_row, _cell, value) => this.complexityLevelList[value].label, - exportCustomFormatter: (_row, _cell, value) => this.complexityLevelList[value].label, + formatter: (_row, _cell, value) => this.state.complexityLevelList[value].label, + exportCustomFormatter: (_row, _cell, value) => this.state.complexityLevelList[value].label, filter: { model: Filters.multipleSelect, - collection: this.complexityLevelList + collection: this.state.complexityLevelList }, editor: { model: Editors.singleSelect, - collection: this.complexityLevelList, + collection: this.state.complexityLevelList, massUpdate: true }, }, @@ -370,7 +379,7 @@ export default class Example30 extends React.Component { }, ]; - this.gridOptions = { + const gridOptions: GridOption = { enableAddRow: true, // <-- this flag is required to work with the (create & clone) modal types enableCellNavigation: true, asyncEditorLoading: false, @@ -415,7 +424,7 @@ export default class Example30 extends React.Component { // composite editors values are saved as array, so let's convert to array in any case and we'll loop through these values const prevSerializedValues = Array.isArray(editCommand.prevSerializedValue) ? editCommand.prevSerializedValue : [editCommand.prevSerializedValue]; const serializedValues = Array.isArray(editCommand.serializedValue) ? editCommand.serializedValue : [editCommand.serializedValue]; - const editorColumns = this.columnDefinitions.filter((col) => col.editor !== undefined); + const editorColumns = this.state.columnDefinitions?.filter((col) => col.editor !== undefined); const modifiedColumns: Column[] = []; prevSerializedValues.forEach((_val, index) => { @@ -424,7 +433,7 @@ export default class Example30 extends React.Component { if (prevSerializedValue !== serializedValue || serializedValue === '') { const finalColumn = Array.isArray(editCommand.prevSerializedValue) ? editorColumns[index] : column; - this.editedItems[this.gridOptions.datasetIdPropertyName || 'id'] = item; // keep items by their row indexes, if the row got edited twice then we'll keep only the last change + this.editedItems[this.state.gridOptions?.datasetIdPropertyName || 'id'] = item; // keep items by their row indexes, if the row got edited twice then we'll keep only the last change this.reactGrid.slickGrid.invalidate(); editCommand.execute(); @@ -441,6 +450,13 @@ export default class Example30 extends React.Component { // when using the cellMenu, you can change some of the default options and all use some of the callback methods enableCellMenu: true, }; + + this.setState((state: State) => ({ + ...state, + gridOptions, + columnDefinitions, + dataset: this.loadData(NB_ITEMS), + })); } loadData(count: number) { @@ -510,8 +526,8 @@ export default class Example30 extends React.Component { return false; } - handleItemDeleted(_e: Event, args: any) { - console.log('item deleted with id:', args.itemId); + handleItemDeleted(itemId: string) { + console.log('item deleted with id:', itemId); } handleOnBeforeEditCell(e: Event, args: any) { @@ -584,7 +600,10 @@ export default class Example30 extends React.Component { handleOnGridStateChanged(gridStateChanges: GridStateChange) { if (Array.isArray(gridStateChanges.gridState?.rowSelection?.dataContextIds)) { - this.isMassSelectionDisabled = gridStateChanges.gridState?.rowSelection?.dataContextIds.length === 0; + this.setState((state: State) => ({ + ...state, + isMassSelectionDisabled: gridStateChanges.gridState?.rowSelection?.dataContextIds?.length === 0, + })); } } @@ -654,13 +673,15 @@ export default class Example30 extends React.Component { this.undoAllEdits(); // then change a single grid options to make the grid non-editable (readonly) - this.isGridEditable = !this.isGridEditable; - this.isCompositeDisabled = !this.isGridEditable; - if (!this.isGridEditable) { - this.isMassSelectionDisabled = true; - } + this.setState((state: State) => ({ + ...state, + isGridEditable: !state.isGridEditable, + isCompositeDisabled: !state.isGridEditable, + isMassSelectionDisabled: !state.isGridEditable, + })); + // dynamically change SlickGrid editable grid option - this.reactGrid.slickGrid.setOptions({ editable: this.isGridEditable }); + this.reactGrid.slickGrid.setOptions({ editable: this.state.isGridEditable }); } removeUnsavedStylingFromCell(_item: any, column: Column, row: number) { @@ -966,7 +987,7 @@ export default class Example30 extends React.Component { } render() { - return ( + return !this.state.gridOptions ? '' : (

      {this.title} @@ -983,11 +1004,11 @@ export default class Example30 extends React.Component {
      @@ -1008,44 +1029,42 @@ export default class Example30 extends React.Component {
      - this.reactGridReady($event.detail), - onBeforeEditCell: $event => this.handleOnBeforeEditCell($event.detail.eventData, $event.detail.args), - onCellChange: $event => this.handleOnCellChange($event.detail.eventData, $event.detail.args), - onOnClick: $event => this.handleOnCellClicked($event.detail.eventData, $event.detail.args), - onCompositeEditorChange: $event => this.handleOnCompositeEditorChange($event.detail.eventData, $event.detail.args), - onItemDeleted: $event => this.handleItemDeleted($event.detail.eventData, $event.detail.args), - onGridStateChanged: $event => this.handleOnGridStateChanged($event.detail), - onFilterChanged: this.handleReRenderUnsavedStyling, - onPaginationChanged: this.handleReRenderUnsavedStyling, - onValidationError: $event => this.handleValidationError($event.detail.eventData, $event.detail.args) - }} + this.reactGridReady($event.detail)} + onBeforeEditCell={$event => this.handleOnBeforeEditCell($event.detail.eventData, $event.detail.args)} + onCellChange={$event => this.handleOnCellChange($event.detail.eventData, $event.detail.args)} + onClick={$event => this.handleOnCellClicked($event.detail.eventData, $event.detail.args)} + onCompositeEditorChange={$event => this.handleOnCompositeEditorChange($event.detail.eventData, $event.detail.args)} + onItemDeleted={$event => this.handleItemDeleted($event.detail)} + onGridStateChanged={$event => this.handleOnGridStateChanged($event.detail)} + onFilterChanged={() => this.handleReRenderUnsavedStyling()} + onPaginationChanged={() => this.handleReRenderUnsavedStyling()} + onValidationError={$event => this.handleValidationError($event.detail.eventData, $event.detail.args)} />
      ); diff --git a/src/examples/slickgrid/example31.scss b/src/examples/slickgrid/example31.scss deleted file mode 100644 index 8a86595f..00000000 --- a/src/examples/slickgrid/example31.scss +++ /dev/null @@ -1,2 +0,0 @@ -/* make sure to add the @import the SlickGrid Theme AFTER the variables changes */ -@import '@slickgrid-universal/common/dist/styles/sass/slickgrid-theme-salesforce.lite.scss'; diff --git a/src/examples/slickgrid/example31.tsx b/src/examples/slickgrid/example31.tsx index c8ce1a31..10169893 100644 --- a/src/examples/slickgrid/example31.tsx +++ b/src/examples/slickgrid/example31.tsx @@ -2,6 +2,7 @@ import { GridOdataService, OdataServiceApi, OdataOption } from '@slickgrid-universal/odata'; import { RxJsResource } from '@slickgrid-universal/rxjs-observable'; import { Observable, of, Subject } from 'rxjs'; +import moment from 'moment-mini'; import { Column, @@ -14,56 +15,71 @@ import { OperatorType, Pagination, ReactGridInstance, - ReactSlickgridCustomElement, + ReactSlickgridComponent, } from '../../slickgrid-react'; import React from 'react'; -import './example31.scss'; // provide custom CSS/SASS styling +import BaseSlickGridState from './state-slick-grid-base'; const defaultPageSize = 20; const sampleDataRoot = 'assets/data'; +interface Status { text: string, class: string } + +interface State extends BaseSlickGridState { + paginationOptions?: Pagination; + metrics: Metrics; + isCountEnabled: boolean; + isSelectEnabled: boolean; + isExpandEnabled: boolean; + odataVersion: number; + odataQuery: string; + processing: boolean; + errorStatus: string; + status: Status; + genderCollection: Array<{ value: string; label: string; }>; +} interface Props { } -export default class Example31 extends React.Component { +export default class Example31 extends React.Component { title = 'Example 31: Grid with OData Backend Service using RxJS Observables'; subTitle = ` Optionally use RxJS instead of Promises, you would typically use this with a Backend Service API (OData/GraphQL) `; reactGrid!: ReactGridInstance; - columnDefinitions: Column[] = []; - gridOptions!: GridOption; - dataset = [] = []; - metrics!: Metrics; - paginationOptions!: Pagination; - - isCountEnabled = true; - isSelectEnabled = false; - isExpandEnabled = false; - odataVersion = 2; - odataQuery = ''; - processing = false; - errorStatus = ''; - isPageErrorTest = false; - status = { text: '', class: '' }; isOtherGenderAdded = false; - genderCollection = [{ value: 'male', label: 'male' }, { value: 'female', label: 'female' }]; constructor(public readonly props: Props) { super(props); - this.initializeGrid(); + this.state = { + gridOptions: undefined, + columnDefinitions: [], + dataset: [], + paginationOptions: undefined, + errorStatus: '', + isCountEnabled: true, + isSelectEnabled: false, + isExpandEnabled: false, + metrics: {} as Metrics, + status: { class: '', text: '' }, + odataVersion: 2, + odataQuery: '', + processing: false, + genderCollection: [{ value: 'male', label: 'male' }, { value: 'female', label: 'female' }], + }; } componentDidMount() { - document.title = this.title; + this.defineGrid(); } reactGridReady(reactGrid: ReactGridInstance) { + console.log('reactGridReady', reactGrid) this.reactGrid = reactGrid; } - initializeGrid() { - this.columnDefinitions = [ + defineGrid() { + const columnDefinitions: Column[] = [ { id: 'name', name: 'Name', field: 'name', sortable: true, type: FieldType.string, @@ -77,11 +93,11 @@ export default class Example31 extends React.Component { editor: { model: Editors.singleSelect, // collection: this.genderCollection, - collectionAsync: of(this.genderCollection) + collectionAsync: of(this.state.genderCollection) }, filter: { model: Filters.singleSelect, - collectionAsync: of(this.genderCollection), + collectionAsync: of(this.state.genderCollection), collectionOptions: { addBlankEntry: true } @@ -91,7 +107,7 @@ export default class Example31 extends React.Component { { id: 'category_name', name: 'Category', field: 'category/name', filterable: true, sortable: true }, ]; - this.gridOptions = { + const gridOptions: GridOption = { enableAutoResize: true, autoResize: { container: '#demo-container', @@ -115,11 +131,11 @@ export default class Example31 extends React.Component { pagination: { pageSizes: [10, 20, 50, 100, 500], pageSize: defaultPageSize, + totalItems: 0 }, presets: { // you can also type operator as string, e.g.: operator: 'EQ' filters: [ - // { columnId: 'name', searchTerms: ['w'], operator: OperatorType.startsWith }, { columnId: 'gender', searchTerms: ['male'], operator: OperatorType.equal }, ], sorters: [ @@ -131,26 +147,37 @@ export default class Example31 extends React.Component { backendServiceApi: { service: new GridOdataService(), options: { - enableCount: this.isCountEnabled, // add the count in the OData query, which will return a property named "__count" (v2) or "@odata.count" (v4) - enableSelect: this.isSelectEnabled, - enableExpand: this.isExpandEnabled, - version: this.odataVersion // defaults to 2, the query string is slightly different between OData 2 and 4 + enableCount: this.state.isCountEnabled, // add the count in the OData query, which will return a property named "__count" (v2) or "@odata.count" (v4) + enableSelect: this.state.isSelectEnabled, + enableExpand: this.state.isExpandEnabled, + version: this.state.odataVersion // defaults to 2, the query string is slightly different between OData 2 and 4 + }, + preProcess: () => { + this.setState((state: State) => ({ ...state, errorStatus: '' })); + this.displaySpinner(true); }, - preProcess: () => this.displaySpinner(true), process: (query) => this.getCustomerApiCall(query), postProcess: (response) => { - this.metrics = response.metrics; + this.setState( + (state: State) => ({ ...state, metrics: response.metrics }), + () => this.getCustomerCallback(response) + ); this.displaySpinner(false); - this.getCustomerCallback(response); } } as OdataServiceApi, registerExternalResources: [new RxJsResource()] }; + + this.setState((state: State) => ({ + ...state, + columnDefinitions, + gridOptions, + })); } addOtherGender() { const newGender = { value: 'other', label: 'other' }; - const genderColumn = this.columnDefinitions.find((column: Column) => column.id === 'gender'); + const genderColumn = this.state.columnDefinitions.find((column: Column) => column.id === 'gender'); if (genderColumn) { let editorCollection = genderColumn.editor!.collection; @@ -163,7 +190,7 @@ export default class Example31 extends React.Component { // editorCollection.push(newGender); // 2. or replace the entire "collection" - genderColumn.editor!.collection = [...this.genderCollection, newGender]; + genderColumn.editor!.collection = [...this.state.genderCollection, newGender]; editorCollection = genderColumn.editor!.collection; // However, for the Filter only, we have to trigger an RxJS/Subject change with the new collection @@ -179,26 +206,36 @@ export default class Example31 extends React.Component { } displaySpinner(isProcessing: boolean) { - this.processing = isProcessing; - this.status = (isProcessing) + const newStatus = (isProcessing) ? { text: 'loading...', class: 'col-md-2 alert alert-warning' } : { text: 'finished!!', class: 'col-md-2 alert alert-success' }; + + this.setState((state: State) => ({ + ...state, + processing: isProcessing, + status: newStatus + })); } getCustomerCallback(data: any) { // totalItems property needs to be filled for pagination to work correctly // however we need to force React to do a dirty check, doing a clone object will do just that let totalItemCount: number = data['totalRecordCount']; // you can use "totalRecordCount" or any name or "odata.count" when "enableCount" is set - if (this.isCountEnabled) { - totalItemCount = (this.odataVersion === 4) ? data['@odata.count'] : data['d']['__count']; - } - if (this.metrics) { - this.metrics.totalItemCount = totalItemCount; + if (this.state.isCountEnabled) { + totalItemCount = (this.state.odataVersion === 4) ? data['@odata.count'] : data['d']['__count']; } - this.paginationOptions = { ...this.gridOptions.pagination, totalItems: totalItemCount } as Pagination; - this.dataset = this.odataVersion === 4 ? data.value : data.d.results; - this.odataQuery = data['query']; + // once pagination totalItems is filled, we can update the dataset + this.setState((state: State) => ({ + ...state, + paginationOptions: { ...state.gridOptions!.pagination, totalItems: totalItemCount } as Pagination, + dataset: state.odataVersion === 4 ? data.value : data.d.results, + odataQuery: data['query'], + metrics: { ...state.metrics, totalItemCount } + })); + + // Slickgrid-React requires the user to update pagination via this pubsub publish + this.reactGrid.eventPubSubService?.publish('onPaginationOptionsChanged', { ...this.state.gridOptions!.pagination, totalItems: totalItemCount } as Pagination, 1); } getCustomerApiCall(query: string) { @@ -335,18 +372,18 @@ export default class Example31 extends React.Component { setTimeout(() => { const backendResult: any = { query }; - if (!this.isCountEnabled) { + if (!this.state.isCountEnabled) { backendResult['totalRecordCount'] = countTotalItems; } - if (this.odataVersion === 4) { + if (this.state.odataVersion === 4) { backendResult['value'] = updatedData; - if (this.isCountEnabled) { + if (this.state.isCountEnabled) { backendResult['@odata.count'] = countTotalItems; } } else { backendResult['d'] = { results: updatedData }; - if (this.isCountEnabled) { + if (this.state.isCountEnabled) { backendResult['d']['__count'] = countTotalItems; } } @@ -364,11 +401,11 @@ export default class Example31 extends React.Component { } goToFirstPage() { - this.reactGrid?.paginationService?.goToFirstPage(); + this.reactGrid.paginationService!.goToFirstPage(); } goToLastPage() { - this.reactGrid?.paginationService?.goToLastPage(); + this.reactGrid.paginationService!.goToLastPage(); } /** Dispatched event of a Grid State Changed event */ @@ -379,14 +416,14 @@ export default class Example31 extends React.Component { setFiltersDynamically() { // we can Set Filters Dynamically (or different filters) afterward through the FilterService - this.reactGrid?.filterService.updateFilters([ + this.reactGrid.filterService.updateFilters([ // { columnId: 'gender', searchTerms: ['male'], operator: OperatorType.equal }, { columnId: 'name', searchTerms: ['A'], operator: 'a*' }, ]); } setSortingDynamically() { - this.reactGrid?.sortService.updateSorting([ + this.reactGrid.sortService.updateSorting([ { columnId: 'name', direction: 'DESC' }, ]); } @@ -415,38 +452,41 @@ export default class Example31 extends React.Component { // --- changeCountEnableFlag() { - this.isCountEnabled = !this.isCountEnabled; - this.resetOptions({ enableCount: this.isCountEnabled }); + const isCountEnabled = !this.state.isCountEnabled; + this.setState((state: State) => ({ ...state, isCountEnabled })); + this.resetOptions({ enableCount: isCountEnabled }); return true; } changeEnableSelectFlag() { - this.isSelectEnabled = !this.isSelectEnabled; - this.resetOptions({ enableSelect: this.isSelectEnabled }); + const isSelectEnabled = !this.state.isSelectEnabled; + this.setState((state: State) => ({ ...state, isSelectEnabled })); + this.resetOptions({ enableSelect: isSelectEnabled }); return true; } changeEnableExpandFlag() { - this.isExpandEnabled = !this.isExpandEnabled; - this.resetOptions({ enableExpand: this.isExpandEnabled }); + const isExpandEnabled = !this.state.isExpandEnabled; + this.setState((state: State) => ({ ...state, isExpandEnabled })); + this.resetOptions({ enableExpand: isExpandEnabled }); return true; } setOdataVersion(version: number) { - this.odataVersion = version; - this.resetOptions({ version: this.odataVersion }); + this.setState((state: State) => ({ ...state, odataVersion: version })); + this.resetOptions({ version }); return true; } private resetOptions(options: Partial) { - const odataService = this.gridOptions.backendServiceApi!.service as GridOdataService; + const odataService = this.state.gridOptions?.backendServiceApi?.service as GridOdataService; odataService.updateOptions(options); odataService.clearFilters(); this.reactGrid?.filterService.clearFilters(); } render() { - return ( + return !this.state.gridOptions ? '' : (

      {this.title} @@ -463,20 +503,21 @@ export default class Example31 extends React.Component {
      - -
      @@ -487,40 +528,40 @@ export default class Example31 extends React.Component {
      - - - +
      @@ -528,22 +569,26 @@ export default class Example31 extends React.Component {
      - OData Query: {this.odataQuery} + OData Query: {this.state.odataQuery}
      -
      - Status: {this.status.text} +
      + Status: {this.state.status.text}
      - this.reactGridReady($event.detail)} - onGridStateChanged={$event => this.gridStateChanged($event.detail)} /> + onGridStateChanged={$event => this.gridStateChanged($event.detail)} + onBeforeSort={$event => this.handleOnBeforeSort($event.detail.eventData)} + onBeforeSearchChange={$event => this.handleOnBeforeSearchChange($event.detail.eventData)} + onBeforePaginationChange={$event => this.handleOnBeforePaginationChange($event.detail.eventData)} + />
      ); } -} +} \ No newline at end of file diff --git a/src/examples/slickgrid/example32.tsx b/src/examples/slickgrid/example32.tsx index de55cb0c..27caed63 100644 --- a/src/examples/slickgrid/example32.tsx +++ b/src/examples/slickgrid/example32.tsx @@ -15,9 +15,10 @@ import { SortComparers, } from '@slickgrid-universal/common'; import { ExcelExportService } from '@slickgrid-universal/excel-export'; - -import { ReactGridInstance, ReactSlickgridCustomElement } from '../../slickgrid-react'; import React from 'react'; + +import BaseSlickGridState from './state-slick-grid-base'; +import { ReactGridInstance, ReactSlickgridComponent } from '../../slickgrid-react'; import './example32.scss'; // provide custom CSS/SASS styling const NB_ITEMS = 5000; @@ -76,47 +77,48 @@ const myCustomTitleValidator = (value: any, args: any) => { }; interface Props { } +interface State extends BaseSlickGridState { + isUsingDefaultResize: boolean; + isGridEditable: boolean; + complexityLevelList: Array<{ value: number; label: string; }>; +} -export default class Example32 extends React.Component { +export default class Example32 extends React.Component { title = 'Example 32: Columns Resize by Content'; subTitle = `The grid below uses the optional resize by cell content (with a fixed 950px for demo purposes), you can click on the 2 buttons to see the difference. The "autosizeColumns" is really the default option used by SlickGrid-Universal, the resize by cell content is optional because it requires to read the first thousand rows and do extra width calculation.`; - - reactGrid!: ReactGridInstance; - gridOptions!: GridOption; - columnDefinitions: Column[] = []; - dataset: any[] = []; editQueue: any[] = []; editedItems: any = {}; - isUsingDefaultResize = false; - isGridEditable = true; - isCompositeDisabled = false; - isMassSelectionDisabled = true; - complexityLevelList = [ - { value: 0, label: 'Very Simple' }, - { value: 1, label: 'Simple' }, - { value: 2, label: 'Straightforward' }, - { value: 3, label: 'Complex' }, - { value: 4, label: 'Very Complex' }, - ]; + reactGrid!: ReactGridInstance; constructor(public readonly props: Props) { super(props); - this.initializeGrid(); - this.componentDidMount(); + + this.state = { + gridOptions: undefined, + columnDefinitions: [], + isUsingDefaultResize: false, + isGridEditable: true, + complexityLevelList: [ + { value: 0, label: 'Very Simple' }, + { value: 1, label: 'Simple' }, + { value: 2, label: 'Straightforward' }, + { value: 3, label: 'Complex' }, + { value: 4, label: 'Very Complex' }, + ], + } } componentDidMount() { document.title = this.title; - // mock some data (different in each dataset) - this.dataset = this.loadData(NB_ITEMS); + this.defineGrid(); } reactGridReady(reactGrid: ReactGridInstance) { this.reactGrid = reactGrid; } - initializeGrid() { - this.columnDefinitions = [ + defineGrid() { + const columnDefinitions: Column[] = [ { id: 'title', name: 'Title', field: 'title', sortable: true, type: FieldType.string, minWidth: 65, // you can adjust the resize calculation via multiple options @@ -172,15 +174,15 @@ export default class Example32 extends React.Component { id: 'complexity', name: 'Complexity', field: 'complexity', resizeCalcWidthRatio: 0.9, // default calc ratio is 1 or ~0.9 for field type of string sortable: true, filterable: true, columnGroup: 'Analysis', - formatter: (_row, _cell, value) => this.complexityLevelList[value].label, - exportCustomFormatter: (_row, _cell, value) => this.complexityLevelList[value].label, + formatter: (_row, _cell, value) => this.state.complexityLevelList[value].label, + exportCustomFormatter: (_row, _cell, value) => this.state.complexityLevelList[value].label, filter: { model: Filters.multipleSelect, - collection: this.complexityLevelList + collection: this.state.complexityLevelList }, editor: { model: Editors.singleSelect, - collection: this.complexityLevelList, + collection: this.state.complexityLevelList, }, }, { @@ -323,7 +325,7 @@ export default class Example32 extends React.Component { }, ]; - this.gridOptions = { + const gridOptions: GridOption = { editable: true, autoAddCustomEditorFormatter: customEditableInputFormatter, enableCellNavigation: true, @@ -375,7 +377,7 @@ export default class Example32 extends React.Component { // composite editors values are saved as array, so let's convert to array in any case and we'll loop through these values const prevSerializedValues = Array.isArray(editCommand.prevSerializedValue) ? editCommand.prevSerializedValue : [editCommand.prevSerializedValue]; const serializedValues = Array.isArray(editCommand.serializedValue) ? editCommand.serializedValue : [editCommand.serializedValue]; - const editorColumns = this.columnDefinitions.filter((col) => col.editor !== undefined); + const editorColumns = this.state.columnDefinitions.filter((col) => col.editor !== undefined); const modifiedColumns: Column[] = []; prevSerializedValues.forEach((_val, index) => { @@ -384,7 +386,7 @@ export default class Example32 extends React.Component { if (prevSerializedValue !== serializedValue) { const finalColumn = Array.isArray(editCommand.prevSerializedValue) ? editorColumns[index] : column; - this.editedItems[this.gridOptions.datasetIdPropertyName || 'id'] = item; // keep items by their row indexes, if the row got edited twice then we'll keep only the last change + this.editedItems[this.state.gridOptions?.datasetIdPropertyName ?? 'id'] = item; // keep items by their row indexes, if the row got edited twice then we'll keep only the last change this.reactGrid.slickGrid.invalidate(); editCommand.execute(); @@ -401,6 +403,13 @@ export default class Example32 extends React.Component { // when using the cellMenu, you can change some of the default options and all use some of the callback methods enableCellMenu: true, }; + + this.setState((state: State) => ({ + ...state, + gridOptions, + columnDefinitions, + dataset: this.loadData(NB_ITEMS) + })); } loadData(count: number) { @@ -499,12 +508,12 @@ export default class Example32 extends React.Component { columns.forEach(col => col.width = col.originalWidth); this.reactGrid.slickGrid.setColumns(columns); this.reactGrid.slickGrid.autosizeColumns(); - this.isUsingDefaultResize = true; + this.setState((state: State) => ({ ...state, isUsingDefaultResize: true })); } handleNewResizeColumns() { this.reactGrid.resizerService.resizeColumnsByCellContent(true); - this.isUsingDefaultResize = false; + this.setState((state: State) => ({ ...state, isUsingDefaultResize: false })); } toggleGridEditReadonly() { @@ -512,13 +521,11 @@ export default class Example32 extends React.Component { this.undoAllEdits(); // then change a single grid options to make the grid non-editable (readonly) - this.isGridEditable = !this.isGridEditable; - this.isCompositeDisabled = !this.isGridEditable; - if (!this.isGridEditable) { - this.isMassSelectionDisabled = true; - } + const isGridEditable = !this.state.isGridEditable; + this.setState((state: State) => ({ ...state, isGridEditable })); + // dynamically change SlickGrid editable grid option - this.reactGrid.slickGrid.setOptions({ editable: this.isGridEditable }); + this.reactGrid.slickGrid.setOptions({ editable: isGridEditable }); } removeUnsavedStylingFromCell(_item: any, column: Column, row: number) { @@ -823,7 +830,7 @@ export default class Example32 extends React.Component { } render() { - return ( + return !this.state.gridOptions ? '' : (

      {this.title} @@ -842,18 +849,18 @@ export default class Example32 extends React.Component {
      -
      @@ -862,15 +869,15 @@ export default class Example32 extends React.Component {
      @@ -878,15 +885,11 @@ export default class Example32 extends React.Component {
      - this.reactGridReady($event.detail), - onGridStateChanged: $event => this.gridStateChanged($event.detail) - }} + this.reactGridReady($event.detail)} />
      diff --git a/src/examples/slickgrid/example4.tsx b/src/examples/slickgrid/example4.tsx index 15c2f789..b0cee1c2 100644 --- a/src/examples/slickgrid/example4.tsx +++ b/src/examples/slickgrid/example4.tsx @@ -14,7 +14,7 @@ import { MultipleSelectOption, OperatorType, ReactGridInstance, - ReactSlickgridCustomElement, + ReactSlickgridComponent, } from '../../slickgrid-react'; import BaseSlickGridState from './state-slick-grid-base'; @@ -385,7 +385,7 @@ export default class Example4 extends React.Component { Set Sorting Dynamically - { constructor(public readonly props: Props) { super(props); this.state = { - gridOptions: {}, + gridOptions: undefined, columnDefinitions: [], dataset: [], - paginationOptions: null, + paginationOptions: undefined, errorStatus: '', - isCountEnabled: false, + isCountEnabled: true, isSelectEnabled: false, isExpandEnabled: false, metrics: {} as Metrics, status: {class:'', text:''}, - odataVersion: 0, + odataVersion: 2, odataQuery:'', processing:false, isPageErrorTest:false @@ -85,10 +89,11 @@ export default class Example5 extends React.Component { } reactGridReady(reactGrid: ReactGridInstance) { + console.log('reactGridReady', reactGrid) this.reactGrid = reactGrid; } - getGridDefinition() { + getGridDefinition(): GridOption { return { enableAutoResize: true, autoResize: { @@ -130,32 +135,26 @@ export default class Example5 extends React.Component { version: this.state.odataVersion // defaults to 2, the query string is slightly different between OData 2 and 4 }, onError: (error: Error) => { - this.setState((state:State, props:Props)=>{ - return { ...state, errorStatus: error.message }; - }); + this.setState((state: State) => ({ ...state, errorStatus: error.message })); this.displaySpinner(false, true); }, preProcess: () => { - this.setState((state:State, props:Props)=>{ - return { ...state, errorStatus: '' }; - }); + this.setState((state: State) => ({ ...state, errorStatus: '' })); this.displaySpinner(true); }, - process: (query) => { - this.getCustomerApiCall(query); - }, + process: (query) => this.getCustomerApiCall(query), postProcess: (response) => { - this.setState((state: State, props: Props) => { - return { ...state, metrics: response.metrics }; - }); + this.setState( + (state: State) => ({ ...state, metrics: response.metrics }), + () => this.getCustomerCallback(response) + ); this.displaySpinner(false); - this.getCustomerCallback(response); } } as OdataServiceApi }; } - getColumnDefinitions() { + getColumnDefinitions(): Column[] { return [ { id: 'name', name: 'Name', field: 'name', sortable: true, @@ -181,32 +180,25 @@ export default class Example5 extends React.Component { const columnDefinitions = this.getColumnDefinitions(); const gridOptions = this.getGridDefinition(); - this.setState((state:any, props:Props)=>{ - return { + this.setState((state: State) => ({ ...state, columnDefinitions, gridOptions, - }; - }); + })); } displaySpinner(isProcessing: boolean, isError?: boolean) { - this.setState((state:State, props:Props)=>{ - return { ...state, processing: isProcessing }; - }); + this.setState((state: State) => ({ ...state, processing: isProcessing })); + if (isError) { - this.setState((state:State, props:Props)=>{ - return { ...state, status: { text: 'ERROR!!!', class: 'alert alert-danger' } }; - }); + this.setState((state: State) => ({ ...state, status: { text: 'ERROR!!!', class: 'alert alert-danger' } })); } else { - this.setState((state:State, props:Props)=>{ - return { - ...state, - status: (isProcessing) - ? { text: 'loading', class: 'alert alert-warning' } - : { text: 'finished', class: 'alert alert-success' } - }; - }); + this.setState((state: State) => ({ + ...state, + status: (isProcessing) + ? { text: 'loading', class: 'alert alert-warning' } + : { text: 'finished', class: 'alert alert-success' } + })); } } @@ -217,25 +209,18 @@ export default class Example5 extends React.Component { if (this.state.isCountEnabled) { totalItemCount = (this.state.odataVersion === 4) ? data['@odata.count'] : data['d']['__count']; } - this.setState((state: State, props: Props) => { - return { - ...state, - paginationOptions: { ...state.gridOptions!.pagination, totalItems: totalItemCount } as Pagination - }; - }); - if (this.state.metrics) { - this.state.metrics.totalItemCount = totalItemCount; - } // once pagination totalItems is filled, we can update the dataset - this.setState((state: State, props: Props) => { - return { + this.setState((state: State) => ({ ...state, paginationOptions: { ...state.gridOptions!.pagination, totalItems: totalItemCount } as Pagination, dataset: state.odataVersion === 4 ? data.value : data.d.results, - odataQuery: data['query'] - }; - }); + odataQuery: data['query'], + metrics: { ...state.metrics, totalItemCount } + })); + + // Slickgrid-React requires the user to update pagination via this pubsub publish + this.reactGrid.eventPubSubService?.publish('onPaginationOptionsChanged', { ...this.state.gridOptions!.pagination, totalItems: totalItemCount } as Pagination, 1); } getCustomerApiCall(query: string) { @@ -259,12 +244,7 @@ export default class Example5 extends React.Component { const columnFilters = {}; if (this.state.isPageErrorTest) { - this.setState((state:State, props:Props)=>{ - return { - ...state, - isPageErrorTest: false, - }; - }); + this.setState((state: State) => ({ ...state, isPageErrorTest: false })); throw new Error('Server timed out trying to retrieve data for the last page'); } @@ -387,6 +367,9 @@ export default class Example5 extends React.Component { case 'lt': return filterTerm.toLowerCase() < searchTerm; case 'gt': return filterTerm.toLowerCase() > searchTerm; case 'ge': return filterTerm.toLowerCase() >= searchTerm; + case 'ends': return filterTerm.toLowerCase().endsWith(searchTerm); + case 'starts': return filterTerm.toLowerCase().startsWith(searchTerm); + case 'substring': return filterTerm.toLowerCase().includes(searchTerm); } } }); @@ -425,6 +408,7 @@ export default class Example5 extends React.Component { } goToLastPage() { + console.log('goto last page', this.reactGrid.paginationService) this.reactGrid.paginationService!.goToLastPage(); } @@ -449,13 +433,10 @@ export default class Example5 extends React.Component { } throwPageChangeError() { - this.setState((state:State, props:Props)=>{ - return { - ...state, - isPageErrorTest: true, - }; - }); - this.reactGrid?.paginationService?.goToLastPage(); + this.setState( + (state: State) => ({ ...state, isPageErrorTest: true }), + () => this.reactGrid?.paginationService?.goToLastPage() + ); } // YOU CAN CHOOSE TO PREVENT EVENT FROM BUBBLING IN THE FOLLOWING 3x EVENTS @@ -482,46 +463,29 @@ export default class Example5 extends React.Component { // --- changeCountEnableFlag() { - this.setState((state:State, props:Props)=>{ - return { - ...state, - isCountEnabled: !this.state.isCountEnabled, - }; - }); - this.resetOptions({ enableCount: this.state.isCountEnabled }); + const isCountEnabled = !this.state.isCountEnabled; + this.setState((state: State) => ({ ...state, isCountEnabled })); + this.resetOptions({ enableCount: isCountEnabled }); return true; } changeEnableSelectFlag() { - this.setState((state: State, props: Props) => { - return { - ...state, - isSelectEnabled: !this.state.isSelectEnabled, - }; - }); - this.resetOptions({ enableSelect: this.state.isSelectEnabled }); + const isSelectEnabled = !this.state.isSelectEnabled; + this.setState((state: State) => ({ ...state, isSelectEnabled })); + this.resetOptions({ enableSelect: isSelectEnabled }); return true; } changeEnableExpandFlag() { - this.setState((state: State, props: Props) => { - return { - ...state, - isExpandEnabled: !this.state.isExpandEnabled, - }; - }); - this.resetOptions({ enableExpand: this.state.isExpandEnabled }); + const isExpandEnabled = !this.state.isExpandEnabled; + this.setState((state: State) => ({ ...state, isExpandEnabled })); + this.resetOptions({ enableExpand: isExpandEnabled }); return true; } setOdataVersion(version: number) { - this.setState((state:State, props:Props)=>{ - return { - ...state, - odataVersion: version, - }; - }); - this.resetOptions({ version: this.state.odataVersion }); + this.setState((state: State) => ({ ...state, odataVersion: version })); + this.resetOptions({ version }); return true; } @@ -533,7 +497,7 @@ export default class Example5 extends React.Component { } render() { - return ( + return !this.state.gridOptions ? '' : (

      {this.title} @@ -575,76 +539,74 @@ export default class Example5 extends React.Component {
      - -
      - {this.state.metrics && - Metrics: {this.state.metrics.endTime} | {this.state.metrics.executionTime}ms | - {this.state.metrics.totalItemCount} - items + {this.state.metrics && <>Metrics: + {moment(this.state.metrics.endTime).format('YYYY-MM-DD HH:mm:ss')} + | {this.state.metrics.itemCount} of {this.state.metrics.totalItemCount} items }
      - +
      - -
      - this.reactGridReady($event.detail.args)} - onGridStateChanged={$event => this.gridStateChanged($event.detail.args)} + paginationOptions={this.state.paginationOptions} + onReactGridCreated={$event => this.reactGridReady($event.detail)} + onGridStateChanged={$event => this.gridStateChanged($event.detail)} onBeforeSort={$event => this.handleOnBeforeSort($event.detail.eventData)} onBeforeSearchChange= {$event => this.handleOnBeforeSearchChange($event.detail.eventData)} onBeforePaginationChange= {$event => this.handleOnBeforePaginationChange($event.detail.eventData)} diff --git a/src/examples/slickgrid/example6.tsx b/src/examples/slickgrid/example6.tsx index a327c5bf..70d84bd0 100644 --- a/src/examples/slickgrid/example6.tsx +++ b/src/examples/slickgrid/example6.tsx @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import { GraphqlService, GraphqlPaginatedResult, GraphqlServiceApi, } from '@slickgrid-universal/graphql'; -import i18next from 'i18next'; +import i18next, { TFunction } from 'i18next'; import * as moment from 'moment-mini'; import { ReactGridInstance, @@ -12,35 +12,30 @@ import { MultipleSelectOption, OperatorType, SortDirection, - ReactSlickgridCustomElement, + ReactSlickgridComponent, } from '../../slickgrid-react'; import React from 'react'; import BaseSlickGridState from './state-slick-grid-base'; - -const defaultPageSize = 20; -const GRAPHQL_QUERY_DATASET_NAME = 'users'; - -i18next.init({ - lng: 'en', -} -); +import { withTranslation } from 'react-i18next'; interface Status { text: string, class: string } - -// eslint-disable-next-line @typescript-eslint/no-empty-interface -interface Props { } +interface Props { + t: TFunction; +} interface State extends BaseSlickGridState{ - graphqlQuery:string, - isWithCursor:boolean, - processing:boolean, - selectedLanguage:string, - metrics: Metrics, + graphqlQuery: string, + isWithCursor: boolean, + processing: boolean, + selectedLanguage: string, + metrics?: Metrics, status: Status, - } -export default class Example6 extends React.Component { +const defaultPageSize = 20; +const GRAPHQL_QUERY_DATASET_NAME = 'users'; + +class Example6 extends React.Component { title = 'Example 6: Grid with Backend GraphQL Service'; subTitle = ` Use it when you need to support Pagination with a GraphQL endpoint (for simple JSON, use a regular grid). @@ -66,13 +61,13 @@ export default class Example6 extends React.Component { const defaultLang = 'en'; this.state = { - gridOptions: {}, + gridOptions: undefined, columnDefinitions: [], dataset: [], - metrics: null, - processing:false, + metrics: undefined, + processing: false, graphqlQuery: '', - isWithCursor:false, + isWithCursor: false, selectedLanguage: defaultLang, status: {} as Status, }; @@ -80,7 +75,7 @@ export default class Example6 extends React.Component { i18next.changeLanguage(defaultLang); } - componentDidMount(): void { + componentDidMount() { this.defineGrid(); } @@ -89,6 +84,7 @@ export default class Example6 extends React.Component { } reactGridReady(reactGrid: ReactGridInstance) { + console.log('reactGridReady', reactGrid) this.reactGrid = reactGrid; } @@ -159,7 +155,6 @@ export default class Example6 extends React.Component { gridOptions }; }); - } getGridOptions(){ @@ -230,12 +225,10 @@ export default class Example6 extends React.Component { postProcess: (result: GraphqlPaginatedResult) => { const metrics = result.metrics as Metrics; - this.setState((state:any, props) => { - return { + this.setState((state: State) => ({ ...state, metrics, - }; - }); + })); this.displaySpinner(false); } @@ -243,7 +236,7 @@ export default class Example6 extends React.Component { }; } clearAllFiltersAndSorts() { - if (this.reactGrid && this.reactGrid.gridService) { + if (this.reactGrid?.gridService) { this.reactGrid.gridService.clearAllFiltersAndSorts(); } } @@ -256,7 +249,7 @@ export default class Example6 extends React.Component { this.setState((state:any, props:any)=>{ return { ...state, - status:newStatus, + status: newStatus, processing: isProcessing, }; }); @@ -295,11 +288,11 @@ export default class Example6 extends React.Component { } goToFirstPage() { - this.reactGrid.paginationService!.goToFirstPage(); + this.reactGrid?.paginationService!.goToFirstPage(); } goToLastPage() { - this.reactGrid.paginationService!.goToLastPage(); + this.reactGrid?.paginationService!.goToLastPage(); } /** Dispatched event of a Grid State Changed event */ @@ -308,7 +301,7 @@ export default class Example6 extends React.Component { } saveCurrentGridState() { - console.log('GraphQL current grid state', this.reactGrid.gridStateService.getCurrentGridState()); + console.log('GraphQL current grid state', this.reactGrid?.gridStateService.getCurrentGridState()); } setFiltersDynamically() { @@ -316,7 +309,7 @@ export default class Example6 extends React.Component { const presetHighestDay = moment().add(20, 'days').format('YYYY-MM-DD'); // we can Set Filters Dynamically (or different filters) afterward through the FilterService - this.reactGrid.filterService.updateFilters([ + this.reactGrid?.filterService.updateFilters([ { columnId: 'gender', searchTerms: ['female'], operator: OperatorType.equal }, { columnId: 'name', searchTerms: ['Jane'], operator: OperatorType.startsWith }, { columnId: 'company', searchTerms: ['acme'], operator: 'IN' }, @@ -326,7 +319,7 @@ export default class Example6 extends React.Component { } setSortingDynamically() { - this.reactGrid.sortService.updateSorting([ + this.reactGrid?.sortService.updateSorting([ // orders matter, whichever is first in array will be the first sorted column { columnId: 'billingAddressZip', direction: 'DESC' }, { columnId: 'company', direction: 'ASC' }, @@ -336,16 +329,11 @@ export default class Example6 extends React.Component { async switchLanguage() { const nextLanguage = (this.state.selectedLanguage === 'en') ? 'fr' : 'en'; await i18next.changeLanguage(nextLanguage); - this.setState((state:any, props:any)=>{ - return { - ...state, - selectedLanguage: nextLanguage, - }; - }); + this.setState((state: State) => ({ ...state, selectedLanguage: nextLanguage })); } render() { - return ( + return !this.state.gridOptions ? '' : (

      {this.title} @@ -363,24 +351,24 @@ export default class Example6 extends React.Component {
      Status: {this.state.status.text} - {!this.state.processing && + {this.state.processing ? - } + : ''}
      -
      @@ -390,30 +378,31 @@ export default class Example6 extends React.Component {
      - - Locale: + Locale: {this.state.selectedLanguage + '.json'}

      - {this.state.metrics &&
      - Metrics: {this.state.metrics.endTime} | {this.state.metrics.executionTime}ms | - {this.state.metrics.totalItemCount} - items -
      } + {this.state.metrics && <>Metrics: + {moment(this.state.metrics.endTime).format('YYYY-MM-DD HH:mm:ss')} + | {this.state.metrics.executionTime}ms + | {this.state.metrics.totalItemCount} items + } +
      - -
      @@ -428,14 +417,16 @@ export default class Example6 extends React.Component {
      - this.reactGridReady($event.detail.args)} - onGridStateChanged= {$event => this.gridStateChanged($event.detail.args)} + onReactGridCreated={$event => this.reactGridReady($event.detail)} + onGridStateChanged={$event => this.gridStateChanged($event.detail)} />
      ); } } + +export default withTranslation()(Example6); \ No newline at end of file diff --git a/src/examples/slickgrid/example7.tsx b/src/examples/slickgrid/example7.tsx index 07c6fa0b..fd6e8095 100644 --- a/src/examples/slickgrid/example7.tsx +++ b/src/examples/slickgrid/example7.tsx @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import React from 'react'; -import { ReactGridInstance, Formatter, SlickGrid, ReactSlickgridCustomElement } from '../../slickgrid-react'; +import { ReactGridInstance, Formatter, SlickGrid, ReactSlickgridComponent } from '../../slickgrid-react'; import BaseSlickGridState from './state-slick-grid-base'; const columnsWithHighlightingById: any = {}; @@ -51,7 +51,7 @@ export default class Example7 extends React.Component { this.state = { columnDefinitions: [], dataset: [], - gridOptions:{} + gridOptions: undefined }; } @@ -115,7 +115,7 @@ export default class Example7 extends React.Component { getData() { // Set up some test columns. - const columnDefinitions = []; + const columnDefinitions: any[] = []; for (let i = 0; i < 10; i++) { columnDefinitions.push({ id: i, @@ -196,7 +196,7 @@ export default class Example7 extends React.Component { }; // mock a dataset - const mockDataset = []; + const mockDataset: any[] = []; for (let i = 0; i < 100; i++) { const d = (mockDataset[i] = {}); (d as any)['id'] = i; @@ -216,7 +216,7 @@ export default class Example7 extends React.Component { } render() { - return ( + return !this.state.gridOptions ? '' : (

      {this.title} @@ -230,11 +230,11 @@ export default class Example7 extends React.Component {

      - this.reactGridReady($event.detail.args)} + onReactGridCreated={$event => this.reactGridReady($event.detail)} />
      ); diff --git a/src/examples/slickgrid/example8.scss b/src/examples/slickgrid/example8.scss index 2942e9ff..aa6ef2fc 100644 --- a/src/examples/slickgrid/example8.scss +++ b/src/examples/slickgrid/example8.scss @@ -1,10 +1,9 @@ - -$header-menu-button-border: 1px solid #cfcfcf; -$header-menu-button-border-width: 0px 1px; -$header-menu-button-icon: "\f0d7"; -$header-menu-button-width: 16px; -$header-menu-button-padding: 10px 0 0 3px; -$sort-indicator-hint-opacity: 0; +$slick-header-menu-button-border: 1px solid #cfcfcf; +$slick-header-menu-button-border-width: 0px 1px; +$slick-header-menu-button-icon: "\f0d7"; +$slick-header-menu-button-width: 16px; +$slick-header-menu-button-padding: 10px 0 0 3px; +$slick-sort-indicator-hint-opacity: 0; /* make sure to add the @import the SlickGrid Bootstrap Theme AFTER the variables changes */ @import '@slickgrid-universal/common/dist/styles/sass/slickgrid-theme-bootstrap.scss'; @@ -24,4 +23,3 @@ $sort-indicator-hint-opacity: 0; .italic { font-style: italic; } - diff --git a/src/examples/slickgrid/example8.tsx b/src/examples/slickgrid/example8.tsx index 10f37a07..7130123e 100644 --- a/src/examples/slickgrid/example8.tsx +++ b/src/examples/slickgrid/example8.tsx @@ -1,19 +1,22 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import i18next from 'i18next'; -import { ReactGridInstance, Column, Formatters, SlickDataView, SlickGrid, ReactSlickgridCustomElement } from '../../slickgrid-react'; +import i18next, { TFunction } from 'i18next'; +import { ReactGridInstance, Column, Formatters, SlickDataView, SlickGrid, ReactSlickgridComponent } from '../../slickgrid-react'; import './example8.scss'; // provide custom CSS/SASS styling import React from 'react'; import BaseSlickGridState from './state-slick-grid-base'; +import { withTranslation } from 'react-i18next'; // eslint-disable-next-line @typescript-eslint/no-empty-interface -interface Props { } +interface Props { + t: TFunction; +} interface State extends BaseSlickGridState{ selectedLanguage:string, visibleColumns: Column[] } -export default class Example8 extends React.Component { +class Example8 extends React.Component { title = 'Example 8: Header Menu Plugin'; subTitle = ` This example demonstrates using the Slick.Plugins.HeaderMenu plugin to easily add menus to colum headers.
      @@ -45,7 +48,7 @@ export default class Example8 extends React.Component { this.state = { columnDefinitions: [], dataset: [], - gridOptions: {}, + gridOptions: undefined, selectedLanguage: i18next.language || 'en', visibleColumns: [] }; @@ -123,7 +126,7 @@ export default class Example8 extends React.Component { return columnDefinitions; } - getGridOptions(){ + getGridOptions() { return { enableAutoResize: true, enableHeaderMenu: true, @@ -149,7 +152,6 @@ export default class Example8 extends React.Component { } defineGrid() { - const gridOptions = this.getGridOptions(); const columnDefinitions = this.getColumnDefinitions(); @@ -164,7 +166,7 @@ export default class Example8 extends React.Component { getData() { // Set up some test columns. - const mockDataset = []; + const mockDataset: any[] = []; for (let i = 0; i < 1000; i++) { mockDataset[i] = { id: i, @@ -197,7 +199,7 @@ export default class Example8 extends React.Component { } render() { - return ( + return !this.state.gridOptions ? '' : (

      {this.title} @@ -211,19 +213,21 @@ export default class Example8 extends React.Component {

      - Locale: {this.state.selectedLanguage + '.json'} - this.reactGridReady($event.detail.args)} + onReactGridCreated={$event => this.reactGridReady($event.detail)} />
      ); } } + +export default withTranslation()(Example8); \ No newline at end of file diff --git a/src/examples/slickgrid/example9.tsx b/src/examples/slickgrid/example9.tsx index 8612f82c..3cc26351 100644 --- a/src/examples/slickgrid/example9.tsx +++ b/src/examples/slickgrid/example9.tsx @@ -1,5 +1,7 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -import i18next from 'i18next'; +import i18next, { TFunction } from 'i18next'; +import React from 'react'; +import { withTranslation } from 'react-i18next'; + import { ReactGridInstance, ExtensionName, @@ -8,20 +10,20 @@ import { Formatters, SlickDataView, SlickGrid, - ReactSlickgridCustomElement, + ReactSlickgridComponent, } from '../../slickgrid-react'; -import React from 'react'; -import './example9.scss'; // provide custom CSS/SASS styling import BaseSlickGridState from './state-slick-grid-base'; +import './example9.scss'; // provide custom CSS/SASS styling -// eslint-disable-next-line @typescript-eslint/no-empty-interface -interface Props { } +interface Props { + t: TFunction; +} interface State extends BaseSlickGridState{ selectedLanguage:string, } -export default class Example9 extends React.Component { +class Example9 extends React.Component { title = 'Example 9: Grid Menu Control'; subTitle = ` This example demonstrates using the Slick.Controls.GridMenu plugin to easily add a Grid Menu (aka hamburger menu) on the top right corner of the grid.
      @@ -51,7 +53,7 @@ export default class Example9 extends React.Component { this.state = { columnDefinitions: [], dataset: [], - gridOptions: {}, + gridOptions: undefined, selectedLanguage: defaultLang }; } @@ -217,7 +219,7 @@ export default class Example9 extends React.Component { getData() { // Set up some test columns. - const mockDataset = []; + const mockDataset: any[] = []; for (let i = 0; i < 500; i++) { mockDataset[i] = { id: i, @@ -274,7 +276,7 @@ export default class Example9 extends React.Component { } render() { - return ( + return !this.state.gridOptions ? '' : (

      {this.title} @@ -290,22 +292,24 @@ export default class Example9 extends React.Component { - Locale: {this.state.selectedLanguage + '.json'} - this.reactGridReady($event.detail.args)} + onReactGridCreated={$event => this.reactGridReady($event.detail)} />

      ); } } + +export default withTranslation()(Example9); \ No newline at end of file diff --git a/src/slickgrid.scss b/src/slickgrid.scss index 19c39446..218dd65d 100644 --- a/src/slickgrid.scss +++ b/src/slickgrid.scss @@ -1,2 +1,2 @@ /* make sure to add the @import the SlickGrid Bootstrap Theme AFTER the variables changes */ -@import '@slickgrid-universal/common/dist/styles/sass/slickgrid-theme-bootstrap.scss'; +// @import '@slickgrid-universal/common/dist/styles/sass/slickgrid-theme-bootstrap.scss'; diff --git a/src/styles.scss b/src/styles.scss index 7c958bf7..9023fd51 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -1,5 +1,5 @@ /* make sure to add the @import the SlickGrid Bootstrap Theme AFTER the variables changes */ -@import '@slickgrid-universal/common/dist/styles/sass/slickgrid-theme-bootstrap.scss'; +// @import '@slickgrid-universal/common/dist/styles/sass/slickgrid-theme-bootstrap.scss'; .btn-group-xs > .btn, .btn-xs { padding : 6px 2px;