import PropTypes from 'prop-types'; import React, { forwardRef, useEffect, useRef, useState } from 'react'; // material-ui import { styled, useTheme } from '@mui/material/styles'; import { Box, Button, Checkbox, Chip, CircularProgress, FormControl, Grid, ListItemText, MenuItem, OutlinedInput, Pagination, Select, Stack, TableCell, TableRow, TextField, Tooltip, Typography } from '@mui/material'; // project-import import { ThemeMode } from 'config'; // third-party import { CSVLink } from 'react-csv'; import { getEmptyImage } from 'react-dnd-html5-backend'; import { useDrop, useDrag, useDragLayer } from 'react-dnd'; // assets import { CaretUpOutlined, CaretDownOutlined, DragOutlined, CheckOutlined, DownloadOutlined } from '@ant-design/icons'; // ==============================|| HEADER HEADER ||============================== // export const HeaderSort = ({ column, sort }) => { const theme = useTheme(); return ( {column.render('Header')} {!column.disableSortBy && ( )} ); }; HeaderSort.propTypes = { column: PropTypes.any, sort: PropTypes.bool }; // ==============================|| TABLE PAGINATION ||============================== // export const TablePagination = ({ gotoPage, rows, setPageSize, pageSize, pageIndex }) => { const [open, setOpen] = useState(false); const handleClose = () => { setOpen(false); }; const handleOpen = () => { setOpen(true); }; const handleChangePagination = (event, value) => { gotoPage(value - 1); }; const handleChange = (event) => { setPageSize(+event.target.value); }; return ( Row per page Go to { const page = e.target.value ? Number(e.target.value) : 0; gotoPage(page - 1); }} sx={{ '& .MuiOutlinedInput-input': { py: 0.75, px: 1.25, width: 36 } }} /> ); }; TablePagination.propTypes = { gotoPage: PropTypes.func, setPageSize: PropTypes.func, pageIndex: PropTypes.number, pageSize: PropTypes.number, rows: PropTypes.array }; // ==============================|| SELECTION - PREVIEW ||============================== // export const IndeterminateCheckbox = forwardRef(({ indeterminate, ...rest }, ref) => { const defaultRef = useRef(); const resolvedRef = ref || defaultRef; return ; }); IndeterminateCheckbox.propTypes = { indeterminate: PropTypes.bool }; export const TableRowSelection = ({ selected }) => ( <> {selected > 0 && ( )} ); TableRowSelection.propTypes = { selected: PropTypes.number }; // ==============================|| DRAG & DROP - DRAGGABLE HEADR ||============================== // export const DraggableHeader = ({ children, column, index, reorder }) => { const theme = useTheme(); const ref = useRef(); const { id, Header } = column; const DND_ITEM_TYPE = 'column'; const [{ isOverCurrent }, drop] = useDrop({ accept: DND_ITEM_TYPE, drop: (item) => { reorder(item, index); }, collect: (monitor) => ({ isOver: monitor.isOver(), isOverCurrent: monitor.isOver({ shallow: true }) }) }); const [{ isDragging }, drag, preview] = useDrag({ type: DND_ITEM_TYPE, item: () => ({ id, index, header: Header }), collect: (monitor) => ({ isDragging: monitor.isDragging() }) }); useEffect(() => { preview(getEmptyImage(), { captureDraggingState: true }); }, [preview]); drag(drop(ref)); let borderColor = theme.palette.text.primary; if (isOverCurrent) { borderColor = theme.palette.primary.main; } return ( {children} ); }; DraggableHeader.propTypes = { column: PropTypes.any, sort: PropTypes.bool, reorder: PropTypes.func, index: PropTypes.number, children: PropTypes.node }; // ==============================|| DRAG & DROP - DRAG PREVIEW ||============================== // const DragHeader = styled('div')(({ theme, x, y }) => ({ color: theme.palette.text.secondary, position: 'fixed', pointerEvents: 'none', left: 12, top: 24, transform: `translate(${x}px, ${y}px)`, opacity: 0.6 })); export const DragPreview = () => { const theme = useTheme(); const { isDragging, item, currentOffset } = useDragLayer((monitor) => ({ item: monitor.getItem(), itemType: monitor.getItemType(), initialOffset: monitor.getInitialSourceClientOffset(), currentOffset: monitor.getSourceClientOffset(), isDragging: monitor.isDragging() })); const { x, y } = currentOffset || {}; return isDragging ? ( {item.header && ( {item.header} )} ) : null; }; // ==============================|| DRAG & DROP - DRAGGABLE ROW ||============================== // export const DraggableRow = ({ index, moveRow, children }) => { const DND_ITEM_TYPE = 'row'; const dropRef = useRef(null); const dragRef = useRef(null); const [, drop] = useDrop({ accept: DND_ITEM_TYPE, hover(item, monitor) { if (!dropRef.current) { return; } const dragIndex = item.index; const hoverIndex = index; if (dragIndex === hoverIndex) { return; } const hoverBoundingRect = dropRef.current.getBoundingClientRect(); const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2; const clientOffset = monitor.getClientOffset(); const hoverClientY = clientOffset.y - hoverBoundingRect.top; if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) { return; } if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) { return; } moveRow(dragIndex, hoverIndex); item.index = hoverIndex; } }); const [{ isDragging }, drag, preview] = useDrag({ type: DND_ITEM_TYPE, item: { index }, collect: (monitor) => ({ isDragging: monitor.isDragging() }) }); const opacity = isDragging ? 0 : 1; preview(drop(dropRef)); drag(dragRef); return ( {children} ); }; DraggableRow.propTypes = { moveRow: PropTypes.func, index: PropTypes.number, children: PropTypes.node }; // ==============================|| COLUMN HIDING - SELECT ||============================== // const ITEM_HEIGHT = 48; const ITEM_PADDING_TOP = 8; const MenuProps = { PaperProps: { style: { maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP, width: 200 } } }; export const HidingSelect = ({ hiddenColumns, setHiddenColumns, allColumns }) => { const handleChange = (event) => { const { target: { value } } = event; setHiddenColumns(typeof value === 'string' ? value.split(',') : value); }; const theme = useTheme(); let visible = allColumns.filter((c) => !hiddenColumns.includes(c.id)).length; return ( ); }; HidingSelect.propTypes = { setHiddenColumns: PropTypes.func, hiddenColumns: PropTypes.array, allColumns: PropTypes.array }; // ==============================|| COLUMN SORTING - SELECT ||============================== // export const SortingSelect = ({ sortBy, setSortBy, allColumns }) => { const [sort, setSort] = useState(sortBy); const handleChange = (event) => { const { target: { value } } = event; setSort(value); setSortBy([{ id: value, desc: false }]); }; return ( ); }; SortingSelect.propTypes = { setSortBy: PropTypes.func, sortBy: PropTypes.string, allColumns: PropTypes.array }; // ==============================|| CSV EXPORT ||============================== // export const CSVExport = ({ data, filename, headers, label, style, btnLoading, onClick }) => { return ( ); }; export default CSVExport; CSVExport.propTypes = { data: PropTypes.array, headers: PropTypes.any, filename: PropTypes.string, label: PropTypes.node, style: PropTypes.object, btnLoading: PropTypes.bool, onClick: PropTypes.func }; // ==============================|| EMPTY TABLE - NO DATA ||============================== // const StyledGridOverlay = styled(Stack)(({ theme }) => ({ height: '400px', '& .ant-empty-img-1': { fill: theme.palette.mode === ThemeMode.DARK ? theme.palette.secondary[200] : theme.palette.secondary[400] }, '& .ant-empty-img-2': { fill: theme.palette.secondary.light }, '& .ant-empty-img-3': { fill: theme.palette.mode === ThemeMode.DARK ? theme.palette.secondary.A200 : theme.palette.secondary[200] }, '& .ant-empty-img-4': { fill: theme.palette.mode === ThemeMode.DARK ? theme.palette.secondary.A300 : theme.palette.secondary.A100 }, '& .ant-empty-img-5': { fillOpacity: theme.palette.mode === ThemeMode.DARK ? '0.09' : '0.95', fill: theme.palette.mode === ThemeMode.DARK ? theme.palette.secondary.darker : theme.palette.secondary.light } })); export const EmptyTable = ({ msg, colSpan }) => { return ( {msg} ); }; EmptyTable.propTypes = { msg: PropTypes.string, colSpan: PropTypes.number };