import { useCallback, useMemo } from 'react'; import { observer } from 'mobx-react'; import { css } from '@patternfly/react-styles'; import styles from '../../../css/topology-components'; import CollapseIcon from '@patternfly/react-icons/dist/esm/icons/compress-alt-icon'; import NodeLabel from '../../../components/nodes/labels/NodeLabel'; import { Layer } from '../../../components/layers'; import { GROUPS_LAYER, TOP_LAYER } from '../../../const'; import { useCombineRefs, useHover, useSize } from '../../../utils'; import { AnchorEnd, isGraph, LabelPosition, Node, ScaleDetailsLevel } from '../../../types'; import { useAnchor, useDragNode } from '../../../behavior'; import { DagreLayoutOptions, TOP_TO_BOTTOM } from '../../../layouts'; import TaskGroupSourceAnchor from '../anchors/TaskGroupSourceAnchor'; import TaskGroupTargetAnchor from '../anchors/TaskGroupTargetAnchor'; import { DefaultTaskGroupProps } from './DefaultTaskGroup'; const DefaultTaskGroupExpanded: React.FunctionComponent & { element: Node }> = observer( ({ className, element, collapsible, selected, onSelect, hover, label, secondaryLabel, showLabel = true, showLabelOnHover, hideDetailsAtMedium, status, GroupLabelComponent = NodeLabel, truncateLength, canDrop, dropTarget, onContextMenu, contextMenuOpen, dragging, dragNodeRef, badge, badgeColor, badgeTextColor, badgeBorderColor, badgeClassName, badgeLocation, labelOffset = 17, centerLabelOnEdge, labelIconClass, labelIcon, labelIconPadding, onCollapseChange, labelPosition, borderRadius = 16 }) => { const [hovered, hoverRef] = useHover(200, 500); const [labelHover, labelHoverRef] = useHover(0); const dragLabelRef = useDragNode()[1]; const refs = useCombineRefs(hoverRef, dragNodeRef); const isHover = hover !== undefined ? hover : hovered || labelHover; const [labelSize, labelRef] = useSize([centerLabelOnEdge]); const verticalLayout = (element.getGraph().getLayoutOptions?.() as DagreLayoutOptions)?.rankdir === TOP_TO_BOTTOM; const groupLabelPosition = labelPosition ?? element.getLabelPosition() ?? LabelPosition.bottom; let parent = element.getParent(); const detailsLevel = element.getGraph().getDetailsLevel(); let altGroup = false; while (!isGraph(parent)) { altGroup = !altGroup; parent = parent.getParent(); } const labelShown = showLabel && (!hideDetailsAtMedium || detailsLevel === ScaleDetailsLevel.high || (isHover && showLabelOnHover)) && (label || element.getLabel()); const anchorOffset = verticalLayout ? labelSize?.height / 2 : labelSize?.width / 2; useAnchor( useCallback( (node: Node) => new TaskGroupSourceAnchor( node, verticalLayout, labelShown && ((centerLabelOnEdge && labelPosition === LabelPosition.bottom && verticalLayout) || (labelPosition === LabelPosition.right && !verticalLayout)) && labelSize ? anchorOffset : 0 ), [labelShown, anchorOffset, centerLabelOnEdge, labelPosition, labelSize, verticalLayout] ), AnchorEnd.source ); useAnchor( useCallback( (node: Node) => new TaskGroupTargetAnchor( node, verticalLayout, labelShown && ((centerLabelOnEdge && labelPosition === LabelPosition.top && verticalLayout) || (labelPosition === LabelPosition.left && !verticalLayout)) && labelSize ? anchorOffset : 0 ), [labelShown, anchorOffset, centerLabelOnEdge, labelPosition, labelSize, verticalLayout] ), AnchorEnd.target ); const bounds = element.getBounds(); const [labelX, labelY] = useMemo(() => { if (!showLabel || !(label || element.getLabel())) { return [0, 0]; } switch (groupLabelPosition) { case LabelPosition.top: return [bounds.x + bounds.width / 2, -bounds.y + (centerLabelOnEdge ? 0 : labelOffset)]; case LabelPosition.right: return [bounds.x + bounds.width + (centerLabelOnEdge ? 0 : labelOffset), bounds.y + bounds.height / 2]; case LabelPosition.left: return [centerLabelOnEdge ? bounds.x : labelOffset, bounds.y + bounds.height / 2]; case LabelPosition.bottom: default: return [bounds.x + bounds.width / 2, bounds.y + bounds.height + (centerLabelOnEdge ? 0 : labelOffset)]; } }, [ showLabel, label, element, groupLabelPosition, bounds.x, bounds.width, bounds.y, bounds.height, centerLabelOnEdge, labelOffset ]); const children = element.getNodes().filter((c) => c.isVisible()); if (children.length === 0) { return null; } const groupClassName = css( styles.topologyGroup, className, altGroup && 'pf-m-alt-group', canDrop && 'pf-m-highlight', dragging && 'pf-m-dragging', selected && 'pf-m-selected' ); const innerGroupClassName = css( styles.topologyGroup, className, altGroup && 'pf-m-alt-group', canDrop && 'pf-m-highlight', dragging && 'pf-m-dragging', selected && 'pf-m-selected', isHover && 'pf-m-hover', canDrop && dropTarget && 'pf-m-drop-target' ); const scale = element.getGraph().getScale(); const medScale = element.getGraph().getDetailsLevelThresholds().medium; const labelScale = detailsLevel !== ScaleDetailsLevel.high ? Math.min(1 / scale, 1 / medScale) : 1; const labelPositionScale = detailsLevel !== ScaleDetailsLevel.high ? 1 / labelScale : 1; const groupLabel = labelShown ? ( : undefined} onActionIconClick={() => onCollapseChange(element, true)} > {label || element.getLabel()} ) : null; return ( {groupLabel && isHover ? ( {groupLabel} ) : ( groupLabel )} ); } ); export default DefaultTaskGroupExpanded;