UNPKG

@coreui/react

Version:

CoreUI React 17 Bootstrap 4 components

593 lines (526 loc) 22.5 kB
import _extends from "@babel/runtime/helpers/esm/extends"; import _defineProperty from "@babel/runtime/helpers/esm/defineProperty"; function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } import React, { useState, useRef, useMemo, useEffect } from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import CPagination from '../pagination/CPagination'; import CElementCover from '../element-cover/CElementCover'; import CIcon from '@coreui/icons-react'; import { cilArrowTop, cilBan, cilFilterX } from '@coreui/icons'; import style from './CDataTable.module.css'; import './CDataTable.css'; //component - CoreUI / CTable var CDataTable = function CDataTable(props) { var _ref2; var innerRef = props.innerRef, overTableSlot = props.overTableSlot, columnHeaderSlot = props.columnHeaderSlot, sortingIconSlot = props.sortingIconSlot, columnFilterSlot = props.columnFilterSlot, noItemsViewSlot = props.noItemsViewSlot, noItemsView = props.noItemsView, captionSlot = props.captionSlot, footerSlot = props.footerSlot, underTableSlot = props.underTableSlot, theadTopSlot = props.theadTopSlot, loadingSlot = props.loadingSlot, scopedSlots = props.scopedSlots, loading = props.loading, fields = props.fields, pagination = props.pagination, activePage = props.activePage, itemsPerPage = props.itemsPerPage, items = props.items, sorter = props.sorter, header = props.header, clickableRows = props.clickableRows, columnFilter = props.columnFilter, tableFilterValue = props.tableFilterValue, tableFilter = props.tableFilter, cleaner = props.cleaner, addTableClasses = props.addTableClasses, size = props.size, dark = props.dark, striped = props.striped, hover = props.hover, border = props.border, outlined = props.outlined, responsive = props.responsive, footer = props.footer, itemsPerPageSelect = props.itemsPerPageSelect, sorterValue = props.sorterValue, columnFilterValue = props.columnFilterValue, onRowClick = props.onRowClick, onSorterValueChange = props.onSorterValueChange, onPaginationChange = props.onPaginationChange, onColumnFilterChange = props.onColumnFilterChange, onPagesChange = props.onPagesChange, onTableFilterChange = props.onTableFilterChange, onPageChange = props.onPageChange, onFilteredItemsChange = props.onFilteredItemsChange; var compData = useRef({ firstRun: true, columnFiltered: 0, changeItems: 0 }).current; // var _useState = useState(itemsPerPage), perPageItems = _useState[0], setPerPageItems = _useState[1]; var _useState2 = useState(sorterValue || {}), sorterState = _useState2[0], setSorterState = _useState2[1]; var _useState3 = useState(tableFilterValue), tableFilterState = _useState3[0], setTableFilterState = _useState3[1]; var _useState4 = useState(columnFilterValue || {}), columnFilterState = _useState4[0], setColumnFilterState = _useState4[1]; var _useState5 = useState(activePage || 1), page = _useState5[0], setPage = _useState5[1]; var _useState6 = useState(items || []), passedItems = _useState6[0], setPassedItems = _useState6[1]; // functions var cellClass = function cellClass(item, colName, index) { var classes = []; if (item._cellClasses && item._cellClasses[colName]) { classes.push(item._cellClasses[colName]); } if (fields && fields[index]._classes) { classes.push(fields[index]._classes); } return classes; }; var pretifyName = function pretifyName(name) { return name.replace(/[-_.]/g, ' ').replace(/ +/g, ' ').replace(/([a-z0-9])([A-Z])/g, '$1 $2').split(' ').map(function (word) { return word.charAt(0).toUpperCase() + word.slice(1); }).join(' '); }; var headerClass = function headerClass(i) { return fields && fields[i]._classes && fields[i]._classes; }; var isSortable = function isSortable(i) { var isDataColumn = itemsDataColumns.includes(rawColumnNames[i]); return sorter && (!fields || fields[i].sorter !== false) && isDataColumn; }; var headerStyles = function headerStyles(index) { var style = { verticalAlign: 'middle', overflow: 'hidden' }; if (isSortable(index)) { style.cursor = 'pointer'; } if (fields && fields[index] && fields[index]._style) { return _objectSpread(_objectSpread({}, style), fields[index]._style); } return style; }; var getIconState = function getIconState(index) { var direction = sorterState.asc ? 'asc' : 'desc'; return rawColumnNames[index] === sorterState.column ? direction : 0; }; var iconClasses = function iconClasses(index) { var state = getIconState(index); return ['position-absolute', style['icon-transition'], style['arrow-position'], !state && style['transparent'], state === 'desc' && style['rotate-icon']]; }; var rowClicked = function rowClicked(item, index, e, detailsClick) { if (detailsClick === void 0) { detailsClick = false; } onRowClick && onRowClick(item, index, getClickedColumnName(e, detailsClick), e); }; var changeSort = function changeSort(column, index) { if (!isSortable(index)) { return; } //if column changed or sort was descending change asc to true var state = sorterState; var columnRepeated = state.column === column; if (!sorter || !sorter.resetable) { state.column = column; } else { state.column = columnRepeated && state.asc === false ? null : column; } state.asc = !(columnRepeated && state.asc); setSorterState(_objectSpread({}, state)); }; useEffect(function () { onSorterValueChange && onSorterValueChange(sorterState); }, [JSON.stringify(sorterState)]); var paginationChange = function paginationChange(e) { onPaginationChange && onPaginationChange(Number(e.target.value)); !itemsPerPageSelect.external && setPerPageItems(Number(e.target.value)); }; var columnFilterEvent = function columnFilterEvent(colName, value, type) { var _objectSpread2; var isLazy = columnFilter && columnFilter.lazy === true; if (isLazy && type === 'input' || !isLazy && type === 'change') { return; } var newState = _objectSpread(_objectSpread({}, columnFilterState), {}, (_objectSpread2 = {}, _objectSpread2["" + colName] = value, _objectSpread2)); setColumnFilterState(newState); }; useEffect(function () { onColumnFilterChange && onColumnFilterChange(columnFilterState); }, [JSON.stringify(columnFilterState)]); var tableFilterChange = function tableFilterChange(value, type) { var isLazy = tableFilter && tableFilter.lazy === true; if (isLazy && type === 'input' || !isLazy && type === 'change') { return; } setTableFilterState(value); }; useEffect(function () { onTableFilterChange && onTableFilterChange(tableFilterState); }, [tableFilterState]); var getClickedColumnName = function getClickedColumnName(e, detailsClick) { if (detailsClick) { return 'details'; } else { var children = Array.from(e.target.closest('tr').children); var clickedCell = children.filter(function (child) { return child.contains(e.target); })[0]; return rawColumnNames[children.indexOf(clickedCell)]; } }; var clean = function clean() { setTableFilterState(''); setColumnFilterState({}); setSorterState({ column: "", asc: true }); }; // computed var genCols = Object.keys(passedItems[0] || {}).filter(function (el) { return el.charAt(0) !== '_'; }); var rawColumnNames = fields ? fields.map(function (el) { return el.key || el; }) : genCols; var itemsDataColumns = rawColumnNames.filter(function (name) { return genCols.includes(name); }); useMemo(function () { compData.columnFiltered++; }, [JSON.stringify(columnFilter), JSON.stringify(columnFilterState), itemsDataColumns.join(''), compData.changeItems]); var columnFiltered = useMemo(function () { var items = passedItems; if (columnFilter && columnFilter.external) { return items; } Object.entries(columnFilterState).forEach(function (_ref) { var key = _ref[0], value = _ref[1]; var columnFilter = String(value).toLowerCase(); if (columnFilter && itemsDataColumns.includes(key)) { items = items.filter(function (item) { return String(item[key]).toLowerCase().includes(columnFilter); }); } }); return items; }, [compData.columnFiltered]); var tableFiltered = useMemo(function () { var items = columnFiltered; if (!tableFilterState || tableFilter && tableFilter.external) { return items; } var filter = tableFilterState.toLowerCase(); var valueContainFilter = function valueContainFilter(val) { return String(val).toLowerCase().includes(filter); }; items = items.filter(function (item) { return !!itemsDataColumns.find(function (key) { return valueContainFilter(item[key]); }); }); return items; }, [compData.columnFiltered, tableFilterState, JSON.stringify(tableFilter)]); var sortedItems = useMemo(function () { var col = sorterState.column; if (!col || !itemsDataColumns.includes(col) || sorter && sorter.external) { return tableFiltered; } //if values in column are to be sorted by numeric value they all have to be type number var flip = sorterState.asc ? 1 : -1; var sorted = tableFiltered.slice().sort(function (item, item2) { var value = item[col]; var value2 = item2[col]; var a = typeof value === 'number' ? value : String(value).toLowerCase(); var b = typeof value2 === 'number' ? value2 : String(value2).toLowerCase(); return a > b ? 1 * flip : b > a ? -1 * flip : 0; }); return sorted; }, [JSON.stringify(tableFiltered), JSON.stringify(sorterState), JSON.stringify(sorter)]); useEffect(function () { !compData.firstRun && onFilteredItemsChange && onFilteredItemsChange(sortedItems); }, [JSON.stringify(sortedItems)]); var tableClasses = ['table', (_ref2 = {}, _ref2["table-" + size] = size, _ref2['table-dark'] = dark, _ref2['table-striped'] = striped, _ref2['table-hover'] = hover, _ref2['table-bordered'] = border, _ref2['border'] = outlined, _ref2), addTableClasses]; var columnNames = useMemo(function () { if (fields) { return fields.map(function (f) { return f.label !== undefined ? f.label : pretifyName(f.key || f); }); } return rawColumnNames.map(function (el) { return pretifyName(el); }); }, [fields, rawColumnNames]); var sortingIconStyles = sorter && 'position-relative pr-4'; var colspan = rawColumnNames.length; var totalPages = Math.ceil(sortedItems.length / perPageItems) || 1; useMemo(function () { !compData.firstRun && onPagesChange && onPagesChange(totalPages); }, [totalPages]); var computedPage = useMemo(function () { var compPage = pagination ? page : activePage; !compData.firstRun && onPageChange && onPageChange(compPage); return compPage; }, [page, activePage, pagination]); var firstItemIndex = (computedPage - 1) * perPageItems || 0; var paginatedItems = sortedItems.slice(firstItemIndex, firstItemIndex + perPageItems); var currentItems = computedPage ? paginatedItems : sortedItems; var tableFilterData = { label: tableFilter && tableFilter.label || 'Filter:', placeholder: tableFilter && tableFilter.placeholder || 'type string...' }; var paginationSelect = { label: itemsPerPageSelect && itemsPerPageSelect.label || 'Items per page:', values: itemsPerPageSelect && itemsPerPageSelect.values || [5, 10, 20, 50] }; var noItemsText = function () { var customValues = noItemsView || {}; if (passedItems.length) { return customValues.noResults || 'No filtering results'; } return customValues.noItems || 'No items'; }(); var isFiltered = tableFilterState || sorterState.column || Object.values(columnFilterState).join(''); var cleanerProps = { content: cilFilterX, className: "mfs-2 " + (isFiltered ? 'text-danger' : 'transparent'), role: isFiltered ? 'button' : null, tabIndex: isFiltered ? 0 : null }; // watch useMemo(function () { return setPerPageItems(itemsPerPage); }, [itemsPerPage]); useMemo(function () { return setSorterState(_objectSpread({}, sorterValue)); }, [sorterValue]); useMemo(function () { return setTableFilterState(tableFilterValue); }, [tableFilterValue]); useMemo(function () { return setColumnFilterState(_objectSpread({}, columnFilterValue)); }, [columnFilterValue]); //items useMemo(function () { if (items && !compData.firstRun && (items.length !== passedItems.length || JSON.stringify(items) !== JSON.stringify(passedItems))) { setPassedItems(items); compData.changeItems++; } }); // render compData.firstRun = false; var paginationProps = typeof pagination === 'object' ? pagination : null; var headerContent = /*#__PURE__*/React.createElement("tr", null, columnNames.map(function (name, index) { return /*#__PURE__*/React.createElement("th", { onClick: function onClick() { changeSort(rawColumnNames[index], index); }, className: classNames([headerClass(index), sortingIconStyles]), style: headerStyles(index), key: index }, columnHeaderSlot["" + rawColumnNames[index]] || /*#__PURE__*/React.createElement("div", { className: "d-inline" }, name), isSortable(index) && (sortingIconSlot && sortingIconSlot(getIconState(index), iconClasses(index)) || /*#__PURE__*/React.createElement(CIcon, { customClasses: classNames(iconClasses(index)), width: 18, content: cilArrowTop }))); })); return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", { ref: innerRef }, (itemsPerPageSelect || tableFilter || cleaner) && /*#__PURE__*/React.createElement("div", { className: "row my-2 mx-0" }, (tableFilter || cleaner) && /*#__PURE__*/React.createElement("div", { className: "col-sm-6 form-inline p-0 c-datatable-filter" }, tableFilter && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("label", { className: "mfe-2" }, tableFilterData.label), /*#__PURE__*/React.createElement("input", { className: "form-control", type: "text", placeholder: tableFilterData.placeholder, onInput: function onInput(e) { tableFilterChange(e.target.value, 'input'); }, onChange: function onChange(e) { tableFilterChange(e.target.value, 'change'); }, value: tableFilterState || '', "aria-label": "table filter input" })), cleaner && (typeof cleaner === 'function' ? cleaner(clean, isFiltered, cleanerProps) : /*#__PURE__*/React.createElement(CIcon, _extends({}, cleanerProps, { onClick: clean, onKeyUp: function onKeyUp(event) { if (event.key === 'Enter') clean(); } })))), itemsPerPageSelect && /*#__PURE__*/React.createElement("div", { className: "col-sm-6 p-0 " + (tableFilter || cleaner ? '' : 'offset-sm-6') }, /*#__PURE__*/React.createElement("div", { className: "form-inline justify-content-sm-end c-datatable-items-per-page" }, /*#__PURE__*/React.createElement("label", { className: "mfe-2" }, paginationSelect.label), /*#__PURE__*/React.createElement("select", { className: "form-control", onChange: paginationChange, "aria-label": "changes number of visible items", value: perPageItems }, paginationSelect.values.map(function (number, key) { return /*#__PURE__*/React.createElement("option", { val: number, key: key }, number); })))))), overTableSlot, /*#__PURE__*/React.createElement("div", { className: "position-relative " + (responsive && 'table-responsive') }, /*#__PURE__*/React.createElement("table", { className: classNames(tableClasses) }, /*#__PURE__*/React.createElement("thead", null, theadTopSlot, header && headerContent, columnFilter && /*#__PURE__*/React.createElement("tr", { className: "table-sm" }, rawColumnNames.map(function (colName, index) { return /*#__PURE__*/React.createElement("th", { className: classNames(headerClass(index)), key: index }, columnFilterSlot["" + rawColumnNames[index]] || (!fields || fields[index].filter !== false) && /*#__PURE__*/React.createElement("input", { className: "form-control form-control-sm", onInput: function onInput(e) { columnFilterEvent(colName, e.target.value, 'input'); }, onChange: function onChange(e) { columnFilterEvent(colName, e.target.value, 'change'); }, value: columnFilterState[colName] || '', "aria-label": "column name: '" + colName + "' filter input" })); }))), /*#__PURE__*/React.createElement("tbody", { style: clickableRows && { cursor: 'pointer' } }, currentItems.map(function (item, itemIndex) { return /*#__PURE__*/React.createElement(React.Fragment, { key: itemIndex }, /*#__PURE__*/React.createElement("tr", { className: classNames(item._classes), tabIndex: clickableRows && 0, onClick: function onClick(e) { rowClicked(item, itemIndex + firstItemIndex, e); } }, rawColumnNames.map(function (colName, index) { return scopedSlots[colName] && /*#__PURE__*/React.cloneElement(scopedSlots[colName](item, itemIndex + firstItemIndex), { 'key': index }) || /*#__PURE__*/React.createElement("td", { className: classNames(cellClass(item, colName, index)), key: index }, String(item[colName])); })), scopedSlots.details && /*#__PURE__*/React.createElement("tr", { onClick: function onClick(e) { rowClicked(item, itemIndex + firstItemIndex, e, true); }, className: "p-0", style: { border: 'none !important' }, key: 'details' + itemIndex }, /*#__PURE__*/React.createElement("td", { colSpan: colspan, className: "p-0", style: { border: 'none !important' } }, scopedSlots.details(item, itemIndex + firstItemIndex)))); }), !currentItems.length && /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("td", { colSpan: colspan }, noItemsViewSlot || /*#__PURE__*/React.createElement("div", { className: "text-center my-5" }, /*#__PURE__*/React.createElement("h2", null, noItemsText + ' ', /*#__PURE__*/React.createElement(CIcon, { width: "30", name: "cilBan", content: cilBan, className: "text-danger mb-2" })))))), footer && currentItems.length > 0 && /*#__PURE__*/React.createElement("tfoot", null, headerContent), footerSlot, captionSlot), loading && (loadingSlot || /*#__PURE__*/React.createElement(CElementCover, { boundaries: [{ sides: ['top'], query: 'td' }, { sides: ['bottom'], query: 'tbody' }] }))), underTableSlot, pagination && /*#__PURE__*/React.createElement(CPagination, _extends({ style: { display: totalPages > 1 ? 'inline' : 'none' }, onActivePageChange: function onActivePageChange(page) { setPage(page); }, pages: totalPages, activePage: page }, paginationProps))); }; CDataTable.propTypes = { // innerRef: PropTypes.oneOfType([PropTypes.object, PropTypes.func]), overTableSlot: PropTypes.node, columnHeaderSlot: PropTypes.object, sortingIconSlot: PropTypes.func, columnFilterSlot: PropTypes.object, noItemsViewSlot: PropTypes.node, noItemsView: PropTypes.object, captionSlot: PropTypes.node, footerSlot: PropTypes.node, underTableSlot: PropTypes.node, scopedSlots: PropTypes.object, theadTopSlot: PropTypes.node, loadingSlot: PropTypes.node, loading: PropTypes.bool, fields: PropTypes.array, pagination: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]), activePage: PropTypes.number, itemsPerPage: PropTypes.number, items: PropTypes.array, sorter: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]), clickableRows: PropTypes.bool, columnFilter: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]), tableFilterValue: PropTypes.string, tableFilter: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]), cleaner: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]), addTableClasses: PropTypes.oneOfType([PropTypes.string, PropTypes.array, PropTypes.object]), size: PropTypes.string, dark: PropTypes.bool, striped: PropTypes.bool, hover: PropTypes.bool, border: PropTypes.bool, outlined: PropTypes.bool, responsive: PropTypes.bool, footer: PropTypes.bool, itemsPerPageSelect: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]), sorterValue: PropTypes.object, columnFilterValue: PropTypes.object, header: PropTypes.bool, onRowClick: PropTypes.func, onSorterValueChange: PropTypes.func, onPaginationChange: PropTypes.func, onColumnFilterChange: PropTypes.func, onPagesChange: PropTypes.func, onTableFilterChange: PropTypes.func, onPageChange: PropTypes.func, onFilteredItemsChange: PropTypes.func }; CDataTable.defaultProps = { itemsPerPage: 10, responsive: true, columnHeaderSlot: {}, columnFilterSlot: {}, scopedSlots: {}, sorterValue: {}, header: true }; export default CDataTable;