UNPKG

@eureca/eureca-ui

Version:

UI component library of Eureca's user and admin apps

348 lines (319 loc) 8.83 kB
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 };