import type { ComponentType, JSXElement } from '../../jsx';
import { Defs, getElementBounds, Group, Path, Rect } from '../../jsx';
import { BtnAdd, BtnRemove, BtnsGroup, ItemsGroup } from '../components';
import { FlexLayout } from '../layouts';
import { getColorPrimary, getPaletteColor } from '../utils';
import { registerStructure } from './registry';
import type { BaseStructureProps } from './types';
export interface SequenceColorSnakeStepsProps extends BaseStructureProps {
gap?: number;
itemsPerRow?: number;
rowGap?: number;
columnGap?: number;
circleStrokeWidth?: number;
}
export const SequenceColorSnakeSteps: ComponentType<
SequenceColorSnakeStepsProps
> = (props) => {
const {
Title,
Item,
data,
gap = 0,
rowGap = 0,
itemsPerRow = 3,
circleStrokeWidth = 18,
options,
} = props;
const { title, desc, items = [] } = data;
const titleContent = Title ?
: null;
const colorPrimary = getColorPrimary(options);
const btnBounds = getElementBounds();
const itemBounds = getElementBounds(
,
);
const btnElements: JSXElement[] = [];
const itemElements: JSXElement[] = [];
const decorElements: JSXElement[] = [];
const arcRadius = (rowGap + itemBounds.height) / 2;
const arcWidth = arcRadius;
items.forEach((item, index) => {
const rowIndex = Math.floor(index / itemsPerRow);
const colIndex = index % itemsPerRow;
const isReversedRow = rowIndex % 2 === 1;
const actualColIndex = isReversedRow
? itemsPerRow - 1 - colIndex
: colIndex;
const itemX = actualColIndex * (itemBounds.width + gap) + arcWidth;
const itemY = rowIndex * (itemBounds.height + rowGap);
const indexes = [index];
itemElements.push(
,
);
btnElements.push(
,
);
if (index === 0) {
btnElements.push(
,
);
}
if (index < items.length - 1) {
const nextRowIndex = Math.floor((index + 1) / itemsPerRow);
const isSameRow = rowIndex === nextRowIndex;
if (isSameRow) {
btnElements.push(
,
);
} else {
const currentItemY = itemY + itemBounds.height / 2;
const nextItemY =
itemY + itemBounds.height + rowGap + itemBounds.height / 2;
let arcX, pathD, sweepFlag;
if (isReversedRow) {
arcX = itemX;
sweepFlag = 0;
pathD = `M ${arcX} ${currentItemY} A ${arcRadius} ${arcRadius} 0 0 ${sweepFlag} ${arcX} ${nextItemY}`;
} else {
arcX = itemX + itemBounds.width;
sweepFlag = 1;
pathD = `M ${arcX} ${currentItemY} A ${arcRadius} ${arcRadius} 0 0 ${sweepFlag} ${arcX} ${nextItemY}`;
}
const arcHeight = nextItemY - currentItemY;
const currentColor = getPaletteColor(options, indexes);
const nextColor = getPaletteColor(options, [index + 1]);
const linearGradientId = `gradient-arc-${index}`;
decorElements.push(
<>
>,
);
const btnX = isReversedRow
? arcX - arcRadius - btnBounds.width / 2
: arcX + arcRadius - btnBounds.width / 2;
const btnY =
itemY + itemBounds.height + rowGap / 2 - btnBounds.height / 2;
btnElements.push();
}
}
});
if (items.length > 0) {
const lastIndex = items.length - 1;
const lastRowIndex = Math.floor(lastIndex / itemsPerRow);
const lastColIndex = lastIndex % itemsPerRow;
const isLastReversedRow = lastRowIndex % 2 === 1;
const lastActualColIndex = isLastReversedRow
? itemsPerRow - 1 - lastColIndex
: lastColIndex;
const lastItemX = lastActualColIndex * (itemBounds.width + gap);
const lastItemY = lastRowIndex * (itemBounds.height + rowGap);
btnElements.push(
,
);
}
// Add left rectangle bar for the first item when there's an arc on the left side
if (items.length / itemsPerRow > 2) {
const arcRadius = (rowGap + itemBounds.height) / 2;
const firstItemColor = getPaletteColor(options, [0]);
decorElements.push(
,
);
}
return (
{titleContent}
{decorElements}
{itemElements}
{btnElements}
);
};
registerStructure('sequence-color-snake-steps', {
component: SequenceColorSnakeSteps,
composites: ['title', 'item'],
});