UNPKG

@appbuckets/react-ui

Version:
805 lines (798 loc) 25.3 kB
'use strict'; var tslib = require('tslib'); var React = require('react'); var clsx = require('clsx'); var reactWindow = require('react-window'); var useElementSize = require('../hooks/useElementSize.js'); require('../BucketTheme/BucketTheme.js'); var BucketContext = require('../BucketTheme/BucketContext.js'); require('../RxTable/RxTable.js'); var RxTable_context = require('../RxTable/RxTable.context.js'); var RxTable_factory = require('../RxTable/RxTable.factory.js'); var BodyRow = require('../RxTable/components/BodyRow.js'); var FooterRow = require('../RxTable/components/FooterRow.js'); var FiltersRow = require('../RxTable/components/FiltersRow.js'); var HeaderRow = require('../RxTable/components/HeaderRow.js'); var StateDependentBodyRow = require('../RxTable/components/StateDependentBodyRow.js'); var RxTableBodyCell = require('../RxTable/defaults/RxTableBodyCell.js'); var RxTableBodyRow = require('../RxTable/defaults/RxTableBodyRow.js'); var RxTableFooterCell = require('../RxTable/defaults/RxTableFooterCell.js'); var RxTableHeaderCell = require('../RxTable/defaults/RxTableHeaderCell.js'); var RxTableEmptyContent = require('../RxTable/defaults/RxTableEmptyContent.js'); var RxTableError = require('../RxTable/defaults/RxTableError.js'); var RxTableLoader = require('../RxTable/defaults/RxTableLoader.js'); var ScrollOnTop = require('./atoms/ScrollOnTop.js'); function _interopDefaultLegacy(e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; } function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty( n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; }, } ); } }); } n['default'] = e; return Object.freeze(n); } var React__namespace = /*#__PURE__*/ _interopNamespace(React); var clsx__default = /*#__PURE__*/ _interopDefaultLegacy(clsx); /* -------- * Memoize the BodyRow Component to be used with VariableSizeList * -------- */ var MemoizedBodyRow = React__namespace.memo(BodyRow, reactWindow.areEqual); /* -------- * Variable Size List Inner Element * --- * Is extracted from original code to avoid * list rerender * -------- */ var VariableSizeListInnerElement = React__namespace.forwardRef(function ( props, ref ) { /** Get Body Component */ var _a = RxTable_context.useRxTable(), Body = _a.Components.Body, classes = _a.classes, styles = _a.styles; /** Extract style and classes from props */ var style = props.style, className = props.className, rest = tslib.__rest(props, ['style', 'className']); /** Merge Body Classes */ var bodyClasses = clsx__default['default'](className, classes.Body); /** Render the Component */ return React__namespace.createElement( Body, tslib.__assign({}, rest, { ref: ref, className: bodyClasses, style: tslib.__assign(tslib.__assign({}, styles.Body), style), }) ); }); /* -------- * Variable Size List Outer Element * --- * Is extracted from original code to avoid * list rerender * -------- */ var VariableSizeListOuterElement = React__namespace.forwardRef(function ( props, ref ) { /** Get Body Component */ var _a = RxTable_context.useRxTable(), BodyWrapper = _a.Components.BodyWrapper, classes = _a.classes, styles = _a.styles; /** Extract style and classes from props */ var style = props.style, className = props.className, rest = tslib.__rest(props, ['style', 'className']); /** Merge Body Classes */ var bodyWrapperClasses = clsx__default['default']( className, classes.BodyWrapper ); /** Render the Component */ return React__namespace.createElement( BodyWrapper, tslib.__assign({}, rest, { ref: ref, className: bodyWrapperClasses, style: tslib.__assign(tslib.__assign({}, styles.BodyWrapper), style), }) ); }); /* -------- * Component Definition * -------- */ var VirtualizedTable = function (receivedProps) { var props = BucketContext.useWithDefaultProps( 'virtualizedTable', receivedProps ); var /** RxTable Shared Props */ userDefinedClasses = props.classes; props.className; var columns = props.columns, userDefinedComponents = props.Components, data = props.data, defaultData = props.defaultData, userDefinedDefaultReverseSorting = props.defaultReverseSorting, userDefinedSelectedData = props.defaultSelectedData, userDefinedDefaultSort = props.defaultSort, disableHeader = props.disableHeader, filterLogic = props.filterLogic, userDefinedGetRowKey = props.getRowKey, userDefinedHeight = props.height, initiallyLoading = props.initiallyLoading, loaderProps = props.loaderProps, noFilteredDataEmptyContentProps = props.noFilteredDataEmptyContentProps, noDataEmptyContentProps = props.noDataEmptyContentProps, onRowClick = props.onRowClick, onSortChange = props.onSortChange, onSelectedDataChange = props.onSelectedDataChange, reloadDependency = props.reloadDependency, reloadSilently = props.reloadSilently, userDefinedReverseSorting = props.reverseSorting, scrollOnTopButtonProps = props.scrollOnTopButtonProps, scrollOnTopOffsetVisibility = props.scrollOnTopOffsetVisibility, selectable = props.selectable, selectColumnProps = props.selectColumnProps, userDefinedSort = props.sort, style = props.style, userDefinedStyles = props.styles, userDefinedWidth = props.width, useScrollOnTop = props.useScrollOnTop, /** Dedicated VirtualizedTable Props */ compressed = props.compressed, userDefinedFilterRowHeight = props.filterRowHeight, userDefinedFooterRowHeight = props.footerRowHeight, userDefinedHeaderHeight = props.headerHeight, rowHeight = props.rowHeight, /** Size Detector Props */ maximumWidth = props.maximumWidth, maximumHeight = props.maximumHeight, minimumWidth = props.minimumWidth, minimumHeight = props.minimumHeight, subtractToWidth = props.subtractToWidth, subtractToHeight = props.subtractToHeight, /** Extracted Variable Size List Props */ direction = props.direction, itemKey = props.itemKey, overscanCount = props.overscanCount, onItemsRendered = props.onItemsRendered, userDefinedOnScroll = props.onScroll, useIsScrolling = props.useIsScrolling, rest = tslib.__rest(props, [ 'classes', 'className', 'columns', 'Components', 'data', 'defaultData', 'defaultReverseSorting', 'defaultSelectedData', 'defaultSort', 'disableHeader', 'filterLogic', 'getRowKey', 'height', 'initiallyLoading', 'loaderProps', 'noFilteredDataEmptyContentProps', 'noDataEmptyContentProps', 'onRowClick', 'onSortChange', 'onSelectedDataChange', 'reloadDependency', 'reloadSilently', 'reverseSorting', 'scrollOnTopButtonProps', 'scrollOnTopOffsetVisibility', 'selectable', 'selectColumnProps', 'sort', 'style', 'styles', 'width', 'useScrollOnTop', 'compressed', 'filterRowHeight', 'footerRowHeight', 'headerHeight', 'rowHeight', 'maximumWidth', 'maximumHeight', 'minimumWidth', 'minimumHeight', 'subtractToWidth', 'subtractToHeight', 'direction', 'itemKey', 'overscanCount', 'onItemsRendered', 'onScroll', 'useIsScrolling', ]); // ---- // Initialize the VariableSizeList ref to use ScrollOnTop // ---- var variableSizeListRef = React__namespace.useRef(null); // ---- // Use an internal State to Show/Hide ScrollOnTop Component // ---- var _a = tslib.__read(React__namespace.useState(false), 2), scrollOnTopVisible = _a[0], setScrollOnTopVisible = _a[1]; // ---- // Checker Builder // ---- var hasFilterRow = React__namespace.useMemo( function () { return columns.some(function (column) { return !!column.filter; }); }, [columns] ); var hasFooterRow = React__namespace.useMemo( function () { return columns.some(function (column) { return !!column.footer; }); }, [columns] ); var hasHeaderRow = React__namespace.useMemo( function () { return columns.some(function (column) { return !!column.header; }); }, [columns] ); // ---- // Initialize the Width Detector // ---- var _b = tslib.__read( useElementSize.useElementSize({ useDetectorWidthOnly: true, fixedHeight: userDefinedHeight, fixedWidth: userDefinedWidth, maximumWidth: maximumWidth, maximumHeight: maximumHeight, minimumWidth: minimumWidth, minimumHeight: minimumHeight, subtractToWidth: subtractToWidth, subtractToHeight: subtractToHeight, }), 2 ), widthDetector = _b[0], _c = _b[1], width = _c.width, height = _c.height; var headerHeight = React__namespace.useMemo( function () { /** If table has not header row, return 0 */ if (!hasHeaderRow) { return 0; } var baseHeaderHeight = typeof userDefinedHeaderHeight === 'number' ? userDefinedHeaderHeight : typeof rowHeight === 'number' ? rowHeight : 0; /** If table is not selectable, return the base height */ if (!selectable || hasFilterRow) { return baseHeaderHeight; } return Math.max(40, baseHeaderHeight); }, [hasFilterRow, hasHeaderRow, rowHeight, selectable, userDefinedHeaderHeight] ); var filterRowHeight = hasFilterRow ? typeof userDefinedFilterRowHeight === 'number' ? userDefinedFilterRowHeight : headerHeight : 0; var footerRowHeight = hasFooterRow ? typeof userDefinedFooterRowHeight === 'number' ? userDefinedFooterRowHeight : headerHeight : 0; // ---- // Load RxTableProps // ---- var rxTableProps = RxTable_factory.useRxTableFactory({ classes: tslib.__assign( { Body: clsx__default['default']( 'virtualized body', userDefinedClasses === null || userDefinedClasses === void 0 ? void 0 : userDefinedClasses.Body ), BodyCell: clsx__default['default']( 'virtualized cell', userDefinedClasses === null || userDefinedClasses === void 0 ? void 0 : userDefinedClasses.BodyCell ), BodyRow: clsx__default['default']( 'virtualized row', userDefinedClasses === null || userDefinedClasses === void 0 ? void 0 : userDefinedClasses.BodyRow ), BodyWrapper: clsx__default['default']( 'virtualized table virtualized-body', userDefinedClasses === null || userDefinedClasses === void 0 ? void 0 : userDefinedClasses.BodyWrapper ), ErrorCell: clsx__default['default']( 'cell error-cell', userDefinedClasses === null || userDefinedClasses === void 0 ? void 0 : userDefinedClasses.ErrorCell ), ErrorRow: clsx__default['default']( 'row error-row', userDefinedClasses === null || userDefinedClasses === void 0 ? void 0 : userDefinedClasses.ErrorRow ), Footer: clsx__default['default']( 'virtualized foot', userDefinedClasses === null || userDefinedClasses === void 0 ? void 0 : userDefinedClasses.Footer ), FooterRow: clsx__default['default']( 'virtualized row', userDefinedClasses === null || userDefinedClasses === void 0 ? void 0 : userDefinedClasses.FooterRow ), FooterWrapper: clsx__default['default']( 'virtualized table virtualized-foot', userDefinedClasses === null || userDefinedClasses === void 0 ? void 0 : userDefinedClasses.FooterWrapper ), Header: clsx__default['default']( 'virtualized head', userDefinedClasses === null || userDefinedClasses === void 0 ? void 0 : userDefinedClasses.Header ), HeaderRow: clsx__default['default']( 'virtualized row', userDefinedClasses === null || userDefinedClasses === void 0 ? void 0 : userDefinedClasses.HeaderRow ), HeaderWrapper: clsx__default['default']( 'virtualized table virtualized-head', userDefinedClasses === null || userDefinedClasses === void 0 ? void 0 : userDefinedClasses.HeaderWrapper ), LoaderCell: clsx__default['default']( 'cell loading-cell', userDefinedClasses === null || userDefinedClasses === void 0 ? void 0 : userDefinedClasses.LoaderCell ), LoaderRow: clsx__default['default']( 'row loading-row', userDefinedClasses === null || userDefinedClasses === void 0 ? void 0 : userDefinedClasses.LoaderRow ), NoContentCell: clsx__default['default']( 'cell no-content-cell', userDefinedClasses === null || userDefinedClasses === void 0 ? void 0 : userDefinedClasses.NoContentCell ), NoContentRow: clsx__default['default']( 'row no-content-row', userDefinedClasses === null || userDefinedClasses === void 0 ? void 0 : userDefinedClasses.NoContentRow ), }, userDefinedClasses ), styles: tslib.__assign( { HeaderCell: tslib.__assign( { height: headerHeight }, userDefinedStyles === null || userDefinedStyles === void 0 ? void 0 : userDefinedStyles.HeaderCell ), FilterCell: tslib.__assign( { height: filterRowHeight }, userDefinedStyles === null || userDefinedStyles === void 0 ? void 0 : userDefinedStyles.FilterCell ), FooterCell: tslib.__assign( { height: footerRowHeight }, userDefinedStyles === null || userDefinedStyles === void 0 ? void 0 : userDefinedStyles.FooterCell ), }, userDefinedStyles ), columns: columns, data: data, defaultData: defaultData, defaultLoading: initiallyLoading, defaultReverseSorting: userDefinedDefaultReverseSorting, defaultSelectedData: userDefinedSelectedData, defaultSort: userDefinedDefaultSort, filterLogic: filterLogic, getRowKey: userDefinedGetRowKey, isVirtualized: true, onRowClick: onRowClick, onSelectedDataChange: onSelectedDataChange, onSortChange: onSortChange, reloadDependency: reloadDependency, reloadSilently: reloadSilently, reverseSorting: userDefinedReverseSorting, selectable: selectable, selectColumnProps: selectColumnProps, sort: userDefinedSort, width: width, }); // ---- // Row Height Calculator // ---- var estimatedItemSize = typeof rowHeight === 'number' ? rowHeight : undefined; var getRowHeight = React__namespace.useCallback( function (index) { if (typeof rowHeight === 'number') { return rowHeight; } return rowHeight(index); }, [rowHeight] ); // ---- // Compute Table Width and Height and Accessor // ---- var tableBodyHeight = height - (!disableHeader ? headerHeight : 0) - filterRowHeight - footerRowHeight; var tableDataHeight = typeof rowHeight === 'number' ? rxTableProps.tableData.length * rowHeight : typeof estimatedItemSize === 'number' ? rxTableProps.tableData.length * estimatedItemSize : Number.MAX_SAFE_INTEGER; var effectiveBodyHeight = Math.max( 0, Math.min(tableBodyHeight, tableDataHeight) ); var effectiveTableHeight = effectiveBodyHeight + (!disableHeader ? headerHeight : 0) + filterRowHeight + footerRowHeight; // ---- // Row Key Getter // ---- var getRowKey = React__namespace.useCallback( function (index) { /** If an itemKey function exists, use it */ if (typeof itemKey === 'function') { return itemKey(index, rxTableProps.tableData); } /** Use the data selector function */ var extractedKey = rxTableProps.selection.getRowKey( rxTableProps.tableData[index], index, rxTableProps.tableData ); return extractedKey === '' ? index : extractedKey; }, [itemKey, rxTableProps.selection, rxTableProps.tableData] ); // ---- // Define Components // ---- var Components = React__namespace.useMemo( function () { return tslib.__assign( { Body: 'div', BodyCell: RxTableBodyCell, BodyRow: RxTableBodyRow, BodyWrapper: 'div', Error: RxTableError, ErrorRow: 'div', ErrorCell: 'div', Footer: 'div', FooterCell: RxTableFooterCell, FooterRow: 'div', FooterWrapper: 'div', Header: 'div', HeaderCell: RxTableHeaderCell, HeaderRow: 'div', HeaderWrapper: 'div', Loader: RxTableLoader, LoaderRow: 'div', LoaderCell: 'div', NoContent: RxTableEmptyContent, NoContentCell: 'div', NoContentRow: 'div', }, userDefinedComponents ); }, [userDefinedComponents] ); // ---- // Context Building // ---- var rxTableContext = tslib.__assign(tslib.__assign({}, rxTableProps), { Components: Components, loaderProps: loaderProps, noFilteredDataEmptyContentProps: noFilteredDataEmptyContentProps, noDataEmptyContentProps: noDataEmptyContentProps, }); // ---- // Fragments could not have properties extra from key // ---- var headerWrapperProps = Components.HeaderWrapper !== React__namespace.Fragment ? { className: rxTableProps.classes.HeaderWrapper, style: rxTableProps.styles.HeaderWrapper, } : {}; var footerWrapperProps = Components.FooterWrapper !== React__namespace.Fragment ? { className: rxTableProps.classes.FooterWrapper, style: rxTableProps.styles.FooterWrapper, } : {}; // ---- // Build a custom onScroll handler to show/hide ScrollOnTop component // ---- var handleTableScroll = React__namespace.useCallback( function (scrollProps) { /** If must use the OnScroll Component, check if this must be visible or invisible */ if (useScrollOnTop && effectiveBodyHeight > 0) { /** Get the offset */ var offset = scrollOnTopOffsetVisibility || effectiveBodyHeight * 2; /** Update only if is necessary */ if (scrollOnTopVisible !== scrollProps.scrollOffset > offset) { setScrollOnTopVisible(scrollProps.scrollOffset > offset); } } /** If a userDefinedOnScroll function exists, use it */ if (typeof userDefinedOnScroll === 'function') { userDefinedOnScroll(scrollProps); } }, [ userDefinedOnScroll, useScrollOnTop, scrollOnTopOffsetVisibility, effectiveBodyHeight, scrollOnTopVisible, ] ); var handleScrollOnTopClick = React__namespace.useCallback(function () { /** Scroll the list on top */ if (variableSizeListRef.current) { variableSizeListRef.current.scrollTo(0); } }, []); // ---- // Wrap the StateDependentRow to avoid multiple Body and BodyWrapper component nest // ---- var isShowingData = !( rxTableProps.dataState.isLoading || rxTableProps.dataState.error || !rxTableProps.tableData.length ); var BodyWrapper = Components.BodyWrapper, Body = Components.Body; var tableBodyContent = React__namespace.useMemo( function () { if (!isShowingData) { var bodyWrapperProps = BodyWrapper !== React__namespace.Fragment ? { className: rxTableProps.classes.BodyWrapper, style: rxTableProps.styles.BodyWrapper, } : {}; return React__namespace.createElement( BodyWrapper, tslib.__assign({}, bodyWrapperProps), React__namespace.createElement( Body, { style: rxTableProps.styles.Body, className: rxTableProps.classes.Body, }, React__namespace.createElement(StateDependentBodyRow, null) ) ); } return React__namespace.createElement( reactWindow.VariableSizeList, { ref: variableSizeListRef, direction: direction, itemKey: getRowKey, overscanCount: overscanCount, onItemsRendered: onItemsRendered, onScroll: useScrollOnTop || typeof userDefinedOnScroll === 'function' ? handleTableScroll : undefined, useIsScrolling: useIsScrolling, width: rxTableProps.layout.effectiveTableWidth, height: effectiveBodyHeight, itemSize: getRowHeight, estimatedItemSize: estimatedItemSize, itemCount: rxTableProps.tableData.length, outerElementType: VariableSizeListOuterElement, innerElementType: VariableSizeListInnerElement, }, MemoizedBodyRow ); }, [ isShowingData, direction, getRowKey, overscanCount, onItemsRendered, useScrollOnTop, userDefinedOnScroll, handleTableScroll, useIsScrolling, rxTableProps.layout.effectiveTableWidth, rxTableProps.tableData.length, rxTableProps.classes.BodyWrapper, rxTableProps.classes.Body, rxTableProps.styles.BodyWrapper, rxTableProps.styles.Body, effectiveBodyHeight, getRowHeight, estimatedItemSize, Body, BodyWrapper, ] ); // ---- // Build Table ClassList // ---- var wrapperClasses = clsx__default['default']( 'virtualized-table', compressed && 'compressed' ); // ---- // Build Wrapper Style // ---- var wrapperStyle = React__namespace.useMemo( function () { return isShowingData ? tslib.__assign( { height: ''.concat(effectiveTableHeight, 'px'), width: ''.concat(width, 'px'), overflow: 'auto', maxHeight: '100vh', minHeight: ''.concat( (!disableHeader ? headerHeight : 0) + filterRowHeight, 'px' ), }, style ) : tslib.__assign({}, style); }, [ effectiveTableHeight, width, style, isShowingData, disableHeader, headerHeight, filterRowHeight, ] ); // ---- // Component Render // ---- return React__namespace.createElement( React__namespace.Fragment, null, widthDetector, React__namespace.createElement( 'div', tslib.__assign({}, rest, { className: wrapperClasses, style: wrapperStyle, }), React__namespace.createElement( RxTable_context.RxTableProvider, { value: rxTableContext }, (rxTableProps.layout.hasHeaderRow || rxTableProps.layout.hasFilterRow) && React__namespace.createElement( Components.HeaderWrapper, tslib.__assign({}, headerWrapperProps), React__namespace.createElement( Components.Header, { className: rxTableProps.classes.Header, style: rxTableProps.styles.Header, }, rxTableProps.layout.hasHeaderRow && React__namespace.createElement(HeaderRow, null), rxTableProps.layout.hasFilterRow && React__namespace.createElement(FiltersRow, null) ) ), tableBodyContent, rxTableProps.layout.hasFooterRow && React__namespace.createElement( Components.FooterWrapper, tslib.__assign({}, footerWrapperProps), React__namespace.createElement( Components.Footer, { style: rxTableProps.styles.Footer, className: rxTableProps.classes.Footer, }, React__namespace.createElement(FooterRow, null) ) ), useScrollOnTop && React__namespace.createElement( ScrollOnTop, tslib.__assign({}, scrollOnTopButtonProps, { visible: scrollOnTopVisible, onClick: handleScrollOnTopClick, }) ) ) ) ); }; VirtualizedTable.displayName = 'VirtualizedTable'; module.exports = VirtualizedTable;