UNPKG

@plone/volto

Version:
407 lines (389 loc) 12.8 kB
/** * Contents item component. * @module components/manage/Contents/ContentsItem */ import React from 'react'; import { Button, Table, Menu, Divider } from 'semantic-ui-react'; import { Link } from 'react-router-dom'; import PropTypes from 'prop-types'; import map from 'lodash/map'; import { useIntl, defineMessages, FormattedMessage } from 'react-intl'; import Circle from '@plone/volto/components/manage/Contents/circle'; import FormattedDate from '@plone/volto/components/theme/FormattedDate/FormattedDate'; import Icon from '@plone/volto/components/theme/Icon/Icon'; import Popup from '@plone/volto/components/theme/Popup/Popup'; import { getContentIcon } from '@plone/volto/helpers/Content/Content'; import moreSVG from '@plone/volto/icons/more.svg'; import checkboxUncheckedSVG from '@plone/volto/icons/checkbox-unchecked.svg'; import checkboxCheckedSVG from '@plone/volto/icons/checkbox-checked.svg'; import cutSVG from '@plone/volto/icons/cut.svg'; import deleteSVG from '@plone/volto/icons/delete.svg'; import copySVG from '@plone/volto/icons/copy.svg'; import showSVG from '@plone/volto/icons/show.svg'; import moveUpSVG from '@plone/volto/icons/move-up.svg'; import moveDownSVG from '@plone/volto/icons/move-down.svg'; import editingSVG from '@plone/volto/icons/editing.svg'; import dragSVG from '@plone/volto/icons/drag.svg'; import cx from 'classnames'; import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable'; const messages = defineMessages({ private: { id: 'private', defaultMessage: 'Private', }, pending: { id: 'pending', defaultMessage: 'Pending', }, published: { id: 'published', defaultMessage: 'Published', }, intranet: { id: 'intranet', defaultMessage: 'Intranet', }, draft: { id: 'draft', defaultMessage: 'Draft', }, no_workflow_state: { id: 'no workflow state', defaultMessage: 'No workflow state', }, none: { id: 'Not available', defaultMessage: 'None', }, }); function getColor(string) { switch (string) { case 'private': return '#ed4033'; case 'published': return '#007bc1'; case 'intranet': return '#51aa55'; case 'draft': return '#f6a808'; default: return 'grey'; } } /** * Contents item component class. * @function ContentsItemComponent * @returns {string} Markup of the component. */ export const ContentsItemComponent = ({ item, selected, onClick, indexes, onCut, onCopy, onDelete, onMoveToTop, onMoveToBottom, connectDragPreview, connectDragSource, connectDropTarget, isDragging, order, }) => { const intl = useIntl(); return connectDropTarget( connectDragPreview( <tr key={item['@id']} className={cx('', { 'dragging-row': isDragging })} aria-label={item['@id']} > <Table.Cell className={cx('', { 'dragging-cell': isDragging })}> {connectDragSource( <div style={{ display: 'inline-block' }}> <Button icon basic> <Icon name={dragSVG} size="20px" color="#878f93" className="content drag handle" /> </Button> </div>, )} </Table.Cell> <Table.Cell className={cx('', { 'dragging-cell': isDragging })}> {selected ? ( <Button icon basic aria-label="Unchecked" onClick={(e) => onClick(e, item['@id'])} > <Icon name={checkboxCheckedSVG} color="#007eb1" size="24px" className="checked" /> </Button> ) : ( <Button icon basic aria-label="Checked" onClick={(e) => onClick(e, item['@id'])} > <Icon name={checkboxUncheckedSVG} color="#826a6a" size="24px" className="unchecked" /> </Button> )} </Table.Cell> <Table.Cell className={cx('', { 'dragging-cell': isDragging })}> <Link className="icon-align-name" to={`${item['@id']}${item.is_folderish ? '/contents' : ''}`} > <div className="expire-align"> <Icon name={getContentIcon(item['@type'], item.is_folderish)} size="20px" className="icon-margin" color="#878f93" title={item['Type'] || item['@type']} />{' '} <span title={item.title}> {item.title}</span> </div> {item.ExpirationDate !== 'None' && new Date(item.ExpirationDate).getTime() < new Date().getTime() && ( <Button className="button-margin" size="mini"> <FormattedMessage id="Expired" defaultMessage="Expired" /> </Button> )} {item.EffectiveDate !== 'None' && new Date(item.EffectiveDate).getTime() > new Date().getTime() && ( <Button className="button-margin effective-future" size="mini"> <FormattedMessage id="Scheduled" defaultMessage="Scheduled" /> </Button> )} </Link> </Table.Cell> {map(indexes, (index) => ( <Table.Cell className={cx('', { 'dragging-cell': isDragging })} key={index.id} > {index.type === 'boolean' && (item[index.id] ? ( <FormattedMessage id="Yes" defaultMessage="Yes" /> ) : ( <FormattedMessage id="No" defaultMessage="No" /> ))} {index.type === 'string' && index.id !== 'review_state' && item[index.id]} {index.id === 'review_state' && ( <div> <span> <Circle color={getColor(item[index.id])} size="15px" /> </span> {messages[item[index.id]] ? intl.formatMessage(messages[item[index.id]]) : item['review_title'] || item['review_state'] || intl.formatMessage(messages.no_workflow_state)} </div> )} {index.type === 'date' && ( <> {item[index?.id] && item[index.id] !== 'None' ? ( <FormattedDate date={item[index.id]} /> ) : ( intl.formatMessage(messages.none) )} </> )} {index.type === 'array' && ( <span>{item[index.id]?.join(', ')}</span> )} </Table.Cell> ))} <Table.Cell className={cx('', { 'dragging-cell': isDragging })} textAlign="right" > <Popup menu={true} position="bottom right" flowing={true} basic={true} on="click" popper={{ className: 'dropdown-popup', }} trigger={ <Icon name={moreSVG} className="dropdown-popup-trigger" size="24px" color="#007eb1" /> } > <Menu vertical borderless fluid> <Link className="item icon-align" to={`${item['@id']}/edit`}> <Icon name={editingSVG} color="#007eb1" size="24px" />{' '} <FormattedMessage id="Edit" defaultMessage="Edit" /> </Link> <Link className="item right-dropdown icon-align" to={item['@id']}> <Icon name={showSVG} color="#007eb1" size="24px" />{' '} <FormattedMessage id="View" defaultMessage="View" /> </Link> <Divider /> <Menu.Item onClick={onCut} value={item['@id']} className="right-dropdown icon-align" > <Icon name={cutSVG} color="#007eb1" size="24px" />{' '} <FormattedMessage id="Cut" defaultMessage="Cut" /> </Menu.Item> <Menu.Item onClick={onCopy} value={item['@id']} className="right-dropdown icon-align" > <Icon name={copySVG} color="#007eb1" size="24px" />{' '} <FormattedMessage id="Copy" defaultMessage="Copy" /> </Menu.Item> <Menu.Item onClick={onDelete} value={item['@id']} className="right-dropdown icon-align" > <Icon name={deleteSVG} color="#e40166" size="24px" />{' '} <FormattedMessage id="Delete" defaultMessage="Delete" /> </Menu.Item> <Divider /> <Menu.Item onClick={onMoveToTop} value={order} className="right-dropdown icon-align" > <Icon name={moveUpSVG} color="#007eb1" size="24px" />{' '} <FormattedMessage id="Move to top of folder" defaultMessage="Move to top of folder" /> </Menu.Item> <Menu.Item onClick={onMoveToBottom} value={order} className="right-dropdown icon-align" > <Icon name={moveDownSVG} color="#007eb1" size="24px" />{' '} <FormattedMessage id="Move to bottom of folder" defaultMessage="Move to bottom of folder" /> </Menu.Item> </Menu> </Popup> </Table.Cell> </tr>, ), ); }; /** * Property types. * @property {Object} propTypes Property types. * @static */ ContentsItemComponent.propTypes = { item: PropTypes.shape({ '@id': PropTypes.string, title: PropTypes.string, is_folderish: PropTypes.bool, '@type': PropTypes.string, }).isRequired, selected: PropTypes.bool.isRequired, onClick: PropTypes.func.isRequired, indexes: PropTypes.arrayOf( PropTypes.shape({ id: PropTypes.string, type: PropTypes.string, }), ).isRequired, onCut: PropTypes.func.isRequired, onCopy: PropTypes.func.isRequired, onDelete: PropTypes.func.isRequired, onMoveToTop: PropTypes.func.isRequired, onMoveToBottom: PropTypes.func.isRequired, connectDragPreview: PropTypes.func.isRequired, connectDragSource: PropTypes.func.isRequired, connectDropTarget: PropTypes.func.isRequired, isDragging: PropTypes.bool.isRequired, order: PropTypes.number.isRequired, onOrderItem: PropTypes.func.isRequired, }; const DragDropConnector = (props) => { const { DropTarget, DragSource } = props.reactDnd; const DndConnectedContentsItem = React.useMemo( () => DropTarget( 'item', { hover(props, monitor) { const id = monitor.getItem().id; const dragOrder = monitor.getItem().order; const hoverOrder = props.order; if (dragOrder === hoverOrder) { return; } props.onOrderItem(id, dragOrder, hoverOrder - dragOrder, false); monitor.getItem().order = hoverOrder; }, drop(props, monitor) { const id = monitor.getItem().id; const dragOrder = monitor.getItem().startOrder; const dropOrder = props.order; if (dragOrder === dropOrder) { return; } props.onOrderItem(id, dragOrder, dropOrder - dragOrder, true); monitor.getItem().order = dropOrder; }, }, (connect) => ({ connectDropTarget: connect.dropTarget(), }), )( DragSource( 'item', { beginDrag(props) { return { id: props.item['@id'], order: props.order, startOrder: props.order, }; }, }, (connect, monitor) => ({ connectDragSource: connect.dragSource(), connectDragPreview: connect.dragPreview(), isDragging: monitor.isDragging(), }), )(ContentsItemComponent), ), [DragSource, DropTarget], ); return <DndConnectedContentsItem {...props} />; }; export default injectLazyLibs('reactDnd')(DragDropConnector);