Skip to content

Commit

Permalink
Enhance table Documentation and Multi-Sort functionality with React e…
Browse files Browse the repository at this point in the history
…xample. (#6879)
  • Loading branch information
deleonio authored Sep 26, 2024
2 parents 345f669 + da59f20 commit 889f331
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ Durch die Verwendung des **`_dataFoot_`**-Attribut können zusammenfassende Info

- Zu jedem Header kann man eine Sortierfunktion definieren.
- Es ist exakt eine oder keine Sortierfunktion aktiviert.
- **Multi-Sort-Funktionalität**: Wenn die Multi-Sort-Option (`_allowMultiSort`) aktiviert ist, kann der Benutzer mehrere Spalten gleichzeitig sortieren. Standardmäßig ist diese Funktion deaktiviert, und nur eine Spalte kann auf einmal sortiert werden. Die Sortierung wechselt zwischen aufsteigend, absteigend und ohne Sortierung. Wenn Multi-Sort deaktiviert ist, wird die Sortierung der vorherigen Spalte aufgehoben, sobald eine neue Spalte sortiert wird.
- Aktuell wird nicht unterstützt, dass bei zweidimensionalen Headern, die Header der jeweils anderen Header-Seite mit sortiert werden. Bei der Anforderung der Sortierung empfehlen wir die Verwendung nur einer Header-Dimension (entweder horizontal oder vertikal).

### Render Funktion
Expand Down
12 changes: 12 additions & 0 deletions packages/components/src/components/table-stateful/shadow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,12 @@ export class KolTableStateful implements TableAPI {
this.sortFunction = sort;
};

/**
* Handles sorting logic for table columns.
* If multi-sort is enabled (`_allowMultiSort`), multiple columns can be sorted at once.
* Otherwise, sorting is cleared when switching between columns.
*/

private changeCellSort(headerCell: KoliBriTableHeaderCellWithLogic) {
if (typeof headerCell.compareFn === 'function') {
if (!this.state._allowMultiSort && headerCell.key != this.sortData[0]?.key) {
Expand Down Expand Up @@ -473,6 +479,12 @@ export class KolTableStateful implements TableAPI {
setState(this, '_sortedData', sortedData);
};

/**
* Renders the pagination controls for the table, showing the current visible data range
* and providing navigation between different pages.
*
* @returns {JSX.Element} The rendered pagination controls including page range and navigation.
*/
private renderPagination(): JSX.Element {
return (
<div class="pagination">
Expand Down
57 changes: 56 additions & 1 deletion packages/components/src/components/table-stateless/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,13 @@ export class KolTableStateless implements TableStatelessAPI {
return this.dataToKeyMap.get(data);
}

/**
* Applies a custom render function to a specific table cell if provided.
* Ensures that the content is updated after a delay to avoid excessive re-renders.
*
* @param {KoliBriTableCell} cell The cell to be rendered, with a possible custom `render` function.
* @param {HTMLElement} el The HTML element where the cell is rendered.
*/
private cellRender(cell: KoliBriTableCell, el?: HTMLElement): void {
if (el) {
clearTimeout(this.cellsToRenderTimeouts.get(el));
Expand Down Expand Up @@ -404,6 +411,15 @@ export class KolTableStateless implements TableStatelessAPI {
this.validateSelection(this._selection);
}

/**
* Renders the selection cell for a row, either as a checkbox (for multiple selection)
* or as a radio button (for single selection). It handles selection states and dispatches
* events for selection changes.
*
* @param {KoliBriTableCell[]} row The row data containing the cell with selection properties.
* @param {number} rowIndex The index of the row.
* @returns {JSX.Element} The rendered selection cell, either with a checkbox or radio input.
*/
private renderSelectionCell(row: (KoliBriTableCell & KoliBriTableDataType)[], rowIndex: number): JSX.Element {
const selection = this.state._selection;
if (!selection) return '';
Expand Down Expand Up @@ -462,6 +478,14 @@ export class KolTableStateless implements TableStatelessAPI {
);
}

/**
* Renders a full table row by mapping over each cell and calling `renderTableCell`.
* It also handles the row's unique key generation and selection functionality.
*
* @param {KoliBriTableCell[]} row The data for the current row.
* @param {number} rowIndex The index of the current row being rendered.
* @returns {JSX.Element} The rendered row with its cells.
*/
private readonly renderTableRow = (row: (KoliBriTableCell & KoliBriTableDataType)[], rowIndex: number): JSX.Element => {
let key = String(rowIndex);
if (this.horizontal && row[0]?.data) {
Expand All @@ -476,6 +500,15 @@ export class KolTableStateless implements TableStatelessAPI {
);
};

/**
* Renders a table cell, either as a data cell (`<td>`) or a header cell (`<th>`).
* If a custom `render` function is provided in the cell, it will be used to display content.
*
* @param {KoliBriTableCell} cell The cell data, containing label, colSpan, rowSpan, and potential render function.
* @param {number} rowIndex The current row index.
* @param {number} colIndex The current column index.
* @returns {JSX.Element} The rendered table cell (either `<td>` or `<th>`).
*/
private readonly renderTableCell = (cell: KoliBriTableCell, rowIndex: number, colIndex: number): JSX.Element => {
let key = `${rowIndex}-${colIndex}-${cell.label}`;
if (cell.data) {
Expand Down Expand Up @@ -512,6 +545,14 @@ export class KolTableStateless implements TableStatelessAPI {
}
};

/**
* Renders the header cell for row selection. This cell contains a checkbox for selecting
* all rows when selection is enabled. If multiple selection is allowed, the checkbox allows
* selecting/deselecting all rows at once. It also supports an indeterminate state
* if only some rows are selected.
*
* @returns {JSX.Element} - The rendered header cell containing the selection checkbox.
*/
private renderHeadingSelectionCell(): JSX.Element {
const selection = this.state._selection;
if (!selection || (!selection.multiple && selection.multiple !== undefined)) return <th key={`thead-0`}></th>;
Expand Down Expand Up @@ -554,6 +595,15 @@ export class KolTableStateless implements TableStatelessAPI {
);
}

/**
* Renders a table header cell (`<th>`), with optional sorting functionality.
* If the cell has a `sortDirection` property, a sort button is rendered within the header.
*
* @param {KoliBriTableHeaderCell} cell The header cell data, containing label, colSpan, rowSpan, and possible sort direction.
* @param {number} rowIndex The index of the current row in the table.
* @param {number} colIndex The index of the current column in the row.
* @returns {JSX.Element} The rendered header cell with possible sorting controls.
*/
private renderHeadingCell(cell: KoliBriTableHeaderCell, rowIndex: number, colIndex: number): JSX.Element {
let ariaSort = undefined;
let sortButtonIcon = 'codicon codicon-fold';
Expand Down Expand Up @@ -607,7 +657,12 @@ export class KolTableStateless implements TableStatelessAPI {
</th>
);
}

/**
* Renders the table footer (`<tfoot>`) using the provided footer data.
* It maps over each row in the footer and renders it similarly to a body row.
*
* @returns {JSX.Element} - The rendered table footer or an empty string if no footer data is provided.
*/
private renderFoot(): JSX.Element {
if (!this.state._dataFoot || this.state._dataFoot.length === 0) {
return '';
Expand Down
1 change: 1 addition & 0 deletions packages/components/src/components/table/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ Die Table-Komponente unterstützt folgende Funktionalitäten **nicht**:

- Zu jedem Header kann man eine Sortierfunktion definieren.
- Es ist exakt eine oder keine Sortierfunktion aktiviert.
- **Multi-Sort-Funktionalität**: Wenn die Multi-Sort-Option (`_allowMultiSort`) aktiviert ist, kann der Benutzer mehrere Spalten gleichzeitig sortieren. Standardmäßig ist diese Funktion deaktiviert, und nur eine Spalte kann auf einmal sortiert werden. Die Sortierung wechselt zwischen aufsteigend, absteigend und ohne Sortierung. Wenn Multi-Sort deaktiviert ist, wird die Sortierung der vorherigen Spalte aufgehoben, sobald eine neue Spalte sortiert wird.
- Aktuell wird nicht unterstützt, dass bei zweidimensionalen Headern, die Header der jeweils anderen Header-Seite mit sortiert werden. Bei der Anforderung der Sortierung empfehlen wir die Verwendung nur einer Header-Dimension (entweder horizontal oder vertikal).

### Render Funktion
Expand Down
116 changes: 116 additions & 0 deletions packages/samples/react/src/components/table/multi-sort.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import type { FC } from 'react';
import React, { useState } from 'react';

import { KolHeading, KolInputCheckbox, KolTable } from '@public-ui/react';
import type { KoliBriTableHeaders, KoliBriTableDataType } from '@public-ui/components';
import type { Data } from './test-data';
import { DATA } from './test-data';
import { SampleDescription } from '../SampleDescription';

const DATE_FORMATTER = Intl.DateTimeFormat('de-DE', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
});

const HEADERS_HORIZONTAL: KoliBriTableHeaders = {
horizontal: [
[
{
label: 'order',
key: 'order',
textAlign: 'center',
compareFn: (data0: KoliBriTableDataType, data1: KoliBriTableDataType) => {
if ((data0 as Data).order < (data1 as Data).order) return -1;
else if ((data1 as Data).order < (data0 as Data).order) return 1;
else return 0;
},
},
{
label: 'date',
key: 'date',
textAlign: 'center',
render: (_el, _cell, tupel) => DATE_FORMATTER.format((tupel as Data).date),
compareFn: (data0: KoliBriTableDataType, data1: KoliBriTableDataType) => {
if ((data0 as Data).date < (data1 as Data).date) return -1;
else if ((data1 as Data).date < (data0 as Data).date) return 1;
else return 0;
},
},
],
],
};

const HEADERS_VERTICAL: KoliBriTableHeaders = {
vertical: [
[
{
label: 'order',
key: 'order',
textAlign: 'center',
compareFn: (data0: KoliBriTableDataType, data1: KoliBriTableDataType) => {
if ((data0 as Data).order < (data1 as Data).order) return -1;
else if ((data1 as Data).order < (data0 as Data).order) return 1;
else return 0;
},
},
{
label: 'date',
key: 'date',
textAlign: 'center',
render: (_el, _cell, tupel) => DATE_FORMATTER.format((tupel as Data).date),
compareFn: (data0: KoliBriTableDataType, data1: KoliBriTableDataType) => {
if ((data0 as Data).date < (data1 as Data).date) return -1;
else if ((data1 as Data).date < (data0 as Data).date) return 1;
else return 0;
},
},
],
],
};
export const MultiSortTable: FC = () => {
const [allowMultiSortVertical, setAllowMultiSortVertical] = useState(false);
const [allowMultiSortHorizontal, setAllowMultiSortHorizontal] = useState(true);
return (
<>
<SampleDescription>
<p>This sample shows KolTable with multi-sort functionality, allowing sorting by both &quot;order&quot; and &quot;date&quot; columns.</p>
</SampleDescription>

<section className="w-full grid gap-4">
<section className="grid gap-4">
<KolHeading _level={2} _label="Vertical" />
<KolInputCheckbox
_checked={allowMultiSortVertical}
_label="Allow Multi-Sort"
_variant="switch"
_on={{ onChange: (_, value) => setAllowMultiSortVertical(Boolean(value)) }}
></KolInputCheckbox>
<KolTable
_label="Sort Table with Order and Date"
_data={DATA.slice(0, 10)}
_headers={HEADERS_VERTICAL}
className="block"
_allowMultiSort={allowMultiSortVertical}
/>
</section>
<section className="grid gap-4">
<KolHeading _level={2} _label="Horizontal" />
<KolInputCheckbox
_checked={allowMultiSortHorizontal}
_label="Allow Multi-Sort"
_variant="switch"
_on={{ onChange: (_, value) => setAllowMultiSortHorizontal(Boolean(value)) }}
></KolInputCheckbox>
<KolTable
_label="Sort Table with Order and Date"
_data={DATA}
_headers={HEADERS_HORIZONTAL}
className="block"
_allowMultiSort={allowMultiSortHorizontal}
/>
</section>
</section>
</>
);
};
2 changes: 2 additions & 0 deletions packages/samples/react/src/components/table/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { TableStatelessWithSelection } from './stateless-with-selection';
import { TableStatelessWithSingleSelection } from './stateless-with-single-selection';
import { TableWithPagination } from './with-pagination';
import { InteractiveChildElements } from './interactive-child-elements';
import { MultiSortTable } from './multi-sort';

export const TABLE_ROUTES: Routes = {
table: {
Expand All @@ -30,5 +31,6 @@ export const TABLE_ROUTES: Routes = {
'with-pagination': TableWithPagination,
'interactive-child-elements': InteractiveChildElements,
stateless: TableStateless,
'multi-sort': MultiSortTable,
},
};

0 comments on commit 889f331

Please sign in to comment.