diff --git a/src/lib/components/Table/shared.ts b/src/lib/components/Table/utils.ts
similarity index 59%
rename from src/lib/components/Table/shared.ts
rename to src/lib/components/Table/utils.ts
index b4e792a7..100f3c80 100644
--- a/src/lib/components/Table/shared.ts
+++ b/src/lib/components/Table/utils.ts
@@ -1,7 +1,10 @@
import dateFormat from 'dateformat';
+import { SvelteComponent } from 'svelte';
+import type { Writable } from 'svelte/store';
+import { Send, Receive } from '$models/Models';
import type { FilterOptionsEnum } from '$models/Enums';
-import type { Columns, Filter, ServerColumn } from '$models/Models';
+import type { Columns, Filter, ServerColumn, ServerConfig } from '$models/Models';
// Function to determine minWidth for a column to simplify the logic in the HTML
export const minWidth = (id: string, columns: Columns | undefined) => {
@@ -158,6 +161,76 @@ export const missingValuesFn = (
return foundKey ? missingValues[foundKey] : key;
};
+// Function to update the server-side table data
+export const updateTable = async (
+ pageSize: number,
+ pageIndex: number,
+ server: ServerConfig | undefined,
+ filters: {
+ [key: string]: { [key in FilterOptionsEnum]?: number | string | Date }
+ },
+ data: Writable,
+ serverItems: Writable | undefined,
+ columns: Columns | undefined,
+ dispatch: any
+) => {
+ const { baseUrl, entityId, versionId, sendModel = new Send() } = server ?? {};
+
+ if (!sendModel) throw new Error('Server-side configuration is missing');
+
+ sendModel.limit = pageSize;
+ sendModel.offset = pageSize * pageIndex;
+ sendModel.version = versionId || -1;
+ sendModel.id = entityId || -1;
+ sendModel.filter = normalizeFilters(filters);
+
+ let fetchData;
+
+ try {
+ fetchData = await fetch(baseUrl || '', {
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ method: 'POST',
+ body: JSON.stringify(sendModel)
+ });
+ } catch (error) {
+ throw new Error(`Network error: ${(error as Error).message}`);
+ }
+
+ if (!fetchData.ok) {
+ throw new Error('Failed to fetch data');
+ }
+
+ const response: Receive = await fetchData.json();
+
+ // Format server columns to the client columns
+ if (response.columns !== undefined) {
+ columns = convertServerColumns(response.columns, columns);
+
+ const clientCols = response.columns.reduce((acc, col) => {
+ acc[col.key] = col.column;
+ return acc;
+ }, {});
+
+ const tmpArr: any[] = [];
+
+ response.data.forEach((row, index) => {
+ const tmp: { [key: string]: any } = {};
+ Object.keys(row).forEach((key) => {
+ tmp[clientCols[key]] = row[key];
+ });
+ tmpArr.push(tmp);
+ });
+ dispatch('fetch', columns);
+ data.set(tmpArr);
+ }
+
+ serverItems?.set(response.count);
+
+ return response;
+};
+
export const convertServerColumns = (
serverColumns: ServerColumn[],
columns: Columns | undefined
@@ -215,3 +288,86 @@ export const convertServerColumns = (
return columnsConfig;
};
+
+// Calculates the maximum height of the cells in each row
+export const getMaxCellHeightInRow = (
+ tableRef: HTMLTableElement,
+ resizable: 'columns' | 'rows' | 'none' | 'both',
+ optionsComponent: typeof SvelteComponent | undefined,
+ rowHeights: Writable<{ [key: number]: { max: number; min: number } }>,
+ tableId: string,
+ rowHeight: number | null
+) => {
+ if (!tableRef || resizable === 'columns' || resizable === 'none') return;
+
+ tableRef.querySelectorAll('tbody tr').forEach((row, index) => {
+ const cells = row.querySelectorAll('td');
+
+ let maxHeight = optionsComponent ? 56 : 44;
+ let minHeight = optionsComponent ? 56 : 44;
+
+ cells.forEach((cell) => {
+ const cellHeight = cell.getBoundingClientRect().height;
+ // + 2 pixels for rendering borders correctly
+ if (cellHeight > maxHeight) {
+ maxHeight = cellHeight + 2;
+ }
+ if (cellHeight < minHeight) {
+ minHeight = cellHeight + 2;
+ }
+ });
+
+ rowHeights.update((rh) => {
+ const id = +row.id.split(`${tableId}-row-`)[1];
+ return {
+ ...rh,
+ [id]: {
+ max: maxHeight - 24,
+ min: Math.max(minHeight - 24, rowHeight ?? 20)
+ }
+ };
+ });
+ });
+};
+
+// Calculates the minimum width of the cells in each column
+export const getMinCellWidthInColumn = (
+ tableRef: HTMLTableElement,
+ colWidths: Writable,
+ headerRowsLength: number,
+ resizable: 'columns' | 'rows' | 'none' | 'both'
+) => {
+ if (!tableRef || resizable === 'rows' || resizable === 'none') return;
+
+ // Initialize the column widths if they are not already initialized
+ colWidths.update((cw) => {
+ if (cw.length === 0) {
+ return Array.from({ length: headerRowsLength }, () => 100);
+ }
+ return cw;
+ });
+
+ colWidths.update((cw) => {
+ tableRef?.querySelectorAll('thead tr th span').forEach((cell, index) => {
+ // + 12 pixels for padding and + 32 pixels for filter icon
+ // If the column width is 100, which means it has not been initialized, then calculate the width
+ cw[index] = cw[index] === 100 ? cell.getBoundingClientRect().width + 12 + 32 : cw[index];
+ });
+ return cw;
+ });
+};
+
+export const getResizeStyles = (
+ rowHeights: { [key: number]: { max: number; min: number } },
+ id: string | number,
+ index: number
+) => {
+ return `
+ min-height: ${rowHeights && rowHeights[+id] ? `${rowHeights[+id].min}px` : 'auto'};
+ max-height: ${index !== 0 && rowHeights && rowHeights[+id]
+ ? `${rowHeights[+id].max}px`
+ : 'auto'
+ };
+ height: ${rowHeights && rowHeights[+id] ? `${rowHeights[+id].min}px` : 'auto'};
+ `;
+}
\ No newline at end of file
diff --git a/src/lib/models/Models.ts b/src/lib/models/Models.ts
index bff6997d..eced55f4 100644
--- a/src/lib/models/Models.ts
+++ b/src/lib/models/Models.ts
@@ -117,6 +117,7 @@ export interface TableConfig {
id: string;
data: Writable;
resizable?: 'none' | 'rows' | 'columns' | 'both'; // none by default
+ showColumnsMenu?: boolean; // false by default
toggle?: boolean; // false by default
search?: boolean; // true by default
fitToScreen?: boolean; // true by default
@@ -126,6 +127,7 @@ export interface TableConfig {
exportable?: boolean; // false by default
pageSizes?: number[]; // [5, 10, 20, 50, 100] by default
defaultPageSize?: number; // 10 by default
+ pageIndexStringType?: 'items' | 'pages'; // pages by default
optionsComponent?: typeof SvelteComponent;
server?: ServerConfig;
diff --git a/src/routes/components/table/data/codeBlocks.ts b/src/routes/components/table/data/codeBlocks.ts
index 5cfbb28b..39970c1c 100644
--- a/src/routes/components/table/data/codeBlocks.ts
+++ b/src/routes/components/table/data/codeBlocks.ts
@@ -146,6 +146,7 @@ export const usersBDHTML = `
const usersBDConfig: TableConfig = {
id: 'usersBD',
data: usersBDStore,
+ pageIndexStringType: 'items',
columns: {
dateOfBirth: {
header: 'Date of birth',
@@ -203,15 +204,20 @@ export interface TableConfig {
id: string;
data: Writable;
resizable?: 'rows' | 'columns' | 'both'; // none by default
+ showColumnsMenu?: boolean; // false by default
toggle?: boolean; // false by default
+ search?: boolean; // true by default
fitToScreen?: boolean; // true by default
height?: null | number; // null by default
rowHeight?: number; // auto by default
columns?: Columns;
exportable?: boolean; // false by default
- pageSizes?: number[]; // [5, 10, 15, 20] by default
+ pageSizes?: number[]; // [5, 10, 20, 50, 100] by default
defaultPageSize?: number; // 10 by default
+ pageIndexStringType?: 'items' | 'pages'; // pages by default
optionsComponent?: typeof SvelteComponent;
+
+ server?: ServerConfig;
}`;
export const columnsTypeCode = `
@@ -332,6 +338,7 @@ export const websitesHTML = `
const websitesConfig: TableConfig = {
id: 'websites',
data: websitesStore,
+ showColumnsMenu: true,
toggle: true,
fitToScreen: false,
columns: {
@@ -707,13 +714,13 @@ export const serverSideTableHTML = `
const serverTableConfig: TableConfig = {
id: 'serverTable', // a unique id for the table
- entityId: 3, // dataset ID
- versionId: -1, // vesion ID
data: tableStore, // store to hold and retrieve data
- serverSide: true, // serverSide needs to be set to true
- // URL for the table to be fetched from
- URL: 'https://dev.bexis2.uni-jena.de/api/datatable/',
- token: '' // API token to access the datasets
+ server: {
+ // URL for the table to be fetched from
+ baseUrl: 'https://dev.bexis2.uni-jena.de/api/datatable/',
+ entityId: 1, // dataset ID
+ versionId: -1, // version ID
+ }
};
diff --git a/src/routes/components/table/docs/TableConfigDocs.svelte b/src/routes/components/table/docs/TableConfigDocs.svelte
index e34d0ac3..a5d13ed7 100644
--- a/src/routes/components/table/docs/TableConfigDocs.svelte
+++ b/src/routes/components/table/docs/TableConfigDocs.svelte
@@ -19,7 +19,7 @@
>
Underlined attributes are required.
-
+
@@ -42,32 +42,31 @@
A writable store of the type T[]T[]. Any changes in the store will be reflected in the table.
-
search:
-
boolean
+
resizable:
+
"rows", "columns" or "both"
- Whether the table should have a search bar. true by default.
+ Whether rows, columns or both should be resizable. Not resizable by default.
-
exportable:
+
showColumnsMenu:
boolean
- Whether the table should be exportable to CSV. false by default.
@@ -80,35 +79,26 @@
- Whether the fitToScreen
toggle should be visible.
- false by default.
-
-
-
-
-
-
resizable:
-
"rows", "columns" or "both"
-
-
-
- Whether rows, columns or both should be resizable. Not resizable by default.
+ false by default.
-
rowHeight:
-
number
+
search:
+
boolean
- Sets height for the rows in pixels. If resizable is set to "both" or "rows", this value can be interpreted as minimum height for the rows.
+ Whether the table should have a search bar. true by default.
@@ -136,15 +126,43 @@
-
optionsComponent:
-
{`SvelteComponent`}
+
rowHeight:
+
number
- Custom Svelte component to apply actions on a specific row. Table will not have an options
- column if no optionsComponent was provided.
+ Sets height for the rows in pixels. If resizable is set to "both" or "rows", this value can be interpreted as minimum height for the rows.
+
+
+
+
+
+
columns:
+
{`Columns`}
+
+
+
+ An object with configuration for specific columns. Columns
+ object is described below.
+
+
+
+
+
+
exportable:
+
boolean
+
+
+
+ Whether the table should be exportable to CSV. false by default.
@@ -155,7 +173,8 @@
- An array of page sizes to be used for the table. By default, page sizes are 5, 10, 20, 50, 100.
+ An array of page sizes to be used for the table. By default, page sizes are 5, 10, 20, 50,
+ 100.
@@ -173,15 +192,31 @@
-
columns:
-
{`Columns`}
+
pageIndexStringType:
+
"items" or "pages"
- An object with configuration for specific columns. Columns"pages"
- object is described below.
+ by default.
+
+
+
+
+
+
optionsComponent:
+
{`SvelteComponent`}
+
+
+
+ Custom Svelte component to apply actions on a specific row. Table will not have an options
+ column if no optionsComponent was provided.
diff --git a/src/routes/components/table/examples/TableServer.svelte b/src/routes/components/table/examples/TableServer.svelte
index 74fb25e5..6a870cf9 100644
--- a/src/routes/components/table/examples/TableServer.svelte
+++ b/src/routes/components/table/examples/TableServer.svelte
@@ -22,13 +22,11 @@
serverTableConfig = {
id: 'serverTable', // a unique id for the table
data: tableStore, // store to hold and retrieve data
- // URL for the table to be fetched from
- pageSizes: [10, 25, 50, 100],
-
server: {
+ // URL for the table to be fetched from
baseUrl: 'https://dev.bexis2.uni-jena.de/api/datatable/',
entityId: 1, // dataset ID
- versionId: -1, // version ID
+ versionId: -1 // version ID
}
};
});
diff --git a/src/routes/components/table/examples/TableURLs.svelte b/src/routes/components/table/examples/TableURLs.svelte
index 4f6582fa..b9f2fadd 100644
--- a/src/routes/components/table/examples/TableURLs.svelte
+++ b/src/routes/components/table/examples/TableURLs.svelte
@@ -10,6 +10,7 @@
const websitesConfig: TableConfig = {
id: 'websites',
data: websitesStore,
+ showColumnsMenu: true,
toggle: true,
fitToScreen: false,
columns: {
@@ -31,7 +32,7 @@