UNPKG

saagie-ui

Version:

Saagie UI from Saagie Design System

252 lines (236 loc) 7.42 kB
/* eslint-env browser */ import React, { useState, useEffect, useRef } from 'react'; import ReactDOM from 'react-dom'; import PropTypes from 'prop-types'; import { Manager, Reference, Popper } from 'react-popper'; import classnames from 'classnames'; import { ClickAwayListener } from '../../core/helpers/clickAwayListener/ClickAwayListener'; import { dropUnwantedProps, modifierCSS } from '../../helpers'; import { Icon } from '../../core/atoms/icon/Icon'; import { TechnologyBadge } from '../../core/atoms/technologyBadge/TechnologyBadge'; import { Badge } from '../../core/atoms/badge/Badge'; import { useMapInteractionContext } from '../mapInteraction/MapInteractionContext'; import { useDragContainerContext } from './PipelineDragContainer'; const propTypes = { children: PropTypes.node, /** * The component used for the root node. * Either a string to use a DOM element or a component. */ tag: PropTypes.elementType, defaultClassName: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]), defaultPlacement: PropTypes.string, className: PropTypes.string, category: PropTypes.string, name: PropTypes.string.isRequired, technology: PropTypes.string, technologyIcon: PropTypes.string, technologyIconUrl: PropTypes.string, message: PropTypes.node, isFocus: PropTypes.bool, onFocus: PropTypes.func, status: PropTypes.string, statusInfos: PropTypes.string, defaultOffset: PropTypes.shape({ top: PropTypes.number, left: PropTypes.number, }), popperClassName: PropTypes.string, context: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), version: PropTypes.string, autoClosePopper: PropTypes.bool, }; const defaultProps = { children: '', tag: 'div', defaultClassName: 'sui-prj-pipeline-job', defaultPlacement: 'top', defaultOffset: { top: 0, left: 0 }, className: '', category: '', technology: '', technologyIcon: '', technologyIconUrl: null, message: '', isFocus: false, onFocus: () => {}, status: '', statusInfos: '', popperClassName: 'sui-prj-pipeline-job__popper', context: false, version: '', autoClosePopper: false, }; export const PipelineJob = ({ isFocus: isFocusProp, onFocus: onFocusProp, children, defaultPlacement, defaultOffset, popperClassName, autoClosePopper, tag: Tag, defaultClassName, className, category, name, technology, technologyIcon, technologyIconUrl, message, status, statusInfos, version, context, ...props }) => { const { isDragging: isDraggingItems } = useDragContainerContext(); const { isDragging: isDraggingMap, scale: scaleMap } = useMapInteractionContext(); const [isFocus, setIsFocus] = useState(isFocusProp); const onFocusRef = useRef(); onFocusRef.current = onFocusProp; const handleFocus = (val) => { setIsFocus(val); onFocusRef.current(val); }; useEffect(() => { setIsFocus(isFocusProp); }, [isFocusProp]); useEffect(() => { handleFocus(false); }, [isDraggingItems, isDraggingMap, scaleMap]); const renderMainElement = (ref) => { const attibutes = dropUnwantedProps(props, ['isFocus', 'autoClosePopper']); const classes = classnames( defaultClassName, className, modifierCSS(status), { 'as--focus': isFocus, 'as--dragging': isDraggingItems, } ); const getIconByStatus = (currentStatus) => { switch (currentStatus) { case 'awaiting': return <Icon name="clock" />; case 'requested': case 'queued': return ( <svg className="sui-prj-slidein-animation" height="10" width="10"> <circle cx="5" cy="5" r="2" /> </svg> ); case 'running': return <Icon name="spinner" className="sui-prj-spiner-animation" />; case 'succeeded': return <Icon name="fa-check" />; case 'failed': return <Icon name="fa-exclamation" />; case 'out_of_memory': return <Icon name="out-of-memory" />; case 'skipped': return <Icon name="fa-long-arrow-right" />; case 'stopping': return <Icon className="sui-prj-pulse-opacity-animation" name="fa-stop" />; case 'stopped': return <Icon name="fa-stop" />; case 'unknown': return <Icon name="fa-question" />; default: return ( <svg height="10" width="10"> <circle cx="5" cy="5" r="5" /> </svg> ); } }; return ( <Tag className={classes} {...attibutes} tabIndex="0" role="button" onKeyDown={(e) => { if (['Enter', ' '].includes(e.key)) { handleFocus(true); } }} onClick={() => handleFocus(true)} > { status && ( <div className="sui-prj-pipeline-job__status"> <i className="sui-prj-pipeline-job__anchor"> { getIconByStatus(status) } </i> {status.replaceAll('_', ' ')} { statusInfos && ( <div className="sui-prj-pipeline-job__info">{statusInfos}</div> )} </div> )} {message} <div className="sui-prj-pipeline-job__content" ref={ref || null}> <div className="sui-prj-pipeline-job__technology"> <TechnologyBadge shadow="none" size="sm" icon={technologyIcon || technology} iconUrl={technologyIconUrl} label={technology} /> </div> <div className="sui-prj-pipeline-job__details"> <div className="sui-g-grid as--no-wrap as--gutter-xs"> <div className="sui-g-grid__item"> <div className="sui-prj-pipeline-job__name"> {name} </div> </div> <div className="sui-g-grid__item as--push"> {version && ( <div className="sui-prj-pipeline-job__version"> <Badge position="end" size="xs">{version}</Badge> </div> )} </div> </div> <div className="sui-prj-pipeline-job__type"> {category} </div> </div> </div> </Tag> ); }; const formattedOffset = { offset: { offset: `${defaultOffset.left}, ${defaultOffset.top}` } }; if (!children) { renderMainElement(); } return ( <Manager> <Reference> {({ ref }) => renderMainElement(ref)} </Reference> {isFocus ? ReactDOM.createPortal( <Popper placement={defaultPlacement} modifiers={formattedOffset}> {({ placement, ref, style }) => ( <ClickAwayListener onClickOutside={() => handleFocus(false)} ref={ref}> <div style={style} data-placement={placement} className={popperClassName} onClick={autoClosePopper ? handleFocus(false) : null} onKeyDown={autoClosePopper ? handleFocus(false) : null} role="presentation" > {children} </div> </ClickAwayListener> )} </Popper>, document.body ) : ''} </Manager> ); }; PipelineJob.propTypes = propTypes; PipelineJob.defaultProps = defaultProps;