@ethlete/cdk 4.47.0 → 4.48.1
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 +12 -0
- package/esm2022/lib/components/bracket/components/new-bracket/bracket-new.mjs +170 -69
- package/esm2022/lib/components/bracket/components/new-bracket/draw-man.mjs +75 -32
- package/esm2022/lib/components/bracket/components/new-bracket/grid-placements.mjs +8 -3
- package/esm2022/lib/components/bracket/components/new-bracket/journey-highlight.mjs +18 -0
- package/esm2022/lib/components/bracket/components/new-bracket/new-bracket.component.mjs +47 -9
- package/esm2022/lib/components/bracket/stories/dummy-data/ET_DUMMY_DATA_DOUBLE_SYNC.mjs +1116 -0
- package/esm2022/lib/components/bracket/stories/dummy-data/ET_DUMMY_DATA_SINGLE.mjs +2691 -0
- package/esm2022/lib/components/bracket/stories/dummy-data/ET_DUMMY_DATA_SWISS.mjs +72479 -0
- package/esm2022/lib/components/bracket/stories/dummy-data/ET_DUMMY_DATE_DOUBLE_ASYNC.mjs +1275 -0
- package/esm2022/lib/components/bracket/stories/dummy-data/FIFA_DUMMY_DATA_DOUBLE.mjs +2453 -0
- package/esm2022/lib/components/bracket/stories/dummy-data/FIFA_DUMMY_DATA_SINGLE.mjs +2570 -0
- package/esm2022/lib/components/bracket/stories/dummy-data/index.mjs +7 -0
- package/fesm2022/ethlete-cdk.mjs +315 -109
- package/fesm2022/ethlete-cdk.mjs.map +1 -1
- package/lib/components/bracket/components/new-bracket/bracket-new.d.ts +1632 -18
- package/lib/components/bracket/components/new-bracket/draw-man.d.ts +5 -3
- package/lib/components/bracket/components/new-bracket/grid-placements.d.ts +4 -0
- package/lib/components/bracket/components/new-bracket/journey-highlight.d.ts +2 -0
- package/lib/components/bracket/components/new-bracket/new-bracket.component.d.ts +7 -2
- package/lib/components/bracket/stories/dummy-data/ET_DUMMY_DATA_DOUBLE_SYNC.d.ts +149 -0
- package/lib/components/bracket/stories/dummy-data/ET_DUMMY_DATA_SINGLE.d.ts +184 -0
- package/lib/components/bracket/stories/dummy-data/ET_DUMMY_DATA_SWISS.d.ts +214 -0
- package/lib/components/bracket/stories/dummy-data/ET_DUMMY_DATE_DOUBLE_ASYNC.d.ts +149 -0
- package/lib/components/bracket/stories/dummy-data/FIFA_DUMMY_DATA_DOUBLE.d.ts +1640 -0
- package/lib/components/bracket/stories/dummy-data/FIFA_DUMMY_DATA_SINGLE.d.ts +2388 -0
- package/lib/components/bracket/stories/dummy-data/index.d.ts +6 -0
- package/lib/components/query-error/directives/query-error/query-error.directive.d.ts +1 -1
- package/package.json +1 -1
package/fesm2022/ethlete-cdk.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as i1 from '@angular/cdk/portal';
|
|
2
2
|
import { CdkPortal, PortalModule, ComponentPortal, TemplatePortal, CdkPortalOutlet } from '@angular/cdk/portal';
|
|
3
|
-
import { AsyncPipe, NgClass, NgTemplateOutlet, NgComponentOutlet, JsonPipe, DOCUMENT, Location } from '@angular/common';
|
|
3
|
+
import { AsyncPipe, NgClass, NgTemplateOutlet, isPlatformBrowser, NgComponentOutlet, JsonPipe, DOCUMENT, Location } from '@angular/common';
|
|
4
4
|
import * as i0 from '@angular/core';
|
|
5
|
-
import { Component, ViewEncapsulation, ChangeDetectionStrategy, InjectionToken, Directive, booleanAttribute, Input, ContentChild, ContentChildren, inject, ElementRef, Injector, HostBinding, input, numberAttribute, computed, signal, contentChildren, viewChild, viewChildren, isDevMode, Injectable, ChangeDetectorRef, TemplateRef, ViewContainerRef, ViewChild, assertInInjectionContext, forwardRef, EventEmitter, Output, ViewChildren, runInInjectionContext, Optional, Inject, SkipSelf, HostListener, contentChild,
|
|
5
|
+
import { Component, ViewEncapsulation, ChangeDetectionStrategy, InjectionToken, Directive, booleanAttribute, Input, ContentChild, ContentChildren, inject, ElementRef, Injector, HostBinding, input, numberAttribute, computed, PLATFORM_ID, Renderer2, effect, DestroyRef, signal, contentChildren, viewChild, viewChildren, isDevMode, Injectable, ChangeDetectorRef, TemplateRef, ViewContainerRef, ViewChild, assertInInjectionContext, forwardRef, EventEmitter, Output, ViewChildren, runInInjectionContext, Optional, Inject, SkipSelf, HostListener, contentChild, untracked, NgZone, NgModule, isSignal, Attribute } from '@angular/core';
|
|
6
6
|
import * as i1$1 from '@ethlete/core';
|
|
7
|
-
import { LetDirective, createDestroy, Memo, signalHostAttributes, signalHostClasses, previousSignalValue, signalHostStyles, nextFrame, syncSignal, signalStyles, injectHostElement, ObserveVisibilityDirective, signalVisibilityChangeClasses, IS_EMAIL, MUST_MATCH, IS_ARRAY_NOT_EMPTY, AT_LEAST_ONE_REQUIRED, equal, switchQueryListChanges, controlValueSignal, signalAttributes, ResizeObserverService, createFlipAnimation, AnimatedOverlayDirective, RuntimeError, SelectionModel, ActiveSelectionModel, KeyPressManager, signalClasses, scrollToElement, isEmptyArray, isObjectArray, isPrimitiveArray,
|
|
7
|
+
import { LetDirective, createDestroy, Memo, createComponentId, signalHostAttributes, signalHostClasses, previousSignalValue, signalHostStyles, nextFrame, syncSignal, signalStyles, injectHostElement, ObserveVisibilityDirective, signalVisibilityChangeClasses, IS_EMAIL, MUST_MATCH, IS_ARRAY_NOT_EMPTY, AT_LEAST_ONE_REQUIRED, equal, switchQueryListChanges, controlValueSignal, signalAttributes, ResizeObserverService, createFlipAnimation, AnimatedOverlayDirective, RuntimeError, SelectionModel, ActiveSelectionModel, KeyPressManager, signalClasses, scrollToElement, isEmptyArray, isObjectArray, isPrimitiveArray, ClickOutsideDirective, ANIMATED_LIFECYCLE_TOKEN, AnimatedLifecycleDirective, ObserveContentDirective, clamp, DELAYABLE_TOKEN, ObserveResizeDirective, SmartBlockScrollStrategy, signalElementDimensions, signalElementScrollState, createIsRenderedSignal, createCanAnimateSignal, useCursorDragScroll, signalElementIntersection, signalElementChildren, getIntersectionInfo, getElementScrollCoordinates, ObserveScrollStateDirective, ClickObserverService, RootBoundaryDirective, elementCanScroll, cloneFormGroup, getFormGroupValue, ViewportService, injectRoute, ROOT_BOUNDARY_TOKEN, injectQueryParam, RouterStateService, fromNextFrame, isElementVisible, AnimatedIfDirective, FocusVisibleService, inferMimeType, ScrollObserverIgnoreTargetDirective, TypedQueryList } from '@ethlete/core';
|
|
8
8
|
import { BehaviorSubject, startWith, map, switchMap, combineLatest, pairwise, tap, takeUntil, skip, of, merge, timer, takeWhile, filter, fromEvent, Subject, Observable, debounceTime, withLatestFrom, distinctUntilChanged, take, skipUntil, skipWhile, catchError, throwError, defer, partition, from, finalize, Subscription } from 'rxjs';
|
|
9
9
|
import { trigger, state, style, transition, animate } from '@angular/animations';
|
|
10
10
|
import { __decorate, __metadata } from 'tslib';
|
|
@@ -1040,12 +1040,18 @@ const BRACKET_ROUND_MIRROR_TYPE = {
|
|
|
1040
1040
|
LEFT: 'left',
|
|
1041
1041
|
RIGHT: 'right',
|
|
1042
1042
|
};
|
|
1043
|
-
const generateRoundTypeFromEthleteRoundType = (type, tournamentMode) => {
|
|
1043
|
+
const generateRoundTypeFromEthleteRoundType = (type, tournamentMode, roundMatchCount) => {
|
|
1044
1044
|
switch (type) {
|
|
1045
1045
|
case 'normal':
|
|
1046
1046
|
switch (tournamentMode) {
|
|
1047
1047
|
case 'single-elimination':
|
|
1048
|
-
|
|
1048
|
+
// This might break if the single elimination contains a 3rd place match + round
|
|
1049
|
+
if (roundMatchCount === 1) {
|
|
1050
|
+
return COMMON_BRACKET_ROUND_TYPE.FINAL;
|
|
1051
|
+
}
|
|
1052
|
+
else {
|
|
1053
|
+
return SINGLE_ELIMINATION_BRACKET_ROUND_TYPE.SINGLE_ELIMINATION_BRACKET;
|
|
1054
|
+
}
|
|
1049
1055
|
case 'group':
|
|
1050
1056
|
return GROUP_BRACKET_ROUND_TYPE.GROUP;
|
|
1051
1057
|
case 'swiss':
|
|
@@ -1164,13 +1170,18 @@ const generateRoundRelations = (bracketData) => {
|
|
|
1164
1170
|
const hasLowerRounds = lowerRounds.length > 0;
|
|
1165
1171
|
const lastLowerRound = lowerRounds[lowerRounds.length - 1] || null;
|
|
1166
1172
|
for (const [currentUpperRoundIndex, currentUpperRound] of upperRounds.entries()) {
|
|
1167
|
-
const
|
|
1168
|
-
|
|
1173
|
+
const isLeftToRight = currentUpperRound.mirrorRoundType === null ||
|
|
1174
|
+
currentUpperRound.mirrorRoundType === BRACKET_ROUND_MIRROR_TYPE.LEFT;
|
|
1175
|
+
const relativePreviousUpperRound = upperRounds[currentUpperRoundIndex - 1] || null;
|
|
1176
|
+
const relativeNextUpperRound = upperRounds[currentUpperRoundIndex + 1] || null;
|
|
1177
|
+
const previousUpperRound = isLeftToRight ? relativePreviousUpperRound : relativeNextUpperRound;
|
|
1178
|
+
const nextUpperRound = isLeftToRight ? relativeNextUpperRound : relativePreviousUpperRound;
|
|
1169
1179
|
const currentLowerRound = lowerRounds[currentUpperRoundIndex] || null;
|
|
1170
1180
|
const previousLowerRound = lowerRounds[currentUpperRoundIndex - 1] || null;
|
|
1171
1181
|
const nextLowerRound = lowerRounds[currentUpperRoundIndex + 1] || null;
|
|
1172
|
-
const
|
|
1173
|
-
|
|
1182
|
+
const isLastUpperRound = !nextUpperRound ||
|
|
1183
|
+
(nextUpperRound.mirrorRoundType === BRACKET_ROUND_MIRROR_TYPE.RIGHT &&
|
|
1184
|
+
currentUpperRound.mirrorRoundType === null);
|
|
1174
1185
|
const isFinal = currentUpperRound.type === COMMON_BRACKET_ROUND_TYPE.FINAL;
|
|
1175
1186
|
if (isFinal && hasLowerRounds) {
|
|
1176
1187
|
// two to one relation
|
|
@@ -1286,7 +1297,7 @@ const generateRoundRelations = (bracketData) => {
|
|
|
1286
1297
|
rootRoundMatchFactor: firstUpperRound.matchCount / currentUpperRound.matchCount,
|
|
1287
1298
|
});
|
|
1288
1299
|
}
|
|
1289
|
-
else if (isFirstRound) {
|
|
1300
|
+
else if (currentUpperRound.isFirstRound) {
|
|
1290
1301
|
// nothing to one relation
|
|
1291
1302
|
if (!nextUpperRound)
|
|
1292
1303
|
throw new Error('nextUpperRound is null');
|
|
@@ -1351,10 +1362,10 @@ const generateMatchRelations = (bracketData, roundRelations, matchPositionMaps)
|
|
|
1351
1362
|
const currentRelation = roundRelations.get(match.round.id);
|
|
1352
1363
|
if (!currentRelation)
|
|
1353
1364
|
throw new Error('Match round not found');
|
|
1354
|
-
const {
|
|
1365
|
+
const { nextRoundMatchPosition, previousLowerRoundMatchPosition, previousUpperRoundMatchPosition } = generateMatchRelationPositions(currentRelation, match);
|
|
1355
1366
|
switch (currentRelation.type) {
|
|
1356
1367
|
case 'nothing-to-one': {
|
|
1357
|
-
const nextMatch = matchPositionMaps.get(currentRelation.nextRound.id)?.get(
|
|
1368
|
+
const nextMatch = matchPositionMaps.get(currentRelation.nextRound.id)?.get(nextRoundMatchPosition);
|
|
1358
1369
|
if (!nextMatch)
|
|
1359
1370
|
throw new Error('Next round match not found');
|
|
1360
1371
|
// means left is nothing. right is one
|
|
@@ -1403,7 +1414,7 @@ const generateMatchRelations = (bracketData, roundRelations, matchPositionMaps)
|
|
|
1403
1414
|
break;
|
|
1404
1415
|
}
|
|
1405
1416
|
case 'one-to-one': {
|
|
1406
|
-
const nextMatch = matchPositionMaps.get(currentRelation.nextRound.id)?.get(
|
|
1417
|
+
const nextMatch = matchPositionMaps.get(currentRelation.nextRound.id)?.get(nextRoundMatchPosition);
|
|
1407
1418
|
const previousUpperMatch = matchPositionMaps
|
|
1408
1419
|
.get(currentRelation.previousRound.id)
|
|
1409
1420
|
?.get(previousUpperRoundMatchPosition);
|
|
@@ -1418,12 +1429,7 @@ const generateMatchRelations = (bracketData, roundRelations, matchPositionMaps)
|
|
|
1418
1429
|
throw new Error('Previous lower round match not found');
|
|
1419
1430
|
// can be either one to one or two to one
|
|
1420
1431
|
const isLeftOne = previousUpperRoundMatchPosition === previousLowerRoundMatchPosition;
|
|
1421
|
-
|
|
1422
|
-
if (currentRelation.currentRound.mirrorRoundType === BRACKET_ROUND_MIRROR_TYPE.RIGHT) {
|
|
1423
|
-
console.log(match, generateMatchRelationPositions(currentRelation, match));
|
|
1424
|
-
}
|
|
1425
|
-
// TODO: Implement one to two
|
|
1426
|
-
if (isLeftOne && isRightOne) {
|
|
1432
|
+
if (isLeftOne) {
|
|
1427
1433
|
// one-to-one
|
|
1428
1434
|
matchRelations.set(match.id, {
|
|
1429
1435
|
type: 'one-to-one',
|
|
@@ -1435,7 +1441,7 @@ const generateMatchRelations = (bracketData, roundRelations, matchPositionMaps)
|
|
|
1435
1441
|
nextRound: currentRelation.nextRound,
|
|
1436
1442
|
});
|
|
1437
1443
|
}
|
|
1438
|
-
else
|
|
1444
|
+
else {
|
|
1439
1445
|
// two-to-one
|
|
1440
1446
|
matchRelations.set(match.id, {
|
|
1441
1447
|
type: 'two-to-one',
|
|
@@ -1449,27 +1455,10 @@ const generateMatchRelations = (bracketData, roundRelations, matchPositionMaps)
|
|
|
1449
1455
|
nextRound: currentRelation.nextRound,
|
|
1450
1456
|
});
|
|
1451
1457
|
}
|
|
1452
|
-
else if (!isRightOne) {
|
|
1453
|
-
const nextLowerMatch = matchPositionMaps.get(currentRelation.nextRound.id)?.get(nextLowerRoundMatchPosition);
|
|
1454
|
-
if (!nextLowerMatch)
|
|
1455
|
-
throw new Error('Next lower round match not found');
|
|
1456
|
-
// one-to-two
|
|
1457
|
-
matchRelations.set(match.id, {
|
|
1458
|
-
type: 'one-to-two',
|
|
1459
|
-
currentMatch: match,
|
|
1460
|
-
currentRound: currentRelation.currentRound,
|
|
1461
|
-
previousMatch: previousUpperMatch,
|
|
1462
|
-
previousRound: currentRelation.previousRound,
|
|
1463
|
-
nextUpperMatch: nextMatch,
|
|
1464
|
-
nextUpperRound: currentRelation.nextRound,
|
|
1465
|
-
nextLowerMatch,
|
|
1466
|
-
nextLowerRound: nextLowerMatch.round,
|
|
1467
|
-
});
|
|
1468
|
-
}
|
|
1469
1458
|
break;
|
|
1470
1459
|
}
|
|
1471
1460
|
case 'two-to-one': {
|
|
1472
|
-
const nextMatch = matchPositionMaps.get(currentRelation.nextRound.id)?.get(
|
|
1461
|
+
const nextMatch = matchPositionMaps.get(currentRelation.nextRound.id)?.get(nextRoundMatchPosition);
|
|
1473
1462
|
const previousUpperMatch = matchPositionMaps
|
|
1474
1463
|
.get(currentRelation.previousUpperRound.id)
|
|
1475
1464
|
?.get(previousUpperRoundMatchPosition);
|
|
@@ -1525,8 +1514,7 @@ const generateMatchRelationPositions = (currentRelation, match) => {
|
|
|
1525
1514
|
switch (currentRelation.type) {
|
|
1526
1515
|
case 'nothing-to-one': {
|
|
1527
1516
|
return {
|
|
1528
|
-
|
|
1529
|
-
nextLowerRoundMatchPosition: FALLBACK_MATCH_POSITION,
|
|
1517
|
+
nextRoundMatchPosition: generateMatchPosition(match, currentRelation.nextRoundMatchFactor),
|
|
1530
1518
|
previousUpperRoundMatchPosition: FALLBACK_MATCH_POSITION,
|
|
1531
1519
|
previousLowerRoundMatchPosition: FALLBACK_MATCH_POSITION,
|
|
1532
1520
|
};
|
|
@@ -1535,8 +1523,7 @@ const generateMatchRelationPositions = (currentRelation, match) => {
|
|
|
1535
1523
|
const previousRoundHasDoubleTheMatchCount = currentRelation.previousRoundMatchFactor === 2;
|
|
1536
1524
|
const doubleUpperMatchCountShift = previousRoundHasDoubleTheMatchCount ? 1 : 0;
|
|
1537
1525
|
return {
|
|
1538
|
-
|
|
1539
|
-
nextLowerRoundMatchPosition: FALLBACK_MATCH_POSITION,
|
|
1526
|
+
nextRoundMatchPosition: FALLBACK_MATCH_POSITION,
|
|
1540
1527
|
previousUpperRoundMatchPosition: (generateMatchPosition(match, currentRelation.previousRoundMatchFactor) -
|
|
1541
1528
|
doubleUpperMatchCountShift),
|
|
1542
1529
|
previousLowerRoundMatchPosition: generateMatchPosition(match, currentRelation.previousRoundMatchFactor),
|
|
@@ -1544,13 +1531,9 @@ const generateMatchRelationPositions = (currentRelation, match) => {
|
|
|
1544
1531
|
}
|
|
1545
1532
|
case 'one-to-one': {
|
|
1546
1533
|
const previousRoundHasDoubleTheMatchCount = currentRelation.previousRoundMatchFactor === 2;
|
|
1547
|
-
const nextRoundHasDoubleTheMatchCount = currentRelation.nextRoundMatchFactor === 2;
|
|
1548
1534
|
const doubleUpperMatchCountShift = previousRoundHasDoubleTheMatchCount ? 1 : 0;
|
|
1549
|
-
const doubleLowerMatchCountShift = nextRoundHasDoubleTheMatchCount ? 1 : 0;
|
|
1550
1535
|
return {
|
|
1551
|
-
|
|
1552
|
-
doubleLowerMatchCountShift),
|
|
1553
|
-
nextLowerRoundMatchPosition: generateMatchPosition(match, currentRelation.nextRoundMatchFactor),
|
|
1536
|
+
nextRoundMatchPosition: generateMatchPosition(match, currentRelation.nextRoundMatchFactor),
|
|
1554
1537
|
previousUpperRoundMatchPosition: (generateMatchPosition(match, currentRelation.previousRoundMatchFactor) -
|
|
1555
1538
|
doubleUpperMatchCountShift),
|
|
1556
1539
|
previousLowerRoundMatchPosition: generateMatchPosition(match, currentRelation.previousRoundMatchFactor),
|
|
@@ -1558,16 +1541,14 @@ const generateMatchRelationPositions = (currentRelation, match) => {
|
|
|
1558
1541
|
}
|
|
1559
1542
|
case 'two-to-one': {
|
|
1560
1543
|
return {
|
|
1561
|
-
|
|
1562
|
-
nextUpperRoundMatchPosition: generateMatchPosition(match, currentRelation.nextRoundMatchFactor),
|
|
1544
|
+
nextRoundMatchPosition: generateMatchPosition(match, currentRelation.nextRoundMatchFactor),
|
|
1563
1545
|
previousUpperRoundMatchPosition: generateMatchPosition(match, currentRelation.previousUpperRoundMatchFactor),
|
|
1564
1546
|
previousLowerRoundMatchPosition: generateMatchPosition(match, currentRelation.previousLowerRoundMatchFactor),
|
|
1565
1547
|
};
|
|
1566
1548
|
}
|
|
1567
1549
|
case 'two-to-nothing': {
|
|
1568
1550
|
return {
|
|
1569
|
-
|
|
1570
|
-
nextUpperRoundMatchPosition: FALLBACK_MATCH_POSITION,
|
|
1551
|
+
nextRoundMatchPosition: FALLBACK_MATCH_POSITION,
|
|
1571
1552
|
previousUpperRoundMatchPosition: generateMatchPosition(match, currentRelation.previousUpperRoundMatchFactor),
|
|
1572
1553
|
previousLowerRoundMatchPosition: generateMatchPosition(match, currentRelation.previousLowerRoundMatchFactor),
|
|
1573
1554
|
};
|
|
@@ -1613,9 +1594,9 @@ const generateMatchParticipantMap = (bracketData) => {
|
|
|
1613
1594
|
let lossesTilNow = 0;
|
|
1614
1595
|
let tiesTilNow = 0;
|
|
1615
1596
|
for (const matchParticipantMatch of participant.matches.values()) {
|
|
1616
|
-
const isWinner = matchParticipantMatch.bracketMatch.
|
|
1617
|
-
const isLooser = matchParticipantMatch.bracketMatch.
|
|
1618
|
-
matchParticipantMatch.bracketMatch.
|
|
1597
|
+
const isWinner = matchParticipantMatch.bracketMatch.winnerSide === matchParticipantMatch.side;
|
|
1598
|
+
const isLooser = matchParticipantMatch.bracketMatch.winnerSide &&
|
|
1599
|
+
matchParticipantMatch.bracketMatch.winnerSide !== matchParticipantMatch.side;
|
|
1619
1600
|
const isTie = matchParticipantMatch.bracketMatch.status === 'completed' && !matchParticipantMatch.bracketMatch.winner;
|
|
1620
1601
|
if (isWinner) {
|
|
1621
1602
|
winsTilNow++;
|
|
@@ -1724,8 +1705,8 @@ const generateBracketRoundSwissGroupMaps = (bracketData, matchParticipantMap) =>
|
|
|
1724
1705
|
}
|
|
1725
1706
|
const emptyMatchIds = [];
|
|
1726
1707
|
for (const match of bracketRound.matches.values()) {
|
|
1727
|
-
const participantHome = match.home ? (matchParticipantMap.get(match.home) ?? null) : null;
|
|
1728
|
-
const participantAway = match.away ? (matchParticipantMap.get(match.away) ?? null) : null;
|
|
1708
|
+
const participantHome = match.home ? (matchParticipantMap.get(match.home.id) ?? null) : null;
|
|
1709
|
+
const participantAway = match.away ? (matchParticipantMap.get(match.away.id) ?? null) : null;
|
|
1729
1710
|
const anyParticipant = participantHome || participantAway;
|
|
1730
1711
|
if (!anyParticipant) {
|
|
1731
1712
|
emptyMatchIds.push(match.id);
|
|
@@ -1772,7 +1753,7 @@ const generateBracketDataForEthlete = (source) => {
|
|
|
1772
1753
|
if (bracketData.rounds.some((r) => r.id === currentItem.round.id)) {
|
|
1773
1754
|
throw new Error(`Round with id ${currentItem.round.id} already exists in the bracket data.`);
|
|
1774
1755
|
}
|
|
1775
|
-
const roundType = generateRoundTypeFromEthleteRoundType(currentItem.round.type, tournamentMode);
|
|
1756
|
+
const roundType = generateRoundTypeFromEthleteRoundType(currentItem.round.type, tournamentMode, currentItem.matches.length);
|
|
1776
1757
|
const bracketRound = {
|
|
1777
1758
|
type: roundType,
|
|
1778
1759
|
id: currentItem.round.id,
|
|
@@ -1800,39 +1781,46 @@ const generateBracketDataForEthlete = (source) => {
|
|
|
1800
1781
|
};
|
|
1801
1782
|
const generateAndSetBracketMatchWithParticipants = (match, options) => {
|
|
1802
1783
|
const matchId = match.id;
|
|
1803
|
-
const { bracketData, bracketRound, currentIndexInRound } = options;
|
|
1784
|
+
const { bracketData, bracketRound, currentIndexInRound, participantCounter } = options;
|
|
1785
|
+
let localParticipantCounter = participantCounter;
|
|
1804
1786
|
const bracketMatch = {
|
|
1805
1787
|
id: matchId,
|
|
1806
1788
|
indexInRound: currentIndexInRound,
|
|
1807
1789
|
position: (currentIndexInRound + 1),
|
|
1808
1790
|
data: match.data,
|
|
1809
1791
|
round: bracketRound,
|
|
1810
|
-
home:
|
|
1811
|
-
away:
|
|
1812
|
-
winner: match.winner,
|
|
1792
|
+
home: null,
|
|
1793
|
+
away: null,
|
|
1813
1794
|
status: match.status,
|
|
1795
|
+
winnerSide: match.winner,
|
|
1796
|
+
winner: null,
|
|
1814
1797
|
};
|
|
1815
1798
|
bracketData.matches.set(matchId, bracketMatch);
|
|
1816
1799
|
bracketRound.matches.set(matchId, bracketMatch);
|
|
1817
|
-
const
|
|
1818
|
-
for (const [participantIndex,
|
|
1819
|
-
if (!
|
|
1800
|
+
const participantIds = [match.home, match.away];
|
|
1801
|
+
for (const [participantIndex, participantId] of participantIds.entries()) {
|
|
1802
|
+
if (!participantId)
|
|
1820
1803
|
continue;
|
|
1821
|
-
const side = participantIndex === 0 ? '
|
|
1822
|
-
if (!bracketData.participants.has(
|
|
1823
|
-
bracketData.participants.set(
|
|
1824
|
-
id:
|
|
1804
|
+
const side = participantIndex === 0 ? 'home' : 'away';
|
|
1805
|
+
if (!bracketData.participants.has(participantId)) {
|
|
1806
|
+
bracketData.participants.set(participantId, {
|
|
1807
|
+
id: participantId,
|
|
1808
|
+
shortId: `p${localParticipantCounter++}`,
|
|
1825
1809
|
name: `${side} ${currentIndexInRound}`,
|
|
1826
1810
|
matches: new Map(),
|
|
1827
1811
|
});
|
|
1828
1812
|
}
|
|
1829
|
-
const participantData = bracketData.participants.get(
|
|
1813
|
+
const participantData = bracketData.participants.get(participantId);
|
|
1830
1814
|
if (!participantData.matches.has(bracketMatch.id)) {
|
|
1831
1815
|
participantData.matches.set(bracketMatch.id, {
|
|
1832
|
-
side:
|
|
1816
|
+
side: match.home === participantId ? 'home' : 'away',
|
|
1833
1817
|
bracketMatch,
|
|
1834
1818
|
});
|
|
1835
1819
|
}
|
|
1820
|
+
bracketMatch[side] = participantData;
|
|
1821
|
+
if (bracketMatch.winnerSide === side) {
|
|
1822
|
+
bracketMatch.winner = participantData;
|
|
1823
|
+
}
|
|
1836
1824
|
}
|
|
1837
1825
|
return bracketMatch;
|
|
1838
1826
|
};
|
|
@@ -1852,11 +1840,12 @@ const generateBracketData = (source, options) => {
|
|
|
1852
1840
|
let currentUpperBracketIndex = 0;
|
|
1853
1841
|
let currentLowerBracketIndex = 0;
|
|
1854
1842
|
const splitRoundsRest = [];
|
|
1855
|
-
for (const round of source.rounds) {
|
|
1843
|
+
for (const [roundIndex, round] of source.rounds.entries()) {
|
|
1856
1844
|
const isLowerBracket = round.type === DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.LOWER_BRACKET;
|
|
1857
1845
|
const matches = source.matches.filter((m) => m.roundId === round.id);
|
|
1858
1846
|
const roundId = round.id;
|
|
1859
1847
|
const shouldSplitRound = shouldSplitRoundsInTwo && matches.length % 2 === 0;
|
|
1848
|
+
const isFirstRound = roundIndex === 0;
|
|
1860
1849
|
if (shouldSplitRound) {
|
|
1861
1850
|
const firstHalfMatchesMaxIndex = matches.length / 2 - 1;
|
|
1862
1851
|
const firstHalfRoundId = `${roundId}--half-1`;
|
|
@@ -1871,6 +1860,7 @@ const generateBracketData = (source, options) => {
|
|
|
1871
1860
|
matchCount: matches.length / 2,
|
|
1872
1861
|
matches: new Map(),
|
|
1873
1862
|
mirrorRoundType: BRACKET_ROUND_MIRROR_TYPE.LEFT,
|
|
1863
|
+
isFirstRound,
|
|
1874
1864
|
};
|
|
1875
1865
|
const bracketRoundSecondHalf = {
|
|
1876
1866
|
type: round.type,
|
|
@@ -1882,6 +1872,7 @@ const generateBracketData = (source, options) => {
|
|
|
1882
1872
|
matchCount: matches.length / 2,
|
|
1883
1873
|
matches: new Map(),
|
|
1884
1874
|
mirrorRoundType: BRACKET_ROUND_MIRROR_TYPE.RIGHT,
|
|
1875
|
+
isFirstRound,
|
|
1885
1876
|
};
|
|
1886
1877
|
bracketData.roundIds.push(firstHalfRoundId);
|
|
1887
1878
|
bracketData.rounds.set(firstHalfRoundId, bracketRoundFirstHalf);
|
|
@@ -1891,15 +1882,20 @@ const generateBracketData = (source, options) => {
|
|
|
1891
1882
|
const matchSecond = matches[firstHalfMatchesMaxIndex + 1 + index];
|
|
1892
1883
|
if (!matchFirst || !matchSecond)
|
|
1893
1884
|
throw new Error('Match not found');
|
|
1885
|
+
const participantCounter = bracketData.participants.size;
|
|
1894
1886
|
const bracketMatchFirst = generateAndSetBracketMatchWithParticipants(matchFirst, {
|
|
1895
1887
|
bracketData,
|
|
1896
1888
|
bracketRound: bracketRoundFirstHalf,
|
|
1897
1889
|
currentIndexInRound: index,
|
|
1890
|
+
participantCounter,
|
|
1898
1891
|
});
|
|
1892
|
+
// The participant counter might have changed after the first half
|
|
1893
|
+
const updatedParticipantCounter = bracketData.participants.size;
|
|
1899
1894
|
const bracketMatchSecond = generateAndSetBracketMatchWithParticipants(matchSecond, {
|
|
1900
1895
|
bracketData,
|
|
1901
1896
|
bracketRound: bracketRoundSecondHalf,
|
|
1902
1897
|
currentIndexInRound: index,
|
|
1898
|
+
participantCounter: updatedParticipantCounter,
|
|
1903
1899
|
});
|
|
1904
1900
|
bracketData.matchIds.push(bracketMatchFirst.id, bracketMatchSecond.id);
|
|
1905
1901
|
}
|
|
@@ -1915,15 +1911,18 @@ const generateBracketData = (source, options) => {
|
|
|
1915
1911
|
matchCount: matches.length,
|
|
1916
1912
|
matches: new Map(),
|
|
1917
1913
|
mirrorRoundType: null,
|
|
1914
|
+
isFirstRound,
|
|
1918
1915
|
};
|
|
1919
1916
|
bracketData.roundIds.push(roundId);
|
|
1920
1917
|
bracketData.rounds.set(roundId, bracketRound);
|
|
1921
1918
|
let currentIndexInRound = 0;
|
|
1922
1919
|
for (const match of matches) {
|
|
1920
|
+
const participantCounter = bracketData.participants.size;
|
|
1923
1921
|
const bracketMatch = generateAndSetBracketMatchWithParticipants(match, {
|
|
1924
1922
|
bracketData,
|
|
1925
1923
|
bracketRound,
|
|
1926
1924
|
currentIndexInRound,
|
|
1925
|
+
participantCounter,
|
|
1927
1926
|
});
|
|
1928
1927
|
bracketData.matchIds.push(bracketMatch.id);
|
|
1929
1928
|
currentIndexInRound++;
|
|
@@ -1952,30 +1951,133 @@ const generateBracketData = (source, options) => {
|
|
|
1952
1951
|
}
|
|
1953
1952
|
return bracketData;
|
|
1954
1953
|
};
|
|
1954
|
+
const generateTournamentModeFormGgData = (source) => {
|
|
1955
|
+
switch (source.mode) {
|
|
1956
|
+
// case 'groups':
|
|
1957
|
+
// return TOURNAMENT_MODE.GROUP;
|
|
1958
|
+
// case 'fifa_swiss': {
|
|
1959
|
+
// const lastRound = source[source.length - 1];
|
|
1960
|
+
// if (!lastRound) throw new Error('No last round found');
|
|
1961
|
+
// if (lastRound.matches.length !== firstRound.matches.length) {
|
|
1962
|
+
// return TOURNAMENT_MODE.SWISS_WITH_ELIMINATION;
|
|
1963
|
+
// } else {
|
|
1964
|
+
// return TOURNAMENT_MODE.SWISS;
|
|
1965
|
+
// }
|
|
1966
|
+
// }
|
|
1967
|
+
case 'double-elimination':
|
|
1968
|
+
return TOURNAMENT_MODE.DOUBLE_ELIMINATION;
|
|
1969
|
+
case 'single-elimination':
|
|
1970
|
+
return TOURNAMENT_MODE.SINGLE_ELIMINATION;
|
|
1971
|
+
default:
|
|
1972
|
+
throw new Error(`Unsupported tournament mode: ${source.mode}`);
|
|
1973
|
+
}
|
|
1974
|
+
};
|
|
1975
|
+
const generateRoundTypeFromGgMatch = (tournamentMode, bracketType, // 'winner' | 'looser' | null,
|
|
1976
|
+
stageNumber, matchCount) => {
|
|
1977
|
+
switch (tournamentMode) {
|
|
1978
|
+
case 'double-elimination': {
|
|
1979
|
+
switch (stageNumber) {
|
|
1980
|
+
case 1: {
|
|
1981
|
+
switch (bracketType) {
|
|
1982
|
+
case 'winner':
|
|
1983
|
+
return DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.UPPER_BRACKET;
|
|
1984
|
+
case 'looser':
|
|
1985
|
+
return DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.LOWER_BRACKET;
|
|
1986
|
+
default:
|
|
1987
|
+
throw new Error(`Unsupported bracket type: ${bracketType}`);
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
case 2: {
|
|
1991
|
+
return COMMON_BRACKET_ROUND_TYPE.FINAL;
|
|
1992
|
+
}
|
|
1993
|
+
case 3: {
|
|
1994
|
+
return DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.REVERSE_FINAL;
|
|
1995
|
+
}
|
|
1996
|
+
default: {
|
|
1997
|
+
throw new Error(`Unsupported stage number: ${stageNumber}`);
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
case 'single-elimination': {
|
|
2002
|
+
if (matchCount === 1) {
|
|
2003
|
+
return COMMON_BRACKET_ROUND_TYPE.FINAL;
|
|
2004
|
+
}
|
|
2005
|
+
else {
|
|
2006
|
+
return SINGLE_ELIMINATION_BRACKET_ROUND_TYPE.SINGLE_ELIMINATION_BRACKET;
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
default: {
|
|
2010
|
+
throw new Error(`Unsupported tournament mode: ${tournamentMode}`);
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
};
|
|
2014
|
+
const generateBracketDataForGg = (source) => {
|
|
2015
|
+
const tournamentMode = generateTournamentModeFormGgData(source);
|
|
2016
|
+
const bracketData = {
|
|
2017
|
+
rounds: [],
|
|
2018
|
+
matches: [],
|
|
2019
|
+
mode: tournamentMode,
|
|
2020
|
+
};
|
|
2021
|
+
const matchesGrouped = new Map();
|
|
2022
|
+
for (const match of source.matches) {
|
|
2023
|
+
const roundId = `${match.roundNumber}-${match.stageNumber}-${match.bracketType || null}`;
|
|
2024
|
+
if (!matchesGrouped.has(roundId)) {
|
|
2025
|
+
matchesGrouped.set(roundId, []);
|
|
2026
|
+
}
|
|
2027
|
+
matchesGrouped.get(roundId)?.push(match);
|
|
2028
|
+
}
|
|
2029
|
+
for (const [roundId, matches] of matchesGrouped.entries()) {
|
|
2030
|
+
const firstMatch = matches[0];
|
|
2031
|
+
if (!firstMatch)
|
|
2032
|
+
throw new Error('First match not found');
|
|
2033
|
+
const roundType = generateRoundTypeFromGgMatch(tournamentMode, firstMatch.bracketType, firstMatch.stageNumber, matches.length);
|
|
2034
|
+
const bracketRound = {
|
|
2035
|
+
type: roundType,
|
|
2036
|
+
id: roundId,
|
|
2037
|
+
data: null,
|
|
2038
|
+
name: firstMatch.roundTitle,
|
|
2039
|
+
};
|
|
2040
|
+
bracketData.rounds.push(bracketRound);
|
|
2041
|
+
for (const match of matches) {
|
|
2042
|
+
const bracketMatch = {
|
|
2043
|
+
id: match.id,
|
|
2044
|
+
data: match,
|
|
2045
|
+
roundId: roundId,
|
|
2046
|
+
home: match.homeMatchSide.participant?.id || null,
|
|
2047
|
+
away: match.awayMatchSide.participant?.id || null,
|
|
2048
|
+
winner: match.winningSide,
|
|
2049
|
+
status: match.status === 'completed' ? 'completed' : 'pending',
|
|
2050
|
+
};
|
|
2051
|
+
bracketData.matches.push(bracketMatch);
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
return bracketData;
|
|
2055
|
+
};
|
|
1955
2056
|
|
|
1956
|
-
const path = (d, options) => `<path d="${d}" stroke="currentColor" fill="none" stroke-width="${options.width}" stroke-dasharray="${options.dashArray}" stroke-dashoffset="${options.dashOffset}" />`;
|
|
2057
|
+
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}" />`;
|
|
1957
2058
|
const curve = (from, to, direction, options) => {
|
|
1958
2059
|
const fromInline = from.inline.end;
|
|
1959
|
-
const fromBlock = from.block.center;
|
|
1960
2060
|
const toInline = to.inline.start;
|
|
1961
2061
|
const toBlock = to.block.center;
|
|
1962
|
-
const
|
|
2062
|
+
const fromBlock = from.block.center;
|
|
2063
|
+
const startingCurveAmount = options.lineStartingCurveAmount;
|
|
2064
|
+
const endingCurveAmount = options.lineEndingCurveAmount;
|
|
1963
2065
|
const totalSpace = toInline - fromInline;
|
|
1964
|
-
const totalSpaceMinusCurve = totalSpace -
|
|
2066
|
+
const totalSpaceMinusCurve = totalSpace - startingCurveAmount - endingCurveAmount;
|
|
1965
2067
|
const lineLength = totalSpaceMinusCurve / 2;
|
|
1966
2068
|
const straightLeftEnd = fromInline + lineLength;
|
|
1967
2069
|
const straightRightStart = toInline - lineLength;
|
|
1968
2070
|
// first 90 degree curve down
|
|
1969
2071
|
const firstCurveStartX = straightLeftEnd;
|
|
1970
|
-
const firstCurveEndX = straightLeftEnd +
|
|
1971
|
-
const firstCurveEndY = direction === 'down' ? fromBlock +
|
|
1972
|
-
const firstCurveBezierX = straightLeftEnd +
|
|
2072
|
+
const firstCurveEndX = straightLeftEnd + startingCurveAmount;
|
|
2073
|
+
const firstCurveEndY = direction === 'down' ? fromBlock + startingCurveAmount : fromBlock - startingCurveAmount;
|
|
2074
|
+
const firstCurveBezierX = straightLeftEnd + startingCurveAmount;
|
|
1973
2075
|
const firstCurveBezierY = fromBlock;
|
|
1974
2076
|
// second 90 degree curve to the right
|
|
1975
|
-
const secondCurveStartY = direction === 'down' ? toBlock -
|
|
2077
|
+
const secondCurveStartY = direction === 'down' ? toBlock - endingCurveAmount : toBlock + endingCurveAmount;
|
|
1976
2078
|
const secondCurveEndX = straightRightStart;
|
|
1977
2079
|
const secondCurveEndY = toBlock;
|
|
1978
|
-
const secondCurveBezierX = straightRightStart -
|
|
2080
|
+
const secondCurveBezierX = straightRightStart - endingCurveAmount;
|
|
1979
2081
|
const secondCurveBezierY = toBlock;
|
|
1980
2082
|
const pathStr = `
|
|
1981
2083
|
M ${fromInline} ${fromBlock}
|
|
@@ -1984,7 +2086,40 @@ const curve = (from, to, direction, options) => {
|
|
|
1984
2086
|
V ${secondCurveStartY}
|
|
1985
2087
|
Q ${secondCurveBezierX} ${secondCurveBezierY}, ${secondCurveEndX} ${secondCurveEndY}
|
|
1986
2088
|
H ${toInline}
|
|
1987
|
-
|
|
2089
|
+
`;
|
|
2090
|
+
return path(pathStr, options.path);
|
|
2091
|
+
};
|
|
2092
|
+
const curveInverted = (from, to, direction, options) => {
|
|
2093
|
+
const fromInline = from.inline.start;
|
|
2094
|
+
const fromBlock = from.block.center;
|
|
2095
|
+
const toInline = to.inline.end;
|
|
2096
|
+
const toBlock = to.block.center;
|
|
2097
|
+
const startingCurveAmount = options.lineStartingCurveAmount;
|
|
2098
|
+
const endingCurveAmount = options.lineEndingCurveAmount;
|
|
2099
|
+
const totalSpace = fromInline - toInline;
|
|
2100
|
+
const totalSpaceMinusCurve = totalSpace - startingCurveAmount - endingCurveAmount;
|
|
2101
|
+
const lineLength = totalSpaceMinusCurve / 2;
|
|
2102
|
+
const straightRightEnd = fromInline - lineLength;
|
|
2103
|
+
const straightLeftStart = toInline + lineLength;
|
|
2104
|
+
const firstCurveStartX = straightRightEnd;
|
|
2105
|
+
const firstCurveEndX = straightRightEnd - startingCurveAmount;
|
|
2106
|
+
const firstCurveEndY = direction === 'down' ? fromBlock + startingCurveAmount : fromBlock - startingCurveAmount;
|
|
2107
|
+
const firstCurveBezierX = straightRightEnd - startingCurveAmount;
|
|
2108
|
+
const firstCurveBezierY = fromBlock;
|
|
2109
|
+
// second 90 degree curve to the right
|
|
2110
|
+
const secondCurveStartY = direction === 'down' ? toBlock - endingCurveAmount : toBlock + endingCurveAmount;
|
|
2111
|
+
const secondCurveEndX = straightLeftStart;
|
|
2112
|
+
const secondCurveEndY = toBlock;
|
|
2113
|
+
const secondCurveBezierX = straightLeftStart + endingCurveAmount;
|
|
2114
|
+
const secondCurveBezierY = toBlock;
|
|
2115
|
+
const pathStr = `
|
|
2116
|
+
M ${fromInline} ${fromBlock}
|
|
2117
|
+
H ${firstCurveStartX}
|
|
2118
|
+
Q ${firstCurveBezierX} ${firstCurveBezierY}, ${firstCurveEndX} ${firstCurveEndY}
|
|
2119
|
+
V ${secondCurveStartY}
|
|
2120
|
+
Q ${secondCurveBezierX} ${secondCurveBezierY}, ${secondCurveEndX} ${secondCurveEndY}
|
|
2121
|
+
H ${toInline}
|
|
2122
|
+
`;
|
|
1988
2123
|
return path(pathStr, options.path);
|
|
1989
2124
|
};
|
|
1990
2125
|
const line = (from, to, options) => {
|
|
@@ -2062,10 +2197,14 @@ const drawMan = (items, firstRounds, dimensions) => {
|
|
|
2062
2197
|
matchHeight,
|
|
2063
2198
|
rowGap,
|
|
2064
2199
|
};
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2200
|
+
const currentMatchParticipantsShortIds = [
|
|
2201
|
+
item.matchRelation.currentMatch.home?.shortId,
|
|
2202
|
+
item.matchRelation.currentMatch.away?.shortId,
|
|
2203
|
+
]
|
|
2204
|
+
.filter((id) => !!id)
|
|
2205
|
+
.join(' ');
|
|
2206
|
+
const pathOptions = { ...dimensions.path, className: currentMatchParticipantsShortIds };
|
|
2207
|
+
const currentMatchPosition = getMatchPosition(round, item.matchRelation.currentMatch.id, staticMeasurements);
|
|
2069
2208
|
// We only draw the left side of the match relation
|
|
2070
2209
|
switch (item.matchRelation.type) {
|
|
2071
2210
|
case 'nothing-to-one': {
|
|
@@ -2073,31 +2212,36 @@ const drawMan = (items, firstRounds, dimensions) => {
|
|
|
2073
2212
|
}
|
|
2074
2213
|
case 'one-to-nothing':
|
|
2075
2214
|
case 'one-to-one': {
|
|
2076
|
-
const
|
|
2077
|
-
const current = getMatchPosition(round, item.matchRelation.currentMatch.id, staticMeasurements);
|
|
2215
|
+
const previousMatchPosition = getMatchPosition(items.get(item.matchRelation.previousRound.id), item.matchRelation.previousMatch.id, staticMeasurements);
|
|
2078
2216
|
// draw a straight line
|
|
2079
|
-
svgParts.push(line(
|
|
2080
|
-
break;
|
|
2081
|
-
}
|
|
2082
|
-
case 'one-to-two': {
|
|
2083
|
-
const nextUpper = getMatchPosition(items.get(item.matchRelation.nextUpperRound.id), item.matchRelation.nextUpperMatch.id, staticMeasurements);
|
|
2084
|
-
const nextLower = getMatchPosition(items.get(item.matchRelation.nextLowerRound.id), item.matchRelation.nextLowerMatch.id, staticMeasurements);
|
|
2085
|
-
const current = getMatchPosition(round, item.matchRelation.currentMatch.id, staticMeasurements);
|
|
2086
|
-
// const curveOptions: CurveOptions = { ...dimensions.curve, path: dimensions.path };
|
|
2087
|
-
// // draw two lines that merge into one in the middle
|
|
2088
|
-
// svgParts.push(curve(nextUpper, current, 'down', curveOptions));
|
|
2089
|
-
// svgParts.push(curve(nextLower, current, 'up', curveOptions));
|
|
2217
|
+
svgParts.push(line(previousMatchPosition, currentMatchPosition, { path: pathOptions }));
|
|
2090
2218
|
break;
|
|
2091
2219
|
}
|
|
2092
2220
|
case 'two-to-nothing':
|
|
2093
2221
|
case 'two-to-one': {
|
|
2094
2222
|
const previousUpper = getMatchPosition(items.get(item.matchRelation.previousUpperRound.id), item.matchRelation.previousUpperMatch.id, staticMeasurements);
|
|
2095
2223
|
const previousLower = getMatchPosition(items.get(item.matchRelation.previousLowerRound.id), item.matchRelation.previousLowerMatch.id, staticMeasurements);
|
|
2096
|
-
const
|
|
2097
|
-
|
|
2224
|
+
const curveOptions = {
|
|
2225
|
+
...dimensions.curve,
|
|
2226
|
+
path: { ...dimensions.path, className: '' },
|
|
2227
|
+
};
|
|
2228
|
+
const curveFn = item.roundRelation.currentRound.mirrorRoundType === BRACKET_ROUND_MIRROR_TYPE.RIGHT ? curveInverted : curve;
|
|
2098
2229
|
// draw two lines that merge into one in the middle
|
|
2099
|
-
svgParts.push(
|
|
2100
|
-
|
|
2230
|
+
svgParts.push(curveFn(previousUpper, currentMatchPosition, 'down', {
|
|
2231
|
+
...curveOptions,
|
|
2232
|
+
path: { ...curveOptions.path, className: item.matchRelation.previousUpperMatch.winner?.shortId || '' },
|
|
2233
|
+
}));
|
|
2234
|
+
svgParts.push(curveFn(previousLower, currentMatchPosition, 'up', {
|
|
2235
|
+
...curveOptions,
|
|
2236
|
+
path: { ...curveOptions.path, className: item.matchRelation.previousLowerMatch.winner?.shortId || '' },
|
|
2237
|
+
}));
|
|
2238
|
+
if (item.matchRelation.currentRound.mirrorRoundType === BRACKET_ROUND_MIRROR_TYPE.RIGHT &&
|
|
2239
|
+
item.matchRelation.type === 'two-to-one' &&
|
|
2240
|
+
item.matchRelation.nextRound.mirrorRoundType === null) {
|
|
2241
|
+
// draw a straight line for the special case of connecting the final match to the mirrored semi final match
|
|
2242
|
+
const next = getMatchPosition(items.get(item.matchRelation.nextRound.id), item.matchRelation.nextMatch.id, staticMeasurements);
|
|
2243
|
+
svgParts.push(line(next, currentMatchPosition, { path: pathOptions }));
|
|
2244
|
+
}
|
|
2101
2245
|
break;
|
|
2102
2246
|
}
|
|
2103
2247
|
}
|
|
@@ -2184,10 +2328,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImpo
|
|
|
2184
2328
|
const generateBracketGridItems = (bracketData, roundTypeMap, swissGroups, roundRelations, matchRelations, options) => {
|
|
2185
2329
|
const roundHeaderComponent = options.headerComponent ?? NewBracketDefaultRoundHeaderComponent;
|
|
2186
2330
|
const matchComponent = options.matchComponent ?? NewBracketDefaultMatchComponent;
|
|
2331
|
+
const finalMatchComponent = options.finalMatchComponent ?? matchComponent;
|
|
2187
2332
|
const optionsWithDefaults = {
|
|
2188
2333
|
...options,
|
|
2189
2334
|
headerComponent: roundHeaderComponent,
|
|
2190
2335
|
matchComponent: matchComponent,
|
|
2336
|
+
finalMatchComponent: finalMatchComponent,
|
|
2191
2337
|
};
|
|
2192
2338
|
switch (bracketData.mode) {
|
|
2193
2339
|
case TOURNAMENT_MODE.DOUBLE_ELIMINATION:
|
|
@@ -2234,6 +2380,7 @@ const generateGenericGridPlacements = (bracketData, roundTypeMap, roundRelations
|
|
|
2234
2380
|
rowStart: currentMatchRow,
|
|
2235
2381
|
rowEnd: ++currentMatchRow,
|
|
2236
2382
|
component: options.headerComponent,
|
|
2383
|
+
className: 'et-bracket-new-item et-bracket-round-header-container',
|
|
2237
2384
|
roundRelation,
|
|
2238
2385
|
data: {
|
|
2239
2386
|
bracketRound: round,
|
|
@@ -2249,15 +2396,17 @@ const generateGenericGridPlacements = (bracketData, roundTypeMap, roundRelations
|
|
|
2249
2396
|
throw new Error('Match relation is missing');
|
|
2250
2397
|
}
|
|
2251
2398
|
const baseRow = match.indexInRound * matchHeight;
|
|
2399
|
+
const participantShortIds = [match.home?.shortId, match.away?.shortId].filter((id) => !!id).join(' ');
|
|
2252
2400
|
const matchItem = {
|
|
2253
2401
|
type: 'match',
|
|
2254
2402
|
id: match.id,
|
|
2255
2403
|
layoutId: `${match.id}-layout`,
|
|
2256
2404
|
rowStart: baseRow + currentMatchRow,
|
|
2257
2405
|
rowEnd: baseRow + currentMatchRow + matchHeight,
|
|
2258
|
-
component: options.matchComponent,
|
|
2406
|
+
component: round.type === COMMON_BRACKET_ROUND_TYPE.FINAL ? options.finalMatchComponent : options.matchComponent,
|
|
2259
2407
|
matchRelation,
|
|
2260
2408
|
roundRelation,
|
|
2409
|
+
className: `et-bracket-new-item et-bracket-match-container ${participantShortIds}`,
|
|
2261
2410
|
data: {
|
|
2262
2411
|
bracketRound: round,
|
|
2263
2412
|
bracketMatch: match,
|
|
@@ -2278,23 +2427,47 @@ const generateSwissWithEliminationGridPlacements = (bracketData, roundTypeMap, s
|
|
|
2278
2427
|
return gridItems;
|
|
2279
2428
|
};
|
|
2280
2429
|
|
|
2430
|
+
const createJourneyHighlight = (bracketData) => {
|
|
2431
|
+
let styles = '';
|
|
2432
|
+
for (const participant of bracketData.participants.values()) {
|
|
2433
|
+
styles += `
|
|
2434
|
+
.et-new-bracket-host:has(.${participant.shortId}:hover) {
|
|
2435
|
+
path, .et-bracket-match-container {
|
|
2436
|
+
opacity: 0.5;
|
|
2437
|
+
}
|
|
2438
|
+
|
|
2439
|
+
.${participant.shortId} {
|
|
2440
|
+
opacity: 1 !important;
|
|
2441
|
+
}
|
|
2442
|
+
}
|
|
2443
|
+
`;
|
|
2444
|
+
}
|
|
2445
|
+
return styles.replace(/\s+/g, ' ').trim();
|
|
2446
|
+
};
|
|
2447
|
+
|
|
2281
2448
|
class NewBracketComponent {
|
|
2449
|
+
#domSanitizer;
|
|
2450
|
+
#elementId;
|
|
2282
2451
|
constructor() {
|
|
2283
2452
|
this.#domSanitizer = inject(DomSanitizer);
|
|
2453
|
+
this.#elementId = createComponentId('et-new-bracket');
|
|
2284
2454
|
this.source = input.required();
|
|
2285
2455
|
this.columnWidth = input(250, { transform: numberAttribute });
|
|
2286
2456
|
this.matchHeight = input(75, { transform: numberAttribute });
|
|
2287
2457
|
this.roundHeaderHeight = input(50, { transform: numberAttribute });
|
|
2288
2458
|
this.columnGap = input(60, { transform: numberAttribute });
|
|
2289
2459
|
this.rowGap = input(30, { transform: numberAttribute });
|
|
2290
|
-
this.
|
|
2460
|
+
this.lineStartingCurveAmount = input(10, { transform: numberAttribute });
|
|
2461
|
+
this.lineEndingCurveAmount = input(0, { transform: numberAttribute });
|
|
2291
2462
|
this.lineWidth = input(2, { transform: numberAttribute });
|
|
2292
2463
|
this.lineDashArray = input(0, { transform: numberAttribute });
|
|
2293
2464
|
this.lineDashOffset = input(0, { transform: numberAttribute });
|
|
2465
|
+
this.disableJourneyHighlight = input(false, { transform: booleanAttribute });
|
|
2294
2466
|
this.layout = input(BRACKET_DATA_LAYOUT.LEFT_TO_RIGHT);
|
|
2295
2467
|
this.hideRoundHeaders = input(false, { transform: booleanAttribute });
|
|
2296
2468
|
this.roundHeaderComponent = input();
|
|
2297
2469
|
this.matchComponent = input();
|
|
2470
|
+
this.finalMatchComponent = input();
|
|
2298
2471
|
this.bracketData = computed(() => generateBracketData(this.source(), { layout: this.layout() }));
|
|
2299
2472
|
this.roundTypeMap = computed(() => generateBracketRoundTypeMap(this.bracketData()));
|
|
2300
2473
|
this.matchParticipantMap = computed(() => generateMatchParticipantMap(this.bracketData()));
|
|
@@ -2306,6 +2479,7 @@ class NewBracketComponent {
|
|
|
2306
2479
|
includeRoundHeaders: !this.hideRoundHeaders(),
|
|
2307
2480
|
headerComponent: this.roundHeaderComponent(),
|
|
2308
2481
|
matchComponent: this.matchComponent(),
|
|
2482
|
+
finalMatchComponent: this.finalMatchComponent(),
|
|
2309
2483
|
}));
|
|
2310
2484
|
this.definitions = computed(() => generateBracketGridDefinitions(this.bracketData(), this.roundTypeMap(), {
|
|
2311
2485
|
includeRoundHeaders: !this.hideRoundHeaders(),
|
|
@@ -2319,7 +2493,8 @@ class NewBracketComponent {
|
|
|
2319
2493
|
rowGap: this.rowGap(),
|
|
2320
2494
|
gridDefinitions: this.definitions(),
|
|
2321
2495
|
curve: {
|
|
2322
|
-
|
|
2496
|
+
lineEndingCurveAmount: this.lineEndingCurveAmount(),
|
|
2497
|
+
lineStartingCurveAmount: this.lineStartingCurveAmount(),
|
|
2323
2498
|
},
|
|
2324
2499
|
path: {
|
|
2325
2500
|
dashArray: this.lineDashArray(),
|
|
@@ -2327,17 +2502,45 @@ class NewBracketComponent {
|
|
|
2327
2502
|
width: this.lineWidth(),
|
|
2328
2503
|
},
|
|
2329
2504
|
})));
|
|
2505
|
+
this.journeyHighlight = computed(() => this.disableJourneyHighlight() ? null : createJourneyHighlight(this.bracketData()));
|
|
2506
|
+
const isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
2507
|
+
if (!isBrowser)
|
|
2508
|
+
return;
|
|
2509
|
+
const renderer = inject(Renderer2);
|
|
2510
|
+
const styleId = `et-new-bracket-journey-highlight--${this.#elementId}`;
|
|
2511
|
+
let oldStyleEl = null;
|
|
2512
|
+
effect(() => {
|
|
2513
|
+
const newHighlightStyle = this.journeyHighlight();
|
|
2514
|
+
const head = document.head;
|
|
2515
|
+
if (oldStyleEl) {
|
|
2516
|
+
renderer.removeChild(head, oldStyleEl);
|
|
2517
|
+
}
|
|
2518
|
+
if (newHighlightStyle) {
|
|
2519
|
+
const el = renderer.createElement('style');
|
|
2520
|
+
renderer.setAttribute(el, 'id', styleId);
|
|
2521
|
+
renderer.appendChild(el, renderer.createText(newHighlightStyle));
|
|
2522
|
+
renderer.appendChild(head, el);
|
|
2523
|
+
oldStyleEl = el;
|
|
2524
|
+
}
|
|
2525
|
+
else {
|
|
2526
|
+
oldStyleEl = null;
|
|
2527
|
+
}
|
|
2528
|
+
});
|
|
2529
|
+
inject(DestroyRef).onDestroy(() => {
|
|
2530
|
+
if (oldStyleEl) {
|
|
2531
|
+
renderer.removeChild(document.head, oldStyleEl);
|
|
2532
|
+
}
|
|
2533
|
+
});
|
|
2330
2534
|
}
|
|
2331
|
-
#domSanitizer;
|
|
2332
2535
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: NewBracketComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2333
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", type: NewBracketComponent, isStandalone: true, selector: "et-new-bracket", inputs: { source: { classPropertyName: "source", publicName: "source", isSignal: true, isRequired: true, transformFunction: null }, columnWidth: { classPropertyName: "columnWidth", publicName: "columnWidth", isSignal: true, isRequired: false, transformFunction: null }, matchHeight: { classPropertyName: "matchHeight", publicName: "matchHeight", isSignal: true, isRequired: false, transformFunction: null }, roundHeaderHeight: { classPropertyName: "roundHeaderHeight", publicName: "roundHeaderHeight", isSignal: true, isRequired: false, transformFunction: null }, columnGap: { classPropertyName: "columnGap", publicName: "columnGap", isSignal: true, isRequired: false, transformFunction: null }, rowGap: { classPropertyName: "rowGap", publicName: "rowGap", isSignal: true, isRequired: false, transformFunction: null },
|
|
2536
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", type: NewBracketComponent, isStandalone: true, selector: "et-new-bracket", inputs: { source: { classPropertyName: "source", publicName: "source", isSignal: true, isRequired: true, transformFunction: null }, columnWidth: { classPropertyName: "columnWidth", publicName: "columnWidth", isSignal: true, isRequired: false, transformFunction: null }, matchHeight: { classPropertyName: "matchHeight", publicName: "matchHeight", isSignal: true, isRequired: false, transformFunction: null }, roundHeaderHeight: { classPropertyName: "roundHeaderHeight", publicName: "roundHeaderHeight", isSignal: true, isRequired: false, transformFunction: null }, columnGap: { classPropertyName: "columnGap", publicName: "columnGap", isSignal: true, isRequired: false, transformFunction: null }, rowGap: { classPropertyName: "rowGap", publicName: "rowGap", isSignal: true, isRequired: false, transformFunction: null }, lineStartingCurveAmount: { classPropertyName: "lineStartingCurveAmount", publicName: "lineStartingCurveAmount", isSignal: true, isRequired: false, transformFunction: null }, lineEndingCurveAmount: { classPropertyName: "lineEndingCurveAmount", publicName: "lineEndingCurveAmount", isSignal: true, isRequired: false, transformFunction: null }, lineWidth: { classPropertyName: "lineWidth", publicName: "lineWidth", isSignal: true, isRequired: false, transformFunction: null }, lineDashArray: { classPropertyName: "lineDashArray", publicName: "lineDashArray", isSignal: true, isRequired: false, transformFunction: null }, lineDashOffset: { classPropertyName: "lineDashOffset", publicName: "lineDashOffset", isSignal: true, isRequired: false, transformFunction: null }, disableJourneyHighlight: { classPropertyName: "disableJourneyHighlight", publicName: "disableJourneyHighlight", isSignal: true, isRequired: false, transformFunction: null }, layout: { classPropertyName: "layout", publicName: "layout", isSignal: true, isRequired: false, transformFunction: null }, hideRoundHeaders: { classPropertyName: "hideRoundHeaders", publicName: "hideRoundHeaders", isSignal: true, isRequired: false, transformFunction: null }, roundHeaderComponent: { classPropertyName: "roundHeaderComponent", publicName: "roundHeaderComponent", isSignal: true, isRequired: false, transformFunction: null }, matchComponent: { classPropertyName: "matchComponent", publicName: "matchComponent", isSignal: true, isRequired: false, transformFunction: null }, finalMatchComponent: { classPropertyName: "finalMatchComponent", publicName: "finalMatchComponent", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "et-new-bracket-host" }, ngImport: i0, template: "<section\n [style.--_cw.px]=\"columnWidth()\"\n [style.--_mh.px]=\"matchHeight()\"\n [style.--_rhh.px]=\"hideRoundHeaders() ? matchHeight() : roundHeaderHeight()\"\n [style.--_cg.px]=\"columnGap()\"\n [style.--_rg.px]=\"rowGap()\"\n [style.--_bcc]=\"definitions().columnCount\"\n [style.--_brc]=\"definitions().rowCount\"\n class=\"et-bracket-new\"\n>\n <svg [innerHTML]=\"drawManData()\" class=\"et-bracket-new-svg\" xmlns=\"http://www.w3.org/2000/svg\" />\n\n @for (round of items().values(); track round.layoutId) {\n <ul [style.--_c]=\"round.columnStart + ' / ' + round.columnEnd\" class=\"et-bracket-new-round\">\n @for (item of round.items.values(); track item.layoutId) {\n <li [style.--_r]=\"item.rowStart + ' / ' + item.rowEnd\" [class]=\"item.className\">\n <ng-container *ngComponentOutlet=\"item.component; inputs: item.data\" />\n </li>\n }\n </ul>\n }\n</section>\n", styles: [".et-bracket-new{display:grid;gap:var(--_cg);grid-auto-columns:var(--_cw);--bracket-line-color: red}.et-bracket-new-round{display:grid;grid-column:var(--_c);grid-row:1;list-style:none;margin:0;padding:0;gap:var(--_rg);grid-template-rows:var(--_rhh);grid-auto-rows:var(--_mh)}.et-bracket-new-item{grid-row:var(--_r);place-self:center;transition:opacity .2s}.et-bracket-new-svg{grid-column:1/calc(var(--_bcc) + 1);grid-row:1/2;inline-size:100%;block-size:100%}.et-bracket-new-svg path{color:var(--bracket-line-color);transition:opacity .2s}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
2334
2537
|
}
|
|
2335
2538
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: NewBracketComponent, decorators: [{
|
|
2336
2539
|
type: Component,
|
|
2337
2540
|
args: [{ selector: 'et-new-bracket', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
|
|
2338
2541
|
class: 'et-new-bracket-host',
|
|
2339
|
-
}, imports: [NgComponentOutlet], template: "<section\n [style.--_cw.px]=\"columnWidth()\"\n [style.--_mh.px]=\"matchHeight()\"\n [style.--_rhh.px]=\"roundHeaderHeight()\"\n [style.--_cg.px]=\"columnGap()\"\n [style.--_rg.px]=\"rowGap()\"\n [style.--_bcc]=\"definitions().columnCount\"\n [style.--_brc]=\"definitions().rowCount\"\n class=\"et-bracket-new\"\n>\n <svg [innerHTML]=\"drawManData()\" class=\"et-bracket-new-svg\" xmlns=\"http://www.w3.org/2000/svg\" />\n\n @for (round of items().values(); track round.layoutId) {\n <ul [style.--_c]=\"round.columnStart + ' / ' + round.columnEnd\" class=\"et-bracket-new-round\">\n @for (item of round.items.values(); track item.layoutId) {\n <li [style.--_r]=\"item.rowStart + ' / ' + item.rowEnd\" class=\"
|
|
2340
|
-
}] });
|
|
2542
|
+
}, imports: [NgComponentOutlet], template: "<section\n [style.--_cw.px]=\"columnWidth()\"\n [style.--_mh.px]=\"matchHeight()\"\n [style.--_rhh.px]=\"hideRoundHeaders() ? matchHeight() : roundHeaderHeight()\"\n [style.--_cg.px]=\"columnGap()\"\n [style.--_rg.px]=\"rowGap()\"\n [style.--_bcc]=\"definitions().columnCount\"\n [style.--_brc]=\"definitions().rowCount\"\n class=\"et-bracket-new\"\n>\n <svg [innerHTML]=\"drawManData()\" class=\"et-bracket-new-svg\" xmlns=\"http://www.w3.org/2000/svg\" />\n\n @for (round of items().values(); track round.layoutId) {\n <ul [style.--_c]=\"round.columnStart + ' / ' + round.columnEnd\" class=\"et-bracket-new-round\">\n @for (item of round.items.values(); track item.layoutId) {\n <li [style.--_r]=\"item.rowStart + ' / ' + item.rowEnd\" [class]=\"item.className\">\n <ng-container *ngComponentOutlet=\"item.component; inputs: item.data\" />\n </li>\n }\n </ul>\n }\n</section>\n", styles: [".et-bracket-new{display:grid;gap:var(--_cg);grid-auto-columns:var(--_cw);--bracket-line-color: red}.et-bracket-new-round{display:grid;grid-column:var(--_c);grid-row:1;list-style:none;margin:0;padding:0;gap:var(--_rg);grid-template-rows:var(--_rhh);grid-auto-rows:var(--_mh)}.et-bracket-new-item{grid-row:var(--_r);place-self:center;transition:opacity .2s}.et-bracket-new-svg{grid-column:1/calc(var(--_bcc) + 1);grid-row:1/2;inline-size:100%;block-size:100%}.et-bracket-new-svg path{color:var(--bracket-line-color);transition:opacity .2s}\n"] }]
|
|
2543
|
+
}], ctorParameters: () => [] });
|
|
2341
2544
|
|
|
2342
2545
|
var index = /*#__PURE__*/Object.freeze({
|
|
2343
2546
|
__proto__: null,
|
|
@@ -2358,6 +2561,7 @@ var index = /*#__PURE__*/Object.freeze({
|
|
|
2358
2561
|
drawMan: drawMan,
|
|
2359
2562
|
generateBracketData: generateBracketData,
|
|
2360
2563
|
generateBracketDataForEthlete: generateBracketDataForEthlete,
|
|
2564
|
+
generateBracketDataForGg: generateBracketDataForGg,
|
|
2361
2565
|
generateBracketGridDefinitions: generateBracketGridDefinitions,
|
|
2362
2566
|
generateBracketGridItems: generateBracketGridItems,
|
|
2363
2567
|
generateBracketRoundSwissGroupMaps: generateBracketRoundSwissGroupMaps,
|
|
@@ -2369,7 +2573,9 @@ var index = /*#__PURE__*/Object.freeze({
|
|
|
2369
2573
|
generateMatchRelations: generateMatchRelations,
|
|
2370
2574
|
generateRoundRelations: generateRoundRelations,
|
|
2371
2575
|
generateRoundTypeFromEthleteRoundType: generateRoundTypeFromEthleteRoundType,
|
|
2576
|
+
generateRoundTypeFromGgMatch: generateRoundTypeFromGgMatch,
|
|
2372
2577
|
generateTournamentModeFormEthleteRounds: generateTournamentModeFormEthleteRounds,
|
|
2578
|
+
generateTournamentModeFormGgData: generateTournamentModeFormGgData,
|
|
2373
2579
|
getAvailableSwissGroupsForRound: getAvailableSwissGroupsForRound,
|
|
2374
2580
|
getFirstRounds: getFirstRounds,
|
|
2375
2581
|
logRoundRelations: logRoundRelations
|