import type { ComponentType, JSXElement } from '../../../jsx'; import { getElementBounds, Group } from '../../../jsx'; import type { HierarchyDatum } from '../../../types'; import { BtnAdd, BtnRemove, BtnsGroup, ItemsGroup } from '../../components'; import { FlexLayout } from '../../layouts'; import { getPaletteColor, getThemeColors } from '../../utils'; import { registerStructure } from '../registry'; import type { BaseStructureProps } from '../types'; import './dividers'; import { getDividerComponent } from './dividers'; export interface CompareBinaryHorizontalProps extends BaseStructureProps { gap?: number; groupGap?: number; opposite?: boolean; flipped?: boolean; dividerType?: string; } export const CompareBinaryHorizontal: ComponentType< CompareBinaryHorizontalProps > = (props) => { const { Title, Item, data, gap = 20, groupGap = 20, opposite = true, flipped = true, dividerType = 'vs', options, } = props; const { title, desc, items = [] } = data; const titleContent = Title ? : null; if (items.length === 0) { return ( <FlexLayout id="infographic-container" flexDirection="column" justifyContent="center" alignItems="center" > {titleContent} <Group> <BtnsGroup> <BtnAdd indexes={[0]} x={0} y={0} /> </BtnsGroup> </Group> </FlexLayout> ); } const leftRoot: HierarchyDatum = items[0] || { children: [] }; const rightRoot: HierarchyDatum = items[1] || { children: [] }; const leftChildren = leftRoot.children || []; const rightChildren = rightRoot.children || []; const colors = getThemeColors(options.themeConfig); const itemBounds = getElementBounds( <Item indexes={[0, 0]} data={data} datum={leftChildren[0] || {}} />, ); const btnBounds = getElementBounds(<BtnAdd indexes={[0]} />); const Divider = getDividerComponent(dividerType); const dividerBounds = Divider ? getElementBounds( <Divider x={0} y={0} colorPrimary={colors.colorPrimary} colorBg={colors.colorBg} colorPositive={colors.colorPrimary} colorNegative={colors.colorPrimary} />, ) : { width: 0, height: 0 }; const itemElements: JSXElement[] = []; const btnElements: JSXElement[] = []; const decorElements: JSXElement[] = []; const maxChildren = Math.max(leftChildren.length, rightChildren.length); const itemsHeight = maxChildren > 0 ? maxChildren * (itemBounds.height + gap) - gap : 0; const btnSpacing = 5; const topOffset = maxChildren > 0 ? gap / 2 + btnBounds.height / 2 : btnBounds.height / 2; const leftX = btnBounds.width + btnSpacing; // groupGap 现在表示左侧数据项到 divider 左边缘的距离 // 总的两组数据项之间的间距 = groupGap + divider宽度 + groupGap const dividerX = leftX + itemBounds.width + groupGap; const rightX = dividerX + dividerBounds.width + groupGap; const leftPositionH = flipped ? 'flipped' : opposite ? 'normal' : 'normal'; const rightPositionH = flipped ? 'normal' : opposite ? 'flipped' : 'normal'; leftChildren.forEach((child, index) => { const childY = topOffset + index * (itemBounds.height + gap); const indexes = [0, index]; itemElements.push( <Item indexes={indexes} datum={child} data={data} x={leftX} y={childY} positionH={leftPositionH} positionV="middle" />, ); btnElements.push( <BtnRemove indexes={indexes} x={leftX - btnBounds.width - btnSpacing} y={childY + (itemBounds.height - btnBounds.height) / 2} />, ); if (index < leftChildren.length - 1) { btnElements.push( <BtnAdd indexes={[0, index + 1]} x={leftX + (itemBounds.width - btnBounds.width) / 2} y={childY + itemBounds.height + gap / 2 - btnBounds.height / 2} />, ); } }); if (leftChildren.length > 0) { btnElements.push( <BtnAdd indexes={[0, 0]} x={leftX + (itemBounds.width - btnBounds.width) / 2} y={topOffset - gap / 2 - btnBounds.height / 2} />, ); const lastChildY = topOffset + (leftChildren.length - 1) * (itemBounds.height + gap); btnElements.push( <BtnAdd indexes={[0, leftChildren.length]} x={leftX + (itemBounds.width - btnBounds.width) / 2} y={lastChildY + itemBounds.height + gap / 2 - btnBounds.height / 2} />, ); } else if (items.length >= 1) { btnElements.push( <BtnAdd indexes={[0, 0]} x={leftX + (itemBounds.width - btnBounds.width) / 2} y={topOffset - btnBounds.height / 2} />, ); } rightChildren.forEach((child, index) => { const childY = topOffset + index * (itemBounds.height + gap); const indexes = [1, index]; itemElements.push( <Item indexes={indexes} datum={child} data={data} x={rightX} y={childY} positionH={rightPositionH} positionV="middle" />, ); btnElements.push( <BtnRemove indexes={indexes} x={rightX + itemBounds.width + btnSpacing} y={childY + (itemBounds.height - btnBounds.height) / 2} />, ); if (index < rightChildren.length - 1) { btnElements.push( <BtnAdd indexes={[1, index + 1]} x={rightX + (itemBounds.width - btnBounds.width) / 2} y={childY + itemBounds.height + gap / 2 - btnBounds.height / 2} />, ); } }); if (rightChildren.length > 0) { btnElements.push( <BtnAdd indexes={[1, 0]} x={rightX + (itemBounds.width - btnBounds.width) / 2} y={topOffset - gap / 2 - btnBounds.height / 2} />, ); const lastChildY = topOffset + (rightChildren.length - 1) * (itemBounds.height + gap); btnElements.push( <BtnAdd indexes={[1, rightChildren.length]} x={rightX + (itemBounds.width - btnBounds.width) / 2} y={lastChildY + itemBounds.height + gap / 2 - btnBounds.height / 2} />, ); } else if (items.length >= 2) { btnElements.push( <BtnAdd indexes={[1, 0]} x={rightX + (itemBounds.width - btnBounds.width) / 2} y={topOffset - btnBounds.height / 2} />, ); } if (items.length < 2) { btnElements.push( <BtnAdd indexes={[1]} x={rightX + (itemBounds.width - btnBounds.width) / 2} y={topOffset + (itemsHeight - btnBounds.height) / 2} />, ); } if (Divider) { decorElements.push( <Divider x={dividerX} y={topOffset + (itemsHeight - dividerBounds.height) / 2} colorPrimary={colors.colorPrimary} colorBg={colors.colorBg} colorPositive={getPaletteColor(options, [0]) || colors.colorPrimary} colorNegative={getPaletteColor(options, [1]) || colors.colorPrimary} />, ); } return ( <FlexLayout id="infographic-container" flexDirection="column" justifyContent="center" alignItems="center" > {titleContent} <Group> <Group>{decorElements}</Group> <ItemsGroup>{itemElements}</ItemsGroup> <BtnsGroup>{btnElements}</BtnsGroup> </Group> </FlexLayout> ); }; registerStructure('compare-binary-horizontal', { component: CompareBinaryHorizontal, composites: ['title', 'item'], });