@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.
Files changed (29) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/esm2022/lib/components/bracket/components/new-bracket/bracket-new.mjs +170 -69
  3. package/esm2022/lib/components/bracket/components/new-bracket/draw-man.mjs +75 -32
  4. package/esm2022/lib/components/bracket/components/new-bracket/grid-placements.mjs +8 -3
  5. package/esm2022/lib/components/bracket/components/new-bracket/journey-highlight.mjs +18 -0
  6. package/esm2022/lib/components/bracket/components/new-bracket/new-bracket.component.mjs +47 -9
  7. package/esm2022/lib/components/bracket/stories/dummy-data/ET_DUMMY_DATA_DOUBLE_SYNC.mjs +1116 -0
  8. package/esm2022/lib/components/bracket/stories/dummy-data/ET_DUMMY_DATA_SINGLE.mjs +2691 -0
  9. package/esm2022/lib/components/bracket/stories/dummy-data/ET_DUMMY_DATA_SWISS.mjs +72479 -0
  10. package/esm2022/lib/components/bracket/stories/dummy-data/ET_DUMMY_DATE_DOUBLE_ASYNC.mjs +1275 -0
  11. package/esm2022/lib/components/bracket/stories/dummy-data/FIFA_DUMMY_DATA_DOUBLE.mjs +2453 -0
  12. package/esm2022/lib/components/bracket/stories/dummy-data/FIFA_DUMMY_DATA_SINGLE.mjs +2570 -0
  13. package/esm2022/lib/components/bracket/stories/dummy-data/index.mjs +7 -0
  14. package/fesm2022/ethlete-cdk.mjs +315 -109
  15. package/fesm2022/ethlete-cdk.mjs.map +1 -1
  16. package/lib/components/bracket/components/new-bracket/bracket-new.d.ts +1632 -18
  17. package/lib/components/bracket/components/new-bracket/draw-man.d.ts +5 -3
  18. package/lib/components/bracket/components/new-bracket/grid-placements.d.ts +4 -0
  19. package/lib/components/bracket/components/new-bracket/journey-highlight.d.ts +2 -0
  20. package/lib/components/bracket/components/new-bracket/new-bracket.component.d.ts +7 -2
  21. package/lib/components/bracket/stories/dummy-data/ET_DUMMY_DATA_DOUBLE_SYNC.d.ts +149 -0
  22. package/lib/components/bracket/stories/dummy-data/ET_DUMMY_DATA_SINGLE.d.ts +184 -0
  23. package/lib/components/bracket/stories/dummy-data/ET_DUMMY_DATA_SWISS.d.ts +214 -0
  24. package/lib/components/bracket/stories/dummy-data/ET_DUMMY_DATE_DOUBLE_ASYNC.d.ts +149 -0
  25. package/lib/components/bracket/stories/dummy-data/FIFA_DUMMY_DATA_DOUBLE.d.ts +1640 -0
  26. package/lib/components/bracket/stories/dummy-data/FIFA_DUMMY_DATA_SINGLE.d.ts +2388 -0
  27. package/lib/components/bracket/stories/dummy-data/index.d.ts +6 -0
  28. package/lib/components/query-error/directives/query-error/query-error.directive.d.ts +1 -1
  29. package/package.json +1 -1
@@ -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, effect, untracked, NgZone, NgModule, DestroyRef, isSignal, Attribute } from '@angular/core';
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, createComponentId, 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';
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
- return SINGLE_ELIMINATION_BRACKET_ROUND_TYPE.SINGLE_ELIMINATION_BRACKET;
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 previousUpperRound = upperRounds[currentUpperRoundIndex - 1] || null;
1168
- const nextUpperRound = upperRounds[currentUpperRoundIndex + 1] || null;
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 isFirstRound = currentUpperRoundIndex === 0;
1173
- const isLastUpperRound = currentUpperRoundIndex === upperRounds.length - 1;
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 { nextUpperRoundMatchPosition, nextLowerRoundMatchPosition, previousLowerRoundMatchPosition, previousUpperRoundMatchPosition, } = generateMatchRelationPositions(currentRelation, match);
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(nextUpperRoundMatchPosition);
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(nextUpperRoundMatchPosition);
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
- const isRightOne = nextUpperRoundMatchPosition === nextLowerRoundMatchPosition;
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 if (!isLeftOne) {
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(nextUpperRoundMatchPosition);
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
- nextUpperRoundMatchPosition: generateMatchPosition(match, currentRelation.nextRoundMatchFactor),
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
- nextUpperRoundMatchPosition: FALLBACK_MATCH_POSITION,
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
- nextUpperRoundMatchPosition: (generateMatchPosition(match, currentRelation.nextRoundMatchFactor) -
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
- nextLowerRoundMatchPosition: FALLBACK_MATCH_POSITION,
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
- nextLowerRoundMatchPosition: FALLBACK_MATCH_POSITION,
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.winner === matchParticipantMatch.side;
1617
- const isLooser = matchParticipantMatch.bracketMatch.winner &&
1618
- matchParticipantMatch.bracketMatch.winner !== matchParticipantMatch.side;
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: match.home,
1811
- away: match.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 participants = [bracketMatch.home, bracketMatch.away];
1818
- for (const [participantIndex, participant] of participants.entries()) {
1819
- if (!participant)
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 ? 'Homeside' : 'Awayside';
1822
- if (!bracketData.participants.has(participant)) {
1823
- bracketData.participants.set(participant, {
1824
- id: participant,
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(participant);
1813
+ const participantData = bracketData.participants.get(participantId);
1830
1814
  if (!participantData.matches.has(bracketMatch.id)) {
1831
1815
  participantData.matches.set(bracketMatch.id, {
1832
- side: bracketMatch.home === participant ? 'home' : 'away',
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 curveAmount = options.curveAmount;
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 - curveAmount * 2;
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 + curveAmount;
1971
- const firstCurveEndY = direction === 'down' ? fromBlock + curveAmount : fromBlock - curveAmount;
1972
- const firstCurveBezierX = straightLeftEnd + curveAmount;
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 - curveAmount : toBlock + curveAmount;
2077
+ const secondCurveStartY = direction === 'down' ? toBlock - endingCurveAmount : toBlock + endingCurveAmount;
1976
2078
  const secondCurveEndX = straightRightStart;
1977
2079
  const secondCurveEndY = toBlock;
1978
- const secondCurveBezierX = straightRightStart - curveAmount;
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
- `.replace(/\s+/g, ' ');
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
- // TODO: No lines for now
2066
- if (item.matchRelation.currentRound.mirrorRoundType === BRACKET_ROUND_MIRROR_TYPE.RIGHT) {
2067
- continue;
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 previous = getMatchPosition(items.get(item.matchRelation.previousRound.id), item.matchRelation.previousMatch.id, staticMeasurements);
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(previous, current, { path: dimensions.path }));
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 current = getMatchPosition(round, item.matchRelation.currentMatch.id, staticMeasurements);
2097
- const curveOptions = { ...dimensions.curve, path: dimensions.path };
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(curve(previousUpper, current, 'down', curveOptions));
2100
- svgParts.push(curve(previousLower, current, 'up', curveOptions));
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.curveAmount = input(20, { transform: numberAttribute });
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
- curveAmount: this.curveAmount(),
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 }, curveAmount: { classPropertyName: "curveAmount", publicName: "curveAmount", 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 }, 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 } }, host: { classAttribute: "et-new-bracket-host" }, ngImport: i0, 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=\"et-bracket-new-item\">\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}.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)}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
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=\"et-bracket-new-item\">\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}.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)}\n"] }]
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