@eureca/eureca-ui
Version:
UI component library of Eureca's user and admin apps
348 lines (319 loc) • 8.83 kB
JavaScript
import React, { useState, useEffect, useCallback } from 'react';
import styled from 'styled-components';
import PropTypes from 'prop-types';
import { format } from 'date-fns';
import { v4 as uuid } from 'uuid';
import { FiMenu, FiEye, FiEyeOff, FiMinusCircle } from 'react-icons/fi';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
import { Grid, Typography, Box, InputBase as Input, MenuItem, Menu } from '@material-ui/core';
import { Flex } from '../Flex';
import { DatePicker } from '../Pickers';
import { colors } from '../../theme/colors';
const tableColumns = [
{
id: 'description',
label: 'Descrição',
width: 4,
},
{
id: 'deadline',
label: 'Prazo',
width: 2,
},
{
id: 'phase',
label: 'Fase',
width: 2,
},
{
id: 'show',
label: 'Mostrar/Esconder',
width: 3,
},
];
const StyledRow = styled(Grid)`
box-sizing: border-box;
color: ${colors.gray2};
background-color: ${colors.white};
padding: 8px 16px;
border-top: 1px solid ${colors.e0e0e0};
min-height: 48px;
`;
const styles = {
container: {
width: '100%',
boxShadow: '0px 2px 16px rgba(0, 0, 0, 0.03)',
},
header: {
boxSizing: 'border-box',
backgroundColor: colors.white,
padding: '16px',
color: colors.gray3,
},
};
function Header() {
return (
<Grid container spacing={1} style={styles.header}>
<Grid item xs={1} />
{tableColumns.map((column, index) => (
<Grid item xs={column.width} key={column.id}>
<Typography>{column.label}</Typography>
</Grid>
))}
</Grid>
);
}
function Picker({ value, onChange }) {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<Flex justifyCenter onClick={() => setIsOpen(true)} w={1} style={{ cursor: 'text' }}>
<Typography style={{ fontSize: '1rem' }}>
{typeof value === 'object' && format(value, 'dd/MM/yy')}
</Typography>
</Flex>
<DatePicker
open={isOpen}
minDate={new Date()}
onOpen={() => setIsOpen(true)}
onClose={() => setIsOpen(false)}
value={value}
onChange={onChange}
style={{ display: 'none' }}
/>
</>
);
}
function Select({ value, onChange, phaseOptions }) {
const [anchorEl, setAnchorEl] = useState(null);
function handleClickItem(item) {
onChange(item.value);
setAnchorEl(null);
}
return (
<>
<Flex justifyCenter onClick={event => setAnchorEl(event.currentTarget)} w={1} cursorPointer>
<Typography style={{ fontSize: '1rem' }}>{value}</Typography>
</Flex>
<Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={() => setAnchorEl(null)}
data-testid="options-menu"
>
{phaseOptions.map(item => (
<MenuItem
key={item.id}
onClick={() => handleClickItem(item)}
data-testid={`option-${item.value}`}
>
{item.label}
</MenuItem>
))}
</Menu>
</>
);
}
function Row({ index, row, handleChange, provided, toggleVisibility, removeRow, phaseOptions }) {
return (
<StyledRow
container
spacing={1}
alignItems="center"
{...provided.draggableProps}
ref={provided.innerRef}
>
<Grid item xs={1} {...provided.dragHandleProps}>
<Flex alignCenter justifyCenter height="100%">
<FiMenu color={colors.gray4} size={22} />
</Flex>
</Grid>
<Grid item xs={4}>
<Input
type="text"
value={row.description}
onChange={e => handleChange('description', index, e.target.value)}
/>
</Grid>
<Grid item xs={2}>
<Picker value={row.deadline} onChange={value => handleChange('deadline', index, value)} />
</Grid>
<Grid item xs={2}>
<Select
value={row.phase}
phaseOptions={phaseOptions}
onChange={value => handleChange('phase', index, value)}
/>
</Grid>
<Grid
item
xs={2}
style={{ cursor: 'pointer' }}
data-testid="visibility-testid"
onClick={() => toggleVisibility(index)}
>
<Flex directionRow alignCenter height={1}>
{row.show ? (
<FiEye color={colors.green1} size={24} />
) : (
<FiEyeOff color={colors.gray2} size={24} />
)}
<Box ml={1}>
<Typography style={{ color: row.show ? colors.gray2 : colors.gray3 }}>
{row.show ? 'Visível' : 'Invisível'}
</Typography>
</Box>
</Flex>
</Grid>
<Grid item xs={1}>
<Flex cursorPointer p={1} onClick={() => removeRow(row)} title="Remover">
<FiMinusCircle color={colors.gray2} size={22} />
</Flex>
</Grid>
</StyledRow>
);
}
function AddRow({ provided, addRow }) {
return (
<StyledRow
container
spacing={1}
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<Grid item xs={4} onClick={addRow}>
<Typography style={{ color: colors.green1, cursor: 'pointer' }}>
+ Adicionar nova data
</Typography>
</Grid>
</StyledRow>
);
}
const CronogramTable = ({ initialData, onChange, phaseOptions, boxProps }) => {
const [data, setData] = useState(initialData);
function reorder(list, startIndex, endIndex) {
const result = Array.from(list);
const [removed] = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
}
function onDragEnd(result) {
// Dropped outside the list
if (!result.destination) {
return;
}
const items = reorder(data, result.source.index, result.destination.index);
setData([...items]);
}
function toggleVisibility(index) {
const copyData = [...data];
const visibility = copyData[index].show;
copyData[index].show = !visibility;
setData([...copyData]);
}
function handleChange(column, index, value) {
const copyData = [...data];
copyData[index][column] = value;
setData([...copyData]);
}
function addRow() {
const emptyRow = {
description: 'Descrição',
deadline: new Date(),
phase: 'Fase',
show: false,
id: uuid(),
};
setData([...data, emptyRow]);
}
function removeRow(row) {
setData(data.filter(i => i !== row));
}
const callback = useCallback(onChange, [onChange]);
useEffect(() => {
callback(data);
}, [callback, data]);
return (
<Box overflow="hidden" {...boxProps}>
<DragDropContext onDragEnd={onDragEnd}>
<Header />
<Droppable droppableId="droppable">
{provided => (
<Box {...provided.droppableProps} ref={provided.innerRef} style={styles.container}>
{data.map((row, index) => {
return (
<Draggable key={row.id} draggableId={`${row.id}-id`} index={index}>
{provided => (
<Row
index={index}
row={row}
handleChange={handleChange}
provided={provided}
toggleVisibility={toggleVisibility}
removeRow={removeRow}
phaseOptions={phaseOptions}
/>
)}
</Draggable>
);
})}
<Draggable
key={data.length + 1}
draggableId={`${data.length + 1}-id`}
index={data.length}
isDragDisabled
>
{provided => <AddRow provided={provided} addRow={addRow} />}
</Draggable>
{provided.placeholder}
</Box>
)}
</Droppable>
</DragDropContext>
</Box>
);
};
CronogramTable.propTypes = {
initialData: PropTypes.array,
onChange: PropTypes.func,
phaseOptions: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
label: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
})
),
boxProps: PropTypes.object,
};
CronogramTable.defaultProps = {
initialData: [
{
description: '',
deadline: new Date(),
phase: 'Online',
show: true,
id: 1,
},
],
onChange: () => {},
phaseOptions: [
{
id: 1,
label: 'Online',
value: 'Online',
},
{
id: 2,
label: 'Presencial',
value: 'Presencial',
},
{
id: 3,
label: 'Geral',
value: 'Geral',
},
],
boxProps: {},
};
export { CronogramTable };