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 (
}
renderValue={(selected) => {
if (selected.length === 0) {
return All columns visible;
}
if (selected.length > 0 && selected.length === allColumns.length) {
return All columns visible;
}
return {visible} column(s) visible;
}}
MenuProps={MenuProps}
size="small"
>
{allColumns.map((column) => {
let ToggleChecked = column.id === '#' ? true : hiddenColumns.indexOf(column.id) > -1 ? false : true;
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 (
}
renderValue={(selected) => {
const selectedColumn = allColumns.filter((column) => column.id === selected)[0];
if (!selected) {
return Sort By;
}
return (
Sort by ({typeof selectedColumn.Header === 'string' ? selectedColumn.Header : selectedColumn?.title})
);
}}
size="small"
>
{allColumns
.filter((column) => column.canSort)
.map((column) => (
))}
);
};
SortingSelect.propTypes = {
setSortBy: PropTypes.func,
sortBy: PropTypes.string,
allColumns: PropTypes.array
};
// ==============================|| CSV EXPORT ||============================== //
export const CSVExport = ({ data, filename, headers, label, style, btnLoading, onClick }) => {
return (
}
variant={btnLoading ? 'outlined' : 'contained'}
sx={{ ...style }}
disabled={btnLoading}
onClick={(e) => {
onClick?.(e);
}}
>
{btnLoading ? : label || 'Download'}
);
};
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
};