UNPKG

decap-cms-core

Version:

Decap CMS core application, see decap-cms package for the main distribution.

292 lines (266 loc) 8.66 kB
import PropTypes from 'prop-types'; import React from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { css } from '@emotion/react'; import styled from '@emotion/styled'; import dayjs from 'dayjs'; import { translate } from 'react-polyglot'; import { colors, lengths } from 'decap-cms-ui-default'; import { status } from '../../constants/publishModes'; import { DragSource, DropTarget, HTML5DragDrop } from '../UI'; import WorkflowCard from './WorkflowCard'; import { selectEntryCollectionTitle } from '../../reducers/collections'; const WorkflowListContainer = styled.div` min-height: 60%; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; @media (min-width: 500px) { gap: 14px; } @media (min-width: 800px) { gap: 40px; } `; const WorkflowListContainerOpenAuthoring = styled.div` min-height: 60%; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; @media (min-width: 500px) { gap: 14px; } @media (min-width: 800px) { gap: 40px; } `; const styles = { columnPosition: idx => idx > 0 && css` &:before { content: ''; display: block; position: absolute; width: 2px; height: 80%; top: 36px; background-color: ${colors.textFieldBorder}; @media (min-width: 800px) { top: 76px; } } &:before { left: -7px; @media (min-width: 500px) { left: -10px; } @media (min-width: 800px) { left: -23px; } } `, column: css` transition: background-color 0.5s ease; border: 2px dashed transparent; border-radius: 4px; position: relative; height: 100%; `, columnHovered: css` border-color: ${colors.active}; `, hiddenColumn: css` display: none; `, }; const ColumnHeader = styled.h2` font-size: 16px; font-weight: normal; padding: 2px 6px; border-radius: ${lengths.borderRadius}; white-space: nowrap; margin-bottom: 6px; @media (min-width: 800px) { font-size: 20px; padding: 4px 12px; margin-bottom: 28px; } ${props => props.name === 'draft' && css` background-color: ${colors.statusDraftBackground}; color: ${colors.statusDraftText}; `} ${props => props.name === 'pending_review' && css` background-color: ${colors.statusReviewBackground}; color: ${colors.statusReviewText}; `} ${props => props.name === 'pending_publish' && css` background-color: ${colors.statusReadyBackground}; color: ${colors.statusReadyText}; `} `; const ColumnCount = styled.p` font-size: 11px; font-weight: 500; color: ${colors.text}; text-transform: uppercase; margin-bottom: 6px; padding-inline: 6px; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; min-width: 0; @media (min-width: 800px) { font-size: 13px; padding-inline: 12px; } `; // This is a namespace so that we can only drop these elements on a DropTarget with the same const DNDNamespace = 'cms-workflow'; function getColumnHeaderText(columnName, t) { switch (columnName) { case 'draft': return t('workflow.workflowList.draftHeader'); case 'pending_review': return t('workflow.workflowList.inReviewHeader'); case 'pending_publish': return t('workflow.workflowList.readyHeader'); } } class WorkflowList extends React.Component { static propTypes = { entries: ImmutablePropTypes.orderedMap, handleChangeStatus: PropTypes.func.isRequired, handlePublish: PropTypes.func.isRequired, handleDelete: PropTypes.func.isRequired, t: PropTypes.func.isRequired, isOpenAuthoring: PropTypes.bool, collections: ImmutablePropTypes.map.isRequired, }; componentDidMount() { // Manually validate PropTypes - React 19 breaking change PropTypes.checkPropTypes(WorkflowList.propTypes, this.props, 'prop', 'WorkflowList'); } handleChangeStatus = (newStatus, dragProps) => { const slug = dragProps.slug; const collection = dragProps.collection; const oldStatus = dragProps.ownStatus; this.props.handleChangeStatus(collection, slug, oldStatus, newStatus); }; requestDelete = (collection, slug, ownStatus) => { if (window.confirm(this.props.t('workflow.workflowList.onDeleteEntry'))) { this.props.handleDelete(collection, slug, ownStatus); } }; requestPublish = (collection, slug, ownStatus) => { if (ownStatus !== status.last()) { window.alert(this.props.t('workflow.workflowList.onPublishingNotReadyEntry')); return; } else if (!window.confirm(this.props.t('workflow.workflowList.onPublishEntry'))) { return; } this.props.handlePublish(collection, slug); }; // eslint-disable-next-line react/display-name renderColumns = (entries, column) => { const { isOpenAuthoring, collections, t } = this.props; if (!entries) return null; if (!column) { return entries.entrySeq().map(([currColumn, currEntries], idx) => ( <DropTarget namespace={DNDNamespace} key={currColumn} onDrop={this.handleChangeStatus.bind(this, currColumn)} > {(connect, { isHovered }) => connect( <div style={{ height: '100%' }}> <div css={[ styles.column, styles.columnPosition(idx), isHovered && styles.columnHovered, isOpenAuthoring && currColumn === 'pending_publish' && styles.hiddenColumn, ]} > <ColumnHeader name={currColumn}> {getColumnHeaderText(currColumn, this.props.t)} </ColumnHeader> <ColumnCount> {this.props.t('workflow.workflowList.currentEntries', { smart_count: currEntries.size, })} </ColumnCount> {this.renderColumns(currEntries, currColumn)} </div> </div>, ) } </DropTarget> )); } return ( <div> {entries.map(entry => { const timestamp = dayjs(entry.get('updatedOn')).format(t('workflow.workflow.dateFormat')); const slug = entry.get('slug'); const collectionName = entry.get('collection'); const editLink = `collections/${collectionName}/entries/${slug}?ref=workflow`; const ownStatus = entry.get('status'); const collection = collections.find( collection => collection.get('name') === collectionName, ); const collectionLabel = collection?.get('label'); const isModification = entry.get('isModification'); const allowPublish = collection?.get('publish'); const canPublish = ownStatus === status.last() && !entry.get('isPersisting', false); const postAuthor = entry.get('author'); return ( <DragSource namespace={DNDNamespace} key={`${collectionName}-${slug}`} slug={slug} collection={collectionName} ownStatus={ownStatus} > {connect => connect( <div> <WorkflowCard collectionLabel={collectionLabel || collectionName} title={selectEntryCollectionTitle(collection, entry)} authorLastChange={entry.getIn(['metaData', 'user'])} body={entry.getIn(['data', 'body'])} isModification={isModification} editLink={editLink} timestamp={timestamp} onDelete={this.requestDelete.bind(this, collectionName, slug, ownStatus)} allowPublish={allowPublish} canPublish={canPublish} onPublish={this.requestPublish.bind(this, collectionName, slug, ownStatus)} postAuthor={postAuthor} /> </div>, ) } </DragSource> ); })} </div> ); }; render() { const columns = this.renderColumns(this.props.entries); const ListContainer = this.props.isOpenAuthoring ? WorkflowListContainerOpenAuthoring : WorkflowListContainer; return <ListContainer>{columns}</ListContainer>; } } export default HTML5DragDrop(translate()(WorkflowList));