From 367fd355de1b1f486cb6397a37d016044ab8cc27 Mon Sep 17 00:00:00 2001 From: Per Fryking Date: Thu, 6 Mar 2025 18:39:12 +0100 Subject: [PATCH 1/7] chore(table): update tabulator to 6 --- package-lock.json | 34 +++--- package.json | 4 +- src/components/table/columns.ts | 42 +++----- .../partial-styles/_table-main-layout.scss | 2 +- .../partial-styles/_tabulator-footer.scss | 6 +- .../table/partial-styles/tabulator-arrow.scss | 59 +++++------ .../tabulator-custom-styles.scss | 4 +- src/components/table/table-selection.spec.ts | 13 +-- src/components/table/table-selection.ts | 25 +++-- src/components/table/table.scss | 4 +- src/components/table/table.spec.ts | 1 + src/components/table/table.tsx | 100 +++++++++++++----- types/tabulator-tables/index.d.ts | 3 - 13 files changed, 171 insertions(+), 126 deletions(-) delete mode 100644 types/tabulator-tables/index.d.ts diff --git a/package-lock.json b/package-lock.json index 93643cc1de..b9f1254b4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ "@types/lodash-es": "^4.17.12", "@types/react": "^19.1.10", "@types/react-dom": "^19.1.7", - "@types/tabulator-tables": "^4.9.4", + "@types/tabulator-tables": "^6.2.4", "codemirror": "^5.65.9", "cross-env": "^7.0.3", "dayjs": "^1.11.13", @@ -65,7 +65,7 @@ "shelljs": "0.10.0", "shx": "^0.4.0", "style-to-object": "^1.0.9", - "tabulator-tables": "^4.9.3", + "tabulator-tables": "^6.3.1", "typescript": "^4.9.5", "unified": "^11.0.5", "unist-util-visit": "^5.0.0" @@ -2874,10 +2874,11 @@ "dev": true }, "node_modules/@types/tabulator-tables": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@types/tabulator-tables/-/tabulator-tables-4.9.4.tgz", - "integrity": "sha512-yx8lZb15X+RP5a0ih6Fw5AaD/1m62+35ULMcm3TxQaheiH76lyBr2Zs20Wf7KNWsIGy1nwMmbjkCt6vjMczJQQ==", - "dev": true + "version": "6.2.4", + "resolved": "https://registry.npmjs.org/@types/tabulator-tables/-/tabulator-tables-6.2.4.tgz", + "integrity": "sha512-FT5OgxMHimUdlexGAMR+YZhR7A5i93a8ss323HMbVNlwcz9HDIwh4leGoAtLswdus96+q1I6LFMAGFBybA5pug==", + "dev": true, + "license": "MIT" }, "node_modules/@types/tern": { "version": "0.23.3", @@ -13679,10 +13680,11 @@ } }, "node_modules/tabulator-tables": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/tabulator-tables/-/tabulator-tables-4.9.3.tgz", - "integrity": "sha512-iwwQqAEGGxlgrBpcmJJvMJrfjGLcCXOB3AOb/DGkXqBy1YKoYA36hIl7qXGp6Jo8dSkzFAlDT6pKLZgyhs9OnQ==", - "dev": true + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/tabulator-tables/-/tabulator-tables-6.3.1.tgz", + "integrity": "sha512-qFW7kfadtcaISQIibKAIy0f3eeIXUVi8242Vly1iJfMD79kfEGzfczNuPBN/80hDxHzQJXYbmJ8VipI40hQtfA==", + "dev": true, + "license": "MIT" }, "node_modules/tar-fs": { "version": "2.1.1", @@ -17298,9 +17300,9 @@ "dev": true }, "@types/tabulator-tables": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@types/tabulator-tables/-/tabulator-tables-4.9.4.tgz", - "integrity": "sha512-yx8lZb15X+RP5a0ih6Fw5AaD/1m62+35ULMcm3TxQaheiH76lyBr2Zs20Wf7KNWsIGy1nwMmbjkCt6vjMczJQQ==", + "version": "6.2.4", + "resolved": "https://registry.npmjs.org/@types/tabulator-tables/-/tabulator-tables-6.2.4.tgz", + "integrity": "sha512-FT5OgxMHimUdlexGAMR+YZhR7A5i93a8ss323HMbVNlwcz9HDIwh4leGoAtLswdus96+q1I6LFMAGFBybA5pug==", "dev": true }, "@types/tern": { @@ -25089,9 +25091,9 @@ } }, "tabulator-tables": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/tabulator-tables/-/tabulator-tables-4.9.3.tgz", - "integrity": "sha512-iwwQqAEGGxlgrBpcmJJvMJrfjGLcCXOB3AOb/DGkXqBy1YKoYA36hIl7qXGp6Jo8dSkzFAlDT6pKLZgyhs9OnQ==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/tabulator-tables/-/tabulator-tables-6.3.1.tgz", + "integrity": "sha512-qFW7kfadtcaISQIibKAIy0f3eeIXUVi8242Vly1iJfMD79kfEGzfczNuPBN/80hDxHzQJXYbmJ8VipI40hQtfA==", "dev": true }, "tar-fs": { diff --git a/package.json b/package.json index 909d844a07..12bd4c3641 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "@types/lodash-es": "^4.17.12", "@types/react": "^19.1.10", "@types/react-dom": "^19.1.7", - "@types/tabulator-tables": "^4.9.4", + "@types/tabulator-tables": "^6.2.4", "codemirror": "^5.65.9", "cross-env": "^7.0.3", "dayjs": "^1.11.13", @@ -90,7 +90,7 @@ "shelljs": "0.10.0", "shx": "^0.4.0", "style-to-object": "^1.0.9", - "tabulator-tables": "^4.9.3", + "tabulator-tables": "^6.3.1", "typescript": "^4.9.5", "unified": "^11.0.5", "unist-util-visit": "^5.0.0" diff --git a/src/components/table/columns.ts b/src/components/table/columns.ts index 5d012f07b1..99f83ae802 100644 --- a/src/components/table/columns.ts +++ b/src/components/table/columns.ts @@ -1,5 +1,12 @@ import { Column, ColumnSorter, ColumnAggregatorFunction } from './table.types'; -import Tabulator from 'tabulator-tables'; +import { + CellComponent, + ColumnCalc, + ColumnComponent, + ColumnDefinition, + Formatter, + SorterFromTable, +} from 'tabulator-tables'; import { escape } from 'html-escaper'; import { ElementPool } from './element-pool'; import { pickBy, negate } from 'lodash-es'; @@ -15,8 +22,8 @@ export class ColumnDefinitionFactory { * @param column - config describing the column * @returns Tabulator column */ - public create(column: Column): Tabulator.ColumnDefinition { - const definition: Tabulator.ColumnDefinition = { + public create(column: Column): ColumnDefinition { + const definition: ColumnDefinition = { title: column.title, field: column.field, hozAlign: column.horizontalAlign, @@ -82,10 +89,7 @@ export const formatHeader = (column: Column) => (): string | HTMLElement => { * @param pool - pool to get custom components from * @returns Tabulator formatter */ -export function createFormatter( - column: Column, - pool: ElementPool -): Tabulator.Formatter { +export function createFormatter(column: Column, pool: ElementPool): Formatter { if (!column.component?.name) { return formatCell; } @@ -100,7 +104,7 @@ export function createFormatter( return formatCell; } - return (cell: Tabulator.CellComponent) => { + return (cell: CellComponent) => { const value = formatCell(cell, column); return createCustomComponent(cell, column, value, pool); @@ -126,10 +130,7 @@ function columnElementExists(column: Column) { * @param column - configuration for the current column * @returns the formatted value */ -export function formatCell( - cell: Tabulator.CellComponent, - column: Column -): string { +export function formatCell(cell: CellComponent, column: Column): string { const data = cell.getData(); let value = cell.getValue(); @@ -154,7 +155,7 @@ export function formatCell( * @returns custom component that renders a value in the table */ export function createCustomComponent( - cell: Tabulator.CellComponent, + cell: CellComponent, column: Column, value: string, pool: ElementPool @@ -233,10 +234,7 @@ function getEventName(eventListener: string): string { return eventListener.charAt(2).toLowerCase() + eventListener.slice(3); } -function createResizeObserver( - element: HTMLElement, - column: Tabulator.ColumnComponent -) { +function createResizeObserver(element: HTMLElement, column: ColumnComponent) { if (!('ResizeObserver' in window)) { return; } @@ -263,12 +261,6 @@ function createResizeObserver( }, RESIZE_TIMEOUT); } -// Tabulator seems to also have this `field` property, that does not appear on -// the interface for some reason -interface TabulatorSorter extends Tabulator.Sorter { - field: string; -} - /** * Create a column sorter from a tabulator sorter * @@ -277,7 +269,7 @@ interface TabulatorSorter extends Tabulator.Sorter { */ export const createColumnSorter = (columns: Column[]) => - (sorter: TabulatorSorter): ColumnSorter => { + (sorter: SorterFromTable): ColumnSorter => { const column = columns.find((col) => col.field === sorter.field); const direction = sorter.dir.toUpperCase() as 'ASC' | 'DESC'; @@ -291,7 +283,7 @@ export const createColumnSorter = * * @param column */ -export function getColumnAggregator(column: Column): Tabulator.ColumnCalc { +export function getColumnAggregator(column: Column): ColumnCalc { const aggregator = column.aggregator; if (isAggregatorFunction(aggregator)) { return (values: any[], data: object[]) => { diff --git a/src/components/table/partial-styles/_table-main-layout.scss b/src/components/table/partial-styles/_table-main-layout.scss index a4989fcd08..08e9e68a73 100644 --- a/src/components/table/partial-styles/_table-main-layout.scss +++ b/src/components/table/partial-styles/_table-main-layout.scss @@ -16,7 +16,7 @@ flex-shrink: 0; } - .tabulator-tableHolder { + .tabulator-tableholder { isolation: isolate; flex-grow: 1; $unset-tabulators-calculated-and-hardcoded-height: unset !important; // tabulator calculates (not so precisely) height of the scrollable area of the table and adds it inline. diff --git a/src/components/table/partial-styles/_tabulator-footer.scss b/src/components/table/partial-styles/_tabulator-footer.scss index 54f73f0468..76a155c764 100644 --- a/src/components/table/partial-styles/_tabulator-footer.scss +++ b/src/components/table/partial-styles/_tabulator-footer.scss @@ -3,13 +3,15 @@ transition: transform 0.5s ease-out, opacity 0.35s ease; //For some reason the footer is not animated when it gets hidden/removed - padding-top: 0; - padding-bottom: 0; color: var(--table-text-color); background-color: rgb(var(--table-header-background-color--hover)); border: none; user-select: auto; + .tabulator-footer-contents { + padding: 0 0.25rem; + } + .tabulator-calcs-holder { border-color: rgb(var(--contrast-500)); background: rgb(var(--contrast-500)) !important; diff --git a/src/components/table/partial-styles/tabulator-arrow.scss b/src/components/table/partial-styles/tabulator-arrow.scss index 8f298cb75a..db375b6290 100644 --- a/src/components/table/partial-styles/tabulator-arrow.scss +++ b/src/components/table/partial-styles/tabulator-arrow.scss @@ -1,56 +1,51 @@ @use '../../../style/functions'; -.tabulator-col-sorter { - transition: opacity 0.2s ease; - right: 0 !important; - position: relative !important; - justify-content: center; - cursor: pointer; - - [aria-sort='none'] & { - opacity: 0; - } - - [aria-sort='desc'] &, - [aria-sort='asc'] & { - opacity: 1; - - &:before { - position: absolute; - content: ''; - width: 0.125rem; - height: 0.4rem; - background-color: var(--table-arrow-color--active); +.tabulator { + .tabulator-header .tabulator-col .tabulator-col-content { + .tabulator-col-sorter { + position: relative; + cursor: pointer; + transition: opacity 0.2s ease; + right: 0; } } } .tabulator-sortable { - &[aria-sort='desc'], - &[aria-sort='asc'] { + &[aria-sort='none'] { + .tabulator-col-sorter { + opacity: 0; + } &:hover { .tabulator-col-sorter { opacity: 1; - animation: indicate-sortable-sorted-column 0.5s ease; + + .tabulator-arrow { + animation: indicate-sortable-unsorted-column 2s ease + forwards; + } } } } - &[aria-sort='none'] { + &[aria-sort='desc'], + &[aria-sort='asc'] { + .tabulator-col-sorter { + opacity: 1; + } + &:hover { .tabulator-col-sorter { opacity: 1; - - .tabulator-arrow { - animation: indicate-sortable-unsorted-column 2s ease - forwards; - } + animation: indicate-sortable-sorted-column 0.5s ease; } } } } .tabulator-arrow { + rotate: 180deg; // changes the direction of the arrow, according to our sorting direction convention + transition: border 0.2s ease; border-left: 0.25rem solid transparent !important; border-right: 0.25rem solid transparent !important; @@ -77,7 +72,7 @@ .tabulator-col.tabulator-sortable[aria-sort='desc'] & { height: 0; - transform: translate3d(0, -0.2rem, 0) rotate(180deg); + transform: translate3d(0, -0.2rem, 0); border-top-width: 0.25rem; border-bottom-width: 0; border-top-color: var(--table-arrow-color--active); @@ -88,7 +83,7 @@ .tabulator-col.tabulator-sortable[aria-sort='asc'] & { height: 0; - transform: translate3d(0, 0.2rem, 0) rotate(180deg); + transform: translate3d(0, 0.2rem, 0); border-top-width: 0; border-bottom-width: 0.25rem; border-bottom-color: var(--table-arrow-color--active); diff --git a/src/components/table/partial-styles/tabulator-custom-styles.scss b/src/components/table/partial-styles/tabulator-custom-styles.scss index 9165dd393f..6e8f5d46bc 100644 --- a/src/components/table/partial-styles/tabulator-custom-styles.scss +++ b/src/components/table/partial-styles/tabulator-custom-styles.scss @@ -163,7 +163,7 @@ order: 2; } - .tabulator-tableHolder { + .tabulator-tableholder { order: 3; } @@ -179,7 +179,7 @@ } &:has(.tabulator-calcs-holder) { - .tabulator-tableHolder { + .tabulator-tableholder { // makes sure aggregations row doesn't cover the last table row, // and the horizontal scroll bar which is shown on windows margin-bottom: $height-of-aggregations-row; diff --git a/src/components/table/table-selection.spec.ts b/src/components/table/table-selection.spec.ts index c70f76d8eb..19af624940 100644 --- a/src/components/table/table-selection.spec.ts +++ b/src/components/table/table-selection.spec.ts @@ -1,3 +1,4 @@ +import { CellComponent, RowComponent, Tabulator } from 'tabulator-tables'; import { ElementPool } from './element-pool'; import { TableSelection } from './table-selection'; @@ -41,7 +42,7 @@ describe('table selection', () => { ...rowData: any[] ) => Map = (...rowData: any[]) => { const rowCheckboxMap = new Map(); - const makeCell: (row: any, data: any) => Tabulator.CellComponent = ( + const makeCell: (row: any, data: any) => CellComponent = ( row: any, data: any ) => { @@ -57,11 +58,11 @@ describe('table selection', () => { return cell as any; }; - const makeRow: ( - data: any, - position: number - ) => Tabulator.RowComponent = (data, position) => { - const row: Partial = { + const makeRow: (data: any, position: number) => RowComponent = ( + data, + position + ) => { + const row: Partial = { getData: () => data, getPosition: () => position, }; diff --git a/src/components/table/table-selection.ts b/src/components/table/table-selection.ts index ad5d693a7b..2ca0f7c8b4 100644 --- a/src/components/table/table-selection.ts +++ b/src/components/table/table-selection.ts @@ -1,5 +1,10 @@ import { EventEmitter } from '@stencil/core'; -import Tabulator from 'tabulator-tables'; +import { + Tabulator, + ColumnDefinition as TabulatorColumnDefinition, + CellComponent as TabulatorCellComponent, + RowComponent as TabulatorRowComponent, +} from 'tabulator-tables'; import { setElementProperties } from './columns'; import { ElementPool } from './element-pool'; import { Selection, SelectionChangeSet } from './selection'; @@ -80,12 +85,12 @@ export class TableSelection { * @returns The column definitions with the checkbox column prepended to it */ public getColumnDefinitions( - columnDefinitions: Tabulator.ColumnDefinition[] - ): Tabulator.ColumnDefinition[] { + columnDefinitions: TabulatorColumnDefinition[] + ): TabulatorColumnDefinition[] { return [this.getRowSelectorColumnDefinition(), ...columnDefinitions]; } - private getRowSelectorColumnDefinition(): Tabulator.ColumnDefinition { + private getRowSelectorColumnDefinition(): TabulatorColumnDefinition { return { title: '', formatter: this.getRowSelectorFormatter(), @@ -104,7 +109,7 @@ export class TableSelection { } private getRowSelectorFormatter() { - return (cell: Tabulator.CellComponent) => { + return (cell: TabulatorCellComponent) => { const element = this.pool.get(LIMEL_CHECKBOX); setElementProperties(element, { checked: this.selection.has(getRowId(cell.getData())), @@ -127,7 +132,7 @@ export class TableSelection { */ protected rowSelectorCellClick = ( ev: PointerEvent, - cell: Tabulator.CellComponent + cell: TabulatorCellComponent ): void => { ev.stopPropagation(); ev.preventDefault(); @@ -135,6 +140,10 @@ export class TableSelection { const clickedRow = cell.getRow(); const rowPosition = clickedRow.getPosition(true); + if (rowPosition === false) { + return; + } + if (ev.shiftKey) { this.updateRowSelectors( this.selection.toggleSelectionFromLastIndex(rowPosition) @@ -162,7 +171,7 @@ export class TableSelection { }; private updateRowSelector = ( - row: Tabulator.RowComponent, + row: TabulatorRowComponent, checked: boolean ): void => { const cell = row.getCells()[0]; @@ -172,7 +181,7 @@ export class TableSelection { } }; - private getActiveRows: () => Tabulator.RowComponent[] = () => { + private getActiveRows: () => TabulatorRowComponent[] = () => { const table = this.getTable(); if (!table) { return []; diff --git a/src/components/table/table.scss b/src/components/table/table.scss index e5f05e64a7..2ef2e88ffb 100644 --- a/src/components/table/table.scss +++ b/src/components/table/table.scss @@ -52,7 +52,7 @@ $height-of-aggregations-row: 1.5rem; .tabulator-header { color: var(--table-text-color); } - .tabulator-tableHolder { + .tabulator-tableholder { .tabulator-table { color: var(--table-text-color); background-color: transparent; @@ -72,7 +72,7 @@ $height-of-aggregations-row: 1.5rem; background-color: rgb(var(--table-header-background-color)); border-right-color: rgb(var(--contrast-200)); - &.tabulator-sortable { + &.tabulator-sortable.tabulator-col-sorter-element { &:hover { background-color: rgb( var(--table-header-background-color--hover) diff --git a/src/components/table/table.spec.ts b/src/components/table/table.spec.ts index 2a5ed3aa0f..42b7655294 100644 --- a/src/components/table/table.spec.ts +++ b/src/components/table/table.spec.ts @@ -11,6 +11,7 @@ describe('limel-table data updates', () => { updateOrAddData: jest.fn(), }; (component as any).setSelection = jest.fn(); + (component as any).initialized = true; }); afterEach(() => { diff --git a/src/components/table/table.tsx b/src/components/table/table.tsx index 0e6637ca51..d29e79e29c 100644 --- a/src/components/table/table.tsx +++ b/src/components/table/table.tsx @@ -8,7 +8,20 @@ import { Event, Host, } from '@stencil/core'; -import TabulatorTable from 'tabulator-tables'; +import { + TabulatorFull, + Tabulator, + Options as TabulatorOptions, + Sorter as TabulatorSorter, + SorterFromTable as TabulatorSorterFromTable, + SortDirection as TabulatorSortDirection, + ColumnComponent as TabulatorColumnComponent, + ColumnDefinition as TabulatorColumnDefinition, + RowComponent as TabulatorRowComponent, + OptionsData as TabulatorOptionsData, + OptionsPagination as TabulatorOptionsPagination, + OptionsColumns as TabulatorOptionsColumns, +} from 'tabulator-tables'; import { Column, TableParams, @@ -215,6 +228,7 @@ export class Table { private pool: ElementPool; private columnFactory: ColumnDefinitionFactory; private firstRequest: boolean; + private initialized = false; private currentSorting: ColumnSorter[]; private tableSelection: TableSelection; private shouldSort = false; @@ -289,7 +303,7 @@ export class Table { const hasRowUpdates = !areRowsEqual(newData, oldData); setTimeout(() => { - if (!this.tabulator) { + if (!this.tabulator || !this.initialized) { return; } @@ -308,7 +322,9 @@ export class Table { return; } - this.tabulator.updateOrAddData(newData); + if (newData.length > 0) { + this.tabulator.updateOrAddData(newData); + } }); } @@ -461,6 +477,7 @@ export class Table { if (this.tabulator) { this.pool.releaseAll(); this.tabulator.destroy(); + this.initialized = false; } const table: HTMLElement = @@ -482,7 +499,7 @@ export class Table { // If that's the case lets just create the table no // matter if its rendered or not. if (!('ResizeObserver' in window)) { - this.tabulator = new TabulatorTable(table, this.getOptions()); + this.tabulator = this.createTabulator(table); this.setSelection(); return; @@ -490,7 +507,7 @@ export class Table { const observer = new ResizeObserver(() => { requestAnimationFrame(() => { - this.tabulator = new TabulatorTable(table, this.getOptions()); + this.tabulator = this.createTabulator(table); this.setSelection(); observer.unobserve(table); observer.disconnect(); @@ -499,6 +516,25 @@ export class Table { observer.observe(table); } + private createTabulator(table: HTMLElement): Tabulator { + const tabulator = new TabulatorFull(table, this.getOptions()); + tabulator.on('rowClick', this.onClickRow); + tabulator.on('dataSorting', this.handleDataSorting); + tabulator.on('pageLoaded', this.handlePageLoaded); + tabulator.on('columnMoved', this.handleMoveColumn); + tabulator.on('renderComplete', this.handleRenderComplete); + tabulator.on('tableBuilt', () => { + this.initialized = true; + if (this.isRemoteMode()) { + this.tabulator.setData('https://localhost'); + } else { + this.updateData(this.data, []); + } + }); + + return tabulator; + } + private initTableSelection() { if (this.selectable) { this.tableSelection = new TableSelection( @@ -523,7 +559,7 @@ export class Table { this.tabulator?.setMaxPage(this.calculatePageCount()); } - private getOptions(): Tabulator.Options { + private getOptions(): TabulatorOptions { const ajaxOptions = this.getAjaxOptions(); const paginationOptions = this.getPaginationOptions(); const columnOptions = this.getColumnOptions(); @@ -532,12 +568,8 @@ export class Table { data: this.data, layout: mapLayout(this.layout), columns: this.getColumnDefinitions(), - dataSorting: this.handleDataSorting, - pageLoaded: this.handlePageLoaded, - renderComplete: this.handleRenderComplete, ...ajaxOptions, ...paginationOptions, - rowClick: this.onClickRow, rowFormatter: this.formatRow, initialSort: this.getInitialSorting(), nestedFieldSeparator: false, @@ -545,7 +577,7 @@ export class Table { }; } - private getInitialSorting(): Tabulator.Sorter[] { + private getInitialSorting(): TabulatorSorter[] { if (this.currentSorting && this.currentSorting.length > 0) { return this.getColumnSorter(this.currentSorting); } @@ -553,16 +585,16 @@ export class Table { return this.getColumnSorter(this.sorting); } - private getColumnSorter(sorting: ColumnSorter[]): Tabulator.Sorter[] { + private getColumnSorter(sorting: ColumnSorter[]): TabulatorSorter[] { return sorting.map((sorter: ColumnSorter) => { return { column: String(sorter.column.field), - dir: sorter.direction.toLocaleLowerCase() as Tabulator.SortDirection, + dir: sorter.direction.toLocaleLowerCase() as TabulatorSortDirection, }; }); } - private getColumnDefinitions(): Tabulator.ColumnDefinition[] { + private getColumnDefinitions(): TabulatorColumnDefinition[] { const columnDefinitions = this.columns .map(this.addColumnAggregator) .map((column) => { @@ -607,7 +639,7 @@ export class Table { return column; } - private getAjaxOptions(): Tabulator.OptionsData { + private getAjaxOptions(): TabulatorOptionsData { if (!this.isRemoteMode()) { return {}; } @@ -621,6 +653,10 @@ export class Table { ajaxURL: remoteUrl, ajaxRequestFunc: this.requestData, ajaxRequesting: this.handleAjaxRequesting, + ajaxParams: { + page: this.tabulator?.getPage() || FIRST_PAGE, + sorters: this.tabulator?.getSorters() || [], + }, }; } @@ -654,13 +690,14 @@ export class Table { return true; } - private getPaginationOptions(): Tabulator.OptionsPagination { + private getPaginationOptions(): TabulatorOptionsPagination { if (!this.pageSize) { return {}; } return { - pagination: this.isRemoteMode() ? 'remote' : 'local', + pagination: true, + paginationMode: this.isRemoteMode() ? 'remote' : 'local', paginationSize: this.pageSize, paginationInitialPage: this.page, }; @@ -703,7 +740,7 @@ export class Table { return this.mode === 'remote'; } - private handleDataSorting(sorters: Tabulator.Sorter[]): void { + private handleDataSorting(sorters: TabulatorSorterFromTable[]): void { if (this.isRemoteMode()) { return; } @@ -731,12 +768,16 @@ export class Table { } } - private onClickRow(_ev, row: Tabulator.RowComponent): void { + private onClickRow(event: PointerEvent, row: TabulatorRowComponent): void { if (row.getPosition === undefined) { // Not a data row, probably a CalcComponent return; } + if (event.defaultPrevented) { + return; + } + if (this.isActiveRow(row)) { this.activeRow = null; } else { @@ -746,7 +787,7 @@ export class Table { this.activate.emit(this.activeRow); } - private getActiveRows: () => Tabulator.RowComponent[] = () => { + private getActiveRows: () => TabulatorRowComponent[] = () => { if (!this.tabulator) { return []; } @@ -778,7 +819,7 @@ export class Table { this.tabulator.getRows().forEach(this.formatRow); } - private formatRow(row: Tabulator.RowComponent) { + private formatRow(row: TabulatorRowComponent) { if (this.isActiveRow(row)) { row.getElement().classList.add('active'); } else { @@ -795,7 +836,7 @@ export class Table { } } - private isActiveRow(row: Tabulator.RowComponent) { + private isActiveRow(row: TabulatorRowComponent) { if (!this.activeRow) { return false; } @@ -822,23 +863,26 @@ export class Table { return columns.some((column) => has(column, 'aggregator')); } - private getColumnOptions = (): Tabulator.OptionsColumns => { + private getColumnOptions = (): TabulatorOptionsColumns => { if (!this.movableColumns) { return {}; } return { movableColumns: true, - columnMoved: this.handleMoveColumn, }; }; - private handleMoveColumn = (_, components: Tabulator.ColumnComponent[]) => { + private handleMoveColumn = (_, components: TabulatorColumnComponent[]) => { + if (!this.movableColumns) { + return; + } + const columns = components.map(this.findColumn).filter(Boolean); this.changeColumns.emit(columns); }; - private findColumn = (component: Tabulator.ColumnComponent): Column => { + private findColumn = (component: TabulatorColumnComponent): Column => { return this.columns.find((column) => { return ( column.field === component.getField() && @@ -848,6 +892,8 @@ export class Table { }; render() { + const totalRows = this.totalRows ?? this.data.length; + return ( this.pageSize, + 'has-pagination': totalRows > this.pageSize, 'has-aggregation': this.hasAggregation(this.columns), 'has-movable-columns': this.movableColumns, 'has-rowselector': this.selectable, diff --git a/types/tabulator-tables/index.d.ts b/types/tabulator-tables/index.d.ts deleted file mode 100644 index 5c44b15d39..0000000000 --- a/types/tabulator-tables/index.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare module 'tabulator-tables' { - export default Tabulator; -} From 4d0f15c31e58e98ba7b7f4a513ebc6d66da15664 Mon Sep 17 00:00:00 2001 From: Kiarokh Moattar Date: Wed, 2 Jul 2025 16:08:29 +0200 Subject: [PATCH 2/7] feat(table): add a prop to easily control the pagination's location You can now render the pagination component on top or at the bottom of the table, using a prop. This was previously an undocumented feature, applicable using a helper class of `has-pagination-on-top`. But now it is more accessible and discoverable feature for the consumer. --- etc/lime-elements.api.md | 2 + .../table/examples/table-pagination.tsx | 76 +++++++++++++++++++ src/components/table/table.tsx | 10 +++ 3 files changed, 88 insertions(+) create mode 100644 src/components/table/examples/table-pagination.tsx diff --git a/etc/lime-elements.api.md b/etc/lime-elements.api.md index 8a82844bb9..6351451e18 100644 --- a/etc/lime-elements.api.md +++ b/etc/lime-elements.api.md @@ -792,6 +792,7 @@ export namespace Components { "movableColumns": boolean; "page": number; "pageSize": number; + "paginationLocation": 'top' | 'bottom'; "selectable": boolean; "selection": object[]; "sortableColumns": boolean; @@ -2024,6 +2025,7 @@ export namespace JSX { "onSort"?: (event: LimelTableCustomEvent) => void; "page"?: number; "pageSize"?: number; + "paginationLocation"?: 'top' | 'bottom'; "selectable"?: boolean; "selection"?: object[]; "sortableColumns"?: boolean; diff --git a/src/components/table/examples/table-pagination.tsx b/src/components/table/examples/table-pagination.tsx new file mode 100644 index 0000000000..bb31f8927d --- /dev/null +++ b/src/components/table/examples/table-pagination.tsx @@ -0,0 +1,76 @@ +import { Component, h, Host, State } from '@stencil/core'; +import { + Column, + Option, + LimelSelectCustomEvent, +} from '@limetech/lime-elements'; +import { data, Bird } from './birds'; +import { capitalize } from 'lodash-es'; + +/** + * Pagination + * By specifying a `pageSize`, you can enable pagination for the table. + * + * Additionally, you can control the location of the pagination controls + * by setting the `paginationLocation` property to either `top` or `bottom`. + * + * @sourceFile birds.ts + */ +@Component({ + tag: 'limel-example-table-pagination', + styleUrl: 'table.scss', + shadow: true, +}) +export class TablePaginationExample { + @State() + private columns: Array> = [ + { title: 'Name', field: 'name' }, + { title: 'Binominal name', field: 'binominalName' }, + { title: 'Nest type', field: 'nest', formatter: capitalize }, + { title: 'Eggs per clutch', field: 'eggs', horizontalAlign: 'right' }, + { title: 'Origin', field: 'origin' }, + ]; + + @State() + private paginationLocation: 'top' | 'bottom' = 'bottom'; + + private paginationLocationOptions: Option[] = [ + { text: 'Top', value: 'top' }, + { text: 'Bottom', value: 'bottom' }, + ]; + + private pageSize = 5; + + render() { + return ( + + + + + + + ); + } + + private getSelectedPaginationLocation = (): Option => { + return this.paginationLocationOptions.find( + (option) => option.value === this.paginationLocation + ); + }; + + private handlePaginationLocationChange = ( + event: LimelSelectCustomEvent