@wix/design-system
Version:
@wix/design-system
156 lines • 6.03 kB
JavaScript
import React, { useState, useMemo, useCallback, createContext } from 'react';
import { getStickyColumnStyle, CELL_PADDING, CELL_EDGE_PADDING, } from '../DataTable/DataTable.utils';
const MIN_COLUMN_WIDTH = 60;
const parseColumnWidth = (width) => {
if (!width)
return MIN_COLUMN_WIDTH;
if (typeof width === 'number') {
return width;
}
if (typeof width === 'string') {
if (width.endsWith('px')) {
const parsed = parseInt(width, 10);
return isNaN(parsed) ? MIN_COLUMN_WIDTH : parsed;
}
if (width.endsWith('%')) {
return width;
}
const parsed = parseInt(width, 10);
return isNaN(parsed) ? MIN_COLUMN_WIDTH : parsed;
}
return MIN_COLUMN_WIDTH;
};
const createDefaultContext = () => ({
columnWidths: null,
getEffectiveColumnWidth: (column) => column.width,
// default handling of sticky columns style is the exsisting util
getStickyColumnStyle: (columns, column) => {
return getStickyColumnStyle(columns, column);
},
getTableWidth: (fallback) => fallback || '100%',
resizeColumn: () => { },
});
export const ColumnResizeContext = createContext(createDefaultContext());
/**
* ColumnResize manages the state and logic of column resizing for tables.
* Given an array of columns with resize configuration, it provides a context with resize handlers,
* width calculations, and sticky column positioning.
*
* Key features:
* - Manages column width state with min/max constraints
* - Provides effective width calculations for columns
* - Handles sticky column positioning with dynamic widths
* - Supports both resizable and non-resizable table modes
* - Offers resize event callbacks (onColumnResize, onColumnResizeStart, onColumnResizeEnd)
*/
export const ColumnResize = ({ columns = [], isTableResizable = false, onColumnResize, onColumnResizeStart, onColumnResizeEnd, minColumnWidth = MIN_COLUMN_WIDTH, maxColumnWidth, children, }) => {
const initializeColumnWidths = () => {
if (!isTableResizable) {
return null;
}
// Resizable flow: Parse all widths to numbers, store in mapping
return columns.reduce((acc, column) => {
const id = column.resizeProps?.id;
if (id) {
const parsedWidth = parseColumnWidth(column.width);
if (typeof parsedWidth === 'number') {
acc[id] = parsedWidth;
}
}
return acc;
}, {});
};
const [columnWidths, setColumnWidths] = useState(initializeColumnWidths);
const getEffectiveColumnWidth = useCallback((column) => {
if (isTableResizable && columnWidths) {
const id = column.resizeProps?.id?.toString();
if (id && columnWidths[id]) {
return columnWidths[id];
}
return parseColumnWidth(column.width);
}
return column.width;
}, [isTableResizable, columnWidths]);
const getStickyColumnStyle = useCallback((columns, column) => {
let left = 0;
for (let i = 0; i < columns.length; i++) {
const col = columns[i];
if (col === column) {
break;
}
const horizontalPadding = i === 0 ? CELL_EDGE_PADDING + CELL_PADDING : 2 * CELL_PADDING;
const effectiveWidth = getEffectiveColumnWidth(col);
const widthValue = typeof effectiveWidth === 'string'
? parseInt(effectiveWidth, 10)
: effectiveWidth;
left += (widthValue ?? MIN_COLUMN_WIDTH) + horizontalPadding;
}
return { left };
}, [getEffectiveColumnWidth]);
const handleColumnResize = useCallback((columnId, newWidth) => {
if (!isTableResizable || !columnWidths) {
return;
}
let clampedWidth = newWidth;
if (minColumnWidth !== undefined && newWidth < minColumnWidth) {
clampedWidth = minColumnWidth;
}
if (maxColumnWidth !== undefined && newWidth > maxColumnWidth) {
clampedWidth = maxColumnWidth;
}
setColumnWidths(prevState => {
if (!prevState) {
return prevState;
}
const updatedColumnWidths = {
...prevState,
[columnId]: clampedWidth,
};
return updatedColumnWidths;
});
if (onColumnResize) {
onColumnResize(columnId, clampedWidth);
}
}, [
isTableResizable,
columnWidths,
minColumnWidth,
maxColumnWidth,
onColumnResize,
]);
const handleColumnResizeStart = useCallback((columnId, e) => {
if (onColumnResizeStart && columnWidths) {
onColumnResizeStart(columnId, columnWidths[columnId], e);
}
}, [onColumnResizeStart, columnWidths]);
const handleColumnResizeEnd = useCallback((columnId, e) => {
if (onColumnResizeEnd && columnWidths) {
onColumnResizeEnd(columnId, columnWidths[columnId], e);
}
}, [onColumnResizeEnd, columnWidths]);
const getTableWidth = useCallback((fallbackWidth) => {
if (!isTableResizable) {
return fallbackWidth || '100%';
}
return 'fit-content';
}, [isTableResizable]);
const contextValue = useMemo(() => ({
columnWidths,
getEffectiveColumnWidth,
getStickyColumnStyle,
getTableWidth,
resizeColumn: handleColumnResize,
startColumnResize: handleColumnResizeStart,
endColumnResize: handleColumnResizeEnd,
}), [
columnWidths,
getEffectiveColumnWidth,
getStickyColumnStyle,
getTableWidth,
handleColumnResize,
handleColumnResizeStart,
handleColumnResizeEnd,
]);
return (React.createElement(ColumnResizeContext.Provider, { value: contextValue }, children));
};
//# sourceMappingURL=ColumnResize.js.map