| 1 | const MAX_PYRAMID_WIDTH_PERCENTAGE = 100; |
| 2 | |
| 3 | type PyramidSegmentGeometryOptions = { |
| 4 | index: number; |
| 5 | totalItems: number; |
| 6 | isFunnel?: boolean; |
| 7 | }; |
| 8 | |
| 9 | function getSafePyramidGeometryValues({ |
| 10 | index, |
| 11 | totalItems, |
| 12 | }: PyramidSegmentGeometryOptions) { |
| 13 | const safeTotalItems = Math.max(totalItems, 1); |
| 14 | const safeIndex = Math.min(Math.max(index, 0), safeTotalItems - 1); |
| 15 | const increment = MAX_PYRAMID_WIDTH_PERCENTAGE / (2 * safeTotalItems); |
| 16 | |
| 17 | return { |
| 18 | increment, |
| 19 | safeIndex, |
| 20 | safeTotalItems, |
| 21 | }; |
| 22 | } |
| 23 | |
| 24 | export function getPyramidSegmentClipPath( |
| 25 | options: PyramidSegmentGeometryOptions, |
| 26 | ) { |
| 27 | const { increment, safeIndex, safeTotalItems } = |
| 28 | getSafePyramidGeometryValues(options); |
| 29 | |
| 30 | if (options.isFunnel) { |
| 31 | const topOffset = increment * (safeTotalItems - safeIndex); |
| 32 | const topLeft = 50 - topOffset; |
| 33 | const topRight = 50 + topOffset; |
| 34 | |
| 35 | if (safeIndex === safeTotalItems - 1) { |
| 36 | return `polygon(${topLeft}% 0%, ${topRight}% 0%, 50% 100%)`; |
| 37 | } |
| 38 | |
| 39 | const bottomOffset = increment * (safeTotalItems - safeIndex - 1); |
| 40 | const bottomLeft = 50 - bottomOffset; |
| 41 | const bottomRight = 50 + bottomOffset; |
| 42 | |
| 43 | return `polygon(${topLeft}% 0%, ${topRight}% 0%, ${bottomRight}% 100%, ${bottomLeft}% 100%)`; |
| 44 | } |
| 45 | |
| 46 | if (safeIndex === 0) { |
| 47 | return `polygon(50% 0%, ${50 - increment}% 100%, ${50 + increment}% 100%)`; |
| 48 | } |
| 49 | |
| 50 | const topOffset = increment * safeIndex; |
| 51 | const bottomOffset = increment * (safeIndex + 1); |
| 52 | const topLeft = 50 - topOffset; |
| 53 | const topRight = 50 + topOffset; |
| 54 | const bottomLeft = 50 - bottomOffset; |
| 55 | const bottomRight = 50 + bottomOffset; |
| 56 | |
| 57 | return `polygon(${topLeft}% 0%, ${topRight}% 0%, ${bottomRight}% 100%, ${bottomLeft}% 100%)`; |
| 58 | } |
| 59 | |
| 60 | export function getPyramidTextOffset(options: PyramidSegmentGeometryOptions) { |
| 61 | const { increment, safeIndex, safeTotalItems } = |
| 62 | getSafePyramidGeometryValues(options); |
| 63 | const halfMaxWidth = MAX_PYRAMID_WIDTH_PERCENTAGE / 2; |
| 64 | |
| 65 | if (options.isFunnel) { |
| 66 | // Widest is at the top of the segment |
| 67 | return halfMaxWidth - (safeTotalItems - safeIndex) * increment; |
| 68 | } |
| 69 | |
| 70 | // Widest is at the bottom of the segment |
| 71 | return halfMaxWidth - (safeIndex + 1) * increment; |
| 72 | } |
| 73 | |
| 74 | export function getPyramidBorderExtension( |
| 75 | options: PyramidSegmentGeometryOptions, |
| 76 | ) { |
| 77 | const { increment } = getSafePyramidGeometryValues(options); |
| 78 | // For Funnel, the border (at bottom) is at the narrowest point, so it needs extension to reach the slanted side |
| 79 | // For Pyramid, the border (at bottom) is at the widest point, so it already touches |
| 80 | if (options.isFunnel) { |
| 81 | return increment; |
| 82 | } |
| 83 | return 0; |
| 84 | } |
| 85 |