@ethlete/cdk 4.68.0 → 4.69.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/fesm2022/ethlete-cdk.mjs +1898 -1890
- package/fesm2022/ethlete-cdk.mjs.map +1 -1
- package/index.d.ts +54 -2
- package/package.json +1 -1
package/fesm2022/ethlete-cdk.mjs
CHANGED
|
@@ -1572,2010 +1572,2010 @@ const generateBracketDataForGg = (source) => {
|
|
|
1572
1572
|
return bracketData;
|
|
1573
1573
|
};
|
|
1574
1574
|
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
}
|
|
1603
|
-
`, isInline: true, styles: [".et-new-bracket-default-round-header-host{display:block;padding:8px;border:1px solid green;inline-size:100%;block-size:100%;display:flex;justify-content:center;align-items:center;box-sizing:border-box}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
1604
|
-
}
|
|
1605
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: NewBracketDefaultRoundHeaderComponent, decorators: [{
|
|
1606
|
-
type: Component,
|
|
1607
|
-
args: [{ selector: 'et-new-bracket-default-round-header', template: `
|
|
1608
|
-
{{ bracketRound().name }}
|
|
1609
|
-
|
|
1610
|
-
@if (bracketRoundSwissGroup()) {
|
|
1611
|
-
({{ bracketRoundSwissGroup()?.name }})
|
|
1612
|
-
}
|
|
1613
|
-
`, standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
|
|
1614
|
-
class: 'et-new-bracket-default-round-header-host',
|
|
1615
|
-
}, styles: [".et-new-bracket-default-round-header-host{display:block;padding:8px;border:1px solid green;inline-size:100%;block-size:100%;display:flex;justify-content:center;align-items:center;box-sizing:border-box}\n"] }]
|
|
1616
|
-
}] });
|
|
1617
|
-
|
|
1618
|
-
const path = (d, options) => `<path d="${d.replace(/\s+/g, ' ').trim()}" stroke="currentColor" fill="none" stroke-width="${options.width}" stroke-dasharray="${options.dashArray}" stroke-dashoffset="${options.dashOffset}" class="${options.className}" />`;
|
|
1619
|
-
|
|
1620
|
-
const curvePath = (from, to, direction, options) => {
|
|
1621
|
-
const inverted = options.inverted ?? false;
|
|
1622
|
-
// Inline/block coordinates depending on direction and inversion
|
|
1623
|
-
const fromInline = inverted ? from.inline.start : from.inline.end;
|
|
1624
|
-
const toInline = inverted ? to.inline.end : to.inline.start;
|
|
1625
|
-
const fromBlock = from.block.center;
|
|
1626
|
-
const toBlock = to.block.center;
|
|
1627
|
-
// Curve parameters
|
|
1628
|
-
const startCurve = options.lineStartingCurveAmount;
|
|
1629
|
-
const endCurve = options.lineEndingCurveAmount;
|
|
1630
|
-
const totalInline = Math.abs(toInline - fromInline);
|
|
1631
|
-
const straightLength = (totalInline - startCurve - endCurve) / 2;
|
|
1632
|
-
// Calculate key points for the path
|
|
1633
|
-
const straightEnd = inverted ? fromInline - straightLength : fromInline + straightLength;
|
|
1634
|
-
const straightStart = inverted ? toInline + straightLength : toInline - straightLength;
|
|
1635
|
-
// First curve (from start)
|
|
1636
|
-
const firstCurveEndX = inverted ? straightEnd - startCurve : straightEnd + startCurve;
|
|
1637
|
-
const firstCurveEndY = direction === 'down' ? fromBlock + startCurve : fromBlock - startCurve;
|
|
1638
|
-
// Second curve (to end)
|
|
1639
|
-
const secondCurveStartY = direction === 'down' ? toBlock - endCurve : toBlock + endCurve;
|
|
1640
|
-
const secondCurveEndX = straightStart;
|
|
1641
|
-
const secondCurveEndY = toBlock;
|
|
1642
|
-
const secondCurveBezierX = inverted ? straightStart + endCurve : straightStart - endCurve;
|
|
1643
|
-
// SVG path string
|
|
1644
|
-
const d = [
|
|
1645
|
-
`M ${fromInline} ${fromBlock}`,
|
|
1646
|
-
`H ${straightEnd}`,
|
|
1647
|
-
`Q ${firstCurveEndX} ${fromBlock}, ${firstCurveEndX} ${firstCurveEndY}`,
|
|
1648
|
-
`V ${secondCurveStartY}`,
|
|
1649
|
-
`Q ${secondCurveBezierX} ${toBlock}, ${secondCurveEndX} ${secondCurveEndY}`,
|
|
1650
|
-
`H ${toInline}`,
|
|
1651
|
-
].join(' ');
|
|
1652
|
-
return path(d, options.path);
|
|
1653
|
-
};
|
|
1654
|
-
|
|
1655
|
-
const linePath = (from, to, options) => {
|
|
1656
|
-
return path(`M ${from.inline.end} ${from.block.center} L ${to.inline.start} ${to.block.center}`, options.path);
|
|
1657
|
-
};
|
|
1658
|
-
|
|
1659
|
-
const makePos = (dimensions) => ({
|
|
1660
|
-
block: {
|
|
1661
|
-
start: dimensions.top,
|
|
1662
|
-
end: dimensions.top + dimensions.height,
|
|
1663
|
-
center: dimensions.top + dimensions.height / 2,
|
|
1664
|
-
},
|
|
1665
|
-
inline: {
|
|
1666
|
-
start: dimensions.left,
|
|
1667
|
-
end: dimensions.left + dimensions.width,
|
|
1668
|
-
center: dimensions.left + dimensions.width / 2,
|
|
1669
|
-
},
|
|
1670
|
-
});
|
|
1671
|
-
const drawMan = (dimensions) => {
|
|
1672
|
-
const svgParts = [];
|
|
1673
|
-
for (const col of dimensions.bracketGrid.columns) {
|
|
1674
|
-
for (const el of col.elements) {
|
|
1675
|
-
if (el.type === 'header')
|
|
1676
|
-
continue;
|
|
1677
|
-
const currentMatchParticipantsShortIds = [el.match.home?.shortId, el.match.away?.shortId]
|
|
1678
|
-
.filter((id) => !!id)
|
|
1679
|
-
.join(' ');
|
|
1680
|
-
const pathOptions = { ...dimensions.path, className: currentMatchParticipantsShortIds };
|
|
1681
|
-
const currentPos = makePos(el.dimensions);
|
|
1682
|
-
// No lines for the third place match
|
|
1683
|
-
if (el.round.type === COMMON_BRACKET_ROUND_TYPE.THIRD_PLACE)
|
|
1684
|
-
continue;
|
|
1685
|
-
switch (el.match.relation.type) {
|
|
1686
|
-
case 'nothing-to-one': {
|
|
1687
|
-
continue;
|
|
1688
|
-
}
|
|
1689
|
-
case 'one-to-nothing':
|
|
1690
|
-
case 'one-to-one': {
|
|
1691
|
-
const prev = dimensions.bracketGrid.matchElementMap.getOrThrow(el.match.relation.previousMatch.id);
|
|
1692
|
-
const prevPos = makePos(prev.dimensions);
|
|
1693
|
-
// draw a straight line
|
|
1694
|
-
svgParts.push(linePath(prevPos, currentPos, { path: pathOptions }));
|
|
1695
|
-
break;
|
|
1696
|
-
}
|
|
1697
|
-
case 'two-to-nothing':
|
|
1698
|
-
case 'two-to-one': {
|
|
1699
|
-
const prevUpper = dimensions.bracketGrid.matchElementMap.getOrThrow(el.match.relation.previousUpperMatch.id);
|
|
1700
|
-
const prevLower = dimensions.bracketGrid.matchElementMap.getOrThrow(el.match.relation.previousLowerMatch.id);
|
|
1701
|
-
const prevUpperPos = makePos(prevUpper.dimensions);
|
|
1702
|
-
const prevLowerPos = makePos(prevLower.dimensions);
|
|
1703
|
-
const isLowerUpperMerger = el.match.relation.previousLowerRound.id !== el.match.relation.previousUpperRound.id;
|
|
1704
|
-
const invertCurve = el.round.mirrorRoundType === BRACKET_ROUND_MIRROR_TYPE.RIGHT;
|
|
1705
|
-
const curveOptions = {
|
|
1706
|
-
...dimensions.curve,
|
|
1707
|
-
inverted: invertCurve,
|
|
1708
|
-
path: { ...dimensions.path, className: '' },
|
|
1709
|
-
};
|
|
1710
|
-
if (isLowerUpperMerger) {
|
|
1711
|
-
svgParts.push(linePath(prevUpperPos, currentPos, { path: pathOptions }));
|
|
1712
|
-
}
|
|
1713
|
-
else {
|
|
1714
|
-
// draw two lines that merge into one in the middle
|
|
1715
|
-
svgParts.push(curvePath(prevUpperPos, currentPos, 'down', {
|
|
1716
|
-
...curveOptions,
|
|
1717
|
-
path: {
|
|
1718
|
-
...curveOptions.path,
|
|
1719
|
-
className: el.match.relation.previousUpperMatch.winner?.shortId || '',
|
|
1720
|
-
},
|
|
1721
|
-
}));
|
|
1722
|
-
}
|
|
1723
|
-
svgParts.push(curvePath(prevLowerPos, currentPos, 'up', {
|
|
1724
|
-
...curveOptions,
|
|
1725
|
-
path: {
|
|
1726
|
-
...curveOptions.path,
|
|
1727
|
-
className: el.match.relation.previousLowerMatch.winner?.shortId || '',
|
|
1728
|
-
},
|
|
1729
|
-
}));
|
|
1730
|
-
if (el.round.mirrorRoundType === BRACKET_ROUND_MIRROR_TYPE.RIGHT &&
|
|
1731
|
-
el.match.relation.type === 'two-to-one' &&
|
|
1732
|
-
el.match.relation.nextRound.mirrorRoundType === null) {
|
|
1733
|
-
// draw a straight line for the special case of connecting the final match to the mirrored semi final match
|
|
1734
|
-
const next = dimensions.bracketGrid.matchElementMap.getOrThrow(el.match.relation.nextMatch.id);
|
|
1735
|
-
const nextPos = makePos(next.dimensions);
|
|
1736
|
-
svgParts.push(linePath(nextPos, currentPos, { path: pathOptions }));
|
|
1737
|
-
}
|
|
1738
|
-
break;
|
|
1739
|
-
}
|
|
1740
|
-
}
|
|
1575
|
+
const FALLBACK_MATCH_RELATION_POSITION = -1;
|
|
1576
|
+
const generateMatchPosition = (match, factor) => Math.ceil(match.position * factor);
|
|
1577
|
+
const generateMatchRelationPositions = (relation, match) => {
|
|
1578
|
+
switch (relation.type) {
|
|
1579
|
+
case 'nothing-to-one':
|
|
1580
|
+
return {
|
|
1581
|
+
nextRoundMatchPosition: generateMatchPosition(match, relation.nextRoundMatchFactor),
|
|
1582
|
+
previousUpperRoundMatchPosition: FALLBACK_MATCH_RELATION_POSITION,
|
|
1583
|
+
previousLowerRoundMatchPosition: FALLBACK_MATCH_RELATION_POSITION,
|
|
1584
|
+
};
|
|
1585
|
+
case 'one-to-nothing': {
|
|
1586
|
+
const double = relation.previousRoundMatchFactor === 2 ? 1 : 0;
|
|
1587
|
+
return {
|
|
1588
|
+
nextRoundMatchPosition: FALLBACK_MATCH_RELATION_POSITION,
|
|
1589
|
+
previousUpperRoundMatchPosition: (generateMatchPosition(match, relation.previousRoundMatchFactor) -
|
|
1590
|
+
double),
|
|
1591
|
+
previousLowerRoundMatchPosition: generateMatchPosition(match, relation.previousRoundMatchFactor),
|
|
1592
|
+
};
|
|
1593
|
+
}
|
|
1594
|
+
case 'one-to-one': {
|
|
1595
|
+
const double = relation.previousRoundMatchFactor === 2 ? 1 : 0;
|
|
1596
|
+
return {
|
|
1597
|
+
nextRoundMatchPosition: generateMatchPosition(match, relation.nextRoundMatchFactor),
|
|
1598
|
+
previousUpperRoundMatchPosition: (generateMatchPosition(match, relation.previousRoundMatchFactor) -
|
|
1599
|
+
double),
|
|
1600
|
+
previousLowerRoundMatchPosition: generateMatchPosition(match, relation.previousRoundMatchFactor),
|
|
1601
|
+
};
|
|
1741
1602
|
}
|
|
1603
|
+
case 'two-to-one':
|
|
1604
|
+
return {
|
|
1605
|
+
nextRoundMatchPosition: generateMatchPosition(match, relation.nextRoundMatchFactor),
|
|
1606
|
+
previousUpperRoundMatchPosition: generateMatchPosition(match, relation.previousUpperRoundMatchFactor),
|
|
1607
|
+
previousLowerRoundMatchPosition: generateMatchPosition(match, relation.previousLowerRoundMatchFactor),
|
|
1608
|
+
};
|
|
1609
|
+
case 'two-to-nothing':
|
|
1610
|
+
return {
|
|
1611
|
+
nextRoundMatchPosition: FALLBACK_MATCH_RELATION_POSITION,
|
|
1612
|
+
previousUpperRoundMatchPosition: generateMatchPosition(match, relation.previousUpperRoundMatchFactor),
|
|
1613
|
+
previousLowerRoundMatchPosition: generateMatchPosition(match, relation.previousLowerRoundMatchFactor),
|
|
1614
|
+
};
|
|
1742
1615
|
}
|
|
1743
|
-
return svgParts.join('');
|
|
1744
1616
|
};
|
|
1745
|
-
|
|
1746
|
-
const
|
|
1747
|
-
const
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
width: 0,
|
|
1751
|
-
height: elementPartHeight,
|
|
1752
|
-
top: 0,
|
|
1753
|
-
left: 0,
|
|
1754
|
-
},
|
|
1755
|
-
};
|
|
1617
|
+
const createNothingToOneRelation$1 = (params) => {
|
|
1618
|
+
const { match, relation, matchPositionMaps, nextRoundMatchPosition } = params;
|
|
1619
|
+
const nextMatch = matchPositionMaps.get(relation.nextRound.id)?.get(nextRoundMatchPosition);
|
|
1620
|
+
if (!nextMatch)
|
|
1621
|
+
throw new Error('Next round match not found');
|
|
1756
1622
|
return {
|
|
1757
|
-
|
|
1623
|
+
type: 'nothing-to-one',
|
|
1624
|
+
currentMatch: match,
|
|
1625
|
+
currentRound: relation.currentRound,
|
|
1626
|
+
nextMatch,
|
|
1627
|
+
nextRound: relation.nextRound,
|
|
1758
1628
|
};
|
|
1759
1629
|
};
|
|
1760
|
-
|
|
1761
|
-
const
|
|
1762
|
-
const
|
|
1763
|
-
const
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
round: config.round,
|
|
1788
|
-
roundSwissGroup: config.roundSwissGroup,
|
|
1789
|
-
};
|
|
1790
|
-
case 'match':
|
|
1791
|
-
return {
|
|
1792
|
-
...newElementBase,
|
|
1793
|
-
type,
|
|
1794
|
-
component: config.component,
|
|
1795
|
-
match: config.match,
|
|
1796
|
-
round: config.round,
|
|
1797
|
-
roundSwissGroup: config.roundSwissGroup,
|
|
1798
|
-
};
|
|
1799
|
-
case 'matchGap':
|
|
1800
|
-
case 'roundHeaderGap':
|
|
1801
|
-
case 'roundGap':
|
|
1802
|
-
case 'colGap':
|
|
1803
|
-
return {
|
|
1804
|
-
...newElementBase,
|
|
1805
|
-
type,
|
|
1806
|
-
};
|
|
1807
|
-
default:
|
|
1808
|
-
throw new Error(`Unknown element type: ${type}`);
|
|
1809
|
-
}
|
|
1810
|
-
})();
|
|
1811
|
-
const pushPart = (...newParts) => {
|
|
1812
|
-
parts.push(...newParts);
|
|
1813
|
-
};
|
|
1814
|
-
for (const partHeight of partHeights) {
|
|
1815
|
-
const { elementPart } = createBracketElementPart({
|
|
1816
|
-
elementPartHeight: partHeight,
|
|
1817
|
-
});
|
|
1818
|
-
pushPart(elementPart);
|
|
1630
|
+
const createOneToNothingOrTwoToNothingRelation = (params) => {
|
|
1631
|
+
const { match, relation, matchPositionMaps, previousUpperRoundMatchPosition, previousLowerRoundMatchPosition } = params;
|
|
1632
|
+
const previousUpperMatch = matchPositionMaps.get(relation.previousRound.id)?.get(previousUpperRoundMatchPosition);
|
|
1633
|
+
const previousLowerMatch = matchPositionMaps.get(relation.previousRound.id)?.get(previousLowerRoundMatchPosition);
|
|
1634
|
+
if (!previousUpperMatch)
|
|
1635
|
+
throw new Error('Previous round match not found');
|
|
1636
|
+
if (previousUpperRoundMatchPosition !== previousLowerRoundMatchPosition) {
|
|
1637
|
+
if (!previousLowerMatch)
|
|
1638
|
+
throw new Error('Previous lower round match not found');
|
|
1639
|
+
return {
|
|
1640
|
+
type: 'two-to-nothing',
|
|
1641
|
+
currentMatch: match,
|
|
1642
|
+
currentRound: relation.currentRound,
|
|
1643
|
+
previousUpperMatch,
|
|
1644
|
+
previousUpperRound: relation.previousRound,
|
|
1645
|
+
previousLowerMatch,
|
|
1646
|
+
previousLowerRound: relation.previousRound,
|
|
1647
|
+
};
|
|
1648
|
+
}
|
|
1649
|
+
else {
|
|
1650
|
+
return {
|
|
1651
|
+
type: 'one-to-nothing',
|
|
1652
|
+
currentMatch: match,
|
|
1653
|
+
currentRound: relation.currentRound,
|
|
1654
|
+
previousMatch: previousUpperMatch,
|
|
1655
|
+
previousRound: relation.previousRound,
|
|
1656
|
+
};
|
|
1819
1657
|
}
|
|
1820
|
-
return {
|
|
1821
|
-
element: newElement,
|
|
1822
|
-
};
|
|
1823
1658
|
};
|
|
1824
|
-
|
|
1825
|
-
const
|
|
1826
|
-
const
|
|
1827
|
-
const
|
|
1828
|
-
const
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
roundSwissGroup: element.roundSwissGroup,
|
|
1859
|
-
};
|
|
1860
|
-
elements.push(matchEl);
|
|
1861
|
-
finalizedElementMap.set(element.match.id, matchEl);
|
|
1862
|
-
}
|
|
1863
|
-
}
|
|
1864
|
-
if (!lastSubColumn.span.isEnd) {
|
|
1865
|
-
for (let index = masterColumnIndex + 1; index < grid.grid.masterColumns.length; index++) {
|
|
1866
|
-
const nextMaster = grid.grid.masterColumns[index];
|
|
1867
|
-
const nextSection = nextMaster?.sections[sectionIndex];
|
|
1868
|
-
if (!nextSection)
|
|
1869
|
-
break;
|
|
1870
|
-
sectionWidth += nextSection.dimensions.width;
|
|
1871
|
-
ignoredSections.push({ masterColumnIndex: index, sectionIndex });
|
|
1872
|
-
if (nextSection.subColumns.some((sc) => sc.span.isEnd)) {
|
|
1873
|
-
break;
|
|
1874
|
-
}
|
|
1875
|
-
}
|
|
1876
|
-
}
|
|
1877
|
-
if (!elements.length)
|
|
1878
|
-
continue;
|
|
1879
|
-
finalizedColumns.push({
|
|
1880
|
-
dimensions: {
|
|
1881
|
-
...section.dimensions,
|
|
1882
|
-
width: sectionWidth,
|
|
1883
|
-
},
|
|
1884
|
-
elements,
|
|
1885
|
-
});
|
|
1886
|
-
}
|
|
1659
|
+
const createOneToOneOrTwoToOneRelation = (params) => {
|
|
1660
|
+
const { match, relation, matchPositionMaps, nextRoundMatchPosition, previousUpperRoundMatchPosition, previousLowerRoundMatchPosition, } = params;
|
|
1661
|
+
const nextMatch = matchPositionMaps.get(relation.nextRound.id)?.get(nextRoundMatchPosition);
|
|
1662
|
+
const previousUpperMatch = matchPositionMaps.get(relation.previousRound.id)?.get(previousUpperRoundMatchPosition);
|
|
1663
|
+
const previousLowerMatch = matchPositionMaps.get(relation.previousRound.id)?.get(previousLowerRoundMatchPosition);
|
|
1664
|
+
if (!nextMatch)
|
|
1665
|
+
throw new Error('Next round match not found');
|
|
1666
|
+
if (!previousUpperMatch)
|
|
1667
|
+
throw new Error('Previous upper round match not found');
|
|
1668
|
+
if (!previousLowerMatch)
|
|
1669
|
+
throw new Error('Previous lower round match not found');
|
|
1670
|
+
if (previousUpperRoundMatchPosition === previousLowerRoundMatchPosition) {
|
|
1671
|
+
return {
|
|
1672
|
+
type: 'one-to-one',
|
|
1673
|
+
currentMatch: match,
|
|
1674
|
+
currentRound: relation.currentRound,
|
|
1675
|
+
previousMatch: previousUpperMatch,
|
|
1676
|
+
previousRound: relation.previousRound,
|
|
1677
|
+
nextMatch,
|
|
1678
|
+
nextRound: relation.nextRound,
|
|
1679
|
+
};
|
|
1680
|
+
}
|
|
1681
|
+
else {
|
|
1682
|
+
return {
|
|
1683
|
+
type: 'two-to-one',
|
|
1684
|
+
currentMatch: match,
|
|
1685
|
+
currentRound: relation.currentRound,
|
|
1686
|
+
previousUpperMatch,
|
|
1687
|
+
previousUpperRound: relation.previousRound,
|
|
1688
|
+
previousLowerMatch,
|
|
1689
|
+
previousLowerRound: relation.previousRound,
|
|
1690
|
+
nextMatch,
|
|
1691
|
+
nextRound: relation.nextRound,
|
|
1692
|
+
};
|
|
1887
1693
|
}
|
|
1694
|
+
};
|
|
1695
|
+
const createTwoToOneRelation$1 = (params) => {
|
|
1696
|
+
const { match, relation, matchPositionMaps, nextRoundMatchPosition, previousUpperRoundMatchPosition, previousLowerRoundMatchPosition, } = params;
|
|
1697
|
+
const nextMatch = matchPositionMaps.get(relation.nextRound.id)?.get(nextRoundMatchPosition);
|
|
1698
|
+
const previousUpperMatch = matchPositionMaps
|
|
1699
|
+
.get(relation.previousUpperRound.id)
|
|
1700
|
+
?.get(previousUpperRoundMatchPosition);
|
|
1701
|
+
const previousLowerMatch = matchPositionMaps
|
|
1702
|
+
.get(relation.previousLowerRound.id)
|
|
1703
|
+
?.get(previousLowerRoundMatchPosition);
|
|
1704
|
+
if (!nextMatch)
|
|
1705
|
+
throw new Error('Next round match not found');
|
|
1706
|
+
if (!previousUpperMatch)
|
|
1707
|
+
throw new Error('Previous upper round match not found');
|
|
1708
|
+
if (!previousLowerMatch)
|
|
1709
|
+
throw new Error('Previous lower round match not found');
|
|
1888
1710
|
return {
|
|
1889
|
-
|
|
1890
|
-
|
|
1711
|
+
type: 'two-to-one',
|
|
1712
|
+
currentMatch: match,
|
|
1713
|
+
currentRound: relation.currentRound,
|
|
1714
|
+
previousUpperMatch,
|
|
1715
|
+
previousUpperRound: relation.previousUpperRound,
|
|
1716
|
+
previousLowerMatch,
|
|
1717
|
+
previousLowerRound: relation.previousLowerRound,
|
|
1718
|
+
nextMatch,
|
|
1719
|
+
nextRound: relation.nextRound,
|
|
1891
1720
|
};
|
|
1892
1721
|
};
|
|
1893
|
-
|
|
1894
|
-
const
|
|
1895
|
-
const
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
const
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1722
|
+
const createTwoToNothingRelation$1 = (params) => {
|
|
1723
|
+
const { match, relation, matchPositionMaps, previousUpperRoundMatchPosition, previousLowerRoundMatchPosition } = params;
|
|
1724
|
+
const previousUpperMatch = matchPositionMaps
|
|
1725
|
+
.get(relation.previousUpperRound.id)
|
|
1726
|
+
?.get(previousUpperRoundMatchPosition);
|
|
1727
|
+
const previousLowerMatch = matchPositionMaps
|
|
1728
|
+
.get(relation.previousUpperRound.id)
|
|
1729
|
+
?.get(previousLowerRoundMatchPosition);
|
|
1730
|
+
if (!previousUpperMatch)
|
|
1731
|
+
throw new Error('Previous upper round match not found');
|
|
1732
|
+
if (!previousLowerMatch)
|
|
1733
|
+
throw new Error('Previous lower round match not found');
|
|
1734
|
+
return {
|
|
1735
|
+
type: 'two-to-nothing',
|
|
1736
|
+
currentMatch: match,
|
|
1737
|
+
currentRound: relation.currentRound,
|
|
1738
|
+
previousUpperMatch,
|
|
1739
|
+
previousUpperRound: relation.previousUpperRound,
|
|
1740
|
+
previousLowerMatch,
|
|
1741
|
+
previousLowerRound: relation.previousUpperRound,
|
|
1904
1742
|
};
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
element.containerDimensions.left = currentSubColumnLeft;
|
|
1964
|
-
element.containerDimensions.top = subColumn.dimensions.top + totalSubColumnHeight;
|
|
1965
|
-
element.dimensions.width = subColumnWidth;
|
|
1966
|
-
element.dimensions.left = currentSubColumnLeft;
|
|
1967
|
-
element.dimensions.top =
|
|
1968
|
-
element.containerDimensions.top + (totalElementHeight - element.dimensions.height) * 0.5;
|
|
1969
|
-
totalSubColumnHeight += totalElementHeight;
|
|
1970
|
-
}
|
|
1971
|
-
subColumn.dimensions.height = totalSubColumnHeight;
|
|
1972
|
-
if (totalSubColumnHeight > maxSectionHeight)
|
|
1973
|
-
maxSectionHeight = totalSubColumnHeight;
|
|
1974
|
-
currentSubColumnLeft += subColumnWidth;
|
|
1975
|
-
}
|
|
1976
|
-
section.dimensions.height = maxSectionHeight + padding.bottom;
|
|
1977
|
-
runningTop += maxSectionHeight + padding.bottom;
|
|
1978
|
-
}
|
|
1979
|
-
masterColumn.dimensions.height = runningTop;
|
|
1980
|
-
if (masterColumn.dimensions.height > maxGridHeight)
|
|
1981
|
-
maxGridHeight = masterColumn.dimensions.height;
|
|
1982
|
-
currentMasterColumnLeft += masterColumn.dimensions.width;
|
|
1743
|
+
};
|
|
1744
|
+
const generateMatchRelationsNew = (bracketData) => {
|
|
1745
|
+
const matchRelations = [];
|
|
1746
|
+
const matchPositionMaps = new Map();
|
|
1747
|
+
for (const round of bracketData.rounds.values()) {
|
|
1748
|
+
const matchMap = new Map([...round.matches.values()].map((m) => [m.position, m]));
|
|
1749
|
+
matchPositionMaps.set(round.id, matchMap);
|
|
1750
|
+
}
|
|
1751
|
+
for (const match of bracketData.matches.values()) {
|
|
1752
|
+
const relation = match.round.relation;
|
|
1753
|
+
const { nextRoundMatchPosition, previousUpperRoundMatchPosition, previousLowerRoundMatchPosition } = generateMatchRelationPositions(relation, match);
|
|
1754
|
+
switch (relation.type) {
|
|
1755
|
+
case 'nothing-to-one':
|
|
1756
|
+
matchRelations.push(createNothingToOneRelation$1({
|
|
1757
|
+
match,
|
|
1758
|
+
relation,
|
|
1759
|
+
matchPositionMaps,
|
|
1760
|
+
nextRoundMatchPosition,
|
|
1761
|
+
}));
|
|
1762
|
+
break;
|
|
1763
|
+
case 'one-to-nothing':
|
|
1764
|
+
matchRelations.push(createOneToNothingOrTwoToNothingRelation({
|
|
1765
|
+
match,
|
|
1766
|
+
relation,
|
|
1767
|
+
matchPositionMaps,
|
|
1768
|
+
previousUpperRoundMatchPosition,
|
|
1769
|
+
previousLowerRoundMatchPosition,
|
|
1770
|
+
}));
|
|
1771
|
+
break;
|
|
1772
|
+
case 'one-to-one':
|
|
1773
|
+
matchRelations.push(createOneToOneOrTwoToOneRelation({
|
|
1774
|
+
match,
|
|
1775
|
+
relation,
|
|
1776
|
+
matchPositionMaps,
|
|
1777
|
+
nextRoundMatchPosition,
|
|
1778
|
+
previousUpperRoundMatchPosition,
|
|
1779
|
+
previousLowerRoundMatchPosition,
|
|
1780
|
+
}));
|
|
1781
|
+
break;
|
|
1782
|
+
case 'two-to-one':
|
|
1783
|
+
matchRelations.push(createTwoToOneRelation$1({
|
|
1784
|
+
match,
|
|
1785
|
+
relation,
|
|
1786
|
+
matchPositionMaps,
|
|
1787
|
+
nextRoundMatchPosition,
|
|
1788
|
+
previousUpperRoundMatchPosition,
|
|
1789
|
+
previousLowerRoundMatchPosition,
|
|
1790
|
+
}));
|
|
1791
|
+
break;
|
|
1792
|
+
case 'two-to-nothing':
|
|
1793
|
+
matchRelations.push(createTwoToNothingRelation$1({
|
|
1794
|
+
match,
|
|
1795
|
+
relation,
|
|
1796
|
+
matchPositionMaps,
|
|
1797
|
+
previousUpperRoundMatchPosition,
|
|
1798
|
+
previousLowerRoundMatchPosition,
|
|
1799
|
+
}));
|
|
1800
|
+
break;
|
|
1983
1801
|
}
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1802
|
+
}
|
|
1803
|
+
return matchRelations;
|
|
1804
|
+
};
|
|
1805
|
+
|
|
1806
|
+
const calculateMatchFactor = (numeratorRound, denominatorRound) => numeratorRound.matchCount / denominatorRound.matchCount;
|
|
1807
|
+
const createNothingToOneRelation = (params) => ({
|
|
1808
|
+
type: 'nothing-to-one',
|
|
1809
|
+
currentRound: params.currentRound,
|
|
1810
|
+
nextRound: params.nextRound,
|
|
1811
|
+
nextRoundMatchFactor: calculateMatchFactor(params.nextRound, params.currentRound),
|
|
1812
|
+
});
|
|
1813
|
+
const createOneToNothingRelation = (params) => ({
|
|
1814
|
+
type: 'one-to-nothing',
|
|
1815
|
+
currentRound: params.currentRound,
|
|
1816
|
+
previousRound: params.previousRound,
|
|
1817
|
+
previousRoundMatchFactor: calculateMatchFactor(params.previousRound, params.currentRound),
|
|
1818
|
+
rootRoundMatchFactor: calculateMatchFactor(params.rootRound, params.currentRound),
|
|
1819
|
+
});
|
|
1820
|
+
const createOneToOneRelation = (params) => ({
|
|
1821
|
+
type: 'one-to-one',
|
|
1822
|
+
currentRound: params.currentRound,
|
|
1823
|
+
previousRound: params.previousRound,
|
|
1824
|
+
nextRound: params.nextRound,
|
|
1825
|
+
nextRoundMatchFactor: calculateMatchFactor(params.nextRound, params.currentRound),
|
|
1826
|
+
previousRoundMatchFactor: calculateMatchFactor(params.previousRound, params.currentRound),
|
|
1827
|
+
rootRoundMatchFactor: calculateMatchFactor(params.rootRound, params.currentRound),
|
|
1828
|
+
});
|
|
1829
|
+
const createTwoToOneRelation = (params) => ({
|
|
1830
|
+
type: 'two-to-one',
|
|
1831
|
+
currentRound: params.currentRound,
|
|
1832
|
+
previousUpperRound: params.previousUpperRound,
|
|
1833
|
+
previousLowerRound: params.previousLowerRound,
|
|
1834
|
+
nextRound: params.nextRound,
|
|
1835
|
+
nextRoundMatchFactor: calculateMatchFactor(params.nextRound, params.currentRound),
|
|
1836
|
+
previousUpperRoundMatchFactor: calculateMatchFactor(params.previousUpperRound, params.currentRound),
|
|
1837
|
+
previousLowerRoundMatchFactor: calculateMatchFactor(params.previousLowerRound, params.currentRound),
|
|
1838
|
+
upperRootRoundMatchFactor: calculateMatchFactor(params.firstUpperRound, params.currentRound),
|
|
1839
|
+
lowerRootRoundMatchFactor: calculateMatchFactor(params.firstLowerRound, params.currentRound),
|
|
1840
|
+
});
|
|
1841
|
+
const createTwoToNothingRelation = (params) => ({
|
|
1842
|
+
type: 'two-to-nothing',
|
|
1843
|
+
currentRound: params.currentRound,
|
|
1844
|
+
previousUpperRound: params.previousUpperRound,
|
|
1845
|
+
previousLowerRound: params.previousLowerRound,
|
|
1846
|
+
previousUpperRoundMatchFactor: calculateMatchFactor(params.previousUpperRound, params.currentRound),
|
|
1847
|
+
previousLowerRoundMatchFactor: calculateMatchFactor(params.previousLowerRound, params.currentRound),
|
|
1848
|
+
upperRootRoundMatchFactor: calculateMatchFactor(params.firstUpperRound, params.currentRound),
|
|
1849
|
+
lowerRootRoundMatchFactor: calculateMatchFactor(params.firstLowerRound, params.currentRound),
|
|
1850
|
+
});
|
|
1851
|
+
const getNavigationContext = (params) => {
|
|
1852
|
+
const { upperRounds, currentUpperRoundIndex } = params;
|
|
1853
|
+
const currentUpperRound = upperRounds[currentUpperRoundIndex];
|
|
1854
|
+
if (!currentUpperRound)
|
|
1855
|
+
throw new Error('currentUpperRound is null');
|
|
1856
|
+
const isLeftToRight = !currentUpperRound.mirrorRoundType || currentUpperRound.mirrorRoundType === BRACKET_ROUND_MIRROR_TYPE.LEFT;
|
|
1857
|
+
const relativePrevious = upperRounds[currentUpperRoundIndex - 1] || null;
|
|
1858
|
+
const relativeNext = upperRounds[currentUpperRoundIndex + 1] || null;
|
|
1859
|
+
const previousUpperRound = isLeftToRight ? relativePrevious : relativeNext;
|
|
1860
|
+
const nextUpperRound = isLeftToRight ? relativeNext : relativePrevious;
|
|
1861
|
+
const isLastUpperRound = !nextUpperRound ||
|
|
1862
|
+
(nextUpperRound.mirrorRoundType === BRACKET_ROUND_MIRROR_TYPE.RIGHT && !currentUpperRound.mirrorRoundType);
|
|
1863
|
+
const isFinal = currentUpperRound.type === COMMON_BRACKET_ROUND_TYPE.FINAL;
|
|
1864
|
+
return {
|
|
1865
|
+
currentUpperRound,
|
|
1866
|
+
previousUpperRound,
|
|
1867
|
+
nextUpperRound,
|
|
1868
|
+
isLastUpperRound,
|
|
1869
|
+
isFinal,
|
|
1987
1870
|
};
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
1871
|
+
};
|
|
1872
|
+
const handleFinalRound = (params) => {
|
|
1873
|
+
const { relations, currentUpperRound, previousUpperRound, nextUpperRound, lowerRounds, currentUpperRoundIndex, firstUpperRound, firstLowerRound, lastLowerRound, } = params;
|
|
1874
|
+
const currentLowerRound = lowerRounds[currentUpperRoundIndex] || null;
|
|
1875
|
+
const nextLowerRound = lowerRounds[currentUpperRoundIndex + 1] || null;
|
|
1876
|
+
const previousLowerRound = lowerRounds[currentUpperRoundIndex - 1] || null;
|
|
1877
|
+
if (!currentLowerRound)
|
|
1878
|
+
throw new Error('currentLowerRound is null');
|
|
1879
|
+
const isAsyncBracket = currentLowerRound.id !== lastLowerRound.id;
|
|
1880
|
+
const finalLowerRound = isAsyncBracket ? nextLowerRound : currentLowerRound;
|
|
1881
|
+
if (!finalLowerRound)
|
|
1882
|
+
throw new Error('finalLowerRound is null');
|
|
1883
|
+
if (finalLowerRound.id !== lastLowerRound.id)
|
|
1884
|
+
throw new Error('finalLowerRound is not the last lower round');
|
|
1885
|
+
if (nextUpperRound) {
|
|
1886
|
+
relations.push(createTwoToOneRelation({
|
|
1887
|
+
currentRound: currentUpperRound,
|
|
1888
|
+
previousUpperRound,
|
|
1889
|
+
previousLowerRound: finalLowerRound,
|
|
1890
|
+
nextRound: nextUpperRound,
|
|
1891
|
+
firstUpperRound,
|
|
1892
|
+
firstLowerRound,
|
|
1893
|
+
}));
|
|
1894
|
+
}
|
|
1895
|
+
else {
|
|
1896
|
+
relations.push(createTwoToNothingRelation({
|
|
1897
|
+
currentRound: currentUpperRound,
|
|
1898
|
+
previousUpperRound,
|
|
1899
|
+
previousLowerRound: finalLowerRound,
|
|
1900
|
+
firstUpperRound,
|
|
1901
|
+
firstLowerRound,
|
|
1902
|
+
}));
|
|
1903
|
+
}
|
|
1904
|
+
if (isAsyncBracket) {
|
|
1905
|
+
const preFinalLowerRound = lowerRounds[lowerRounds.length - 2];
|
|
1906
|
+
const prePreFinalLowerRound = lowerRounds[lowerRounds.length - 3] || null;
|
|
1907
|
+
if (!preFinalLowerRound)
|
|
1908
|
+
throw new Error('preFinalLowerRound is null');
|
|
1909
|
+
relations.push(createOneToOneRelation({
|
|
1910
|
+
currentRound: finalLowerRound,
|
|
1911
|
+
previousRound: preFinalLowerRound,
|
|
1912
|
+
nextRound: currentUpperRound,
|
|
1913
|
+
rootRound: firstLowerRound,
|
|
1914
|
+
}));
|
|
1915
|
+
if (prePreFinalLowerRound) {
|
|
1916
|
+
relations.push(createOneToOneRelation({
|
|
1917
|
+
currentRound: preFinalLowerRound,
|
|
1918
|
+
previousRound: prePreFinalLowerRound,
|
|
1919
|
+
nextRound: finalLowerRound,
|
|
1920
|
+
rootRound: firstLowerRound,
|
|
1921
|
+
}));
|
|
2022
1922
|
}
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
if (span.masterColumnStart === span.masterColumnEnd) {
|
|
2029
|
-
const masterColumn = masterColumns[span.masterColumnStart];
|
|
2030
|
-
if (masterColumn) {
|
|
2031
|
-
const section = masterColumn.sections[span.sectionStart];
|
|
2032
|
-
if (section) {
|
|
2033
|
-
const subColumnWidth = section.dimensions.width / section.subColumns.length;
|
|
2034
|
-
const totalWidth = subColumnWidth * (span.subColumnEnd - span.subColumnStart + 1);
|
|
2035
|
-
spannedWidthCache.set(key, totalWidth);
|
|
2036
|
-
return totalWidth;
|
|
2037
|
-
}
|
|
2038
|
-
}
|
|
2039
|
-
spannedWidthCache.set(key, 0);
|
|
2040
|
-
return 0;
|
|
1923
|
+
else {
|
|
1924
|
+
relations.push(createNothingToOneRelation({
|
|
1925
|
+
currentRound: preFinalLowerRound,
|
|
1926
|
+
nextRound: finalLowerRound,
|
|
1927
|
+
}));
|
|
2041
1928
|
}
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
1929
|
+
}
|
|
1930
|
+
else {
|
|
1931
|
+
if (!previousLowerRound)
|
|
1932
|
+
throw new Error('previousLowerRound is null');
|
|
1933
|
+
relations.push(createOneToOneRelation({
|
|
1934
|
+
currentRound: finalLowerRound,
|
|
1935
|
+
previousRound: previousLowerRound,
|
|
1936
|
+
nextRound: currentUpperRound,
|
|
1937
|
+
rootRound: firstLowerRound,
|
|
1938
|
+
}));
|
|
1939
|
+
}
|
|
1940
|
+
};
|
|
1941
|
+
const handleFirstRound = (params) => {
|
|
1942
|
+
const { relations, currentUpperRound, nextUpperRound, lowerRounds, currentUpperRoundIndex } = params;
|
|
1943
|
+
relations.push(createNothingToOneRelation({
|
|
1944
|
+
currentRound: currentUpperRound,
|
|
1945
|
+
nextRound: nextUpperRound,
|
|
1946
|
+
}));
|
|
1947
|
+
const currentLowerRound = lowerRounds[currentUpperRoundIndex] || null;
|
|
1948
|
+
const nextLowerRound = lowerRounds[currentUpperRoundIndex + 1] || null;
|
|
1949
|
+
if (currentLowerRound && nextLowerRound) {
|
|
1950
|
+
relations.push(createNothingToOneRelation({
|
|
1951
|
+
currentRound: currentLowerRound,
|
|
1952
|
+
nextRound: nextLowerRound,
|
|
1953
|
+
}));
|
|
1954
|
+
}
|
|
1955
|
+
};
|
|
1956
|
+
const handleRegularRound = (params) => {
|
|
1957
|
+
const { relations, currentUpperRound, previousUpperRound, nextUpperRound, lowerRounds, currentUpperRoundIndex, firstUpperRound, firstLowerRound, } = params;
|
|
1958
|
+
relations.push(createOneToOneRelation({
|
|
1959
|
+
currentRound: currentUpperRound,
|
|
1960
|
+
previousRound: previousUpperRound,
|
|
1961
|
+
nextRound: nextUpperRound,
|
|
1962
|
+
rootRound: firstUpperRound,
|
|
1963
|
+
}));
|
|
1964
|
+
const currentLowerRound = lowerRounds[currentUpperRoundIndex] || null;
|
|
1965
|
+
const previousLowerRound = lowerRounds[currentUpperRoundIndex - 1] || null;
|
|
1966
|
+
const nextLowerRound = lowerRounds[currentUpperRoundIndex + 1] || null;
|
|
1967
|
+
if (currentLowerRound &&
|
|
1968
|
+
currentUpperRound.type === DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.UPPER_BRACKET &&
|
|
1969
|
+
previousLowerRound &&
|
|
1970
|
+
nextLowerRound &&
|
|
1971
|
+
firstLowerRound) {
|
|
1972
|
+
relations.push(createOneToOneRelation({
|
|
1973
|
+
currentRound: currentLowerRound,
|
|
1974
|
+
previousRound: previousLowerRound,
|
|
1975
|
+
nextRound: nextLowerRound,
|
|
1976
|
+
rootRound: firstLowerRound,
|
|
1977
|
+
}));
|
|
1978
|
+
}
|
|
1979
|
+
};
|
|
1980
|
+
const generateRoundRelationsNew = (bracketData) => {
|
|
1981
|
+
const relations = [];
|
|
1982
|
+
const allRounds = [...bracketData.rounds.values()];
|
|
1983
|
+
const upperRounds = allRounds.filter((r) => r.type !== DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.LOWER_BRACKET);
|
|
1984
|
+
const lowerRounds = allRounds.filter((r) => r.type === DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.LOWER_BRACKET);
|
|
1985
|
+
const firstUpperRound = upperRounds[0];
|
|
1986
|
+
const firstLowerRound = lowerRounds[0] || null;
|
|
1987
|
+
const lastLowerRound = lowerRounds[lowerRounds.length - 1] || null;
|
|
1988
|
+
if (!firstUpperRound)
|
|
1989
|
+
throw new Error('No upper rounds found');
|
|
1990
|
+
const hasLowerRounds = lowerRounds.length > 0;
|
|
1991
|
+
for (const [currentUpperRoundIndex] of upperRounds.entries()) {
|
|
1992
|
+
const nav = getNavigationContext({
|
|
1993
|
+
upperRounds,
|
|
1994
|
+
currentUpperRoundIndex,
|
|
1995
|
+
});
|
|
1996
|
+
if (nav.isFinal && hasLowerRounds && lastLowerRound && firstLowerRound && nav.previousUpperRound) {
|
|
1997
|
+
handleFinalRound({
|
|
1998
|
+
relations,
|
|
1999
|
+
currentUpperRound: nav.currentUpperRound,
|
|
2000
|
+
previousUpperRound: nav.previousUpperRound,
|
|
2001
|
+
nextUpperRound: nav.nextUpperRound,
|
|
2002
|
+
lowerRounds,
|
|
2003
|
+
currentUpperRoundIndex,
|
|
2004
|
+
firstUpperRound,
|
|
2005
|
+
firstLowerRound,
|
|
2006
|
+
lastLowerRound,
|
|
2007
|
+
});
|
|
2008
|
+
}
|
|
2009
|
+
else if (nav.isLastUpperRound && nav.previousUpperRound) {
|
|
2010
|
+
relations.push(createOneToNothingRelation({
|
|
2011
|
+
currentRound: nav.currentUpperRound,
|
|
2012
|
+
previousRound: nav.previousUpperRound,
|
|
2013
|
+
rootRound: firstUpperRound,
|
|
2014
|
+
}));
|
|
2015
|
+
}
|
|
2016
|
+
else if (nav.currentUpperRound.isFirstRound && nav.nextUpperRound) {
|
|
2017
|
+
handleFirstRound({
|
|
2018
|
+
relations,
|
|
2019
|
+
currentUpperRound: nav.currentUpperRound,
|
|
2020
|
+
nextUpperRound: nav.nextUpperRound,
|
|
2021
|
+
lowerRounds,
|
|
2022
|
+
currentUpperRoundIndex,
|
|
2023
|
+
});
|
|
2024
|
+
}
|
|
2025
|
+
else if (nav.previousUpperRound && nav.nextUpperRound) {
|
|
2026
|
+
handleRegularRound({
|
|
2027
|
+
relations,
|
|
2028
|
+
currentUpperRound: nav.currentUpperRound,
|
|
2029
|
+
previousUpperRound: nav.previousUpperRound,
|
|
2030
|
+
nextUpperRound: nav.nextUpperRound,
|
|
2031
|
+
lowerRounds,
|
|
2032
|
+
currentUpperRoundIndex,
|
|
2033
|
+
firstUpperRound,
|
|
2034
|
+
firstLowerRound,
|
|
2035
|
+
});
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
return relations;
|
|
2039
|
+
};
|
|
2040
|
+
|
|
2041
|
+
const createNewBracket = (source, options) => {
|
|
2042
|
+
const bracketNewBase = createNewBracketBase(source, options);
|
|
2043
|
+
const rounds = new BracketMap();
|
|
2044
|
+
const roundsByType = new BracketMap();
|
|
2045
|
+
for (const roundBase of bracketNewBase.rounds.values()) {
|
|
2046
|
+
const newRound = {
|
|
2047
|
+
...roundBase,
|
|
2048
|
+
matches: new BracketMap(),
|
|
2049
|
+
relation: { type: 'dummy' },
|
|
2050
|
+
};
|
|
2051
|
+
rounds.set(roundBase.id, newRound);
|
|
2052
|
+
if (!roundsByType.has(roundBase.type)) {
|
|
2053
|
+
roundsByType.set(roundBase.type, new BracketMap());
|
|
2054
|
+
}
|
|
2055
|
+
roundsByType.getOrThrow(roundBase.type).set(roundBase.id, newRound);
|
|
2056
|
+
}
|
|
2057
|
+
const participants = new BracketMap();
|
|
2058
|
+
for (const participantBase of bracketNewBase.participants.values()) {
|
|
2059
|
+
participants.set(participantBase.id, {
|
|
2060
|
+
...participantBase,
|
|
2061
|
+
matches: new BracketMap(),
|
|
2062
|
+
});
|
|
2063
|
+
}
|
|
2064
|
+
const matches = new BracketMap();
|
|
2065
|
+
for (const matchBase of bracketNewBase.matches.values()) {
|
|
2066
|
+
const round = rounds.getOrThrow(matchBase.roundId);
|
|
2067
|
+
const homeParticipant = matchBase.home
|
|
2068
|
+
? { ...matchBase.home, matches: new BracketMap() }
|
|
2069
|
+
: null;
|
|
2070
|
+
const awayParticipant = matchBase.away
|
|
2071
|
+
? { ...matchBase.away, matches: new BracketMap() }
|
|
2072
|
+
: null;
|
|
2073
|
+
const newMatch = {
|
|
2074
|
+
...matchBase,
|
|
2075
|
+
home: homeParticipant,
|
|
2076
|
+
away: awayParticipant,
|
|
2077
|
+
winner: null,
|
|
2078
|
+
round,
|
|
2079
|
+
relation: { type: 'dummy' },
|
|
2080
|
+
};
|
|
2081
|
+
if (matchBase.winner) {
|
|
2082
|
+
const winnerParticipant = homeParticipant?.id === matchBase.winner.id ? homeParticipant : awayParticipant;
|
|
2083
|
+
if (!winnerParticipant)
|
|
2084
|
+
throw new Error(`Winner participant with id ${matchBase.winner.id} not found in match base`);
|
|
2085
|
+
newMatch.winner = winnerParticipant;
|
|
2064
2086
|
}
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
spanStartLeftCache.set(key, 0);
|
|
2075
|
-
return 0;
|
|
2087
|
+
matches.set(matchBase.id, newMatch);
|
|
2088
|
+
round.matches.set(matchBase.id, newMatch);
|
|
2089
|
+
if (homeParticipant) {
|
|
2090
|
+
const participant = participants.getOrThrow(homeParticipant.id);
|
|
2091
|
+
participant.matches.set(matchBase.id, {
|
|
2092
|
+
...newMatch,
|
|
2093
|
+
me: homeParticipant,
|
|
2094
|
+
opponent: awayParticipant,
|
|
2095
|
+
});
|
|
2076
2096
|
}
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2097
|
+
if (awayParticipant) {
|
|
2098
|
+
const participant = participants.getOrThrow(awayParticipant.id);
|
|
2099
|
+
participant.matches.set(matchBase.id, {
|
|
2100
|
+
...newMatch,
|
|
2101
|
+
me: awayParticipant,
|
|
2102
|
+
opponent: homeParticipant,
|
|
2103
|
+
});
|
|
2081
2104
|
}
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
const section = sections[secIdx];
|
|
2092
|
-
const subColumns = section.subColumns;
|
|
2093
|
-
for (let scIdx = 0; scIdx < subColumns.length; scIdx++) {
|
|
2094
|
-
const subColumn = subColumns[scIdx];
|
|
2095
|
-
if (subColumn.span.isStart && subColumn.span.isEnd)
|
|
2096
|
-
continue;
|
|
2097
|
-
let spanStart = { masterColumnIndex: mcIdx, sectionIndex: secIdx, subColumnIndex: scIdx };
|
|
2098
|
-
if (!subColumn.span.isStart) {
|
|
2099
|
-
outer: for (let m = mcIdx; m >= 0; m--) {
|
|
2100
|
-
const mc = masterCols[m];
|
|
2101
|
-
if (!mc)
|
|
2102
|
-
continue;
|
|
2103
|
-
const sec = mc.sections[secIdx];
|
|
2104
|
-
if (!sec)
|
|
2105
|
-
continue;
|
|
2106
|
-
const end = m === mcIdx ? scIdx : sec.subColumns.length - 1;
|
|
2107
|
-
for (let s = end; s >= 0; s--) {
|
|
2108
|
-
if (sec.subColumns[s]?.span.isStart) {
|
|
2109
|
-
spanStart = { masterColumnIndex: m, sectionIndex: secIdx, subColumnIndex: s };
|
|
2110
|
-
break outer;
|
|
2111
|
-
}
|
|
2112
|
-
}
|
|
2113
|
-
}
|
|
2114
|
-
}
|
|
2115
|
-
let spanEnd = { masterColumnIndex: mcIdx, sectionIndex: secIdx, subColumnIndex: scIdx };
|
|
2116
|
-
if (!subColumn.span.isEnd) {
|
|
2117
|
-
outer: for (let m = mcIdx; m < masterCols.length; m++) {
|
|
2118
|
-
const mc = masterCols[m];
|
|
2119
|
-
if (!mc)
|
|
2120
|
-
continue;
|
|
2121
|
-
const sec = mc.sections[secIdx];
|
|
2122
|
-
if (!sec)
|
|
2123
|
-
continue;
|
|
2124
|
-
const start = m === mcIdx ? scIdx : 0;
|
|
2125
|
-
for (let s = start; s < sec.subColumns.length; s++) {
|
|
2126
|
-
if (sec.subColumns[s]?.span.isEnd) {
|
|
2127
|
-
spanEnd = { masterColumnIndex: m, sectionIndex: secIdx, subColumnIndex: s };
|
|
2128
|
-
break outer;
|
|
2129
|
-
}
|
|
2130
|
-
}
|
|
2131
|
-
}
|
|
2132
|
-
}
|
|
2133
|
-
const elements = subColumn.elements;
|
|
2134
|
-
for (let elIdx = 0; elIdx < elements.length; elIdx++) {
|
|
2135
|
-
elements[elIdx].span = {
|
|
2136
|
-
masterColumnStart: spanStart.masterColumnIndex,
|
|
2137
|
-
masterColumnEnd: spanEnd.masterColumnIndex,
|
|
2138
|
-
sectionStart: spanStart.sectionIndex,
|
|
2139
|
-
sectionEnd: spanEnd.sectionIndex,
|
|
2140
|
-
subColumnStart: spanStart.subColumnIndex,
|
|
2141
|
-
subColumnEnd: spanEnd.subColumnIndex,
|
|
2142
|
-
};
|
|
2143
|
-
}
|
|
2144
|
-
}
|
|
2145
|
-
}
|
|
2105
|
+
}
|
|
2106
|
+
for (const participant of participants.values()) {
|
|
2107
|
+
for (const match of participant.matches.values()) {
|
|
2108
|
+
if (match.home?.id === participant.id)
|
|
2109
|
+
match.home.matches = participant.matches;
|
|
2110
|
+
if (match.away?.id === participant.id)
|
|
2111
|
+
match.away.matches = participant.matches;
|
|
2112
|
+
if (match.winner?.id === participant.id)
|
|
2113
|
+
match.winner.matches = participant.matches;
|
|
2146
2114
|
}
|
|
2115
|
+
}
|
|
2116
|
+
const newBracket = {
|
|
2117
|
+
matches,
|
|
2118
|
+
participants,
|
|
2119
|
+
rounds,
|
|
2120
|
+
roundsByType,
|
|
2121
|
+
mode: bracketNewBase.mode,
|
|
2147
2122
|
};
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
const { columnWidth, padding } = config;
|
|
2158
|
-
const sections = [];
|
|
2159
|
-
const newMasterColumn = {
|
|
2160
|
-
dimensions: {
|
|
2161
|
-
width: columnWidth,
|
|
2162
|
-
height: 0,
|
|
2163
|
-
top: 0,
|
|
2164
|
-
left: 0,
|
|
2165
|
-
},
|
|
2166
|
-
padding,
|
|
2167
|
-
sections,
|
|
2168
|
-
};
|
|
2169
|
-
const pushSection = (...newSections) => {
|
|
2170
|
-
sections.push(...newSections);
|
|
2171
|
-
};
|
|
2172
|
-
return {
|
|
2173
|
-
masterColumn: newMasterColumn,
|
|
2174
|
-
pushSection,
|
|
2175
|
-
};
|
|
2176
|
-
};
|
|
2177
|
-
|
|
2178
|
-
const createBracketMasterColumnSection = (config) => {
|
|
2179
|
-
const { type } = config;
|
|
2180
|
-
const subColumns = [];
|
|
2181
|
-
const newMasterColumnSection = {
|
|
2182
|
-
dimensions: {
|
|
2183
|
-
width: 0,
|
|
2184
|
-
height: 0,
|
|
2185
|
-
top: 0,
|
|
2186
|
-
left: 0,
|
|
2187
|
-
},
|
|
2188
|
-
subColumns,
|
|
2189
|
-
type,
|
|
2190
|
-
};
|
|
2191
|
-
const pushSubColumn = (...newSubColumns) => {
|
|
2192
|
-
subColumns.push(...newSubColumns);
|
|
2193
|
-
};
|
|
2194
|
-
return {
|
|
2195
|
-
masterColumnSection: newMasterColumnSection,
|
|
2196
|
-
pushSubColumn,
|
|
2197
|
-
};
|
|
2123
|
+
const roundRelations = generateRoundRelationsNew(newBracket);
|
|
2124
|
+
for (const roundRelation of roundRelations) {
|
|
2125
|
+
roundRelation.currentRound.relation = roundRelation;
|
|
2126
|
+
}
|
|
2127
|
+
const matchRelations = generateMatchRelationsNew(newBracket);
|
|
2128
|
+
for (const matchRelation of matchRelations) {
|
|
2129
|
+
matchRelation.currentMatch.relation = matchRelation;
|
|
2130
|
+
}
|
|
2131
|
+
return newBracket;
|
|
2198
2132
|
};
|
|
2199
2133
|
|
|
2200
|
-
const
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2134
|
+
const pad = (str, len) => str.padEnd(len, ' ');
|
|
2135
|
+
const color = (str, code) => `\x1b[${code}m${str}\x1b[0m`;
|
|
2136
|
+
const roundColor = (str) => color(str, 36); // cyan
|
|
2137
|
+
const arrowColor = (str) => color(str, 90); // gray
|
|
2138
|
+
const labelColor = (str) => color(str, 33); // yellow
|
|
2139
|
+
const factorColor = (str) => color(str, 32); // green
|
|
2140
|
+
const logRoundRelations = (bracketData) => {
|
|
2141
|
+
// Find max round name length for alignment
|
|
2142
|
+
let maxNameLen = 0;
|
|
2143
|
+
for (const round of bracketData.rounds.values()) {
|
|
2144
|
+
maxNameLen = Math.max(maxNameLen, round.name.length);
|
|
2145
|
+
}
|
|
2146
|
+
const colWidth = maxNameLen + 2;
|
|
2147
|
+
// Build rows
|
|
2148
|
+
const rows = [];
|
|
2149
|
+
for (const round of bracketData.rounds.values()) {
|
|
2150
|
+
const relation = round.relation;
|
|
2151
|
+
switch (relation.type) {
|
|
2152
|
+
case 'nothing-to-one':
|
|
2153
|
+
rows.push([
|
|
2154
|
+
labelColor(pad('START', colWidth)),
|
|
2155
|
+
arrowColor('──▶'),
|
|
2156
|
+
roundColor(pad(round.name, colWidth)),
|
|
2157
|
+
arrowColor('──▶'),
|
|
2158
|
+
roundColor(pad(relation.nextRound.name, colWidth)),
|
|
2159
|
+
factorColor(`[${relation.nextRoundMatchFactor}]`),
|
|
2160
|
+
]);
|
|
2161
|
+
break;
|
|
2162
|
+
case 'one-to-nothing':
|
|
2163
|
+
rows.push([
|
|
2164
|
+
roundColor(pad(relation.previousRound.name, colWidth)),
|
|
2165
|
+
arrowColor('──▶'),
|
|
2166
|
+
roundColor(pad(round.name, colWidth)),
|
|
2167
|
+
arrowColor('──▶'),
|
|
2168
|
+
labelColor(pad('END', colWidth)),
|
|
2169
|
+
factorColor(`[${relation.previousRoundMatchFactor}]`),
|
|
2170
|
+
]);
|
|
2171
|
+
break;
|
|
2172
|
+
case 'one-to-one':
|
|
2173
|
+
rows.push([
|
|
2174
|
+
roundColor(pad(relation.previousRound.name, colWidth)),
|
|
2175
|
+
arrowColor('──▶'),
|
|
2176
|
+
roundColor(pad(round.name, colWidth)),
|
|
2177
|
+
arrowColor('──▶'),
|
|
2178
|
+
roundColor(pad(relation.nextRound.name, colWidth)),
|
|
2179
|
+
factorColor(`[Prev: ${relation.previousRoundMatchFactor}, Next: ${relation.nextRoundMatchFactor}]`),
|
|
2180
|
+
]);
|
|
2181
|
+
break;
|
|
2182
|
+
case 'two-to-one':
|
|
2183
|
+
rows.push([
|
|
2184
|
+
roundColor(pad(relation.previousUpperRound.name, colWidth)),
|
|
2185
|
+
arrowColor(' │ '),
|
|
2186
|
+
roundColor(pad(relation.previousLowerRound.name, colWidth)),
|
|
2187
|
+
arrowColor('──▶'),
|
|
2188
|
+
roundColor(pad(round.name, colWidth)),
|
|
2189
|
+
arrowColor('──▶'),
|
|
2190
|
+
roundColor(pad(relation.nextRound.name, colWidth)),
|
|
2191
|
+
factorColor(`[Upper: ${relation.previousUpperRoundMatchFactor}, Lower: ${relation.previousLowerRoundMatchFactor}, Next: ${relation.nextRoundMatchFactor}]`),
|
|
2192
|
+
]);
|
|
2193
|
+
break;
|
|
2194
|
+
case 'two-to-nothing':
|
|
2195
|
+
rows.push([
|
|
2196
|
+
roundColor(pad(relation.previousUpperRound.name, colWidth)),
|
|
2197
|
+
arrowColor(' │'),
|
|
2198
|
+
roundColor(pad(relation.previousLowerRound.name, colWidth)),
|
|
2199
|
+
arrowColor(' ▼'),
|
|
2200
|
+
roundColor(pad(round.name, colWidth)),
|
|
2201
|
+
arrowColor('──▶'),
|
|
2202
|
+
labelColor(pad('END', colWidth)),
|
|
2203
|
+
factorColor(`[Upper: ${relation.previousUpperRoundMatchFactor}, Lower: ${relation.previousLowerRoundMatchFactor}]`),
|
|
2204
|
+
]);
|
|
2205
|
+
break;
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
// Print header
|
|
2209
|
+
const divider = (label) => {
|
|
2210
|
+
// eslint-disable-next-line no-control-regex
|
|
2211
|
+
const width = rows[0]?.reduce((w, col) => w + col.replace(/\x1b\[[0-9;]*m/g, '').length + 2, 0) || 60;
|
|
2212
|
+
return `\n${'='.repeat(width)}\n${labelColor(label)}\n${'='.repeat(width)}\n`;
|
|
2219
2213
|
};
|
|
2214
|
+
console.log(divider('Bracket Structure'));
|
|
2215
|
+
// Print rows
|
|
2216
|
+
for (const row of rows) {
|
|
2217
|
+
console.log(row.join(' '));
|
|
2218
|
+
}
|
|
2219
|
+
console.log();
|
|
2220
2220
|
};
|
|
2221
2221
|
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2222
|
+
const factorialCache = new Map();
|
|
2223
|
+
const getAvailableSwissGroupsForRound = (roundNumber, totalMatchesInRound) => {
|
|
2224
|
+
const ADVANCE_WINS = 3;
|
|
2225
|
+
const ELIMINATE_LOSSES = 3;
|
|
2226
|
+
// Cache factorial calculations
|
|
2227
|
+
const getFactorial = (n) => {
|
|
2228
|
+
if (n <= 1)
|
|
2229
|
+
return 1;
|
|
2230
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2231
|
+
if (factorialCache.has(n))
|
|
2232
|
+
return factorialCache.get(n);
|
|
2233
|
+
const result = n * getFactorial(n - 1);
|
|
2234
|
+
factorialCache.set(n, result);
|
|
2235
|
+
return result;
|
|
2236
|
+
};
|
|
2237
|
+
// Pre-calculate roundFactorial
|
|
2238
|
+
const roundFactorial = getFactorial(roundNumber);
|
|
2239
|
+
let totalCombinations = 0;
|
|
2240
|
+
const validGroups = [];
|
|
2241
|
+
// Single loop to gather valid groups and total combinations
|
|
2242
|
+
for (let wins = roundNumber; wins >= 0; wins--) {
|
|
2243
|
+
const losses = roundNumber - wins;
|
|
2244
|
+
const remainingGames = ADVANCE_WINS + ELIMINATE_LOSSES - (wins + losses) - 1;
|
|
2245
|
+
const notYetEliminated = losses < ELIMINATE_LOSSES;
|
|
2246
|
+
const canStillAdvance = wins < ADVANCE_WINS && remainingGames >= 0;
|
|
2247
|
+
if (!canStillAdvance || !notYetEliminated)
|
|
2248
|
+
continue;
|
|
2249
|
+
const combinations = roundFactorial / (getFactorial(wins) * getFactorial(losses));
|
|
2250
|
+
totalCombinations += combinations;
|
|
2251
|
+
validGroups.push({ wins, losses, combinations });
|
|
2235
2252
|
}
|
|
2236
|
-
//
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2253
|
+
// Create final groups with calculated proportions
|
|
2254
|
+
return validGroups.map(({ wins, losses, combinations }) => ({
|
|
2255
|
+
id: `${wins}-${losses}`,
|
|
2256
|
+
name: `${wins}-${losses}`,
|
|
2257
|
+
matchesInGroup: Math.round((combinations / totalCombinations) * totalMatchesInRound),
|
|
2258
|
+
}));
|
|
2259
|
+
};
|
|
2260
|
+
const generateBracketRoundSwissGroupMaps = (bracketData) => {
|
|
2261
|
+
if (bracketData.mode !== TOURNAMENT_MODE.SWISS_WITH_ELIMINATION) {
|
|
2262
|
+
return null;
|
|
2242
2263
|
}
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
const
|
|
2248
|
-
|
|
2249
|
-
const width = columnWidths[colIndex] ?? 0;
|
|
2250
|
-
return area.padEnd(width);
|
|
2251
|
-
})
|
|
2252
|
-
.join(' | ');
|
|
2253
|
-
console.log(`${String(rowIndex).padStart(2, '0')}: ${formattedRow}`);
|
|
2254
|
-
});
|
|
2255
|
-
console.log('═'.repeat(columnWidths.reduce((sum, width) => sum + width + 3, 0)));
|
|
2256
|
-
console.log(`📊 Grid dimensions: ${rows.length} rows × ${columnCount} columns\n`);
|
|
2257
|
-
}
|
|
2258
|
-
function gridColumnsToGridProperty(grid) {
|
|
2259
|
-
if (!grid.length) {
|
|
2260
|
-
return {
|
|
2261
|
-
gridTemplateAreas: '',
|
|
2262
|
-
gridTemplateRows: '',
|
|
2263
|
-
gridTemplateColumns: '',
|
|
2264
|
+
const roundsWithSwissGroups = new Map();
|
|
2265
|
+
let roundNumber = 0;
|
|
2266
|
+
for (const bracketRound of bracketData.rounds.values()) {
|
|
2267
|
+
const availableGroups = getAvailableSwissGroupsForRound(roundNumber, bracketRound.matchCount);
|
|
2268
|
+
const roundSwissData = {
|
|
2269
|
+
groups: new Map(),
|
|
2264
2270
|
};
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
});
|
|
2281
|
-
});
|
|
2282
|
-
});
|
|
2283
|
-
});
|
|
2284
|
-
// Group sections by their index to create rows
|
|
2285
|
-
const sectionsByIndex = new Map();
|
|
2286
|
-
let globalColumnIndex = 0;
|
|
2287
|
-
grid.forEach((masterColumn, masterColumnIndex) => {
|
|
2288
|
-
const maxSubColumns = Math.max(...masterColumn.sections.map((section) => section.subColumns.length));
|
|
2289
|
-
masterColumn.sections.forEach((section, sectionIndex) => {
|
|
2290
|
-
if (!sectionsByIndex.has(sectionIndex)) {
|
|
2291
|
-
sectionsByIndex.set(sectionIndex, []);
|
|
2271
|
+
for (const group of availableGroups) {
|
|
2272
|
+
const subGroup = {
|
|
2273
|
+
id: group.id,
|
|
2274
|
+
name: group.name,
|
|
2275
|
+
matches: new BracketMap(),
|
|
2276
|
+
allowedMatchCount: group.matchesInGroup,
|
|
2277
|
+
};
|
|
2278
|
+
roundSwissData.groups.set(group.id, subGroup);
|
|
2279
|
+
}
|
|
2280
|
+
const emptyMatchIds = [];
|
|
2281
|
+
for (const match of bracketRound.matches.values()) {
|
|
2282
|
+
const anyParticipant = match.home || match.away;
|
|
2283
|
+
if (!anyParticipant) {
|
|
2284
|
+
emptyMatchIds.push(match.id);
|
|
2285
|
+
continue;
|
|
2292
2286
|
}
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
// Find max rows needed for this section
|
|
2309
|
-
const maxRowsInSection = Math.max(...sectionColumns.map((col) => col.elements.reduce((total, element) => total + element.parts.length, 0)), 0);
|
|
2310
|
-
// Create rows for this section
|
|
2311
|
-
for (let rowIndex = 0; rowIndex < maxRowsInSection; rowIndex++) {
|
|
2312
|
-
const areaRow = new Array(totalColumns).fill('.');
|
|
2313
|
-
let currentRowHeight;
|
|
2314
|
-
// Fill in areas for each column in this section
|
|
2315
|
-
for (const sectionColumn of sectionColumns) {
|
|
2316
|
-
let accumulatedRows = 0;
|
|
2317
|
-
let area = '.';
|
|
2318
|
-
let height;
|
|
2319
|
-
// Find which element this row belongs to
|
|
2320
|
-
for (const element of sectionColumn.elements) {
|
|
2321
|
-
const elementRowCount = element.parts.length;
|
|
2322
|
-
if (rowIndex >= accumulatedRows && rowIndex < accumulatedRows + elementRowCount) {
|
|
2323
|
-
const relativeRowIndex = rowIndex - accumulatedRows;
|
|
2324
|
-
area = element.area;
|
|
2325
|
-
height = element.parts[relativeRowIndex]?.dimensions.height;
|
|
2326
|
-
break;
|
|
2327
|
-
}
|
|
2328
|
-
accumulatedRows += elementRowCount;
|
|
2329
|
-
}
|
|
2330
|
-
areaRow[sectionColumn.columnIndex] = area;
|
|
2331
|
-
// Use the first defined height for this row
|
|
2332
|
-
if (!currentRowHeight && height !== undefined) {
|
|
2333
|
-
currentRowHeight = `${height}px`;
|
|
2287
|
+
const wins = anyParticipant.winCount;
|
|
2288
|
+
const losses = anyParticipant.lossCount;
|
|
2289
|
+
const group = roundSwissData.groups.get(`${wins}-${losses}`);
|
|
2290
|
+
if (!group)
|
|
2291
|
+
throw new Error('Group not found for match: ' + match.id);
|
|
2292
|
+
group.matches.set(match.id, match);
|
|
2293
|
+
}
|
|
2294
|
+
for (const emptyMatchId of emptyMatchIds) {
|
|
2295
|
+
const match = bracketRound.matches.getOrThrow(emptyMatchId);
|
|
2296
|
+
let groupFound = false;
|
|
2297
|
+
for (const group of roundSwissData.groups.values()) {
|
|
2298
|
+
if (group.matches.size < group.allowedMatchCount) {
|
|
2299
|
+
group.matches.set(match.id, match);
|
|
2300
|
+
groupFound = true;
|
|
2301
|
+
break;
|
|
2334
2302
|
}
|
|
2335
2303
|
}
|
|
2336
|
-
|
|
2337
|
-
|
|
2304
|
+
if (!groupFound) {
|
|
2305
|
+
throw new Error('No group found for empty match');
|
|
2306
|
+
}
|
|
2338
2307
|
}
|
|
2308
|
+
roundsWithSwissGroups.set(bracketRound.id, roundSwissData);
|
|
2309
|
+
roundNumber++;
|
|
2339
2310
|
}
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
}
|
|
2311
|
+
return roundsWithSwissGroups;
|
|
2312
|
+
};
|
|
2313
|
+
|
|
2314
|
+
class NewBracketDefaultMatchComponent {
|
|
2315
|
+
constructor() {
|
|
2316
|
+
this.bracketRound = input.required(...(ngDevMode ? [{ debugName: "bracketRound" }] : []));
|
|
2317
|
+
this.bracketMatch = input.required(...(ngDevMode ? [{ debugName: "bracketMatch" }] : []));
|
|
2318
|
+
this.bracketRoundSwissGroup = input.required(...(ngDevMode ? [{ debugName: "bracketRoundSwissGroup" }] : []));
|
|
2349
2319
|
}
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
const gridTemplateRows = rowHeights.join(' ');
|
|
2353
|
-
const gridTemplateColumns = columnWidths.join(' ');
|
|
2354
|
-
const result = {
|
|
2355
|
-
gridTemplateAreas,
|
|
2356
|
-
gridTemplateRows,
|
|
2357
|
-
gridTemplateColumns,
|
|
2358
|
-
};
|
|
2359
|
-
// logGridAreasFormatted(gridTemplateAreas);
|
|
2360
|
-
return result;
|
|
2320
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: NewBracketDefaultMatchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2321
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.0", type: NewBracketDefaultMatchComponent, isStandalone: true, selector: "et-new-bracket-default-match", inputs: { bracketRound: { classPropertyName: "bracketRound", publicName: "bracketRound", isSignal: true, isRequired: true, transformFunction: null }, bracketMatch: { classPropertyName: "bracketMatch", publicName: "bracketMatch", isSignal: true, isRequired: true, transformFunction: null }, bracketRoundSwissGroup: { classPropertyName: "bracketRoundSwissGroup", publicName: "bracketRoundSwissGroup", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "et-new-bracket-default-match-host" }, ngImport: i0, template: ` {{ bracketMatch().id }} `, isInline: true, styles: [".et-new-bracket-default-match-host{display:block;padding:8px;border:1px solid yellow;inline-size:100%;block-size:100%;display:flex;justify-content:center;align-items:center;box-sizing:border-box;font-size:12px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
2361
2322
|
}
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2323
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: NewBracketDefaultMatchComponent, decorators: [{
|
|
2324
|
+
type: Component,
|
|
2325
|
+
args: [{ selector: 'et-new-bracket-default-match', template: ` {{ bracketMatch().id }} `, standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
|
|
2326
|
+
class: 'et-new-bracket-default-match-host',
|
|
2327
|
+
}, styles: [".et-new-bracket-default-match-host{display:block;padding:8px;border:1px solid yellow;inline-size:100%;block-size:100%;display:flex;justify-content:center;align-items:center;box-sizing:border-box;font-size:12px}\n"] }]
|
|
2328
|
+
}] });
|
|
2329
|
+
|
|
2330
|
+
class NewBracketDefaultRoundHeaderComponent {
|
|
2331
|
+
constructor() {
|
|
2332
|
+
this.bracketRound = input.required(...(ngDevMode ? [{ debugName: "bracketRound" }] : []));
|
|
2333
|
+
this.bracketRoundSwissGroup = input.required(...(ngDevMode ? [{ debugName: "bracketRoundSwissGroup" }] : []));
|
|
2370
2334
|
}
|
|
2371
|
-
|
|
2372
|
-
}
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
if (upperToLowerRatio === 1.5) {
|
|
2378
|
-
return 2; // Split into 2 sub-columns for 1.5 ratio
|
|
2335
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: NewBracketDefaultRoundHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2336
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.0", type: NewBracketDefaultRoundHeaderComponent, isStandalone: true, selector: "et-new-bracket-default-round-header", inputs: { bracketRound: { classPropertyName: "bracketRound", publicName: "bracketRound", isSignal: true, isRequired: true, transformFunction: null }, bracketRoundSwissGroup: { classPropertyName: "bracketRoundSwissGroup", publicName: "bracketRoundSwissGroup", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "et-new-bracket-default-round-header-host" }, ngImport: i0, template: `
|
|
2337
|
+
{{ bracketRound().name }}
|
|
2338
|
+
|
|
2339
|
+
@if (bracketRoundSwissGroup()) {
|
|
2340
|
+
({{ bracketRoundSwissGroup()?.name }})
|
|
2379
2341
|
}
|
|
2380
|
-
|
|
2381
|
-
|
|
2342
|
+
`, isInline: true, styles: [".et-new-bracket-default-round-header-host{display:block;padding:8px;border:1px solid green;inline-size:100%;block-size:100%;display:flex;justify-content:center;align-items:center;box-sizing:border-box}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
2343
|
+
}
|
|
2344
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: NewBracketDefaultRoundHeaderComponent, decorators: [{
|
|
2345
|
+
type: Component,
|
|
2346
|
+
args: [{ selector: 'et-new-bracket-default-round-header', template: `
|
|
2347
|
+
{{ bracketRound().name }}
|
|
2348
|
+
|
|
2349
|
+
@if (bracketRoundSwissGroup()) {
|
|
2350
|
+
({{ bracketRoundSwissGroup()?.name }})
|
|
2382
2351
|
}
|
|
2383
|
-
|
|
2352
|
+
`, standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
|
|
2353
|
+
class: 'et-new-bracket-default-round-header-host',
|
|
2354
|
+
}, styles: [".et-new-bracket-default-round-header-host{display:block;padding:8px;border:1px solid green;inline-size:100%;block-size:100%;display:flex;justify-content:center;align-items:center;box-sizing:border-box}\n"] }]
|
|
2355
|
+
}] });
|
|
2356
|
+
|
|
2357
|
+
const path = (d, options) => `<path d="${d.replace(/\s+/g, ' ').trim()}" stroke="currentColor" fill="none" stroke-width="${options.width}" stroke-dasharray="${options.dashArray}" stroke-dashoffset="${options.dashOffset}" class="${options.className}" />`;
|
|
2358
|
+
|
|
2359
|
+
const curvePath = (from, to, direction, options) => {
|
|
2360
|
+
const inverted = options.inverted ?? false;
|
|
2361
|
+
// Inline/block coordinates depending on direction and inversion
|
|
2362
|
+
const fromInline = inverted ? from.inline.start : from.inline.end;
|
|
2363
|
+
const toInline = inverted ? to.inline.end : to.inline.start;
|
|
2364
|
+
const fromBlock = from.block.center;
|
|
2365
|
+
const toBlock = to.block.center;
|
|
2366
|
+
// Curve parameters
|
|
2367
|
+
const startCurve = options.lineStartingCurveAmount;
|
|
2368
|
+
const endCurve = options.lineEndingCurveAmount;
|
|
2369
|
+
const totalInline = Math.abs(toInline - fromInline);
|
|
2370
|
+
const straightLength = (totalInline - startCurve - endCurve) / 2;
|
|
2371
|
+
// Calculate key points for the path
|
|
2372
|
+
const straightEnd = inverted ? fromInline - straightLength : fromInline + straightLength;
|
|
2373
|
+
const straightStart = inverted ? toInline + straightLength : toInline - straightLength;
|
|
2374
|
+
// First curve (from start)
|
|
2375
|
+
const firstCurveEndX = inverted ? straightEnd - startCurve : straightEnd + startCurve;
|
|
2376
|
+
const firstCurveEndY = direction === 'down' ? fromBlock + startCurve : fromBlock - startCurve;
|
|
2377
|
+
// Second curve (to end)
|
|
2378
|
+
const secondCurveStartY = direction === 'down' ? toBlock - endCurve : toBlock + endCurve;
|
|
2379
|
+
const secondCurveEndX = straightStart;
|
|
2380
|
+
const secondCurveEndY = toBlock;
|
|
2381
|
+
const secondCurveBezierX = inverted ? straightStart + endCurve : straightStart - endCurve;
|
|
2382
|
+
// SVG path string
|
|
2383
|
+
const d = [
|
|
2384
|
+
`M ${fromInline} ${fromBlock}`,
|
|
2385
|
+
`H ${straightEnd}`,
|
|
2386
|
+
`Q ${firstCurveEndX} ${fromBlock}, ${firstCurveEndX} ${firstCurveEndY}`,
|
|
2387
|
+
`V ${secondCurveStartY}`,
|
|
2388
|
+
`Q ${secondCurveBezierX} ${toBlock}, ${secondCurveEndX} ${secondCurveEndY}`,
|
|
2389
|
+
`H ${toInline}`,
|
|
2390
|
+
].join(' ');
|
|
2391
|
+
return path(d, options.path);
|
|
2384
2392
|
};
|
|
2385
|
-
|
|
2386
|
-
const
|
|
2387
|
-
|
|
2388
|
-
const completeColumnIndex = Math.floor(subColumnIndex / splitFactor);
|
|
2389
|
-
// For the first complete column (index 0), always use the first upper round (index 0)
|
|
2390
|
-
if (completeColumnIndex === 0) {
|
|
2391
|
-
return 0;
|
|
2392
|
-
}
|
|
2393
|
-
// For subsequent columns, map based on the ratio
|
|
2394
|
-
// We subtract 1 because the first column is handled separately
|
|
2395
|
-
const adjustedColumnIndex = completeColumnIndex - 1;
|
|
2396
|
-
// Calculate which upper round this column should use
|
|
2397
|
-
// We add 1 because we start from the second upper round (index 1)
|
|
2398
|
-
return Math.floor(adjustedColumnIndex / upperToLowerRatio) + 1;
|
|
2393
|
+
|
|
2394
|
+
const linePath = (from, to, options) => {
|
|
2395
|
+
return path(`M ${from.inline.end} ${from.block.center} L ${to.inline.start} ${to.block.center}`, options.path);
|
|
2399
2396
|
};
|
|
2400
2397
|
|
|
2401
|
-
const
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
}
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2398
|
+
const makePos = (dimensions) => ({
|
|
2399
|
+
block: {
|
|
2400
|
+
start: dimensions.top,
|
|
2401
|
+
end: dimensions.top + dimensions.height,
|
|
2402
|
+
center: dimensions.top + dimensions.height / 2,
|
|
2403
|
+
},
|
|
2404
|
+
inline: {
|
|
2405
|
+
start: dimensions.left,
|
|
2406
|
+
end: dimensions.left + dimensions.width,
|
|
2407
|
+
center: dimensions.left + dimensions.width / 2,
|
|
2408
|
+
},
|
|
2409
|
+
});
|
|
2410
|
+
const drawMan = (dimensions) => {
|
|
2411
|
+
const svgParts = [];
|
|
2412
|
+
for (const col of dimensions.bracketGrid.columns) {
|
|
2413
|
+
for (const el of col.elements) {
|
|
2414
|
+
if (el.type === 'header')
|
|
2415
|
+
continue;
|
|
2416
|
+
const currentMatchParticipantsShortIds = [el.match.home?.shortId, el.match.away?.shortId]
|
|
2417
|
+
.filter((id) => !!id)
|
|
2418
|
+
.join(' ');
|
|
2419
|
+
const pathOptions = { ...dimensions.path, className: currentMatchParticipantsShortIds };
|
|
2420
|
+
const currentPos = makePos(el.dimensions);
|
|
2421
|
+
// No lines for the third place match
|
|
2422
|
+
if (el.round.type === COMMON_BRACKET_ROUND_TYPE.THIRD_PLACE)
|
|
2423
|
+
continue;
|
|
2424
|
+
switch (el.match.relation.type) {
|
|
2425
|
+
case 'nothing-to-one': {
|
|
2426
|
+
continue;
|
|
2427
|
+
}
|
|
2428
|
+
case 'one-to-nothing':
|
|
2429
|
+
case 'one-to-one': {
|
|
2430
|
+
const prev = dimensions.bracketGrid.matchElementMap.getOrThrow(el.match.relation.previousMatch.id);
|
|
2431
|
+
const prevPos = makePos(prev.dimensions);
|
|
2432
|
+
// draw a straight line
|
|
2433
|
+
svgParts.push(linePath(prevPos, currentPos, { path: pathOptions }));
|
|
2434
|
+
break;
|
|
2435
|
+
}
|
|
2436
|
+
case 'two-to-nothing':
|
|
2437
|
+
case 'two-to-one': {
|
|
2438
|
+
const prevUpper = dimensions.bracketGrid.matchElementMap.getOrThrow(el.match.relation.previousUpperMatch.id);
|
|
2439
|
+
const prevLower = dimensions.bracketGrid.matchElementMap.getOrThrow(el.match.relation.previousLowerMatch.id);
|
|
2440
|
+
const prevUpperPos = makePos(prevUpper.dimensions);
|
|
2441
|
+
const prevLowerPos = makePos(prevLower.dimensions);
|
|
2442
|
+
const isLowerUpperMerger = el.match.relation.previousLowerRound.id !== el.match.relation.previousUpperRound.id;
|
|
2443
|
+
const invertCurve = el.round.mirrorRoundType === BRACKET_ROUND_MIRROR_TYPE.RIGHT;
|
|
2444
|
+
const curveOptions = {
|
|
2445
|
+
...dimensions.curve,
|
|
2446
|
+
inverted: invertCurve,
|
|
2447
|
+
path: { ...dimensions.path, className: '' },
|
|
2448
|
+
};
|
|
2449
|
+
if (isLowerUpperMerger) {
|
|
2450
|
+
svgParts.push(linePath(prevUpperPos, currentPos, { path: pathOptions }));
|
|
2444
2451
|
}
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2452
|
+
else {
|
|
2453
|
+
// draw two lines that merge into one in the middle
|
|
2454
|
+
svgParts.push(curvePath(prevUpperPos, currentPos, 'down', {
|
|
2455
|
+
...curveOptions,
|
|
2456
|
+
path: {
|
|
2457
|
+
...curveOptions.path,
|
|
2458
|
+
className: el.match.relation.previousUpperMatch.winner?.shortId || '',
|
|
2459
|
+
},
|
|
2460
|
+
}));
|
|
2461
|
+
}
|
|
2462
|
+
svgParts.push(curvePath(prevLowerPos, currentPos, 'up', {
|
|
2463
|
+
...curveOptions,
|
|
2464
|
+
path: {
|
|
2465
|
+
...curveOptions.path,
|
|
2466
|
+
className: el.match.relation.previousLowerMatch.winner?.shortId || '',
|
|
2467
|
+
},
|
|
2468
|
+
}));
|
|
2469
|
+
if (el.round.mirrorRoundType === BRACKET_ROUND_MIRROR_TYPE.RIGHT &&
|
|
2470
|
+
el.match.relation.type === 'two-to-one' &&
|
|
2471
|
+
el.match.relation.nextRound.mirrorRoundType === null) {
|
|
2472
|
+
// draw a straight line for the special case of connecting the final match to the mirrored semi final match
|
|
2473
|
+
const next = dimensions.bracketGrid.matchElementMap.getOrThrow(el.match.relation.nextMatch.id);
|
|
2474
|
+
const nextPos = makePos(next.dimensions);
|
|
2475
|
+
svgParts.push(linePath(nextPos, currentPos, { path: pathOptions }));
|
|
2476
|
+
}
|
|
2477
|
+
break;
|
|
2470
2478
|
}
|
|
2471
|
-
|
|
2472
|
-
area: `.`,
|
|
2473
|
-
type: 'colGap',
|
|
2474
|
-
elementHeight: lastSubColumnElement.dimensions.height,
|
|
2475
|
-
partHeights,
|
|
2476
|
-
};
|
|
2477
|
-
})();
|
|
2478
|
-
pushElement(createBracketElement(elementToCreate).element);
|
|
2479
|
+
}
|
|
2479
2480
|
}
|
|
2480
|
-
pushSubColumn(subColumn);
|
|
2481
|
-
pushSection(masterColumnSection);
|
|
2482
2481
|
}
|
|
2483
|
-
return
|
|
2482
|
+
return svgParts.join('');
|
|
2483
|
+
};
|
|
2484
|
+
|
|
2485
|
+
const createBracketElementPart = (config) => {
|
|
2486
|
+
const { elementPartHeight } = config;
|
|
2487
|
+
const newElementPart = {
|
|
2488
|
+
dimensions: {
|
|
2489
|
+
width: 0,
|
|
2490
|
+
height: elementPartHeight,
|
|
2491
|
+
top: 0,
|
|
2492
|
+
left: 0,
|
|
2493
|
+
},
|
|
2494
|
+
};
|
|
2495
|
+
return {
|
|
2496
|
+
elementPart: newElementPart,
|
|
2497
|
+
};
|
|
2484
2498
|
};
|
|
2485
2499
|
|
|
2486
|
-
const
|
|
2487
|
-
const {
|
|
2488
|
-
const
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2500
|
+
const createBracketElement = (config) => {
|
|
2501
|
+
const { type, area, elementHeight, partHeights } = config;
|
|
2502
|
+
const parts = [];
|
|
2503
|
+
const newElementBase = {
|
|
2504
|
+
dimensions: {
|
|
2505
|
+
width: 0,
|
|
2506
|
+
height: elementHeight,
|
|
2507
|
+
top: 0,
|
|
2508
|
+
left: 0,
|
|
2509
|
+
},
|
|
2510
|
+
containerDimensions: {
|
|
2511
|
+
width: 0,
|
|
2512
|
+
height: 0,
|
|
2513
|
+
top: 0,
|
|
2514
|
+
left: 0,
|
|
2515
|
+
},
|
|
2516
|
+
parts,
|
|
2517
|
+
area,
|
|
2518
|
+
};
|
|
2519
|
+
const newElement = (() => {
|
|
2520
|
+
switch (type) {
|
|
2521
|
+
case 'header':
|
|
2522
|
+
return {
|
|
2523
|
+
...newElementBase,
|
|
2524
|
+
type,
|
|
2525
|
+
component: config.component,
|
|
2526
|
+
round: config.round,
|
|
2527
|
+
roundSwissGroup: config.roundSwissGroup,
|
|
2528
|
+
};
|
|
2529
|
+
case 'match':
|
|
2530
|
+
return {
|
|
2531
|
+
...newElementBase,
|
|
2532
|
+
type,
|
|
2533
|
+
component: config.component,
|
|
2534
|
+
match: config.match,
|
|
2535
|
+
round: config.round,
|
|
2536
|
+
roundSwissGroup: config.roundSwissGroup,
|
|
2537
|
+
};
|
|
2538
|
+
case 'matchGap':
|
|
2539
|
+
case 'roundHeaderGap':
|
|
2540
|
+
case 'roundGap':
|
|
2541
|
+
case 'colGap':
|
|
2542
|
+
return {
|
|
2543
|
+
...newElementBase,
|
|
2544
|
+
type,
|
|
2545
|
+
};
|
|
2546
|
+
default:
|
|
2547
|
+
throw new Error(`Unknown element type: ${type}`);
|
|
2523
2548
|
}
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
elementHeight: isFinalMatch ? options.finalMatchHeight : options.matchHeight,
|
|
2532
|
-
component: isFinalMatch ? config.components.finalMatch : config.components.match,
|
|
2533
|
-
match,
|
|
2534
|
-
round,
|
|
2535
|
-
roundSwissGroup: null,
|
|
2549
|
+
})();
|
|
2550
|
+
const pushPart = (...newParts) => {
|
|
2551
|
+
parts.push(...newParts);
|
|
2552
|
+
};
|
|
2553
|
+
for (const partHeight of partHeights) {
|
|
2554
|
+
const { elementPart } = createBracketElementPart({
|
|
2555
|
+
elementPartHeight: partHeight,
|
|
2536
2556
|
});
|
|
2537
|
-
|
|
2538
|
-
elementsToCreate.push({
|
|
2539
|
-
type: 'matchGap',
|
|
2540
|
-
area: '.',
|
|
2541
|
-
partHeights: [options.rowGap],
|
|
2542
|
-
elementHeight: options.rowGap,
|
|
2543
|
-
});
|
|
2544
|
-
}
|
|
2545
|
-
}
|
|
2546
|
-
// Create all elements at once
|
|
2547
|
-
for (const elementData of elementsToCreate) {
|
|
2548
|
-
const { element } = createBracketElement(elementData);
|
|
2549
|
-
pushElement(element);
|
|
2557
|
+
pushPart(elementPart);
|
|
2550
2558
|
}
|
|
2551
|
-
return
|
|
2559
|
+
return {
|
|
2560
|
+
element: newElement,
|
|
2561
|
+
};
|
|
2552
2562
|
};
|
|
2553
2563
|
|
|
2554
|
-
const
|
|
2555
|
-
const
|
|
2556
|
-
const
|
|
2557
|
-
const
|
|
2558
|
-
const
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
const hasReverseFinal = !!bracketData.roundsByType.get(DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.REVERSE_FINAL)?.first();
|
|
2563
|
-
const firstUpperRound = upperBracketRounds[0];
|
|
2564
|
-
const firstLowerRound = lowerBracketRounds[0];
|
|
2565
|
-
if (!firstUpperRound || !firstLowerRound) {
|
|
2566
|
-
throw new Error('No upper or lower rounds found in bracket data');
|
|
2567
|
-
}
|
|
2568
|
-
const upperToLowerRatio = calculateUpperLowerRatio(upperBracketRounds.length, lowerBracketRounds.length);
|
|
2569
|
-
const columnSplitFactor = calculateColumnSplitFactor(upperToLowerRatio);
|
|
2570
|
-
let lastRoundLastSubColumnUpperIndex = -1;
|
|
2571
|
-
let lastRoundLastSubColumnLowerIndex = -1;
|
|
2572
|
-
for (const [lowerRoundIndex, lowerRound] of lowerBracketRounds.entries()) {
|
|
2573
|
-
const isLastLowerRound = lowerRoundIndex === lowerBracketRounds.length - 1;
|
|
2574
|
-
const { masterColumn, pushSection } = createBracketMasterColumn({
|
|
2575
|
-
columnWidth: options.columnWidth,
|
|
2576
|
-
padding: {
|
|
2577
|
-
bottom: 0,
|
|
2578
|
-
left: 0,
|
|
2579
|
-
right: 0,
|
|
2580
|
-
top: 0,
|
|
2581
|
-
},
|
|
2582
|
-
});
|
|
2583
|
-
const { masterColumnSection: upperSection, pushSubColumn: pushUpperSubColumn } = createBracketMasterColumnSection({
|
|
2584
|
-
type: 'round',
|
|
2585
|
-
});
|
|
2586
|
-
const { masterColumnSection: upperLowerGapSection, pushSubColumn: pushUpperLowerSubColumn } = createBracketMasterColumnSection({
|
|
2587
|
-
type: 'gap',
|
|
2588
|
-
});
|
|
2589
|
-
const { masterColumnSection: lowerSection, pushSubColumn: pushLowerSubColumn } = createBracketMasterColumnSection({
|
|
2590
|
-
type: 'round',
|
|
2591
|
-
});
|
|
2592
|
-
for (let currentColumnSplitFactor = 1; currentColumnSplitFactor <= columnSplitFactor; currentColumnSplitFactor++) {
|
|
2593
|
-
const subColumnIndex = lowerRoundIndex * columnSplitFactor + (currentColumnSplitFactor - 1);
|
|
2594
|
-
const currentUpperRoundIndex = calculateUpperRoundIndex(subColumnIndex, upperToLowerRatio, columnSplitFactor);
|
|
2595
|
-
const currentLowerRoundIndex = lowerRoundIndex;
|
|
2596
|
-
const isFirstSubColumnInMasterColumn = currentColumnSplitFactor === 1;
|
|
2597
|
-
const isLastSubColumnInMasterColumn = currentColumnSplitFactor === columnSplitFactor;
|
|
2598
|
-
const previousSubColumnIndex = subColumnIndex - 1;
|
|
2599
|
-
const nextSubColumnIndex = subColumnIndex + 1;
|
|
2600
|
-
const previousUpperRoundIndex = previousSubColumnIndex >= 0
|
|
2601
|
-
? calculateUpperRoundIndex(previousSubColumnIndex, upperToLowerRatio, columnSplitFactor)
|
|
2602
|
-
: -1;
|
|
2603
|
-
const nextUpperRoundIndex = calculateUpperRoundIndex(nextSubColumnIndex, upperToLowerRatio, columnSplitFactor);
|
|
2604
|
-
const previousLowerRoundIndex = previousSubColumnIndex >= 0 ? calculateLowerRoundIndex(previousSubColumnIndex, columnSplitFactor) : -1;
|
|
2605
|
-
const nextLowerRoundIndex = calculateLowerRoundIndex(nextSubColumnIndex, columnSplitFactor);
|
|
2606
|
-
const upperRound = upperBracketRounds[currentUpperRoundIndex];
|
|
2607
|
-
if (!upperRound) {
|
|
2608
|
-
throw new Error('Upper round not found for subColumnIndex: ' + subColumnIndex);
|
|
2564
|
+
const finalizeBracketGrid = (grid) => {
|
|
2565
|
+
const finalizedColumns = [];
|
|
2566
|
+
const finalizedElementMap = new BracketMap();
|
|
2567
|
+
const ignoredSections = [];
|
|
2568
|
+
for (const [masterColumnIndex, masterColumn] of grid.grid.masterColumns.entries()) {
|
|
2569
|
+
for (const [sectionIndex, section] of masterColumn.sections.entries()) {
|
|
2570
|
+
if (ignoredSections.some((s) => s.masterColumnIndex === masterColumnIndex && s.sectionIndex === sectionIndex)) {
|
|
2571
|
+
continue;
|
|
2609
2572
|
}
|
|
2610
|
-
|
|
2611
|
-
const
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
}
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
}
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
hasReverseFinal,
|
|
2660
|
-
span: {
|
|
2661
|
-
isStart: isLowerSpanStart,
|
|
2662
|
-
isEnd: isLowerSpanEnd,
|
|
2573
|
+
const elements = [];
|
|
2574
|
+
const firstSubColumn = section.subColumns[0];
|
|
2575
|
+
const lastSubColumn = section.subColumns[section.subColumns.length - 1];
|
|
2576
|
+
if (!firstSubColumn || !lastSubColumn)
|
|
2577
|
+
continue;
|
|
2578
|
+
let sectionWidth = section.dimensions.width;
|
|
2579
|
+
for (const element of firstSubColumn.elements) {
|
|
2580
|
+
if (element.type === 'header') {
|
|
2581
|
+
elements.push({
|
|
2582
|
+
type: 'header',
|
|
2583
|
+
dimensions: element.dimensions,
|
|
2584
|
+
component: element.component,
|
|
2585
|
+
round: element.round,
|
|
2586
|
+
roundSwissGroup: element.roundSwissGroup,
|
|
2587
|
+
});
|
|
2588
|
+
}
|
|
2589
|
+
else if (element.type === 'match') {
|
|
2590
|
+
const matchEl = {
|
|
2591
|
+
type: 'match',
|
|
2592
|
+
dimensions: element.dimensions,
|
|
2593
|
+
component: element.component,
|
|
2594
|
+
match: element.match,
|
|
2595
|
+
round: element.round,
|
|
2596
|
+
classes: [element.match.home?.shortId, element.match.away?.shortId].filter((v) => !!v).join(' '),
|
|
2597
|
+
roundSwissGroup: element.roundSwissGroup,
|
|
2598
|
+
};
|
|
2599
|
+
elements.push(matchEl);
|
|
2600
|
+
finalizedElementMap.set(element.match.id, matchEl);
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
if (!lastSubColumn.span.isEnd) {
|
|
2604
|
+
for (let index = masterColumnIndex + 1; index < grid.grid.masterColumns.length; index++) {
|
|
2605
|
+
const nextMaster = grid.grid.masterColumns[index];
|
|
2606
|
+
const nextSection = nextMaster?.sections[sectionIndex];
|
|
2607
|
+
if (!nextSection)
|
|
2608
|
+
break;
|
|
2609
|
+
sectionWidth += nextSection.dimensions.width;
|
|
2610
|
+
ignoredSections.push({ masterColumnIndex: index, sectionIndex });
|
|
2611
|
+
if (nextSection.subColumns.some((sc) => sc.span.isEnd)) {
|
|
2612
|
+
break;
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
if (!elements.length)
|
|
2617
|
+
continue;
|
|
2618
|
+
finalizedColumns.push({
|
|
2619
|
+
dimensions: {
|
|
2620
|
+
...section.dimensions,
|
|
2621
|
+
width: sectionWidth,
|
|
2663
2622
|
},
|
|
2664
|
-
|
|
2623
|
+
elements,
|
|
2665
2624
|
});
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
return {
|
|
2628
|
+
columns: finalizedColumns,
|
|
2629
|
+
elementMap: finalizedElementMap,
|
|
2630
|
+
};
|
|
2631
|
+
};
|
|
2632
|
+
|
|
2633
|
+
const createBracketGrid = (config) => {
|
|
2634
|
+
const masterColumns = [];
|
|
2635
|
+
const spannedWidthCache = new Map();
|
|
2636
|
+
const spanStartLeftCache = new Map();
|
|
2637
|
+
const newGrid = {
|
|
2638
|
+
dimensions: { width: 0, height: 0, top: 0, left: 0 },
|
|
2639
|
+
masterColumns,
|
|
2640
|
+
};
|
|
2641
|
+
const pushMasterColumn = (...newMasterColumns) => {
|
|
2642
|
+
masterColumns.push(...newMasterColumns);
|
|
2643
|
+
};
|
|
2644
|
+
const calculateDimensions = () => {
|
|
2645
|
+
let currentMasterColumnLeft = 0;
|
|
2646
|
+
let maxGridHeight = 0;
|
|
2647
|
+
const masterCols = newGrid.masterColumns;
|
|
2648
|
+
for (let mcIdx = 0; mcIdx < masterCols.length; mcIdx++) {
|
|
2649
|
+
const masterColumn = masterCols[mcIdx];
|
|
2650
|
+
const { padding } = masterColumn;
|
|
2651
|
+
masterColumn.dimensions.left = currentMasterColumnLeft;
|
|
2652
|
+
masterColumn.dimensions.top = 0;
|
|
2653
|
+
masterColumn.dimensions.width += padding.left + padding.right;
|
|
2654
|
+
const sections = masterColumn.sections;
|
|
2655
|
+
let runningTop = 0;
|
|
2656
|
+
let firstSectionIsHeader = false;
|
|
2657
|
+
if (sections.length > 0) {
|
|
2658
|
+
const firstSubColumn = sections[0].subColumns[0];
|
|
2659
|
+
const firstElement = firstSubColumn?.elements[0];
|
|
2660
|
+
firstSectionIsHeader = !!(firstElement && firstElement.type === 'header');
|
|
2661
|
+
}
|
|
2662
|
+
for (let secIdx = 0; secIdx < sections.length; secIdx++) {
|
|
2663
|
+
const section = sections[secIdx];
|
|
2664
|
+
let sectionPaddingTop;
|
|
2665
|
+
if (secIdx === 0) {
|
|
2666
|
+
sectionPaddingTop = firstSectionIsHeader ? 0 : padding.top;
|
|
2667
|
+
}
|
|
2668
|
+
else {
|
|
2669
|
+
sectionPaddingTop = padding.top;
|
|
2670
|
+
}
|
|
2671
|
+
section.dimensions.width = masterColumn.dimensions.width;
|
|
2672
|
+
section.dimensions.left = masterColumn.dimensions.left;
|
|
2673
|
+
section.dimensions.top = runningTop;
|
|
2674
|
+
runningTop += sectionPaddingTop;
|
|
2675
|
+
const contentWidth = masterColumn.dimensions.width - padding.left - padding.right;
|
|
2676
|
+
const subColumns = section.subColumns;
|
|
2677
|
+
const totalSubColumns = subColumns.length;
|
|
2678
|
+
const subColumnWidth = contentWidth / totalSubColumns;
|
|
2679
|
+
let currentSubColumnLeft = masterColumn.dimensions.left + padding.left;
|
|
2680
|
+
let maxSectionHeight = 0;
|
|
2681
|
+
for (let scIdx = 0; scIdx < totalSubColumns; scIdx++) {
|
|
2682
|
+
const subColumn = subColumns[scIdx];
|
|
2683
|
+
subColumn.dimensions.width = subColumnWidth;
|
|
2684
|
+
subColumn.dimensions.left = currentSubColumnLeft;
|
|
2685
|
+
// TODO: The problem is here somewhere
|
|
2686
|
+
subColumn.dimensions.top = section.dimensions.top + sectionPaddingTop;
|
|
2687
|
+
let totalSubColumnHeight = 0;
|
|
2688
|
+
const elements = subColumn.elements;
|
|
2689
|
+
for (let elIdx = 0; elIdx < elements.length; elIdx++) {
|
|
2690
|
+
const element = elements[elIdx];
|
|
2691
|
+
let totalElementHeight = 0;
|
|
2692
|
+
const parts = element.parts;
|
|
2693
|
+
for (let pIdx = 0; pIdx < parts.length; pIdx++) {
|
|
2694
|
+
const part = parts[pIdx];
|
|
2695
|
+
part.dimensions.width = subColumnWidth;
|
|
2696
|
+
part.dimensions.left = currentSubColumnLeft;
|
|
2697
|
+
part.dimensions.top = subColumn.dimensions.top + totalSubColumnHeight + totalElementHeight;
|
|
2698
|
+
totalElementHeight += part.dimensions.height;
|
|
2699
|
+
}
|
|
2700
|
+
element.containerDimensions.height = totalElementHeight;
|
|
2701
|
+
element.containerDimensions.width = subColumnWidth;
|
|
2702
|
+
element.containerDimensions.left = currentSubColumnLeft;
|
|
2703
|
+
element.containerDimensions.top = subColumn.dimensions.top + totalSubColumnHeight;
|
|
2704
|
+
element.dimensions.width = subColumnWidth;
|
|
2705
|
+
element.dimensions.left = currentSubColumnLeft;
|
|
2706
|
+
element.dimensions.top =
|
|
2707
|
+
element.containerDimensions.top + (totalElementHeight - element.dimensions.height) * 0.5;
|
|
2708
|
+
totalSubColumnHeight += totalElementHeight;
|
|
2709
|
+
}
|
|
2710
|
+
subColumn.dimensions.height = totalSubColumnHeight;
|
|
2711
|
+
if (totalSubColumnHeight > maxSectionHeight)
|
|
2712
|
+
maxSectionHeight = totalSubColumnHeight;
|
|
2713
|
+
currentSubColumnLeft += subColumnWidth;
|
|
2714
|
+
}
|
|
2715
|
+
section.dimensions.height = maxSectionHeight + padding.bottom;
|
|
2716
|
+
runningTop += maxSectionHeight + padding.bottom;
|
|
2670
2717
|
}
|
|
2718
|
+
masterColumn.dimensions.height = runningTop;
|
|
2719
|
+
if (masterColumn.dimensions.height > maxGridHeight)
|
|
2720
|
+
maxGridHeight = masterColumn.dimensions.height;
|
|
2721
|
+
currentMasterColumnLeft += masterColumn.dimensions.width;
|
|
2671
2722
|
}
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2723
|
+
newGrid.dimensions.width = currentMasterColumnLeft;
|
|
2724
|
+
newGrid.dimensions.height = maxGridHeight;
|
|
2725
|
+
calculateSpanningElementDimensions();
|
|
2726
|
+
};
|
|
2727
|
+
const calculateSpanningElementDimensions = () => {
|
|
2728
|
+
const spanDimensions = new Map();
|
|
2729
|
+
const masterCols = newGrid.masterColumns;
|
|
2730
|
+
for (let mcIdx = 0; mcIdx < masterCols.length; mcIdx++) {
|
|
2731
|
+
const masterColumn = masterCols[mcIdx];
|
|
2732
|
+
const sections = masterColumn.sections;
|
|
2733
|
+
for (let secIdx = 0; secIdx < sections.length; secIdx++) {
|
|
2734
|
+
const section = sections[secIdx];
|
|
2735
|
+
const subColumns = section.subColumns;
|
|
2736
|
+
for (let scIdx = 0; scIdx < subColumns.length; scIdx++) {
|
|
2737
|
+
const subColumn = subColumns[scIdx];
|
|
2738
|
+
const elements = subColumn.elements;
|
|
2739
|
+
for (let elIdx = 0; elIdx < elements.length; elIdx++) {
|
|
2740
|
+
const element = elements[elIdx];
|
|
2741
|
+
if (!element.span)
|
|
2742
|
+
continue;
|
|
2743
|
+
const span = element.span;
|
|
2744
|
+
const isStartPosition = mcIdx === span.masterColumnStart && secIdx === span.sectionStart && scIdx === span.subColumnStart;
|
|
2745
|
+
const spanKey = `${span.masterColumnStart}-${span.masterColumnEnd}-${span.sectionStart}-${span.sectionEnd}-${span.subColumnStart}-${span.subColumnEnd}`;
|
|
2746
|
+
if (isStartPosition && !spanDimensions.has(spanKey)) {
|
|
2747
|
+
const totalSpannedWidth = calculateSpannedWidth(span, masterCols);
|
|
2748
|
+
const spanStartLeft = calculateSpanStartLeft(span, masterCols);
|
|
2749
|
+
const width = config.spanElementWidth;
|
|
2750
|
+
spanDimensions.set(spanKey, { width, left: spanStartLeft + (totalSpannedWidth - width) * 0.5 });
|
|
2751
|
+
}
|
|
2752
|
+
const storedDimensions = spanDimensions.get(spanKey);
|
|
2753
|
+
if (storedDimensions) {
|
|
2754
|
+
element.dimensions.width = storedDimensions.width;
|
|
2755
|
+
element.dimensions.left = storedDimensions.left;
|
|
2756
|
+
element.isHidden = !isStartPosition;
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
}
|
|
2679
2761
|
}
|
|
2680
|
-
}
|
|
2681
|
-
|
|
2682
|
-
const
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
});
|
|
2699
|
-
const { masterColumnSection: upperLowerGapSection, pushSubColumn: pushUpperLowerSubColumn } = createBracketMasterColumnSection({
|
|
2700
|
-
type: 'gap',
|
|
2701
|
-
});
|
|
2702
|
-
const { masterColumnSection: lowerSection, pushSubColumn: pushLowerSubColumn } = createBracketMasterColumnSection({
|
|
2703
|
-
type: 'round',
|
|
2704
|
-
});
|
|
2705
|
-
const upperSubColumn = createRoundBracketSubColumnRelativeToFirstRound({
|
|
2706
|
-
firstRound: firstUpperRound,
|
|
2707
|
-
round,
|
|
2708
|
-
options,
|
|
2709
|
-
hasReverseFinal,
|
|
2710
|
-
span: {
|
|
2711
|
-
isStart: true,
|
|
2712
|
-
isEnd: true,
|
|
2713
|
-
},
|
|
2714
|
-
components,
|
|
2715
|
-
});
|
|
2716
|
-
pushUpperSubColumn(upperSubColumn);
|
|
2717
|
-
const upperLowerGapSubColumn = createBracketSubColumn({
|
|
2718
|
-
span: {
|
|
2719
|
-
isStart: true,
|
|
2720
|
-
isEnd: true,
|
|
2721
|
-
},
|
|
2722
|
-
});
|
|
2723
|
-
const upperLowerGapElement = createBracketElement({
|
|
2724
|
-
area: '.',
|
|
2725
|
-
type: 'roundGap',
|
|
2726
|
-
elementHeight: options.rowRoundGap,
|
|
2727
|
-
partHeights: [options.rowRoundGap],
|
|
2728
|
-
});
|
|
2729
|
-
upperLowerGapSubColumn.pushElement(upperLowerGapElement.element);
|
|
2730
|
-
pushUpperLowerSubColumn(upperLowerGapSubColumn.subColumn);
|
|
2731
|
-
if (thirdPlaceRound) {
|
|
2732
|
-
const lowerSubColumn = createRoundBracketSubColumnRelativeToFirstRound({
|
|
2733
|
-
firstRound: firstLowerRound,
|
|
2734
|
-
round: thirdPlaceRound,
|
|
2735
|
-
options,
|
|
2736
|
-
hasReverseFinal,
|
|
2737
|
-
span: {
|
|
2738
|
-
isStart: isFirstRound,
|
|
2739
|
-
isEnd: isLastRound,
|
|
2740
|
-
},
|
|
2741
|
-
components,
|
|
2742
|
-
});
|
|
2743
|
-
pushLowerSubColumn(lowerSubColumn);
|
|
2762
|
+
};
|
|
2763
|
+
const calculateSpannedWidth = (span, masterColumns) => {
|
|
2764
|
+
const key = `${span.masterColumnStart}-${span.masterColumnEnd}-${span.sectionStart}-${span.sectionEnd}-${span.subColumnStart}-${span.subColumnEnd}`;
|
|
2765
|
+
if (spannedWidthCache.has(key))
|
|
2766
|
+
return spannedWidthCache.get(key);
|
|
2767
|
+
if (span.masterColumnStart === span.masterColumnEnd) {
|
|
2768
|
+
const masterColumn = masterColumns[span.masterColumnStart];
|
|
2769
|
+
if (masterColumn) {
|
|
2770
|
+
const section = masterColumn.sections[span.sectionStart];
|
|
2771
|
+
if (section) {
|
|
2772
|
+
const subColumnWidth = section.dimensions.width / section.subColumns.length;
|
|
2773
|
+
const totalWidth = subColumnWidth * (span.subColumnEnd - span.subColumnStart + 1);
|
|
2774
|
+
spannedWidthCache.set(key, totalWidth);
|
|
2775
|
+
return totalWidth;
|
|
2776
|
+
}
|
|
2777
|
+
}
|
|
2778
|
+
spannedWidthCache.set(key, 0);
|
|
2779
|
+
return 0;
|
|
2744
2780
|
}
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
partHeights: element.parts.map((p) => p.dimensions.height),
|
|
2767
|
-
});
|
|
2768
|
-
lowerSubColumn.pushElement(el.element);
|
|
2781
|
+
let totalWidth = 0;
|
|
2782
|
+
for (let mcIdx = span.masterColumnStart; mcIdx <= span.masterColumnEnd; mcIdx++) {
|
|
2783
|
+
const masterColumn = masterColumns[mcIdx];
|
|
2784
|
+
if (!masterColumn)
|
|
2785
|
+
continue;
|
|
2786
|
+
if (mcIdx === span.masterColumnStart) {
|
|
2787
|
+
const section = masterColumn.sections[span.sectionStart];
|
|
2788
|
+
if (section) {
|
|
2789
|
+
const subColumnWidth = section.dimensions.width / section.subColumns.length;
|
|
2790
|
+
totalWidth += subColumnWidth * (section.subColumns.length - span.subColumnStart);
|
|
2791
|
+
}
|
|
2792
|
+
}
|
|
2793
|
+
else if (mcIdx === span.masterColumnEnd) {
|
|
2794
|
+
const section = masterColumn.sections[span.sectionEnd];
|
|
2795
|
+
if (section) {
|
|
2796
|
+
const subColumnWidth = section.dimensions.width / section.subColumns.length;
|
|
2797
|
+
totalWidth += subColumnWidth * (span.subColumnEnd + 1);
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
else {
|
|
2801
|
+
totalWidth += masterColumn.dimensions.width;
|
|
2769
2802
|
}
|
|
2770
|
-
pushLowerSubColumn(lowerSubColumn.subColumn);
|
|
2771
2803
|
}
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2804
|
+
spannedWidthCache.set(key, totalWidth);
|
|
2805
|
+
return totalWidth;
|
|
2806
|
+
};
|
|
2807
|
+
const calculateSpanStartLeft = (span, masterColumns) => {
|
|
2808
|
+
const key = `${span.masterColumnStart}-${span.sectionStart}-${span.subColumnStart}`;
|
|
2809
|
+
if (spanStartLeftCache.has(key))
|
|
2810
|
+
return spanStartLeftCache.get(key);
|
|
2811
|
+
const startMasterColumn = masterColumns[span.masterColumnStart];
|
|
2812
|
+
if (!startMasterColumn) {
|
|
2813
|
+
spanStartLeftCache.set(key, 0);
|
|
2814
|
+
return 0;
|
|
2779
2815
|
}
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
matchElementMap: finalizedGrid.elementMap,
|
|
2816
|
+
let startLeft = startMasterColumn.dimensions.left;
|
|
2817
|
+
const section = startMasterColumn.sections[span.sectionStart];
|
|
2818
|
+
if (section) {
|
|
2819
|
+
startLeft += (section.dimensions.width / section.subColumns.length) * span.subColumnStart;
|
|
2820
|
+
}
|
|
2821
|
+
spanStartLeftCache.set(key, startLeft);
|
|
2822
|
+
return startLeft;
|
|
2788
2823
|
};
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2824
|
+
const setupElementSpans = () => {
|
|
2825
|
+
const masterCols = newGrid.masterColumns;
|
|
2826
|
+
for (let mcIdx = 0; mcIdx < masterCols.length; mcIdx++) {
|
|
2827
|
+
const masterColumn = masterCols[mcIdx];
|
|
2828
|
+
const sections = masterColumn.sections;
|
|
2829
|
+
for (let secIdx = 0; secIdx < sections.length; secIdx++) {
|
|
2830
|
+
const section = sections[secIdx];
|
|
2831
|
+
const subColumns = section.subColumns;
|
|
2832
|
+
for (let scIdx = 0; scIdx < subColumns.length; scIdx++) {
|
|
2833
|
+
const subColumn = subColumns[scIdx];
|
|
2834
|
+
if (subColumn.span.isStart && subColumn.span.isEnd)
|
|
2835
|
+
continue;
|
|
2836
|
+
let spanStart = { masterColumnIndex: mcIdx, sectionIndex: secIdx, subColumnIndex: scIdx };
|
|
2837
|
+
if (!subColumn.span.isStart) {
|
|
2838
|
+
outer: for (let m = mcIdx; m >= 0; m--) {
|
|
2839
|
+
const mc = masterCols[m];
|
|
2840
|
+
if (!mc)
|
|
2841
|
+
continue;
|
|
2842
|
+
const sec = mc.sections[secIdx];
|
|
2843
|
+
if (!sec)
|
|
2844
|
+
continue;
|
|
2845
|
+
const end = m === mcIdx ? scIdx : sec.subColumns.length - 1;
|
|
2846
|
+
for (let s = end; s >= 0; s--) {
|
|
2847
|
+
if (sec.subColumns[s]?.span.isStart) {
|
|
2848
|
+
spanStart = { masterColumnIndex: m, sectionIndex: secIdx, subColumnIndex: s };
|
|
2849
|
+
break outer;
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
let spanEnd = { masterColumnIndex: mcIdx, sectionIndex: secIdx, subColumnIndex: scIdx };
|
|
2855
|
+
if (!subColumn.span.isEnd) {
|
|
2856
|
+
outer: for (let m = mcIdx; m < masterCols.length; m++) {
|
|
2857
|
+
const mc = masterCols[m];
|
|
2858
|
+
if (!mc)
|
|
2859
|
+
continue;
|
|
2860
|
+
const sec = mc.sections[secIdx];
|
|
2861
|
+
if (!sec)
|
|
2862
|
+
continue;
|
|
2863
|
+
const start = m === mcIdx ? scIdx : 0;
|
|
2864
|
+
for (let s = start; s < sec.subColumns.length; s++) {
|
|
2865
|
+
if (sec.subColumns[s]?.span.isEnd) {
|
|
2866
|
+
spanEnd = { masterColumnIndex: m, sectionIndex: secIdx, subColumnIndex: s };
|
|
2867
|
+
break outer;
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
const elements = subColumn.elements;
|
|
2873
|
+
for (let elIdx = 0; elIdx < elements.length; elIdx++) {
|
|
2874
|
+
elements[elIdx].span = {
|
|
2875
|
+
masterColumnStart: spanStart.masterColumnIndex,
|
|
2876
|
+
masterColumnEnd: spanEnd.masterColumnIndex,
|
|
2877
|
+
sectionStart: spanStart.sectionIndex,
|
|
2878
|
+
sectionEnd: spanEnd.sectionIndex,
|
|
2879
|
+
subColumnStart: spanStart.subColumnIndex,
|
|
2880
|
+
subColumnEnd: spanEnd.subColumnIndex,
|
|
2881
|
+
};
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2831
2885
|
}
|
|
2832
|
-
}
|
|
2833
|
-
grid.calculateDimensions();
|
|
2834
|
-
const finalizedGrid = finalizeBracketGrid(grid);
|
|
2886
|
+
};
|
|
2835
2887
|
return {
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2888
|
+
grid: newGrid,
|
|
2889
|
+
pushMasterColumn,
|
|
2890
|
+
calculateDimensions,
|
|
2891
|
+
setupElementSpans,
|
|
2839
2892
|
};
|
|
2840
2893
|
};
|
|
2841
2894
|
|
|
2842
|
-
const
|
|
2843
|
-
const
|
|
2844
|
-
const
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
const double = relation.previousRoundMatchFactor === 2 ? 1 : 0;
|
|
2863
|
-
return {
|
|
2864
|
-
nextRoundMatchPosition: generateMatchPosition(match, relation.nextRoundMatchFactor),
|
|
2865
|
-
previousUpperRoundMatchPosition: (generateMatchPosition(match, relation.previousRoundMatchFactor) -
|
|
2866
|
-
double),
|
|
2867
|
-
previousLowerRoundMatchPosition: generateMatchPosition(match, relation.previousRoundMatchFactor),
|
|
2868
|
-
};
|
|
2869
|
-
}
|
|
2870
|
-
case 'two-to-one':
|
|
2871
|
-
return {
|
|
2872
|
-
nextRoundMatchPosition: generateMatchPosition(match, relation.nextRoundMatchFactor),
|
|
2873
|
-
previousUpperRoundMatchPosition: generateMatchPosition(match, relation.previousUpperRoundMatchFactor),
|
|
2874
|
-
previousLowerRoundMatchPosition: generateMatchPosition(match, relation.previousLowerRoundMatchFactor),
|
|
2875
|
-
};
|
|
2876
|
-
case 'two-to-nothing':
|
|
2877
|
-
return {
|
|
2878
|
-
nextRoundMatchPosition: FALLBACK_MATCH_RELATION_POSITION,
|
|
2879
|
-
previousUpperRoundMatchPosition: generateMatchPosition(match, relation.previousUpperRoundMatchFactor),
|
|
2880
|
-
previousLowerRoundMatchPosition: generateMatchPosition(match, relation.previousLowerRoundMatchFactor),
|
|
2881
|
-
};
|
|
2882
|
-
}
|
|
2895
|
+
const createBracketMasterColumn = (config) => {
|
|
2896
|
+
const { columnWidth, padding } = config;
|
|
2897
|
+
const sections = [];
|
|
2898
|
+
const newMasterColumn = {
|
|
2899
|
+
dimensions: {
|
|
2900
|
+
width: columnWidth,
|
|
2901
|
+
height: 0,
|
|
2902
|
+
top: 0,
|
|
2903
|
+
left: 0,
|
|
2904
|
+
},
|
|
2905
|
+
padding,
|
|
2906
|
+
sections,
|
|
2907
|
+
};
|
|
2908
|
+
const pushSection = (...newSections) => {
|
|
2909
|
+
sections.push(...newSections);
|
|
2910
|
+
};
|
|
2911
|
+
return {
|
|
2912
|
+
masterColumn: newMasterColumn,
|
|
2913
|
+
pushSection,
|
|
2914
|
+
};
|
|
2883
2915
|
};
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
const
|
|
2887
|
-
|
|
2888
|
-
|
|
2916
|
+
|
|
2917
|
+
const createBracketMasterColumnSection = (config) => {
|
|
2918
|
+
const { type } = config;
|
|
2919
|
+
const subColumns = [];
|
|
2920
|
+
const newMasterColumnSection = {
|
|
2921
|
+
dimensions: {
|
|
2922
|
+
width: 0,
|
|
2923
|
+
height: 0,
|
|
2924
|
+
top: 0,
|
|
2925
|
+
left: 0,
|
|
2926
|
+
},
|
|
2927
|
+
subColumns,
|
|
2928
|
+
type,
|
|
2929
|
+
};
|
|
2930
|
+
const pushSubColumn = (...newSubColumns) => {
|
|
2931
|
+
subColumns.push(...newSubColumns);
|
|
2932
|
+
};
|
|
2889
2933
|
return {
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
currentRound: relation.currentRound,
|
|
2893
|
-
nextMatch,
|
|
2894
|
-
nextRound: relation.nextRound,
|
|
2934
|
+
masterColumnSection: newMasterColumnSection,
|
|
2935
|
+
pushSubColumn,
|
|
2895
2936
|
};
|
|
2896
2937
|
};
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
const
|
|
2900
|
-
const
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2938
|
+
|
|
2939
|
+
const createBracketSubColumn = (config) => {
|
|
2940
|
+
const { span } = config;
|
|
2941
|
+
const elements = [];
|
|
2942
|
+
const newSubColumn = {
|
|
2943
|
+
dimensions: {
|
|
2944
|
+
width: 0,
|
|
2945
|
+
height: 0,
|
|
2946
|
+
top: 0,
|
|
2947
|
+
left: 0,
|
|
2948
|
+
},
|
|
2949
|
+
elements,
|
|
2950
|
+
span,
|
|
2951
|
+
};
|
|
2952
|
+
const pushElement = (...newElements) => {
|
|
2953
|
+
elements.push(...newElements);
|
|
2954
|
+
};
|
|
2955
|
+
return {
|
|
2956
|
+
subColumn: newSubColumn,
|
|
2957
|
+
pushElement,
|
|
2958
|
+
};
|
|
2959
|
+
};
|
|
2960
|
+
|
|
2961
|
+
function logGridAreasFormatted(gridTemplateAreas) {
|
|
2962
|
+
if (!gridTemplateAreas) {
|
|
2963
|
+
console.log('No grid areas to display');
|
|
2964
|
+
return;
|
|
2915
2965
|
}
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2966
|
+
// Parse the grid template areas string
|
|
2967
|
+
const rows = gridTemplateAreas
|
|
2968
|
+
.split('\n')
|
|
2969
|
+
.map((row) => row.replace(/^"/, '').replace(/"$/, '')) // Remove quotes
|
|
2970
|
+
.map((row) => row.split(' ')); // Split into individual areas
|
|
2971
|
+
if (rows.length === 0) {
|
|
2972
|
+
console.log('No grid areas to display');
|
|
2973
|
+
return;
|
|
2924
2974
|
}
|
|
2925
|
-
|
|
2926
|
-
const
|
|
2927
|
-
const
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
if (!nextMatch)
|
|
2932
|
-
throw new Error('Next round match not found');
|
|
2933
|
-
if (!previousUpperMatch)
|
|
2934
|
-
throw new Error('Previous upper round match not found');
|
|
2935
|
-
if (!previousLowerMatch)
|
|
2936
|
-
throw new Error('Previous lower round match not found');
|
|
2937
|
-
if (previousUpperRoundMatchPosition === previousLowerRoundMatchPosition) {
|
|
2938
|
-
return {
|
|
2939
|
-
type: 'one-to-one',
|
|
2940
|
-
currentMatch: match,
|
|
2941
|
-
currentRound: relation.currentRound,
|
|
2942
|
-
previousMatch: previousUpperMatch,
|
|
2943
|
-
previousRound: relation.previousRound,
|
|
2944
|
-
nextMatch,
|
|
2945
|
-
nextRound: relation.nextRound,
|
|
2946
|
-
};
|
|
2975
|
+
// Find the maximum width for each column to align properly
|
|
2976
|
+
const columnCount = rows[0]?.length ?? 0;
|
|
2977
|
+
const columnWidths = [];
|
|
2978
|
+
for (let colIndex = 0; colIndex < columnCount; colIndex++) {
|
|
2979
|
+
const maxWidth = Math.max(...rows.map((row) => (row[colIndex] || '').length));
|
|
2980
|
+
columnWidths.push(Math.max(maxWidth, 3)); // Minimum width of 3
|
|
2947
2981
|
}
|
|
2948
|
-
|
|
2982
|
+
console.log('\n📋 Grid Template Areas (Formatted):');
|
|
2983
|
+
console.log('═'.repeat(columnWidths.reduce((sum, width) => sum + width + 3, 0)));
|
|
2984
|
+
// Log each row with proper spacing
|
|
2985
|
+
rows.forEach((row, rowIndex) => {
|
|
2986
|
+
const formattedRow = row
|
|
2987
|
+
.map((area, colIndex) => {
|
|
2988
|
+
const width = columnWidths[colIndex] ?? 0;
|
|
2989
|
+
return area.padEnd(width);
|
|
2990
|
+
})
|
|
2991
|
+
.join(' | ');
|
|
2992
|
+
console.log(`${String(rowIndex).padStart(2, '0')}: ${formattedRow}`);
|
|
2993
|
+
});
|
|
2994
|
+
console.log('═'.repeat(columnWidths.reduce((sum, width) => sum + width + 3, 0)));
|
|
2995
|
+
console.log(`📊 Grid dimensions: ${rows.length} rows × ${columnCount} columns\n`);
|
|
2996
|
+
}
|
|
2997
|
+
function gridColumnsToGridProperty(grid) {
|
|
2998
|
+
if (!grid.length) {
|
|
2949
2999
|
return {
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
previousUpperMatch,
|
|
2954
|
-
previousUpperRound: relation.previousRound,
|
|
2955
|
-
previousLowerMatch,
|
|
2956
|
-
previousLowerRound: relation.previousRound,
|
|
2957
|
-
nextMatch,
|
|
2958
|
-
nextRound: relation.nextRound,
|
|
3000
|
+
gridTemplateAreas: '',
|
|
3001
|
+
gridTemplateRows: '',
|
|
3002
|
+
gridTemplateColumns: '',
|
|
2959
3003
|
};
|
|
2960
3004
|
}
|
|
2961
|
-
|
|
2962
|
-
const
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3005
|
+
// Calculate the total number of columns (subcolumns across all master columns)
|
|
3006
|
+
const totalColumns = grid.reduce((total, masterColumn) => {
|
|
3007
|
+
return total + Math.max(...masterColumn.sections.map((section) => section.subColumns.length));
|
|
3008
|
+
}, 0);
|
|
3009
|
+
// Find all unique sections across all master columns to determine rows
|
|
3010
|
+
const allSections = [];
|
|
3011
|
+
grid.forEach((masterColumn, masterColumnIndex) => {
|
|
3012
|
+
masterColumn.sections.forEach((section, sectionIndex) => {
|
|
3013
|
+
// For each section, we need to process all its subcolumns
|
|
3014
|
+
section.subColumns.forEach((subColumn) => {
|
|
3015
|
+
allSections.push({
|
|
3016
|
+
elements: subColumn.elements,
|
|
3017
|
+
sectionIndex,
|
|
3018
|
+
masterColumnIndex,
|
|
3019
|
+
});
|
|
3020
|
+
});
|
|
3021
|
+
});
|
|
3022
|
+
});
|
|
3023
|
+
// Group sections by their index to create rows
|
|
3024
|
+
const sectionsByIndex = new Map();
|
|
3025
|
+
let globalColumnIndex = 0;
|
|
3026
|
+
grid.forEach((masterColumn, masterColumnIndex) => {
|
|
3027
|
+
const maxSubColumns = Math.max(...masterColumn.sections.map((section) => section.subColumns.length));
|
|
3028
|
+
masterColumn.sections.forEach((section, sectionIndex) => {
|
|
3029
|
+
if (!sectionsByIndex.has(sectionIndex)) {
|
|
3030
|
+
sectionsByIndex.set(sectionIndex, []);
|
|
3031
|
+
}
|
|
3032
|
+
section.subColumns.forEach((subColumn, subColumnIndex) => {
|
|
3033
|
+
sectionsByIndex.get(sectionIndex).push({
|
|
3034
|
+
elements: subColumn.elements,
|
|
3035
|
+
columnIndex: globalColumnIndex + subColumnIndex,
|
|
3036
|
+
masterColumnIndex,
|
|
3037
|
+
});
|
|
3038
|
+
});
|
|
3039
|
+
});
|
|
3040
|
+
globalColumnIndex += maxSubColumns;
|
|
3041
|
+
});
|
|
3042
|
+
// Build the grid matrix - sections become rows, subcolumns become columns
|
|
3043
|
+
const areaMatrix = [];
|
|
3044
|
+
const rowHeights = [];
|
|
3045
|
+
// Process each section (which becomes a row group)
|
|
3046
|
+
for (const [sectionIndex, sectionColumns] of sectionsByIndex.entries()) {
|
|
3047
|
+
// Find max rows needed for this section
|
|
3048
|
+
const maxRowsInSection = Math.max(...sectionColumns.map((col) => col.elements.reduce((total, element) => total + element.parts.length, 0)), 0);
|
|
3049
|
+
// Create rows for this section
|
|
3050
|
+
for (let rowIndex = 0; rowIndex < maxRowsInSection; rowIndex++) {
|
|
3051
|
+
const areaRow = new Array(totalColumns).fill('.');
|
|
3052
|
+
let currentRowHeight;
|
|
3053
|
+
// Fill in areas for each column in this section
|
|
3054
|
+
for (const sectionColumn of sectionColumns) {
|
|
3055
|
+
let accumulatedRows = 0;
|
|
3056
|
+
let area = '.';
|
|
3057
|
+
let height;
|
|
3058
|
+
// Find which element this row belongs to
|
|
3059
|
+
for (const element of sectionColumn.elements) {
|
|
3060
|
+
const elementRowCount = element.parts.length;
|
|
3061
|
+
if (rowIndex >= accumulatedRows && rowIndex < accumulatedRows + elementRowCount) {
|
|
3062
|
+
const relativeRowIndex = rowIndex - accumulatedRows;
|
|
3063
|
+
area = element.area;
|
|
3064
|
+
height = element.parts[relativeRowIndex]?.dimensions.height;
|
|
3065
|
+
break;
|
|
3066
|
+
}
|
|
3067
|
+
accumulatedRows += elementRowCount;
|
|
3068
|
+
}
|
|
3069
|
+
areaRow[sectionColumn.columnIndex] = area;
|
|
3070
|
+
// Use the first defined height for this row
|
|
3071
|
+
if (!currentRowHeight && height !== undefined) {
|
|
3072
|
+
currentRowHeight = `${height}px`;
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
areaMatrix.push(areaRow);
|
|
3076
|
+
rowHeights.push(currentRowHeight ?? 'auto');
|
|
3077
|
+
}
|
|
3078
|
+
}
|
|
3079
|
+
// Calculate column widths
|
|
3080
|
+
const columnWidths = [];
|
|
3081
|
+
for (const masterColumn of grid) {
|
|
3082
|
+
const maxSubColumns = Math.max(...masterColumn.sections.map((section) => section.subColumns.length));
|
|
3083
|
+
for (let i = 0; i < maxSubColumns; i++) {
|
|
3084
|
+
// Find the width from any section that has this subcolumn
|
|
3085
|
+
const subColumnWidth = masterColumn.sections.find((section) => section.subColumns[i])?.subColumns[i]?.dimensions.width ?? 0;
|
|
3086
|
+
columnWidths.push(`${subColumnWidth}px`);
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
// Build the CSS properties
|
|
3090
|
+
const gridTemplateAreas = areaMatrix.map((row) => `"${row.join(' ')}"`).join('\n');
|
|
3091
|
+
const gridTemplateRows = rowHeights.join(' ');
|
|
3092
|
+
const gridTemplateColumns = columnWidths.join(' ');
|
|
3093
|
+
const result = {
|
|
3094
|
+
gridTemplateAreas,
|
|
3095
|
+
gridTemplateRows,
|
|
3096
|
+
gridTemplateColumns,
|
|
3009
3097
|
};
|
|
3098
|
+
// logGridAreasFormatted(gridTemplateAreas);
|
|
3099
|
+
return result;
|
|
3100
|
+
}
|
|
3101
|
+
|
|
3102
|
+
const calculateUpperLowerRatio = (upperRoundsCount, lowerRoundsCount) => {
|
|
3103
|
+
// In double elimination, after the first aligned column,
|
|
3104
|
+
// we need to distribute the remaining upper rounds across the remaining lower rounds
|
|
3105
|
+
const remainingUpperRounds = upperRoundsCount - 1; // Subtract the first round
|
|
3106
|
+
const remainingLowerRounds = lowerRoundsCount - 1; // Subtract the first round
|
|
3107
|
+
if (remainingUpperRounds === 0) {
|
|
3108
|
+
return 1; // Edge case: only one upper round
|
|
3109
|
+
}
|
|
3110
|
+
return remainingLowerRounds / remainingUpperRounds;
|
|
3010
3111
|
};
|
|
3011
|
-
const
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3112
|
+
const calculateColumnSplitFactor = (upperToLowerRatio) => {
|
|
3113
|
+
// The split factor determines how many sub-columns each match column will have
|
|
3114
|
+
// For example, if the ratio is 1.5, we split each match column
|
|
3115
|
+
// into 2 sub-columns so that matches can be centered correctly
|
|
3116
|
+
if (upperToLowerRatio === 1.5) {
|
|
3117
|
+
return 2; // Split into 2 sub-columns for 1.5 ratio
|
|
3017
3118
|
}
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
const { nextRoundMatchPosition, previousUpperRoundMatchPosition, previousLowerRoundMatchPosition } = generateMatchRelationPositions(relation, match);
|
|
3021
|
-
switch (relation.type) {
|
|
3022
|
-
case 'nothing-to-one':
|
|
3023
|
-
matchRelations.push(createNothingToOneRelation$1({
|
|
3024
|
-
match,
|
|
3025
|
-
relation,
|
|
3026
|
-
matchPositionMaps,
|
|
3027
|
-
nextRoundMatchPosition,
|
|
3028
|
-
}));
|
|
3029
|
-
break;
|
|
3030
|
-
case 'one-to-nothing':
|
|
3031
|
-
matchRelations.push(createOneToNothingOrTwoToNothingRelation({
|
|
3032
|
-
match,
|
|
3033
|
-
relation,
|
|
3034
|
-
matchPositionMaps,
|
|
3035
|
-
previousUpperRoundMatchPosition,
|
|
3036
|
-
previousLowerRoundMatchPosition,
|
|
3037
|
-
}));
|
|
3038
|
-
break;
|
|
3039
|
-
case 'one-to-one':
|
|
3040
|
-
matchRelations.push(createOneToOneOrTwoToOneRelation({
|
|
3041
|
-
match,
|
|
3042
|
-
relation,
|
|
3043
|
-
matchPositionMaps,
|
|
3044
|
-
nextRoundMatchPosition,
|
|
3045
|
-
previousUpperRoundMatchPosition,
|
|
3046
|
-
previousLowerRoundMatchPosition,
|
|
3047
|
-
}));
|
|
3048
|
-
break;
|
|
3049
|
-
case 'two-to-one':
|
|
3050
|
-
matchRelations.push(createTwoToOneRelation$1({
|
|
3051
|
-
match,
|
|
3052
|
-
relation,
|
|
3053
|
-
matchPositionMaps,
|
|
3054
|
-
nextRoundMatchPosition,
|
|
3055
|
-
previousUpperRoundMatchPosition,
|
|
3056
|
-
previousLowerRoundMatchPosition,
|
|
3057
|
-
}));
|
|
3058
|
-
break;
|
|
3059
|
-
case 'two-to-nothing':
|
|
3060
|
-
matchRelations.push(createTwoToNothingRelation$1({
|
|
3061
|
-
match,
|
|
3062
|
-
relation,
|
|
3063
|
-
matchPositionMaps,
|
|
3064
|
-
previousUpperRoundMatchPosition,
|
|
3065
|
-
previousLowerRoundMatchPosition,
|
|
3066
|
-
}));
|
|
3067
|
-
break;
|
|
3068
|
-
}
|
|
3119
|
+
if (upperToLowerRatio === 2) {
|
|
3120
|
+
return 1; // Split into 1 sub-columns for 2 ratio
|
|
3069
3121
|
}
|
|
3070
|
-
return
|
|
3122
|
+
return 1;
|
|
3071
3123
|
};
|
|
3072
|
-
|
|
3073
|
-
const
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
}
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
});
|
|
3087
|
-
const createOneToOneRelation = (params) => ({
|
|
3088
|
-
type: 'one-to-one',
|
|
3089
|
-
currentRound: params.currentRound,
|
|
3090
|
-
previousRound: params.previousRound,
|
|
3091
|
-
nextRound: params.nextRound,
|
|
3092
|
-
nextRoundMatchFactor: calculateMatchFactor(params.nextRound, params.currentRound),
|
|
3093
|
-
previousRoundMatchFactor: calculateMatchFactor(params.previousRound, params.currentRound),
|
|
3094
|
-
rootRoundMatchFactor: calculateMatchFactor(params.rootRound, params.currentRound),
|
|
3095
|
-
});
|
|
3096
|
-
const createTwoToOneRelation = (params) => ({
|
|
3097
|
-
type: 'two-to-one',
|
|
3098
|
-
currentRound: params.currentRound,
|
|
3099
|
-
previousUpperRound: params.previousUpperRound,
|
|
3100
|
-
previousLowerRound: params.previousLowerRound,
|
|
3101
|
-
nextRound: params.nextRound,
|
|
3102
|
-
nextRoundMatchFactor: calculateMatchFactor(params.nextRound, params.currentRound),
|
|
3103
|
-
previousUpperRoundMatchFactor: calculateMatchFactor(params.previousUpperRound, params.currentRound),
|
|
3104
|
-
previousLowerRoundMatchFactor: calculateMatchFactor(params.previousLowerRound, params.currentRound),
|
|
3105
|
-
upperRootRoundMatchFactor: calculateMatchFactor(params.firstUpperRound, params.currentRound),
|
|
3106
|
-
lowerRootRoundMatchFactor: calculateMatchFactor(params.firstLowerRound, params.currentRound),
|
|
3107
|
-
});
|
|
3108
|
-
const createTwoToNothingRelation = (params) => ({
|
|
3109
|
-
type: 'two-to-nothing',
|
|
3110
|
-
currentRound: params.currentRound,
|
|
3111
|
-
previousUpperRound: params.previousUpperRound,
|
|
3112
|
-
previousLowerRound: params.previousLowerRound,
|
|
3113
|
-
previousUpperRoundMatchFactor: calculateMatchFactor(params.previousUpperRound, params.currentRound),
|
|
3114
|
-
previousLowerRoundMatchFactor: calculateMatchFactor(params.previousLowerRound, params.currentRound),
|
|
3115
|
-
upperRootRoundMatchFactor: calculateMatchFactor(params.firstUpperRound, params.currentRound),
|
|
3116
|
-
lowerRootRoundMatchFactor: calculateMatchFactor(params.firstLowerRound, params.currentRound),
|
|
3117
|
-
});
|
|
3118
|
-
const getNavigationContext = (params) => {
|
|
3119
|
-
const { upperRounds, currentUpperRoundIndex } = params;
|
|
3120
|
-
const currentUpperRound = upperRounds[currentUpperRoundIndex];
|
|
3121
|
-
if (!currentUpperRound)
|
|
3122
|
-
throw new Error('currentUpperRound is null');
|
|
3123
|
-
const isLeftToRight = !currentUpperRound.mirrorRoundType || currentUpperRound.mirrorRoundType === BRACKET_ROUND_MIRROR_TYPE.LEFT;
|
|
3124
|
-
const relativePrevious = upperRounds[currentUpperRoundIndex - 1] || null;
|
|
3125
|
-
const relativeNext = upperRounds[currentUpperRoundIndex + 1] || null;
|
|
3126
|
-
const previousUpperRound = isLeftToRight ? relativePrevious : relativeNext;
|
|
3127
|
-
const nextUpperRound = isLeftToRight ? relativeNext : relativePrevious;
|
|
3128
|
-
const isLastUpperRound = !nextUpperRound ||
|
|
3129
|
-
(nextUpperRound.mirrorRoundType === BRACKET_ROUND_MIRROR_TYPE.RIGHT && !currentUpperRound.mirrorRoundType);
|
|
3130
|
-
const isFinal = currentUpperRound.type === COMMON_BRACKET_ROUND_TYPE.FINAL;
|
|
3131
|
-
return {
|
|
3132
|
-
currentUpperRound,
|
|
3133
|
-
previousUpperRound,
|
|
3134
|
-
nextUpperRound,
|
|
3135
|
-
isLastUpperRound,
|
|
3136
|
-
isFinal,
|
|
3137
|
-
};
|
|
3124
|
+
const calculateLowerRoundIndex = (subColumnIndex, splitFactor) => Math.floor(subColumnIndex / splitFactor);
|
|
3125
|
+
const calculateUpperRoundIndex = (subColumnIndex, upperToLowerRatio, splitFactor) => {
|
|
3126
|
+
// Calculate which complete column we're in
|
|
3127
|
+
const completeColumnIndex = Math.floor(subColumnIndex / splitFactor);
|
|
3128
|
+
// For the first complete column (index 0), always use the first upper round (index 0)
|
|
3129
|
+
if (completeColumnIndex === 0) {
|
|
3130
|
+
return 0;
|
|
3131
|
+
}
|
|
3132
|
+
// For subsequent columns, map based on the ratio
|
|
3133
|
+
// We subtract 1 because the first column is handled separately
|
|
3134
|
+
const adjustedColumnIndex = completeColumnIndex - 1;
|
|
3135
|
+
// Calculate which upper round this column should use
|
|
3136
|
+
// We add 1 because we start from the second upper round (index 1)
|
|
3137
|
+
return Math.floor(adjustedColumnIndex / upperToLowerRatio) + 1;
|
|
3138
3138
|
};
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
const
|
|
3142
|
-
const
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
const
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3139
|
+
|
|
3140
|
+
const createBracketGapMasterColumn = (config) => {
|
|
3141
|
+
const { existingMasterColumns, columnGap } = config;
|
|
3142
|
+
const lastMasterColumn = existingMasterColumns[existingMasterColumns.length - 1];
|
|
3143
|
+
if (!lastMasterColumn) {
|
|
3144
|
+
throw new Error('No last master column found in existing master columns');
|
|
3145
|
+
}
|
|
3146
|
+
const { masterColumn, pushSection } = createBracketMasterColumn({
|
|
3147
|
+
columnWidth: columnGap,
|
|
3148
|
+
padding: {
|
|
3149
|
+
bottom: 0,
|
|
3150
|
+
left: 0,
|
|
3151
|
+
right: 0,
|
|
3152
|
+
top: 0,
|
|
3153
|
+
},
|
|
3154
|
+
});
|
|
3155
|
+
for (const section of lastMasterColumn.sections) {
|
|
3156
|
+
const { masterColumnSection, pushSubColumn } = createBracketMasterColumnSection({
|
|
3157
|
+
type: 'gap',
|
|
3158
|
+
});
|
|
3159
|
+
const lastSubColumn = section.subColumns[section.subColumns.length - 1];
|
|
3160
|
+
if (!lastSubColumn) {
|
|
3161
|
+
throw new Error('No last sub column found in section');
|
|
3162
|
+
}
|
|
3163
|
+
const { pushElement, subColumn } = createBracketSubColumn({
|
|
3164
|
+
span: {
|
|
3165
|
+
isStart: lastSubColumn.span.isEnd ? true : false,
|
|
3166
|
+
isEnd: lastSubColumn.span.isEnd ? true : false,
|
|
3167
|
+
},
|
|
3168
|
+
});
|
|
3169
|
+
for (const lastSubColumnElement of lastSubColumn.elements) {
|
|
3170
|
+
const partHeights = lastSubColumnElement.parts.map((part) => part.dimensions.height);
|
|
3171
|
+
const elementToCreate = (() => {
|
|
3172
|
+
switch (lastSubColumnElement.type) {
|
|
3173
|
+
case 'matchGap':
|
|
3174
|
+
case 'roundHeaderGap':
|
|
3175
|
+
case 'roundGap':
|
|
3176
|
+
case 'colGap': {
|
|
3177
|
+
return {
|
|
3178
|
+
area: !lastSubColumn.span.isEnd ? lastSubColumnElement.area : `.`,
|
|
3179
|
+
type: !lastSubColumn.span.isEnd ? lastSubColumnElement.type : 'colGap',
|
|
3180
|
+
elementHeight: lastSubColumnElement.dimensions.height,
|
|
3181
|
+
partHeights,
|
|
3182
|
+
};
|
|
3183
|
+
}
|
|
3184
|
+
case 'header':
|
|
3185
|
+
if (lastSubColumn.span.isEnd)
|
|
3186
|
+
break;
|
|
3187
|
+
return {
|
|
3188
|
+
area: lastSubColumnElement.area,
|
|
3189
|
+
type: lastSubColumnElement.type,
|
|
3190
|
+
elementHeight: lastSubColumnElement.dimensions.height,
|
|
3191
|
+
partHeights,
|
|
3192
|
+
component: lastSubColumnElement.component,
|
|
3193
|
+
round: lastSubColumnElement.round,
|
|
3194
|
+
roundSwissGroup: lastSubColumnElement.roundSwissGroup,
|
|
3195
|
+
};
|
|
3196
|
+
case 'match':
|
|
3197
|
+
if (lastSubColumn.span.isEnd)
|
|
3198
|
+
break;
|
|
3199
|
+
return {
|
|
3200
|
+
area: lastSubColumnElement.area,
|
|
3201
|
+
type: lastSubColumnElement.type,
|
|
3202
|
+
elementHeight: lastSubColumnElement.dimensions.height,
|
|
3203
|
+
partHeights,
|
|
3204
|
+
component: lastSubColumnElement.component,
|
|
3205
|
+
round: lastSubColumnElement.round,
|
|
3206
|
+
match: lastSubColumnElement.match,
|
|
3207
|
+
roundSwissGroup: lastSubColumnElement.roundSwissGroup,
|
|
3208
|
+
};
|
|
3209
|
+
}
|
|
3210
|
+
return {
|
|
3211
|
+
area: `.`,
|
|
3212
|
+
type: 'colGap',
|
|
3213
|
+
elementHeight: lastSubColumnElement.dimensions.height,
|
|
3214
|
+
partHeights,
|
|
3215
|
+
};
|
|
3216
|
+
})();
|
|
3217
|
+
pushElement(createBracketElement(elementToCreate).element);
|
|
3218
|
+
}
|
|
3219
|
+
pushSubColumn(subColumn);
|
|
3220
|
+
pushSection(masterColumnSection);
|
|
3161
3221
|
}
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3222
|
+
return masterColumn;
|
|
3223
|
+
};
|
|
3224
|
+
|
|
3225
|
+
const createRoundBracketSubColumnRelativeToFirstRound = (config) => {
|
|
3226
|
+
const { firstRound, round, options, span, hasReverseFinal } = config;
|
|
3227
|
+
const { subColumn, pushElement } = createBracketSubColumn({
|
|
3228
|
+
span,
|
|
3229
|
+
});
|
|
3230
|
+
const matchFactor = firstRound.matchCount / round.matchCount;
|
|
3231
|
+
const matches = Array.from(round.matches.values());
|
|
3232
|
+
const elementsToCreate = [];
|
|
3233
|
+
// Only include a header row if headers exist
|
|
3234
|
+
if (options.roundHeaderHeight > 0) {
|
|
3235
|
+
elementsToCreate.push({
|
|
3236
|
+
type: 'header',
|
|
3237
|
+
area: `h${round.shortId}`,
|
|
3238
|
+
partHeights: [options.roundHeaderHeight],
|
|
3239
|
+
elementHeight: options.roundHeaderHeight,
|
|
3240
|
+
component: config.components.roundHeader,
|
|
3241
|
+
round,
|
|
3242
|
+
roundSwissGroup: null,
|
|
3243
|
+
}, {
|
|
3244
|
+
type: 'roundHeaderGap',
|
|
3245
|
+
area: '.',
|
|
3246
|
+
partHeights: [options.roundHeaderGap],
|
|
3247
|
+
elementHeight: options.roundHeaderGap,
|
|
3248
|
+
});
|
|
3170
3249
|
}
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
const
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
relations.push(createOneToOneRelation({
|
|
3184
|
-
currentRound: preFinalLowerRound,
|
|
3185
|
-
previousRound: prePreFinalLowerRound,
|
|
3186
|
-
nextRound: finalLowerRound,
|
|
3187
|
-
rootRound: firstLowerRound,
|
|
3188
|
-
}));
|
|
3250
|
+
// Add match elements to create
|
|
3251
|
+
for (const [matchIndex, match] of matches.entries()) {
|
|
3252
|
+
const isLastMatch = matchIndex === matches.length - 1;
|
|
3253
|
+
const matchRows = [];
|
|
3254
|
+
for (let factorIndex = 0; factorIndex < matchFactor; factorIndex++) {
|
|
3255
|
+
const isLastFactor = factorIndex === matchFactor - 1;
|
|
3256
|
+
// Add the match height
|
|
3257
|
+
matchRows.push(options.matchHeight);
|
|
3258
|
+
if (isLastFactor)
|
|
3259
|
+
continue;
|
|
3260
|
+
// Add gap between match factors (except after the last one)
|
|
3261
|
+
matchRows.push(options.rowGap);
|
|
3189
3262
|
}
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3263
|
+
const isFinalMatch = hasReverseFinal
|
|
3264
|
+
? round.type === DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.REVERSE_FINAL
|
|
3265
|
+
: round.type === COMMON_BRACKET_ROUND_TYPE.FINAL;
|
|
3266
|
+
elementsToCreate.push({
|
|
3267
|
+
type: 'match',
|
|
3268
|
+
area: `m${match.shortId}`,
|
|
3269
|
+
partHeights: matchRows,
|
|
3270
|
+
elementHeight: isFinalMatch ? options.finalMatchHeight : options.matchHeight,
|
|
3271
|
+
component: isFinalMatch ? config.components.finalMatch : config.components.match,
|
|
3272
|
+
match,
|
|
3273
|
+
round,
|
|
3274
|
+
roundSwissGroup: null,
|
|
3275
|
+
});
|
|
3276
|
+
if (!isLastMatch) {
|
|
3277
|
+
elementsToCreate.push({
|
|
3278
|
+
type: 'matchGap',
|
|
3279
|
+
area: '.',
|
|
3280
|
+
partHeights: [options.rowGap],
|
|
3281
|
+
elementHeight: options.rowGap,
|
|
3282
|
+
});
|
|
3195
3283
|
}
|
|
3196
3284
|
}
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
currentRound: finalLowerRound,
|
|
3202
|
-
previousRound: previousLowerRound,
|
|
3203
|
-
nextRound: currentUpperRound,
|
|
3204
|
-
rootRound: firstLowerRound,
|
|
3205
|
-
}));
|
|
3206
|
-
}
|
|
3207
|
-
};
|
|
3208
|
-
const handleFirstRound = (params) => {
|
|
3209
|
-
const { relations, currentUpperRound, nextUpperRound, lowerRounds, currentUpperRoundIndex } = params;
|
|
3210
|
-
relations.push(createNothingToOneRelation({
|
|
3211
|
-
currentRound: currentUpperRound,
|
|
3212
|
-
nextRound: nextUpperRound,
|
|
3213
|
-
}));
|
|
3214
|
-
const currentLowerRound = lowerRounds[currentUpperRoundIndex] || null;
|
|
3215
|
-
const nextLowerRound = lowerRounds[currentUpperRoundIndex + 1] || null;
|
|
3216
|
-
if (currentLowerRound && nextLowerRound) {
|
|
3217
|
-
relations.push(createNothingToOneRelation({
|
|
3218
|
-
currentRound: currentLowerRound,
|
|
3219
|
-
nextRound: nextLowerRound,
|
|
3220
|
-
}));
|
|
3285
|
+
// Create all elements at once
|
|
3286
|
+
for (const elementData of elementsToCreate) {
|
|
3287
|
+
const { element } = createBracketElement(elementData);
|
|
3288
|
+
pushElement(element);
|
|
3221
3289
|
}
|
|
3290
|
+
return subColumn;
|
|
3222
3291
|
};
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
const
|
|
3232
|
-
const
|
|
3233
|
-
const
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
nextLowerRound &&
|
|
3238
|
-
firstLowerRound) {
|
|
3239
|
-
relations.push(createOneToOneRelation({
|
|
3240
|
-
currentRound: currentLowerRound,
|
|
3241
|
-
previousRound: previousLowerRound,
|
|
3242
|
-
nextRound: nextLowerRound,
|
|
3243
|
-
rootRound: firstLowerRound,
|
|
3244
|
-
}));
|
|
3292
|
+
|
|
3293
|
+
const createDoubleEliminationGrid = (bracketData, options, components) => {
|
|
3294
|
+
const grid = createBracketGrid({ spanElementWidth: options.columnWidth });
|
|
3295
|
+
const upperBracketRounds = Array.from(bracketData.roundsByType.getOrThrow(DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.UPPER_BRACKET).values());
|
|
3296
|
+
const lowerBracketRounds = Array.from(bracketData.roundsByType.getOrThrow(DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.LOWER_BRACKET).values());
|
|
3297
|
+
const remainingRounds = Array.from(bracketData.rounds.values()).filter((r) => r.type !== DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.UPPER_BRACKET &&
|
|
3298
|
+
r.type !== DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.LOWER_BRACKET &&
|
|
3299
|
+
r.type !== COMMON_BRACKET_ROUND_TYPE.THIRD_PLACE);
|
|
3300
|
+
const thirdPlaceRound = bracketData.roundsByType.get(COMMON_BRACKET_ROUND_TYPE.THIRD_PLACE)?.first() ?? null;
|
|
3301
|
+
const hasReverseFinal = !!bracketData.roundsByType.get(DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.REVERSE_FINAL)?.first();
|
|
3302
|
+
const firstUpperRound = upperBracketRounds[0];
|
|
3303
|
+
const firstLowerRound = lowerBracketRounds[0];
|
|
3304
|
+
if (!firstUpperRound || !firstLowerRound) {
|
|
3305
|
+
throw new Error('No upper or lower rounds found in bracket data');
|
|
3245
3306
|
}
|
|
3246
|
-
|
|
3247
|
-
const
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
const
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
upperRounds,
|
|
3261
|
-
currentUpperRoundIndex,
|
|
3307
|
+
const upperToLowerRatio = calculateUpperLowerRatio(upperBracketRounds.length, lowerBracketRounds.length);
|
|
3308
|
+
const columnSplitFactor = calculateColumnSplitFactor(upperToLowerRatio);
|
|
3309
|
+
let lastRoundLastSubColumnUpperIndex = -1;
|
|
3310
|
+
let lastRoundLastSubColumnLowerIndex = -1;
|
|
3311
|
+
for (const [lowerRoundIndex, lowerRound] of lowerBracketRounds.entries()) {
|
|
3312
|
+
const isLastLowerRound = lowerRoundIndex === lowerBracketRounds.length - 1;
|
|
3313
|
+
const { masterColumn, pushSection } = createBracketMasterColumn({
|
|
3314
|
+
columnWidth: options.columnWidth,
|
|
3315
|
+
padding: {
|
|
3316
|
+
bottom: 0,
|
|
3317
|
+
left: 0,
|
|
3318
|
+
right: 0,
|
|
3319
|
+
top: 0,
|
|
3320
|
+
},
|
|
3262
3321
|
});
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3322
|
+
const { masterColumnSection: upperSection, pushSubColumn: pushUpperSubColumn } = createBracketMasterColumnSection({
|
|
3323
|
+
type: 'round',
|
|
3324
|
+
});
|
|
3325
|
+
const { masterColumnSection: upperLowerGapSection, pushSubColumn: pushUpperLowerSubColumn } = createBracketMasterColumnSection({
|
|
3326
|
+
type: 'gap',
|
|
3327
|
+
});
|
|
3328
|
+
const { masterColumnSection: lowerSection, pushSubColumn: pushLowerSubColumn } = createBracketMasterColumnSection({
|
|
3329
|
+
type: 'round',
|
|
3330
|
+
});
|
|
3331
|
+
for (let currentColumnSplitFactor = 1; currentColumnSplitFactor <= columnSplitFactor; currentColumnSplitFactor++) {
|
|
3332
|
+
const subColumnIndex = lowerRoundIndex * columnSplitFactor + (currentColumnSplitFactor - 1);
|
|
3333
|
+
const currentUpperRoundIndex = calculateUpperRoundIndex(subColumnIndex, upperToLowerRatio, columnSplitFactor);
|
|
3334
|
+
const currentLowerRoundIndex = lowerRoundIndex;
|
|
3335
|
+
const isFirstSubColumnInMasterColumn = currentColumnSplitFactor === 1;
|
|
3336
|
+
const isLastSubColumnInMasterColumn = currentColumnSplitFactor === columnSplitFactor;
|
|
3337
|
+
const previousSubColumnIndex = subColumnIndex - 1;
|
|
3338
|
+
const nextSubColumnIndex = subColumnIndex + 1;
|
|
3339
|
+
const previousUpperRoundIndex = previousSubColumnIndex >= 0
|
|
3340
|
+
? calculateUpperRoundIndex(previousSubColumnIndex, upperToLowerRatio, columnSplitFactor)
|
|
3341
|
+
: -1;
|
|
3342
|
+
const nextUpperRoundIndex = calculateUpperRoundIndex(nextSubColumnIndex, upperToLowerRatio, columnSplitFactor);
|
|
3343
|
+
const previousLowerRoundIndex = previousSubColumnIndex >= 0 ? calculateLowerRoundIndex(previousSubColumnIndex, columnSplitFactor) : -1;
|
|
3344
|
+
const nextLowerRoundIndex = calculateLowerRoundIndex(nextSubColumnIndex, columnSplitFactor);
|
|
3345
|
+
const upperRound = upperBracketRounds[currentUpperRoundIndex];
|
|
3346
|
+
if (!upperRound) {
|
|
3347
|
+
throw new Error('Upper round not found for subColumnIndex: ' + subColumnIndex);
|
|
3348
|
+
}
|
|
3349
|
+
// For upper bracket spans - check if this round is different from the previous occurrence
|
|
3350
|
+
const isUpperSpanStart = isFirstSubColumnInMasterColumn
|
|
3351
|
+
? lowerRoundIndex === 0 || lastRoundLastSubColumnUpperIndex !== currentUpperRoundIndex
|
|
3352
|
+
: previousUpperRoundIndex !== currentUpperRoundIndex;
|
|
3353
|
+
// For upper bracket spans - check if this round will be different in the next occurrence
|
|
3354
|
+
const isUpperSpanEnd = isLastSubColumnInMasterColumn
|
|
3355
|
+
? isLastLowerRound ||
|
|
3356
|
+
calculateUpperRoundIndex((lowerRoundIndex + 1) * columnSplitFactor, upperToLowerRatio, columnSplitFactor) !==
|
|
3357
|
+
currentUpperRoundIndex
|
|
3358
|
+
: nextUpperRoundIndex !== currentUpperRoundIndex;
|
|
3359
|
+
// For lower bracket spans - similar logic
|
|
3360
|
+
const isLowerSpanStart = isFirstSubColumnInMasterColumn
|
|
3361
|
+
? lowerRoundIndex === 0 || lastRoundLastSubColumnLowerIndex !== currentLowerRoundIndex
|
|
3362
|
+
: previousLowerRoundIndex !== currentLowerRoundIndex;
|
|
3363
|
+
const isLowerSpanEnd = isLastSubColumnInMasterColumn
|
|
3364
|
+
? isLastLowerRound ||
|
|
3365
|
+
calculateLowerRoundIndex((lowerRoundIndex + 1) * columnSplitFactor, columnSplitFactor) !==
|
|
3366
|
+
currentLowerRoundIndex
|
|
3367
|
+
: nextLowerRoundIndex !== currentLowerRoundIndex;
|
|
3368
|
+
const upperSubColumn = createRoundBracketSubColumnRelativeToFirstRound({
|
|
3369
|
+
firstRound: firstUpperRound,
|
|
3370
|
+
round: upperRound,
|
|
3371
|
+
options,
|
|
3372
|
+
hasReverseFinal,
|
|
3373
|
+
span: {
|
|
3374
|
+
isStart: isUpperSpanStart,
|
|
3375
|
+
isEnd: isUpperSpanEnd,
|
|
3376
|
+
},
|
|
3377
|
+
components,
|
|
3274
3378
|
});
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
}));
|
|
3282
|
-
}
|
|
3283
|
-
else if (nav.currentUpperRound.isFirstRound && nav.nextUpperRound) {
|
|
3284
|
-
handleFirstRound({
|
|
3285
|
-
relations,
|
|
3286
|
-
currentUpperRound: nav.currentUpperRound,
|
|
3287
|
-
nextUpperRound: nav.nextUpperRound,
|
|
3288
|
-
lowerRounds,
|
|
3289
|
-
currentUpperRoundIndex,
|
|
3379
|
+
pushUpperSubColumn(upperSubColumn);
|
|
3380
|
+
const upperLowerGapSubColumn = createBracketSubColumn({
|
|
3381
|
+
span: {
|
|
3382
|
+
isStart: true,
|
|
3383
|
+
isEnd: true,
|
|
3384
|
+
},
|
|
3290
3385
|
});
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3386
|
+
const upperLowerGapElement = createBracketElement({
|
|
3387
|
+
area: '.',
|
|
3388
|
+
type: 'roundGap',
|
|
3389
|
+
elementHeight: options.rowRoundGap,
|
|
3390
|
+
partHeights: [options.rowRoundGap],
|
|
3391
|
+
});
|
|
3392
|
+
upperLowerGapSubColumn.pushElement(upperLowerGapElement.element);
|
|
3393
|
+
pushUpperLowerSubColumn(upperLowerGapSubColumn.subColumn);
|
|
3394
|
+
const lowerSubColumn = createRoundBracketSubColumnRelativeToFirstRound({
|
|
3395
|
+
firstRound: firstLowerRound,
|
|
3396
|
+
round: lowerRound,
|
|
3397
|
+
options,
|
|
3398
|
+
hasReverseFinal,
|
|
3399
|
+
span: {
|
|
3400
|
+
isStart: isLowerSpanStart,
|
|
3401
|
+
isEnd: isLowerSpanEnd,
|
|
3402
|
+
},
|
|
3403
|
+
components,
|
|
3302
3404
|
});
|
|
3405
|
+
pushLowerSubColumn(lowerSubColumn);
|
|
3406
|
+
if (isLastSubColumnInMasterColumn) {
|
|
3407
|
+
lastRoundLastSubColumnUpperIndex = currentUpperRoundIndex;
|
|
3408
|
+
lastRoundLastSubColumnLowerIndex = currentLowerRoundIndex;
|
|
3409
|
+
}
|
|
3303
3410
|
}
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
const roundsByType = new BracketMap();
|
|
3312
|
-
for (const roundBase of bracketNewBase.rounds.values()) {
|
|
3313
|
-
const newRound = {
|
|
3314
|
-
...roundBase,
|
|
3315
|
-
matches: new BracketMap(),
|
|
3316
|
-
relation: { type: 'dummy' },
|
|
3317
|
-
};
|
|
3318
|
-
rounds.set(roundBase.id, newRound);
|
|
3319
|
-
if (!roundsByType.has(roundBase.type)) {
|
|
3320
|
-
roundsByType.set(roundBase.type, new BracketMap());
|
|
3411
|
+
pushSection(upperSection, upperLowerGapSection, lowerSection);
|
|
3412
|
+
grid.pushMasterColumn(masterColumn);
|
|
3413
|
+
if (remainingRounds.length) {
|
|
3414
|
+
grid.pushMasterColumn(createBracketGapMasterColumn({
|
|
3415
|
+
existingMasterColumns: grid.grid.masterColumns,
|
|
3416
|
+
columnGap: options.columnGap,
|
|
3417
|
+
}));
|
|
3321
3418
|
}
|
|
3322
|
-
roundsByType.getOrThrow(roundBase.type).set(roundBase.id, newRound);
|
|
3323
3419
|
}
|
|
3324
|
-
const
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3420
|
+
for (const [roundIndex, round] of remainingRounds.entries()) {
|
|
3421
|
+
const isLastRound = roundIndex === remainingRounds.length - 1;
|
|
3422
|
+
const isFirstRound = roundIndex === 0;
|
|
3423
|
+
const isAnyFinal = hasReverseFinal
|
|
3424
|
+
? round.type === DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.REVERSE_FINAL
|
|
3425
|
+
: round.type === COMMON_BRACKET_ROUND_TYPE.FINAL;
|
|
3426
|
+
const { masterColumn, pushSection } = createBracketMasterColumn({
|
|
3427
|
+
columnWidth: isAnyFinal ? options.finalColumnWidth : options.columnWidth,
|
|
3428
|
+
padding: {
|
|
3429
|
+
bottom: 0,
|
|
3430
|
+
left: 0,
|
|
3431
|
+
right: 0,
|
|
3432
|
+
top: 0,
|
|
3433
|
+
},
|
|
3329
3434
|
});
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
const
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
...matchBase,
|
|
3342
|
-
home: homeParticipant,
|
|
3343
|
-
away: awayParticipant,
|
|
3344
|
-
winner: null,
|
|
3435
|
+
const { masterColumnSection: upperSection, pushSubColumn: pushUpperSubColumn } = createBracketMasterColumnSection({
|
|
3436
|
+
type: 'round',
|
|
3437
|
+
});
|
|
3438
|
+
const { masterColumnSection: upperLowerGapSection, pushSubColumn: pushUpperLowerSubColumn } = createBracketMasterColumnSection({
|
|
3439
|
+
type: 'gap',
|
|
3440
|
+
});
|
|
3441
|
+
const { masterColumnSection: lowerSection, pushSubColumn: pushLowerSubColumn } = createBracketMasterColumnSection({
|
|
3442
|
+
type: 'round',
|
|
3443
|
+
});
|
|
3444
|
+
const upperSubColumn = createRoundBracketSubColumnRelativeToFirstRound({
|
|
3445
|
+
firstRound: firstUpperRound,
|
|
3345
3446
|
round,
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
}
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3447
|
+
options,
|
|
3448
|
+
hasReverseFinal,
|
|
3449
|
+
span: {
|
|
3450
|
+
isStart: true,
|
|
3451
|
+
isEnd: true,
|
|
3452
|
+
},
|
|
3453
|
+
components,
|
|
3454
|
+
});
|
|
3455
|
+
pushUpperSubColumn(upperSubColumn);
|
|
3456
|
+
const upperLowerGapSubColumn = createBracketSubColumn({
|
|
3457
|
+
span: {
|
|
3458
|
+
isStart: true,
|
|
3459
|
+
isEnd: true,
|
|
3460
|
+
},
|
|
3461
|
+
});
|
|
3462
|
+
const upperLowerGapElement = createBracketElement({
|
|
3463
|
+
area: '.',
|
|
3464
|
+
type: 'roundGap',
|
|
3465
|
+
elementHeight: options.rowRoundGap,
|
|
3466
|
+
partHeights: [options.rowRoundGap],
|
|
3467
|
+
});
|
|
3468
|
+
upperLowerGapSubColumn.pushElement(upperLowerGapElement.element);
|
|
3469
|
+
pushUpperLowerSubColumn(upperLowerGapSubColumn.subColumn);
|
|
3470
|
+
if (thirdPlaceRound) {
|
|
3471
|
+
const lowerSubColumn = createRoundBracketSubColumnRelativeToFirstRound({
|
|
3472
|
+
firstRound: firstLowerRound,
|
|
3473
|
+
round: thirdPlaceRound,
|
|
3474
|
+
options,
|
|
3475
|
+
hasReverseFinal,
|
|
3476
|
+
span: {
|
|
3477
|
+
isStart: isFirstRound,
|
|
3478
|
+
isEnd: isLastRound,
|
|
3479
|
+
},
|
|
3480
|
+
components,
|
|
3362
3481
|
});
|
|
3482
|
+
pushLowerSubColumn(lowerSubColumn);
|
|
3363
3483
|
}
|
|
3364
|
-
|
|
3365
|
-
const
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3484
|
+
else {
|
|
3485
|
+
const lowerSubColumn = createBracketSubColumn({
|
|
3486
|
+
span: {
|
|
3487
|
+
isStart: true,
|
|
3488
|
+
isEnd: true,
|
|
3489
|
+
},
|
|
3370
3490
|
});
|
|
3491
|
+
const firstMasterRound = grid.grid.masterColumns[0];
|
|
3492
|
+
if (!firstMasterRound)
|
|
3493
|
+
throw new Error('No first master round found in grid');
|
|
3494
|
+
const lastMasterColumnSection = firstMasterRound.sections[firstMasterRound.sections.length - 1];
|
|
3495
|
+
if (!lastMasterColumnSection)
|
|
3496
|
+
throw new Error('No last master column section found in grid');
|
|
3497
|
+
const firstSubColumn = lastMasterColumnSection.subColumns[0];
|
|
3498
|
+
if (!firstSubColumn)
|
|
3499
|
+
throw new Error('No first sub column found in grid');
|
|
3500
|
+
for (const element of firstSubColumn.elements) {
|
|
3501
|
+
const el = createBracketElement({
|
|
3502
|
+
area: '.',
|
|
3503
|
+
type: 'colGap',
|
|
3504
|
+
elementHeight: element.dimensions.height,
|
|
3505
|
+
partHeights: element.parts.map((p) => p.dimensions.height),
|
|
3506
|
+
});
|
|
3507
|
+
lowerSubColumn.pushElement(el.element);
|
|
3508
|
+
}
|
|
3509
|
+
pushLowerSubColumn(lowerSubColumn.subColumn);
|
|
3371
3510
|
}
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
if (match.winner?.id === participant.id)
|
|
3380
|
-
match.winner.matches = participant.matches;
|
|
3381
|
-
}
|
|
3382
|
-
}
|
|
3383
|
-
const newBracket = {
|
|
3384
|
-
matches,
|
|
3385
|
-
participants,
|
|
3386
|
-
rounds,
|
|
3387
|
-
roundsByType,
|
|
3388
|
-
mode: bracketNewBase.mode,
|
|
3389
|
-
};
|
|
3390
|
-
const roundRelations = generateRoundRelationsNew(newBracket);
|
|
3391
|
-
for (const roundRelation of roundRelations) {
|
|
3392
|
-
roundRelation.currentRound.relation = roundRelation;
|
|
3393
|
-
}
|
|
3394
|
-
const matchRelations = generateMatchRelationsNew(newBracket);
|
|
3395
|
-
for (const matchRelation of matchRelations) {
|
|
3396
|
-
matchRelation.currentMatch.relation = matchRelation;
|
|
3397
|
-
}
|
|
3398
|
-
return newBracket;
|
|
3399
|
-
};
|
|
3400
|
-
|
|
3401
|
-
const pad = (str, len) => str.padEnd(len, ' ');
|
|
3402
|
-
const color = (str, code) => `\x1b[${code}m${str}\x1b[0m`;
|
|
3403
|
-
const roundColor = (str) => color(str, 36); // cyan
|
|
3404
|
-
const arrowColor = (str) => color(str, 90); // gray
|
|
3405
|
-
const labelColor = (str) => color(str, 33); // yellow
|
|
3406
|
-
const factorColor = (str) => color(str, 32); // green
|
|
3407
|
-
const logRoundRelations = (bracketData) => {
|
|
3408
|
-
// Find max round name length for alignment
|
|
3409
|
-
let maxNameLen = 0;
|
|
3410
|
-
for (const round of bracketData.rounds.values()) {
|
|
3411
|
-
maxNameLen = Math.max(maxNameLen, round.name.length);
|
|
3412
|
-
}
|
|
3413
|
-
const colWidth = maxNameLen + 2;
|
|
3414
|
-
// Build rows
|
|
3415
|
-
const rows = [];
|
|
3416
|
-
for (const round of bracketData.rounds.values()) {
|
|
3417
|
-
const relation = round.relation;
|
|
3418
|
-
switch (relation.type) {
|
|
3419
|
-
case 'nothing-to-one':
|
|
3420
|
-
rows.push([
|
|
3421
|
-
labelColor(pad('START', colWidth)),
|
|
3422
|
-
arrowColor('──▶'),
|
|
3423
|
-
roundColor(pad(round.name, colWidth)),
|
|
3424
|
-
arrowColor('──▶'),
|
|
3425
|
-
roundColor(pad(relation.nextRound.name, colWidth)),
|
|
3426
|
-
factorColor(`[${relation.nextRoundMatchFactor}]`),
|
|
3427
|
-
]);
|
|
3428
|
-
break;
|
|
3429
|
-
case 'one-to-nothing':
|
|
3430
|
-
rows.push([
|
|
3431
|
-
roundColor(pad(relation.previousRound.name, colWidth)),
|
|
3432
|
-
arrowColor('──▶'),
|
|
3433
|
-
roundColor(pad(round.name, colWidth)),
|
|
3434
|
-
arrowColor('──▶'),
|
|
3435
|
-
labelColor(pad('END', colWidth)),
|
|
3436
|
-
factorColor(`[${relation.previousRoundMatchFactor}]`),
|
|
3437
|
-
]);
|
|
3438
|
-
break;
|
|
3439
|
-
case 'one-to-one':
|
|
3440
|
-
rows.push([
|
|
3441
|
-
roundColor(pad(relation.previousRound.name, colWidth)),
|
|
3442
|
-
arrowColor('──▶'),
|
|
3443
|
-
roundColor(pad(round.name, colWidth)),
|
|
3444
|
-
arrowColor('──▶'),
|
|
3445
|
-
roundColor(pad(relation.nextRound.name, colWidth)),
|
|
3446
|
-
factorColor(`[Prev: ${relation.previousRoundMatchFactor}, Next: ${relation.nextRoundMatchFactor}]`),
|
|
3447
|
-
]);
|
|
3448
|
-
break;
|
|
3449
|
-
case 'two-to-one':
|
|
3450
|
-
rows.push([
|
|
3451
|
-
roundColor(pad(relation.previousUpperRound.name, colWidth)),
|
|
3452
|
-
arrowColor(' │ '),
|
|
3453
|
-
roundColor(pad(relation.previousLowerRound.name, colWidth)),
|
|
3454
|
-
arrowColor('──▶'),
|
|
3455
|
-
roundColor(pad(round.name, colWidth)),
|
|
3456
|
-
arrowColor('──▶'),
|
|
3457
|
-
roundColor(pad(relation.nextRound.name, colWidth)),
|
|
3458
|
-
factorColor(`[Upper: ${relation.previousUpperRoundMatchFactor}, Lower: ${relation.previousLowerRoundMatchFactor}, Next: ${relation.nextRoundMatchFactor}]`),
|
|
3459
|
-
]);
|
|
3460
|
-
break;
|
|
3461
|
-
case 'two-to-nothing':
|
|
3462
|
-
rows.push([
|
|
3463
|
-
roundColor(pad(relation.previousUpperRound.name, colWidth)),
|
|
3464
|
-
arrowColor(' │'),
|
|
3465
|
-
roundColor(pad(relation.previousLowerRound.name, colWidth)),
|
|
3466
|
-
arrowColor(' ▼'),
|
|
3467
|
-
roundColor(pad(round.name, colWidth)),
|
|
3468
|
-
arrowColor('──▶'),
|
|
3469
|
-
labelColor(pad('END', colWidth)),
|
|
3470
|
-
factorColor(`[Upper: ${relation.previousUpperRoundMatchFactor}, Lower: ${relation.previousLowerRoundMatchFactor}]`),
|
|
3471
|
-
]);
|
|
3472
|
-
break;
|
|
3511
|
+
pushSection(upperSection, upperLowerGapSection, lowerSection);
|
|
3512
|
+
grid.pushMasterColumn(masterColumn);
|
|
3513
|
+
if (!isLastRound) {
|
|
3514
|
+
grid.pushMasterColumn(createBracketGapMasterColumn({
|
|
3515
|
+
existingMasterColumns: grid.grid.masterColumns,
|
|
3516
|
+
columnGap: options.columnGap,
|
|
3517
|
+
}));
|
|
3473
3518
|
}
|
|
3474
3519
|
}
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3520
|
+
grid.setupElementSpans();
|
|
3521
|
+
grid.calculateDimensions();
|
|
3522
|
+
const finalizedGrid = finalizeBracketGrid(grid);
|
|
3523
|
+
return {
|
|
3524
|
+
raw: grid,
|
|
3525
|
+
columns: finalizedGrid.columns,
|
|
3526
|
+
matchElementMap: finalizedGrid.elementMap,
|
|
3480
3527
|
};
|
|
3481
|
-
console.log(divider('Bracket Structure'));
|
|
3482
|
-
// Print rows
|
|
3483
|
-
for (const row of rows) {
|
|
3484
|
-
console.log(row.join(' '));
|
|
3485
|
-
}
|
|
3486
|
-
console.log();
|
|
3487
3528
|
};
|
|
3488
3529
|
|
|
3489
|
-
const
|
|
3490
|
-
const
|
|
3491
|
-
const
|
|
3492
|
-
const
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
if (n <= 1)
|
|
3496
|
-
return 1;
|
|
3497
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
3498
|
-
if (factorialCache.has(n))
|
|
3499
|
-
return factorialCache.get(n);
|
|
3500
|
-
const result = n * getFactorial(n - 1);
|
|
3501
|
-
factorialCache.set(n, result);
|
|
3502
|
-
return result;
|
|
3503
|
-
};
|
|
3504
|
-
// Pre-calculate roundFactorial
|
|
3505
|
-
const roundFactorial = getFactorial(roundNumber);
|
|
3506
|
-
let totalCombinations = 0;
|
|
3507
|
-
const validGroups = [];
|
|
3508
|
-
// Single loop to gather valid groups and total combinations
|
|
3509
|
-
for (let wins = roundNumber; wins >= 0; wins--) {
|
|
3510
|
-
const losses = roundNumber - wins;
|
|
3511
|
-
const remainingGames = ADVANCE_WINS + ELIMINATE_LOSSES - (wins + losses) - 1;
|
|
3512
|
-
const notYetEliminated = losses < ELIMINATE_LOSSES;
|
|
3513
|
-
const canStillAdvance = wins < ADVANCE_WINS && remainingGames >= 0;
|
|
3514
|
-
if (!canStillAdvance || !notYetEliminated)
|
|
3515
|
-
continue;
|
|
3516
|
-
const combinations = roundFactorial / (getFactorial(wins) * getFactorial(losses));
|
|
3517
|
-
totalCombinations += combinations;
|
|
3518
|
-
validGroups.push({ wins, losses, combinations });
|
|
3519
|
-
}
|
|
3520
|
-
// Create final groups with calculated proportions
|
|
3521
|
-
return validGroups.map(({ wins, losses, combinations }) => ({
|
|
3522
|
-
id: `${wins}-${losses}`,
|
|
3523
|
-
name: `${wins}-${losses}`,
|
|
3524
|
-
matchesInGroup: Math.round((combinations / totalCombinations) * totalMatchesInRound),
|
|
3525
|
-
}));
|
|
3526
|
-
};
|
|
3527
|
-
const generateBracketRoundSwissGroupMaps = (bracketData) => {
|
|
3528
|
-
if (bracketData.mode !== TOURNAMENT_MODE.SWISS_WITH_ELIMINATION) {
|
|
3529
|
-
return null;
|
|
3530
|
+
const createSingleEliminationGrid = (bracketData, options, components) => {
|
|
3531
|
+
const grid = createBracketGrid({ spanElementWidth: options.columnWidth });
|
|
3532
|
+
const rounds = Array.from(bracketData.rounds.values());
|
|
3533
|
+
const firstRound = bracketData.rounds.first();
|
|
3534
|
+
if (!firstRound) {
|
|
3535
|
+
throw new Error('No rounds found in bracket data');
|
|
3530
3536
|
}
|
|
3531
|
-
const
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
}
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
for (const group of roundSwissData.groups.values()) {
|
|
3565
|
-
if (group.matches.size < group.allowedMatchCount) {
|
|
3566
|
-
group.matches.set(match.id, match);
|
|
3567
|
-
groupFound = true;
|
|
3568
|
-
break;
|
|
3569
|
-
}
|
|
3570
|
-
}
|
|
3571
|
-
if (!groupFound) {
|
|
3572
|
-
throw new Error('No group found for empty match');
|
|
3573
|
-
}
|
|
3537
|
+
for (const [roundIndex, round] of rounds.entries()) {
|
|
3538
|
+
const isLastRound = roundIndex === rounds.length - 1;
|
|
3539
|
+
const { masterColumn, ...mutableMasterColumn } = createBracketMasterColumn({
|
|
3540
|
+
columnWidth: round.type === COMMON_BRACKET_ROUND_TYPE.FINAL ? options.finalColumnWidth : options.columnWidth,
|
|
3541
|
+
padding: {
|
|
3542
|
+
bottom: 0,
|
|
3543
|
+
left: 0,
|
|
3544
|
+
right: 0,
|
|
3545
|
+
top: 0,
|
|
3546
|
+
},
|
|
3547
|
+
});
|
|
3548
|
+
const { masterColumnSection, pushSubColumn } = createBracketMasterColumnSection({
|
|
3549
|
+
type: 'round',
|
|
3550
|
+
});
|
|
3551
|
+
const sub = createRoundBracketSubColumnRelativeToFirstRound({
|
|
3552
|
+
firstRound,
|
|
3553
|
+
round,
|
|
3554
|
+
options,
|
|
3555
|
+
hasReverseFinal: false,
|
|
3556
|
+
span: {
|
|
3557
|
+
isStart: true,
|
|
3558
|
+
isEnd: true,
|
|
3559
|
+
},
|
|
3560
|
+
components,
|
|
3561
|
+
});
|
|
3562
|
+
pushSubColumn(sub);
|
|
3563
|
+
mutableMasterColumn.pushSection(masterColumnSection);
|
|
3564
|
+
grid.pushMasterColumn(masterColumn);
|
|
3565
|
+
if (!isLastRound) {
|
|
3566
|
+
grid.pushMasterColumn(createBracketGapMasterColumn({
|
|
3567
|
+
existingMasterColumns: grid.grid.masterColumns,
|
|
3568
|
+
columnGap: options.columnGap,
|
|
3569
|
+
}));
|
|
3574
3570
|
}
|
|
3575
|
-
roundsWithSwissGroups.set(bracketRound.id, roundSwissData);
|
|
3576
|
-
roundNumber++;
|
|
3577
3571
|
}
|
|
3578
|
-
|
|
3572
|
+
grid.calculateDimensions();
|
|
3573
|
+
const finalizedGrid = finalizeBracketGrid(grid);
|
|
3574
|
+
return {
|
|
3575
|
+
raw: grid,
|
|
3576
|
+
columns: finalizedGrid.columns,
|
|
3577
|
+
matchElementMap: finalizedGrid.elementMap,
|
|
3578
|
+
};
|
|
3579
3579
|
};
|
|
3580
3580
|
|
|
3581
3581
|
// TODO
|
|
@@ -3857,16 +3857,24 @@ var index = /*#__PURE__*/Object.freeze({
|
|
|
3857
3857
|
TOURNAMENT_MODE: TOURNAMENT_MODE,
|
|
3858
3858
|
canRenderLayoutInTournamentMode: canRenderLayoutInTournamentMode,
|
|
3859
3859
|
createMatchesMapBase: createMatchesMapBase,
|
|
3860
|
+
createNewBracket: createNewBracket,
|
|
3860
3861
|
createNewBracketBase: createNewBracketBase,
|
|
3861
3862
|
createNewMatchParticipantBase: createNewMatchParticipantBase,
|
|
3862
3863
|
createParticipantsMapBase: createParticipantsMapBase,
|
|
3863
3864
|
createRoundsMapBase: createRoundsMapBase,
|
|
3864
3865
|
generateBracketDataForEthlete: generateBracketDataForEthlete,
|
|
3865
3866
|
generateBracketDataForGg: generateBracketDataForGg,
|
|
3867
|
+
generateBracketRoundSwissGroupMaps: generateBracketRoundSwissGroupMaps,
|
|
3868
|
+
generateMatchPosition: generateMatchPosition,
|
|
3869
|
+
generateMatchRelationPositions: generateMatchRelationPositions,
|
|
3870
|
+
generateMatchRelationsNew: generateMatchRelationsNew,
|
|
3871
|
+
generateRoundRelationsNew: generateRoundRelationsNew,
|
|
3866
3872
|
generateRoundTypeFromEthleteRoundType: generateRoundTypeFromEthleteRoundType,
|
|
3867
3873
|
generateRoundTypeFromGgMatch: generateRoundTypeFromGgMatch,
|
|
3868
3874
|
generateTournamentModeFormEthleteRounds: generateTournamentModeFormEthleteRounds,
|
|
3869
|
-
generateTournamentModeFormGgData: generateTournamentModeFormGgData
|
|
3875
|
+
generateTournamentModeFormGgData: generateTournamentModeFormGgData,
|
|
3876
|
+
getAvailableSwissGroupsForRound: getAvailableSwissGroupsForRound,
|
|
3877
|
+
logRoundRelations: logRoundRelations
|
|
3870
3878
|
});
|
|
3871
3879
|
|
|
3872
3880
|
const VALIDATOR_ERROR_SERVICE_TOKEN = new InjectionToken('VALIDATOR_ERROR_SERVICE');
|