@indico-data/design-system 2.48.0 → 2.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/table/Table.stories.d.ts +1 -0
- package/lib/components/table/components/HorizontalStickyHeader.d.ts +10 -0
- package/lib/components/table/components/__tests__/HorizontalStickyHeader.test.d.ts +1 -0
- package/lib/components/table/components/helpers.d.ts +6 -0
- package/lib/components/table/hooks/usePinnedColumnsManager.d.ts +8 -0
- package/lib/components/table/sampleData.d.ts +4 -0
- package/lib/components/table/types.d.ts +11 -1
- package/lib/components/table/utils/processColumns.d.ts +2 -0
- package/lib/index.css +188 -89
- package/lib/index.d.ts +12 -1
- package/lib/index.esm.css +188 -89
- package/lib/index.esm.js +238 -2
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +238 -2
- package/lib/index.js.map +1 -1
- package/lib/stylesAndAnimations/utilityClasses/UtilityClassesData.d.ts +7 -0
- package/lib/stylesAndAnimations/utilityClasses/UtilityClassesTable.d.ts +1 -0
- package/lib/stylesAndAnimations/utilityClasses/UtilityClassesTable.stories.d.ts +6 -0
- package/lib/utils/getPreviousHeadersWidth.d.ts +1 -0
- package/package.json +1 -1
- package/src/components/table/Table.mdx +134 -0
- package/src/components/table/Table.stories.tsx +71 -2
- package/src/components/table/Table.tsx +16 -1
- package/src/components/table/components/HorizontalStickyHeader.tsx +57 -0
- package/src/components/table/components/__tests__/HorizontalStickyHeader.test.tsx +104 -0
- package/src/components/table/components/helpers.ts +90 -0
- package/src/components/table/hooks/usePinnedColumnsManager.ts +146 -0
- package/src/components/table/{sampleData.ts → sampleData.tsx} +156 -1
- package/src/components/table/styles/Table.scss +32 -15
- package/src/components/table/styles/_variables.scss +2 -0
- package/src/components/table/types.ts +13 -1
- package/src/components/table/utils/processColumns.tsx +35 -0
- package/src/setup/setupTests.ts +8 -0
- package/src/storybookDocs/Permafrost.mdx +22 -11
- package/src/styles/_borders.scss +2 -1
- package/src/stylesAndAnimations/borders/BorderColor.tsx +14 -6
- package/src/stylesAndAnimations/utilityClasses/UtilityClasses.mdx +24 -0
- package/src/stylesAndAnimations/utilityClasses/UtilityClassesData.ts +230 -0
- package/src/stylesAndAnimations/utilityClasses/UtilityClassesTable.stories.tsx +13 -0
- package/src/stylesAndAnimations/utilityClasses/UtilityClassesTable.tsx +146 -0
- package/src/utils/getPreviousHeadersWidth.ts +12 -0
package/lib/index.js
CHANGED
|
@@ -2628,6 +2628,16 @@ function __rest(s, e) {
|
|
|
2628
2628
|
return t;
|
|
2629
2629
|
}
|
|
2630
2630
|
|
|
2631
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
2632
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
2633
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
2634
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
2635
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
2636
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
2637
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
2638
|
+
});
|
|
2639
|
+
}
|
|
2640
|
+
|
|
2631
2641
|
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
2632
2642
|
var e = new Error(message);
|
|
2633
2643
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
@@ -7281,8 +7291,234 @@ const TablePagination = ({ rowsPerPage, rowCount, onChangePage, currentPage, tot
|
|
|
7281
7291
|
return (jsxRuntime.jsx("div", { className: "table__pagination", children: jsxRuntime.jsxs(Row, { align: "center", justify: "between", children: [jsxRuntime.jsx(Col, { xs: "content", children: totalEntriesText && (jsxRuntime.jsx("span", { "data-testid": "table-pagination-total-entries", className: "table__pagination-total-entries", children: totalEntriesText })) }), jsxRuntime.jsx(Col, { xs: "content", children: jsxRuntime.jsx(Pagination, { "data-testid": "table-pagination-component", totalPages: totalPages, currentPage: currentPage, onChange: (page) => onChangePage(page, rowsPerPage) }) })] }) }));
|
|
7282
7292
|
};
|
|
7283
7293
|
|
|
7294
|
+
// Gets the width of the previous pinned columns
|
|
7295
|
+
const getPreviousHeadersWidth = (position, pinnedColumnIds) => {
|
|
7296
|
+
let totalWidth = 0;
|
|
7297
|
+
// Add checkbox column width if it's pinned
|
|
7298
|
+
if (pinnedColumnIds.includes('checkbox-column')) {
|
|
7299
|
+
const checkboxCell = document.querySelector('.rdt_TableCol:not([data-column-id])');
|
|
7300
|
+
if (checkboxCell) {
|
|
7301
|
+
totalWidth += checkboxCell.offsetWidth;
|
|
7302
|
+
}
|
|
7303
|
+
}
|
|
7304
|
+
// Add widths of other pinned columns before this position
|
|
7305
|
+
const previousHeaders = Array.from({ length: position }, (_, i) => {
|
|
7306
|
+
const header = document.querySelector(`[data-column-id="sticky-column-${i}"]`);
|
|
7307
|
+
if (header && pinnedColumnIds.includes(`sticky-column-${i}`)) {
|
|
7308
|
+
return header;
|
|
7309
|
+
}
|
|
7310
|
+
return null;
|
|
7311
|
+
}).filter((header) => header !== null);
|
|
7312
|
+
// Calculate base width from previous columns
|
|
7313
|
+
totalWidth = previousHeaders.reduce((acc, header) => {
|
|
7314
|
+
return acc + header.offsetWidth;
|
|
7315
|
+
}, totalWidth);
|
|
7316
|
+
// Leave this for if we ever try to fix the auto width columns
|
|
7317
|
+
// There is a bug where borders cause the offset to be wrong, this keeps it in sync.
|
|
7318
|
+
// Add offset that increases by 1 every two columns after index 1
|
|
7319
|
+
// if (position >= 2) {
|
|
7320
|
+
// const additionalOffset = Math.floor((position - 2) / 2) + 1;
|
|
7321
|
+
// totalWidth += additionalOffset;
|
|
7322
|
+
// }
|
|
7323
|
+
return totalWidth;
|
|
7324
|
+
};
|
|
7325
|
+
// Applies sticky styles to the column header
|
|
7326
|
+
const applyStickyStylesToTableHeader = (position, left) => __awaiter(void 0, void 0, void 0, function* () {
|
|
7327
|
+
const header = document.querySelector(`[data-column-id="sticky-column-${position}"]`);
|
|
7328
|
+
if (header) {
|
|
7329
|
+
header.style.position = 'sticky';
|
|
7330
|
+
header.style.left = `${left}px`;
|
|
7331
|
+
header.style.zIndex = '3';
|
|
7332
|
+
header.style.backgroundColor =
|
|
7333
|
+
'var(--pf-table-pinned-column-background-color)';
|
|
7334
|
+
}
|
|
7335
|
+
});
|
|
7336
|
+
// Sorts the pinned columns so that any column that is pinned comes before any column that is not.
|
|
7337
|
+
const sortPinnedColumns = (columns, pinnedColumnIds) => {
|
|
7338
|
+
return [...columns].sort((a, b) => {
|
|
7339
|
+
const aIsPinned = pinnedColumnIds.includes(a.id);
|
|
7340
|
+
const bIsPinned = pinnedColumnIds.includes(b.id);
|
|
7341
|
+
if (aIsPinned && !bIsPinned)
|
|
7342
|
+
return -1; // a comes first
|
|
7343
|
+
if (!aIsPinned && bIsPinned)
|
|
7344
|
+
return 1; // b comes first
|
|
7345
|
+
return 0; // maintain relative order for columns with same pinned state
|
|
7346
|
+
});
|
|
7347
|
+
};
|
|
7348
|
+
// Gets the styles for the pinned columns
|
|
7349
|
+
const getPinnedColumnStyles = (isPinned, index, pinnedColumnIds) => {
|
|
7350
|
+
return isPinned
|
|
7351
|
+
? {
|
|
7352
|
+
position: 'sticky',
|
|
7353
|
+
left: `${getPreviousHeadersWidth(index, pinnedColumnIds)}px`,
|
|
7354
|
+
zIndex: 3,
|
|
7355
|
+
backgroundColor: 'var(--pf-table-pinned-column-background-color)',
|
|
7356
|
+
}
|
|
7357
|
+
: {
|
|
7358
|
+
position: undefined,
|
|
7359
|
+
left: undefined,
|
|
7360
|
+
zIndex: undefined,
|
|
7361
|
+
backgroundColor: undefined,
|
|
7362
|
+
};
|
|
7363
|
+
};
|
|
7364
|
+
const clearStickyStyles = (header) => {
|
|
7365
|
+
header.style.position = '';
|
|
7366
|
+
header.style.left = '';
|
|
7367
|
+
header.style.zIndex = '';
|
|
7368
|
+
header.style.backgroundColor = '';
|
|
7369
|
+
};
|
|
7370
|
+
|
|
7371
|
+
const HorizontalStickyHeader = ({ children, position, onPinColumn, isPinned = false, pinnedColumnIds, }) => {
|
|
7372
|
+
React.useEffect(() => {
|
|
7373
|
+
const calculateWidth = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
7374
|
+
yield new Promise((resolve) => setTimeout(resolve, 0));
|
|
7375
|
+
const header = document.querySelector(`[data-column-id="sticky-column-${position}"]`);
|
|
7376
|
+
if (header) {
|
|
7377
|
+
if (isPinned) {
|
|
7378
|
+
const width = getPreviousHeadersWidth(position, pinnedColumnIds);
|
|
7379
|
+
yield applyStickyStylesToTableHeader(position, width);
|
|
7380
|
+
}
|
|
7381
|
+
else {
|
|
7382
|
+
clearStickyStyles(header);
|
|
7383
|
+
}
|
|
7384
|
+
}
|
|
7385
|
+
});
|
|
7386
|
+
calculateWidth();
|
|
7387
|
+
}, [position, isPinned, pinnedColumnIds]);
|
|
7388
|
+
return (jsxRuntime.jsxs("div", { className: "table__header-cell", "data-testid": `sticky-column-${position}`, children: [jsxRuntime.jsx(Button$1, { "data-testid": `sticky-header-pin-button-${position}`, variant: "link", size: "sm", iconLeft: "pin", onClick: onPinColumn, ariaLabel: isPinned ? 'Unpin column' : 'Pin column', className: `table__column--${isPinned ? 'is-pinned' : 'is-not-pinned'} table__column__pin-action` }), jsxRuntime.jsx("div", { className: "table__header-content", children: children })] }));
|
|
7389
|
+
};
|
|
7390
|
+
|
|
7391
|
+
const processColumns = (columns, pinnedColumnIds, togglePinnedColumn) => {
|
|
7392
|
+
return columns.map((column, index) => {
|
|
7393
|
+
const dataColumnId = `sticky-column-${index}`;
|
|
7394
|
+
const isPinned = pinnedColumnIds.includes(dataColumnId);
|
|
7395
|
+
const headerContent = column.isPinned !== undefined ? (jsxRuntime.jsx(HorizontalStickyHeader, { position: index, isPinned: isPinned, onPinColumn: () => togglePinnedColumn(column.id), pinnedColumnIds: pinnedColumnIds, children: column.name })) : (jsxRuntime.jsx(jsxRuntime.Fragment, { children: column.name }));
|
|
7396
|
+
return Object.assign(Object.assign({}, column), { name: headerContent, id: dataColumnId, style: getPinnedColumnStyles(isPinned, index, pinnedColumnIds) });
|
|
7397
|
+
});
|
|
7398
|
+
};
|
|
7399
|
+
|
|
7400
|
+
/**
|
|
7401
|
+
* Hook to manage pinned columns in a table
|
|
7402
|
+
* Handles initialization, toggling, positioning and resizing of pinned columns
|
|
7403
|
+
*/
|
|
7404
|
+
const usePinnedColumnsManager = (columns, canPinColumns, onPinnedColumnsChange) => {
|
|
7405
|
+
const pinnedColumnIds = columns.filter((column) => column.isPinned).map((column) => column.id);
|
|
7406
|
+
// `dataColumnIds` is the list of IDs used as `data-column-id` attributes on the table headers and cells
|
|
7407
|
+
const dataColumnIds = React.useMemo(() => {
|
|
7408
|
+
const ids = columns
|
|
7409
|
+
.map((column, index) => (column.isPinned ? `sticky-column-${index}` : null))
|
|
7410
|
+
.filter((id) => id !== null);
|
|
7411
|
+
return ids.length > 0 ? ['checkbox-column', ...ids] : ids;
|
|
7412
|
+
}, [columns]);
|
|
7413
|
+
// Toggle individual column pin state
|
|
7414
|
+
const togglePinnedColumn = React.useCallback((columnId) => {
|
|
7415
|
+
const prevPinnedColumns = pinnedColumnIds;
|
|
7416
|
+
// Handle unpinning
|
|
7417
|
+
if (prevPinnedColumns.some((id) => id === columnId)) {
|
|
7418
|
+
onPinnedColumnsChange === null || onPinnedColumnsChange === void 0 ? void 0 : onPinnedColumnsChange(prevPinnedColumns.filter((id) => id !== columnId));
|
|
7419
|
+
}
|
|
7420
|
+
else {
|
|
7421
|
+
onPinnedColumnsChange === null || onPinnedColumnsChange === void 0 ? void 0 : onPinnedColumnsChange(prevPinnedColumns.concat(columnId));
|
|
7422
|
+
}
|
|
7423
|
+
}, [pinnedColumnIds, onPinnedColumnsChange]);
|
|
7424
|
+
// Handle resize events and recalculate pinned column positions
|
|
7425
|
+
React.useEffect(() => {
|
|
7426
|
+
if (!canPinColumns)
|
|
7427
|
+
return;
|
|
7428
|
+
const recalculatePositions = () => {
|
|
7429
|
+
// Reset all column styles and remove last-pinned-column class
|
|
7430
|
+
const allCells = document.querySelectorAll('.rdt_TableCol, .rdt_TableCell');
|
|
7431
|
+
allCells.forEach((cell) => {
|
|
7432
|
+
cell.style.position = '';
|
|
7433
|
+
cell.style.left = '';
|
|
7434
|
+
cell.style.zIndex = '';
|
|
7435
|
+
cell.style.backgroundColor = '';
|
|
7436
|
+
cell.style.borderRight = '';
|
|
7437
|
+
});
|
|
7438
|
+
// Apply styles to pinned columns
|
|
7439
|
+
dataColumnIds.forEach((column, index) => {
|
|
7440
|
+
const isLastPinnedColumn = index === dataColumnIds.length - 1;
|
|
7441
|
+
if (column === 'checkbox-column') {
|
|
7442
|
+
// Handle header checkbox
|
|
7443
|
+
const headerCheckbox = document.querySelector('.rdt_TableCol:not([data-column-id])');
|
|
7444
|
+
if (headerCheckbox) {
|
|
7445
|
+
headerCheckbox.style.position = 'sticky';
|
|
7446
|
+
headerCheckbox.style.left = '0';
|
|
7447
|
+
headerCheckbox.style.zIndex = '4';
|
|
7448
|
+
headerCheckbox.style.backgroundColor =
|
|
7449
|
+
'var(--pf-table-background-color)';
|
|
7450
|
+
}
|
|
7451
|
+
// Handle cell checkboxes
|
|
7452
|
+
const cellCheckboxes = document.querySelectorAll('.rdt_TableCell:first-child');
|
|
7453
|
+
cellCheckboxes.forEach((cell) => {
|
|
7454
|
+
cell.style.position = 'sticky';
|
|
7455
|
+
cell.style.left = '0';
|
|
7456
|
+
cell.style.zIndex = '2';
|
|
7457
|
+
cell.style.backgroundColor =
|
|
7458
|
+
'var(--pf-table-pinned-column-background-color)';
|
|
7459
|
+
if (isLastPinnedColumn) {
|
|
7460
|
+
cell.style.borderRight =
|
|
7461
|
+
`2px solid var(--pf-table-pinned-column-border-color)`;
|
|
7462
|
+
}
|
|
7463
|
+
});
|
|
7464
|
+
}
|
|
7465
|
+
else {
|
|
7466
|
+
const columnIndex = parseInt(column.split('-')[2]);
|
|
7467
|
+
const left = getPreviousHeadersWidth(columnIndex, dataColumnIds);
|
|
7468
|
+
// Headers
|
|
7469
|
+
const headers = document.querySelectorAll(`.rdt_TableCol[data-column-id="sticky-column-${columnIndex}"]`);
|
|
7470
|
+
headers.forEach((header) => {
|
|
7471
|
+
header.style.position = 'sticky';
|
|
7472
|
+
header.style.left = `${left}px`;
|
|
7473
|
+
header.style.zIndex = '2';
|
|
7474
|
+
header.style.backgroundColor =
|
|
7475
|
+
'var(--pf-table-pinned-column-background-color)';
|
|
7476
|
+
if (isLastPinnedColumn) {
|
|
7477
|
+
header.style.borderRight =
|
|
7478
|
+
`2px solid var(--pf-table-pinned-column-border-color)`;
|
|
7479
|
+
}
|
|
7480
|
+
});
|
|
7481
|
+
// Cells
|
|
7482
|
+
const cells = document.querySelectorAll(`.rdt_TableCell[data-column-id="sticky-column-${columnIndex}"]`);
|
|
7483
|
+
cells.forEach((cell) => {
|
|
7484
|
+
cell.style.position = 'sticky';
|
|
7485
|
+
cell.style.left = `${left}px`;
|
|
7486
|
+
cell.style.zIndex = '2';
|
|
7487
|
+
cell.style.backgroundColor =
|
|
7488
|
+
'var(--pf-table-pinned-column-background-color)';
|
|
7489
|
+
if (isLastPinnedColumn) {
|
|
7490
|
+
cell.style.borderRight =
|
|
7491
|
+
`2px solid var(--pf-table-pinned-column-border-color)`;
|
|
7492
|
+
}
|
|
7493
|
+
});
|
|
7494
|
+
}
|
|
7495
|
+
});
|
|
7496
|
+
};
|
|
7497
|
+
// Set up resize observers
|
|
7498
|
+
const table = document.querySelector('.rdt_Table');
|
|
7499
|
+
const resizeObserver = new ResizeObserver(recalculatePositions);
|
|
7500
|
+
if (table) {
|
|
7501
|
+
resizeObserver.observe(table);
|
|
7502
|
+
}
|
|
7503
|
+
window.addEventListener('resize', recalculatePositions);
|
|
7504
|
+
return () => {
|
|
7505
|
+
resizeObserver.disconnect();
|
|
7506
|
+
window.removeEventListener('resize', recalculatePositions);
|
|
7507
|
+
};
|
|
7508
|
+
}, [canPinColumns, dataColumnIds]);
|
|
7509
|
+
// Process columns for rendering with pin state
|
|
7510
|
+
const columnsWithPinning = canPinColumns
|
|
7511
|
+
? sortPinnedColumns(processColumns(columns, dataColumnIds, togglePinnedColumn), dataColumnIds)
|
|
7512
|
+
: columns;
|
|
7513
|
+
return {
|
|
7514
|
+
columnsWithPinning, // Columns with pin state and handlers applied
|
|
7515
|
+
};
|
|
7516
|
+
};
|
|
7517
|
+
|
|
7284
7518
|
const Table = (props) => {
|
|
7285
|
-
const { responsive = true, direction = 'auto', keyField = 'id', striped = false, noDataComponent = 'built-in', isDisabled, isLoading, isFullHeight = false, subHeaderAlign = 'left', className, paginationTotalRows, totalEntriesText } = props, rest = __rest(props, ["responsive", "direction", "keyField", "striped", "noDataComponent", "isDisabled", "isLoading", "isFullHeight", "subHeaderAlign", "className", "paginationTotalRows", "totalEntriesText"]);
|
|
7519
|
+
const { responsive = true, direction = 'auto', keyField = 'id', striped = false, noDataComponent = 'built-in', isDisabled, isLoading, isFullHeight = false, subHeaderAlign = 'left', className, paginationTotalRows, totalEntriesText, data, columns: initialColumns, canPinColumns = false, onPinnedColumnsChange } = props, rest = __rest(props, ["responsive", "direction", "keyField", "striped", "noDataComponent", "isDisabled", "isLoading", "isFullHeight", "subHeaderAlign", "className", "paginationTotalRows", "totalEntriesText", "data", "columns", "canPinColumns", "onPinnedColumnsChange"]);
|
|
7520
|
+
// Turns on/off column pinning.
|
|
7521
|
+
const { columnsWithPinning } = usePinnedColumnsManager(initialColumns, canPinColumns, onPinnedColumnsChange);
|
|
7286
7522
|
const combinedClassName = classNames(className, {
|
|
7287
7523
|
'table--striped': striped,
|
|
7288
7524
|
'table-body': true,
|
|
@@ -7290,7 +7526,7 @@ const Table = (props) => {
|
|
|
7290
7526
|
const tableWrapperClassName = classNames('table', {
|
|
7291
7527
|
'table--full-height': isFullHeight,
|
|
7292
7528
|
});
|
|
7293
|
-
return (jsxRuntime.jsx("div", { className: tableWrapperClassName, "data-testid": "table", children: jsxRuntime.jsx(Xe, Object.assign({ responsive: responsive, direction: direction, subHeaderAlign: subHeaderAlign, keyField: keyField, striped: striped, className: combinedClassName, disabled: isDisabled, noDataComponent: noDataComponent, progressPending: isLoading, progressComponent: jsxRuntime.jsx(LoadingComponent, {}), pagination: true, paginationComponent: (props) => (jsxRuntime.jsx(TablePagination, Object.assign({}, props, { totalEntriesText: totalEntriesText }))), paginationTotalRows: paginationTotalRows }, rest)) }));
|
|
7529
|
+
return (jsxRuntime.jsx("div", { className: tableWrapperClassName, "data-testid": "table", children: jsxRuntime.jsx(Xe, Object.assign({ data: data, columns: columnsWithPinning, responsive: responsive, direction: direction, subHeaderAlign: subHeaderAlign, keyField: keyField, striped: striped, className: combinedClassName, disabled: isDisabled, noDataComponent: noDataComponent, progressPending: isLoading, progressComponent: jsxRuntime.jsx(LoadingComponent, {}), pagination: true, paginationComponent: (props) => (jsxRuntime.jsx(TablePagination, Object.assign({}, props, { totalEntriesText: totalEntriesText }))), paginationTotalRows: paginationTotalRows, highlightOnHover: true, pointerOnHover: true }, rest)) }));
|
|
7294
7530
|
};
|
|
7295
7531
|
|
|
7296
7532
|
const Radio = React__namespace.default.forwardRef((_a, ref) => {
|