UNPKG

@gravityforms/components

Version:

UI components for use in Gravity Forms development. Both React and vanilla js flavors.

289 lines (278 loc) 8.35 kB
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;