@plone/volto
Version:
Volto
363 lines (353 loc) • 11 kB
JSX
/**
* 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,
order,
dndKitSortable,
dndKitUtilities,
}) => {
const intl = useIntl();
const { useSortable } = dndKitSortable;
const { CSS } = dndKitUtilities;
const {
attributes,
listeners,
setNodeRef,
transform,
transition,
isDragging,
} = useSortable({
id: item['@id'],
data: {
type: 'item',
id: item['@id'],
},
});
const style = {
transform: CSS.Transform.toString(transform),
transition,
};
return (
<tr
key={item['@id']}
ref={setNodeRef}
style={style}
className={cx('', { 'dragging-row': isDragging })}
aria-label={item['@id']}
>
<Table.Cell className={cx('', { 'dragging-cell': isDragging })}>
<div style={{ display: 'inline-block' }}>
<Button icon basic {...attributes} {...listeners}>
<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 expired-past" 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>
)}
{item.is_working_copy && (
<Button className="button-margin working-copy" size="mini">
<FormattedMessage
id="Working copy"
defaultMessage="Working copy"
/>
</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,
order: PropTypes.number.isRequired,
};
export default injectLazyLibs(['dndKitSortable', 'dndKitUtilities'])(
ContentsItemComponent,
);