@gravityforms/components
Version:
UI components for use in Gravity Forms development. Both React and vanilla js flavors.
289 lines (278 loc) • 8.35 kB
JavaScript
import { React, ReactDND, PropTypes, classnames } from '@gravityforms/libraries';
import { sprintf } from '@gravityforms/utils';
import itemTypes from './item-types';
import KanbanLoader from './KanbanLoader';
import Box from '../../elements/Box';
import Card from '../Cards/Card';
import Droplist from '../Droplist';
const { useDrag } = ReactDND;
const NEEDS_I18N_LABEL = 'Needs i18n';
/**
* @module KanbanCard
* @description A component that represents a card in a Kanban board.
*
* @since 5.8.4
*
* @param {object} props Component props.
* @param {JSX.Element|null} props.children The content of the card.
* @param {Array<object>} props.columns The columns in the Kanban board.
* @param {string} props.columnId The ID of the column this card belongs to.
* @param {object} props.customAttributes Custom attributes for the card.
* @param {string|Array|object} props.customClasses Custom classes for the card.
* @param {JSX.Element|null} props.footerContent The content to display in the card footer.
* @param {JSX.Element|null} props.headerContent The content to display in the card header.
* @param {boolean} props.hoveredAbove Whether the card is being hovered above.
* @param {boolean} props.hoveredBelow Whether the card is being hovered below.
* @param {object} props.i18n I18n strings for the Kanban component.
* @param {string} props.id The ID of the card.
* @param {number} props.index The index of the card within its column.
* @param {boolean} props.isFirstCard Whether this card is the first in its column.
* @param {boolean} props.isLastCard Whether this card is the last in its column.
* @param {boolean} props.isLoading Whether the card is in a loading state.
* @param {string} props.kanbanId The ID for the Kanban board.
* @param {Function} props.moveCard Function to move the card to a different position or column.
* @param {Function} props.onDelete Callback when the card is deleted.
* @param {Function} props.onDragEnd Callback when the drag operation ends.
* @param {Function} props.onEdit Callback when the card is edited.
* @param {boolean} props.showActions Whether to show the action menu on the card.
* @param {string|number|Array|object} props.spacing Spacing for the card.
*
* @return {JSX.Element} The Kanban Card component.
*/
const KanbanCard = ( {
children = null,
columns = [],
columnId = '',
customAttributes = {},
customClasses = [],
footerContent = null,
headerContent = null,
hoveredAbove = false,
hoveredBelow = false,
i18n = {},
id = '',
index = 0,
isFirstCard = false,
isLastCard = false,
isLoading = false,
kanbanId = '',
moveCard = () => {},
onDelete = () => {},
onDragEnd = () => {},
onEdit = () => {},
showActions = true,
spacing = 0,
} ) => {
const cardItemType = `${ itemTypes.KANBAN_CARD }_${ kanbanId }`;
const [ { isDragging }, drag, preview ] = useDrag( {
type: cardItemType,
item: { id, index, columnId },
collect: ( monitor ) => ( {
isDragging: monitor.isDragging(),
} ),
end: () => {
onDragEnd();
},
} );
const droplistProps = {
align: 'right',
closeOnClick: true,
id: `${ id }-droplist`,
listItems: [
{
triggerAttributes: {
id: `${ id }-move-to-column`,
iconBefore: 'switch-horizontal',
iconAfter: 'chevron-right',
label: i18n?.moveToColumn || NEEDS_I18N_LABEL,
},
listItems: columns.map( ( column ) => ( {
key: `${ id }-move-to-column-${ column.id }`,
props: {
element: 'button',
iconAfter: column.id === columnId ? 'check-mark-alt' : undefined,
label: column.label,
customAttributes: {
id: `${ id }-move-to-column-${ column.id }`,
onClick: () => {
if ( column.id === columnId ) {
return;
}
moveCard( columnId, column.id, index, column.count, id );
},
},
},
} ) ),
},
{
key: `${ id }-move-up`,
props: {
element: 'button',
id: `${ id }-move-up`,
label: i18n?.moveUp || NEEDS_I18N_LABEL,
iconBefore: 'arrow-sm-up',
customAttributes: {
disabled: isFirstCard,
onClick: () => {
if ( isFirstCard ) {
return;
}
moveCard( columnId, columnId, index, index - 1, id );
},
},
},
},
{
key: `${ id }-move-down`,
props: {
element: 'button',
id: `${ id }-move-down`,
label: i18n?.moveDown || NEEDS_I18N_LABEL,
iconBefore: 'arrow-sm-down',
customAttributes: {
disabled: isLastCard,
onClick: () => {
if ( isLastCard ) {
return;
}
moveCard( columnId, columnId, index, index + 1, id );
},
},
},
},
{
key: `${ id }-edit-card`,
props: {
element: 'button',
id: `${ id }-edit-card`,
label: i18n?.editCard || NEEDS_I18N_LABEL,
iconBefore: 'edit',
customAttributes: {
onClick: () => {
onEdit( id );
},
},
},
},
{
key: `${ id }-delete-card`,
props: {
element: 'button',
id: `${ id }-delete-card`,
label: i18n?.deleteCard || NEEDS_I18N_LABEL,
iconBefore: 'trash',
style: 'error',
customAttributes: {
onClick: () => {
onDelete( id );
},
},
},
},
],
stackNestedGroups: true,
triggerAttributes: {
icon: 'dots-horizontal',
iconPrefix: 'gravity-component-icon',
size: 'size-height-m',
title: i18n?.cardActionsTrigger ? sprintf( i18n.cardActionsTrigger, id ) : NEEDS_I18N_LABEL,
type: 'icon-grey',
},
width: 230,
};
const cardProps = {
afterCard: (
<Box customClasses={ [ 'gform-kanban__card-footer' ] } tagName="footer">
{
isLoading
? <KanbanLoader />
: footerContent
}
</Box>
),
beforeCard: (
<Box customClasses={ [ 'gform-kanban__card-header' ] } display="flex" tagName="header" ref={ drag }>
{
isLoading
? <KanbanLoader />
: <>
{ headerContent }
{ showActions && <Droplist { ...droplistProps } /> }
</>
}
</Box>
),
spacing,
style: 'kanban',
...customAttributes,
customAttributes: {
id,
},
customClasses: classnames( {
'gform-kanban__card': true,
'gform-kanban__card--is-dragging': isDragging,
'gform-kanban__card--drop-above': hoveredAbove,
'gform-kanban__card--drop-below': hoveredBelow,
'gform-kanban__card--loading': isLoading,
}, customClasses ),
};
return (
<Card { ...cardProps } ref={ preview }>
<Box customClasses={ [ 'gform-kanban__card-content' ] }>
{
isLoading
? <KanbanLoader />
: children
}
</Box>
</Card>
);
};
KanbanCard.propTypes = {
children: PropTypes.oneOfType( [
PropTypes.arrayOf( PropTypes.node ),
PropTypes.node,
] ),
columns: PropTypes.arrayOf( PropTypes.shape( {
count: PropTypes.number,
id: PropTypes.string,
label: PropTypes.string,
} ) ),
columnId: PropTypes.string,
customAttributes: PropTypes.object,
customClasses: PropTypes.oneOfType( [
PropTypes.string,
PropTypes.array,
PropTypes.object,
] ),
footerContent: PropTypes.oneOfType( [
PropTypes.arrayOf( PropTypes.node ),
PropTypes.node,
] ),
headerContent: PropTypes.oneOfType( [
PropTypes.arrayOf( PropTypes.node ),
PropTypes.node,
] ),
hoveredAbove: PropTypes.bool,
hoveredBelow: PropTypes.bool,
i18n: PropTypes.object,
id: PropTypes.string,
index: PropTypes.number,
isFirstCard: PropTypes.bool,
isLastCard: PropTypes.bool,
isLoading: PropTypes.bool,
kanbanId: PropTypes.string,
moveCard: PropTypes.func,
onDelete: PropTypes.func,
onDragEnd: PropTypes.func,
onEdit: PropTypes.func,
showActions: PropTypes.bool,
spacing: PropTypes.oneOfType( [
PropTypes.string,
PropTypes.number,
PropTypes.array,
PropTypes.object,
] ),
};
KanbanCard.displayName = 'KanbanCard';
export default KanbanCard;