@ethlete/cdk 4.61.1 → 4.61.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/fesm2022/ethlete-cdk.mjs +1439 -1084
- package/fesm2022/ethlete-cdk.mjs.map +1 -1
- package/index.d.ts +385 -461
- package/package.json +1 -1
package/fesm2022/ethlete-cdk.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, inject, TemplateRef, ViewContainerRef, Directive, input, booleanAttribute, computed, isDevMode, model, contentChild, effect, untracked, ChangeDetectionStrategy, ViewEncapsulation, Component, contentChildren, ElementRef, Injector, HostBinding, Input, numberAttribute,
|
|
2
|
+
import { InjectionToken, inject, TemplateRef, ViewContainerRef, Directive, input, booleanAttribute, computed, isDevMode, model, contentChild, effect, untracked, ChangeDetectionStrategy, ViewEncapsulation, Component, contentChildren, ElementRef, Injector, HostBinding, Input, numberAttribute, Renderer2, DOCUMENT, signal, viewChild, viewChildren, ContentChild, Injectable, ChangeDetectorRef, ViewChild, ContentChildren, assertInInjectionContext, forwardRef, runInInjectionContext, EventEmitter, Output, ViewChildren, Optional, Inject, NgZone, HostListener, SkipSelf, NgModule, DestroyRef, isSignal, IterableDiffers, ComponentFactoryResolver, HostAttributeToken } from '@angular/core';
|
|
3
3
|
import * as i2 from '@angular/cdk/portal';
|
|
4
4
|
import { CdkPortal, PortalModule, ComponentPortal, TemplatePortal, CdkPortalOutlet } from '@angular/cdk/portal';
|
|
5
5
|
import { DomSanitizer, Title } from '@angular/platform-browser';
|
|
@@ -7,7 +7,7 @@ import { trigger, state, transition, style, animate } from '@angular/animations'
|
|
|
7
7
|
import { toObservable, takeUntilDestroyed, toSignal, outputFromObservable } from '@angular/core/rxjs-interop';
|
|
8
8
|
import { map, switchMap, combineLatest, pairwise, tap, BehaviorSubject, takeUntil, skip, of, merge, timer, takeWhile, filter, fromEvent, Subject, Observable, startWith, debounceTime, withLatestFrom, distinctUntilChanged, take, skipUntil, skipWhile, catchError, throwError, defer, partition, from, finalize, Subscription } from 'rxjs';
|
|
9
9
|
import { __decorate, __metadata } from 'tslib';
|
|
10
|
-
import { AsyncPipe,
|
|
10
|
+
import { AsyncPipe, NgComponentOutlet, NgClass, NgTemplateOutlet, DOCUMENT as DOCUMENT$1, Location, NgStyle } from '@angular/common';
|
|
11
11
|
import * as i1 from '@ethlete/core';
|
|
12
12
|
import { Memo, createComponentId, signalHostAttributes, signalHostClasses, createDestroy, 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, AnimatedLifecycleDirective, ANIMATED_LIFECYCLE_TOKEN, ObserveContentDirective, clamp, DELAYABLE_TOKEN, ObserveResizeDirective, SmartBlockScrollStrategy, signalElementDimensions, signalElementScrollState, createIsRenderedSignal, createCanAnimateSignal, useCursorDragScroll, signalElementIntersection, signalElementChildren, getIntersectionInfo, getElementScrollCoordinates, ClickObserverService, RootBoundaryDirective, elementCanScroll, cloneFormGroup, getFormGroupValue, ViewportService, injectRoute, ROOT_BOUNDARY_TOKEN, injectQueryParam, RouterStateService, fromNextFrame, isElementVisible, AnimatedIfDirective, FocusVisibleService, inferMimeType, TypedQueryList } from '@ethlete/core';
|
|
13
13
|
import { extractQuery, isQueryStateSuccess, isQueryStateFailure, isQueryStateLoading, QueryDirective, queryComputed, switchQueryState, shouldRetryRequest, ExperimentalQuery, isClassValidatorError, isSymfonyFormViolationListError, isSymfonyListError } from '@ethlete/query';
|
|
@@ -238,13 +238,13 @@ class AccordionComponent {
|
|
|
238
238
|
this.isOpen.set(false);
|
|
239
239
|
}
|
|
240
240
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: AccordionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
241
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.3", type: AccordionComponent, isStandalone: true, selector: "et-accordion", inputs: { isOpenByDefault: { classPropertyName: "isOpenByDefault", publicName: "isOpenByDefault", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { isOpen: "isOpenChange" }, host: { classAttribute: "et-accordion" }, providers: [{ provide: ACCORDION_COMPONENT, useExisting: AccordionComponent }, provideIcons(CHEVRON_ICON)], queries: [{ propertyName: "templateLabel", first: true, predicate: ACCORDION_LABEL_WRAPPER_DIRECTIVE, descendants: true, isSignal: true }, { propertyName: "templateHint", first: true, predicate: ACCORDION_HINT_WRAPPER_DIRECTIVE, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"et-accordion-container\">\n <h3 class=\"et-accordion-header-wrapper\">\n <button\n [disabled]=\"disabled()\"\n [id]=\"HEADER_ID\"\n [attr.aria-controls]=\"BODY_ID\"\n [attr.aria-expanded]=\"isOpen()\"\n [class.et-accordion-has-hint]=\"!!templateHint\"\n (click)=\"toggleAccordionOpen()\"\n class=\"et-accordion-header\"\n type=\"button\"\n >\n <div>\n @if (templateLabel(); as templateLabel) {\n <ng-template [cdkPortalOutlet]=\"templateLabel\" />\n } @else {\n <span et-accordion-label>{{ label() }} </span>\n }\n </div>\n\n @if (templateHint(); as templateHint) {\n <ng-template [cdkPortalOutlet]=\"templateHint\" />\n }\n
|
|
241
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.3", type: AccordionComponent, isStandalone: true, selector: "et-accordion", inputs: { isOpenByDefault: { classPropertyName: "isOpenByDefault", publicName: "isOpenByDefault", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { isOpen: "isOpenChange" }, host: { classAttribute: "et-accordion" }, providers: [{ provide: ACCORDION_COMPONENT, useExisting: AccordionComponent }, provideIcons(CHEVRON_ICON)], queries: [{ propertyName: "templateLabel", first: true, predicate: ACCORDION_LABEL_WRAPPER_DIRECTIVE, descendants: true, isSignal: true }, { propertyName: "templateHint", first: true, predicate: ACCORDION_HINT_WRAPPER_DIRECTIVE, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"et-accordion-container\">\n <h3 class=\"et-accordion-header-wrapper\">\n <button\n [disabled]=\"disabled()\"\n [id]=\"HEADER_ID\"\n [attr.aria-controls]=\"BODY_ID\"\n [attr.aria-expanded]=\"isOpen()\"\n [class.et-accordion-has-hint]=\"!!templateHint()\"\n (click)=\"toggleAccordionOpen()\"\n class=\"et-accordion-header\"\n type=\"button\"\n >\n <div>\n @if (templateLabel(); as templateLabel) {\n <ng-template [cdkPortalOutlet]=\"templateLabel\" />\n } @else {\n <span et-accordion-label>{{ label() }} </span>\n }\n </div>\n\n @if (templateHint(); as templateHint) {\n <ng-template [cdkPortalOutlet]=\"templateHint\" />\n }\n <i etIcon=\"et-chevron\"> </i>\n </button>\n </h3>\n\n <div\n [@animateOpenClose]=\"isOpen() ? 'open' : 'close'\"\n [attr.aria-labelledby]=\"HEADER_ID\"\n [id]=\"BODY_ID\"\n class=\"et-accordion-body\"\n role=\"region\"\n >\n <div class=\"et-accordion-body-container\"><ng-content /></div>\n </div>\n\n <hr class=\"et-accordion-separator\" />\n</div>\n", styles: [":where(.et-accordion){--chevron-size: 15px}.et-accordion .et-accordion-container{position:relative;z-index:1}.et-accordion .et-icon--et-chevron{display:block;transform:rotate(180deg);transition:transform .3s var(--ease-5);inline-size:var(--chevron-size);block-size:var(--chevron-size)}.et-accordion [aria-expanded=true] .et-icon--et-chevron{transform:rotate(0)}.et-accordion .et-accordion-header{display:grid;grid-template-columns:1fr auto;inline-size:100%;block-size:100%;text-align:left;background-color:transparent;border:none;align-items:center;padding:0;gap:15px}.et-accordion .et-accordion-header:not(:disabled){cursor:pointer}.et-accordion .et-accordion-header.et-accordion-has-hint{grid-template-columns:1fr auto auto}.et-accordion .et-accordion-header-wrapper{margin:0}.et-accordion .et-accordion-body{overflow:hidden}.et-accordion .et-accordion-separator{position:relative;z-index:-1}\n"], dependencies: [{ kind: "directive", type: AccordionLabelDirective, selector: "[et-accordion-label]" }, { kind: "ngmodule", type: PortalModule }, { kind: "directive", type: i2.CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { kind: "directive", type: IconDirective, selector: "[etIcon]", inputs: ["etIcon", "allowHardcodedColor"] }], animations: [accordionAnimations.animateOpenClose], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
242
242
|
}
|
|
243
243
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: AccordionComponent, decorators: [{
|
|
244
244
|
type: Component,
|
|
245
245
|
args: [{ selector: 'et-accordion', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, providers: [{ provide: ACCORDION_COMPONENT, useExisting: AccordionComponent }, provideIcons(CHEVRON_ICON)], imports: [AccordionLabelDirective, PortalModule, IconDirective], animations: [accordionAnimations.animateOpenClose], host: {
|
|
246
246
|
class: 'et-accordion',
|
|
247
|
-
}, template: "<div class=\"et-accordion-container\">\n <h3 class=\"et-accordion-header-wrapper\">\n <button\n [disabled]=\"disabled()\"\n [id]=\"HEADER_ID\"\n [attr.aria-controls]=\"BODY_ID\"\n [attr.aria-expanded]=\"isOpen()\"\n [class.et-accordion-has-hint]=\"!!templateHint\"\n (click)=\"toggleAccordionOpen()\"\n class=\"et-accordion-header\"\n type=\"button\"\n >\n <div>\n @if (templateLabel(); as templateLabel) {\n <ng-template [cdkPortalOutlet]=\"templateLabel\" />\n } @else {\n <span et-accordion-label>{{ label() }} </span>\n }\n </div>\n\n @if (templateHint(); as templateHint) {\n <ng-template [cdkPortalOutlet]=\"templateHint\" />\n }\n
|
|
247
|
+
}, template: "<div class=\"et-accordion-container\">\n <h3 class=\"et-accordion-header-wrapper\">\n <button\n [disabled]=\"disabled()\"\n [id]=\"HEADER_ID\"\n [attr.aria-controls]=\"BODY_ID\"\n [attr.aria-expanded]=\"isOpen()\"\n [class.et-accordion-has-hint]=\"!!templateHint()\"\n (click)=\"toggleAccordionOpen()\"\n class=\"et-accordion-header\"\n type=\"button\"\n >\n <div>\n @if (templateLabel(); as templateLabel) {\n <ng-template [cdkPortalOutlet]=\"templateLabel\" />\n } @else {\n <span et-accordion-label>{{ label() }} </span>\n }\n </div>\n\n @if (templateHint(); as templateHint) {\n <ng-template [cdkPortalOutlet]=\"templateHint\" />\n }\n <i etIcon=\"et-chevron\"> </i>\n </button>\n </h3>\n\n <div\n [@animateOpenClose]=\"isOpen() ? 'open' : 'close'\"\n [attr.aria-labelledby]=\"HEADER_ID\"\n [id]=\"BODY_ID\"\n class=\"et-accordion-body\"\n role=\"region\"\n >\n <div class=\"et-accordion-body-container\"><ng-content /></div>\n </div>\n\n <hr class=\"et-accordion-separator\" />\n</div>\n", styles: [":where(.et-accordion){--chevron-size: 15px}.et-accordion .et-accordion-container{position:relative;z-index:1}.et-accordion .et-icon--et-chevron{display:block;transform:rotate(180deg);transition:transform .3s var(--ease-5);inline-size:var(--chevron-size);block-size:var(--chevron-size)}.et-accordion [aria-expanded=true] .et-icon--et-chevron{transform:rotate(0)}.et-accordion .et-accordion-header{display:grid;grid-template-columns:1fr auto;inline-size:100%;block-size:100%;text-align:left;background-color:transparent;border:none;align-items:center;padding:0;gap:15px}.et-accordion .et-accordion-header:not(:disabled){cursor:pointer}.et-accordion .et-accordion-header.et-accordion-has-hint{grid-template-columns:1fr auto auto}.et-accordion .et-accordion-header-wrapper{margin:0}.et-accordion .et-accordion-body{overflow:hidden}.et-accordion .et-accordion-separator{position:relative;z-index:-1}\n"] }]
|
|
248
248
|
}], ctorParameters: () => [] });
|
|
249
249
|
|
|
250
250
|
class AccordionGroupComponent {
|
|
@@ -1032,7 +1032,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.3", ngImpor
|
|
|
1032
1032
|
|
|
1033
1033
|
const BracketImports = [BracketComponent, BracketMatchComponent, BracketRoundHeaderComponent];
|
|
1034
1034
|
|
|
1035
|
-
const FALLBACK_MATCH_POSITION = -1;
|
|
1036
1035
|
const TOURNAMENT_MODE = {
|
|
1037
1036
|
SINGLE_ELIMINATION: 'single-elimination',
|
|
1038
1037
|
DOUBLE_ELIMINATION: 'double-elimination',
|
|
@@ -1040,6 +1039,45 @@ const TOURNAMENT_MODE = {
|
|
|
1040
1039
|
SWISS: 'swiss',
|
|
1041
1040
|
SWISS_WITH_ELIMINATION: 'swiss-with-elimination',
|
|
1042
1041
|
};
|
|
1042
|
+
|
|
1043
|
+
const canRenderLayoutInTournamentMode = (layout, mode) => {
|
|
1044
|
+
switch (mode) {
|
|
1045
|
+
case TOURNAMENT_MODE.SINGLE_ELIMINATION:
|
|
1046
|
+
return layout === BRACKET_DATA_LAYOUT.LEFT_TO_RIGHT || layout === BRACKET_DATA_LAYOUT.MIRRORED;
|
|
1047
|
+
default:
|
|
1048
|
+
return layout === BRACKET_DATA_LAYOUT.LEFT_TO_RIGHT;
|
|
1049
|
+
}
|
|
1050
|
+
};
|
|
1051
|
+
const BRACKET_DATA_LAYOUT = {
|
|
1052
|
+
LEFT_TO_RIGHT: 'left-to-right',
|
|
1053
|
+
// Currently only supported in single elimination brackets. Will throw an error if used in other bracket types
|
|
1054
|
+
MIRRORED: 'mirrored',
|
|
1055
|
+
};
|
|
1056
|
+
|
|
1057
|
+
class BracketMap extends Map {
|
|
1058
|
+
constructor() {
|
|
1059
|
+
super();
|
|
1060
|
+
}
|
|
1061
|
+
getOrThrow(key) {
|
|
1062
|
+
const value = super.get(key);
|
|
1063
|
+
if (value === undefined) {
|
|
1064
|
+
throw new Error(`Value for key ${key} not found in bracket map`);
|
|
1065
|
+
}
|
|
1066
|
+
return value;
|
|
1067
|
+
}
|
|
1068
|
+
first() {
|
|
1069
|
+
return this.values().next().value;
|
|
1070
|
+
}
|
|
1071
|
+
last() {
|
|
1072
|
+
const values = Array.from(this.values());
|
|
1073
|
+
return values.length > 0 ? values[values.length - 1] : undefined;
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
const BRACKET_ROUND_MIRROR_TYPE = {
|
|
1078
|
+
LEFT: 'left',
|
|
1079
|
+
RIGHT: 'right',
|
|
1080
|
+
};
|
|
1043
1081
|
const COMMON_BRACKET_ROUND_TYPE = {
|
|
1044
1082
|
THIRD_PLACE: 'third-place',
|
|
1045
1083
|
FINAL: 'final',
|
|
@@ -1058,10 +1096,283 @@ const SWISS_BRACKET_ROUND_TYPE = {
|
|
|
1058
1096
|
const GROUP_BRACKET_ROUND_TYPE = {
|
|
1059
1097
|
GROUP: 'group',
|
|
1060
1098
|
};
|
|
1061
|
-
const
|
|
1062
|
-
|
|
1063
|
-
|
|
1099
|
+
const createRoundsMapBase = (source, options) => {
|
|
1100
|
+
const map = new BracketMap();
|
|
1101
|
+
const shouldSplitRoundsInTwo = options.layout === BRACKET_DATA_LAYOUT.MIRRORED;
|
|
1102
|
+
const isDoubleElimination = source.mode === TOURNAMENT_MODE.DOUBLE_ELIMINATION;
|
|
1103
|
+
let currentUpperBracketIndex = 0;
|
|
1104
|
+
let currentLowerBracketIndex = 0;
|
|
1105
|
+
const splitRoundsRest = [];
|
|
1106
|
+
for (const [roundIndex, round] of source.rounds.entries()) {
|
|
1107
|
+
const isUpperBracket = round.type === DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.UPPER_BRACKET;
|
|
1108
|
+
const isLowerBracket = round.type === DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.LOWER_BRACKET;
|
|
1109
|
+
const isCommonDoubleEliminationRound = !isUpperBracket && !isLowerBracket && isDoubleElimination;
|
|
1110
|
+
const matches = source.matches.filter((m) => m.roundId === round.id);
|
|
1111
|
+
const roundId = round.id;
|
|
1112
|
+
const shouldSplitRound = shouldSplitRoundsInTwo && matches.length % 2 === 0;
|
|
1113
|
+
const isFirstRound = roundIndex === 0;
|
|
1114
|
+
const isLastRound = roundIndex === source.rounds.length - 1;
|
|
1115
|
+
const isFirstOfType = source.rounds.findIndex((r) => r.type === round.type) === roundIndex;
|
|
1116
|
+
const isLastOfType = source.rounds
|
|
1117
|
+
.slice()
|
|
1118
|
+
.reverse()
|
|
1119
|
+
.findIndex((r) => r.type === round.type) ===
|
|
1120
|
+
source.rounds.length - roundIndex - 1;
|
|
1121
|
+
if (shouldSplitRound) {
|
|
1122
|
+
const firstHalfRoundId = `${roundId}--half-1`;
|
|
1123
|
+
const secondHalfRoundId = `${roundId}--half-2`;
|
|
1124
|
+
const firstHalfMatchesMaxIndex = matches.length / 2 - 1;
|
|
1125
|
+
const bracketRoundFirstHalf = {
|
|
1126
|
+
type: round.type,
|
|
1127
|
+
id: firstHalfRoundId,
|
|
1128
|
+
logicalIndex: isLowerBracket || isCommonDoubleEliminationRound ? currentLowerBracketIndex : currentUpperBracketIndex,
|
|
1129
|
+
data: round.data,
|
|
1130
|
+
position: ((isLowerBracket || isCommonDoubleEliminationRound
|
|
1131
|
+
? currentLowerBracketIndex
|
|
1132
|
+
: currentUpperBracketIndex) + 1),
|
|
1133
|
+
name: round.name,
|
|
1134
|
+
matchCount: matches.length / 2,
|
|
1135
|
+
matchIds: matches.slice(0, firstHalfMatchesMaxIndex + 1).map((m) => m.id),
|
|
1136
|
+
mirrorRoundType: BRACKET_ROUND_MIRROR_TYPE.LEFT,
|
|
1137
|
+
isFirstRound,
|
|
1138
|
+
isLastRound,
|
|
1139
|
+
isFirstOfType,
|
|
1140
|
+
isLastOfType,
|
|
1141
|
+
};
|
|
1142
|
+
const bracketRoundSecondHalf = {
|
|
1143
|
+
type: round.type,
|
|
1144
|
+
id: secondHalfRoundId,
|
|
1145
|
+
logicalIndex: -1,
|
|
1146
|
+
data: round.data,
|
|
1147
|
+
position: -1,
|
|
1148
|
+
name: round.name,
|
|
1149
|
+
matchCount: matches.length / 2,
|
|
1150
|
+
matchIds: matches.slice(firstHalfMatchesMaxIndex + 1).map((m) => m.id),
|
|
1151
|
+
mirrorRoundType: BRACKET_ROUND_MIRROR_TYPE.RIGHT,
|
|
1152
|
+
isFirstRound,
|
|
1153
|
+
isLastRound,
|
|
1154
|
+
isFirstOfType,
|
|
1155
|
+
isLastOfType,
|
|
1156
|
+
};
|
|
1157
|
+
map.set(firstHalfRoundId, bracketRoundFirstHalf);
|
|
1158
|
+
splitRoundsRest.unshift(bracketRoundSecondHalf);
|
|
1159
|
+
}
|
|
1160
|
+
else {
|
|
1161
|
+
const bracketRound = {
|
|
1162
|
+
type: round.type,
|
|
1163
|
+
id: roundId,
|
|
1164
|
+
logicalIndex: isLowerBracket || isCommonDoubleEliminationRound ? currentLowerBracketIndex : currentUpperBracketIndex,
|
|
1165
|
+
data: round.data,
|
|
1166
|
+
position: ((isLowerBracket || isCommonDoubleEliminationRound
|
|
1167
|
+
? currentLowerBracketIndex
|
|
1168
|
+
: currentUpperBracketIndex) + 1),
|
|
1169
|
+
name: round.name,
|
|
1170
|
+
matchCount: matches.length,
|
|
1171
|
+
matchIds: matches.map((m) => m.id),
|
|
1172
|
+
mirrorRoundType: null,
|
|
1173
|
+
isFirstRound,
|
|
1174
|
+
isLastRound,
|
|
1175
|
+
isFirstOfType,
|
|
1176
|
+
isLastOfType,
|
|
1177
|
+
};
|
|
1178
|
+
map.set(roundId, bracketRound);
|
|
1179
|
+
}
|
|
1180
|
+
if (isLowerBracket || isCommonDoubleEliminationRound) {
|
|
1181
|
+
currentLowerBracketIndex++;
|
|
1182
|
+
}
|
|
1183
|
+
else {
|
|
1184
|
+
currentUpperBracketIndex++;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
if (splitRoundsRest.length) {
|
|
1188
|
+
const lastRound = map.last();
|
|
1189
|
+
if (!lastRound)
|
|
1190
|
+
throw new Error('Last round not found');
|
|
1191
|
+
for (const [splitRoundIndex, splitRound] of splitRoundsRest.entries()) {
|
|
1192
|
+
splitRound.logicalIndex = lastRound.logicalIndex + splitRoundIndex + 1;
|
|
1193
|
+
splitRound.position = (lastRound.position + splitRoundIndex + 1);
|
|
1194
|
+
map.set(splitRound.id, splitRound);
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
return map;
|
|
1198
|
+
};
|
|
1199
|
+
|
|
1200
|
+
const createNewMatchParticipantBase = (source, participantId, match, rounds, matchRoundId, participants) => {
|
|
1201
|
+
if (!participantId)
|
|
1202
|
+
return null;
|
|
1203
|
+
const participantBase = participants.getOrThrow(participantId);
|
|
1204
|
+
const roundBase = rounds.getOrThrow(matchRoundId);
|
|
1205
|
+
const matchIndex = participantBase.matchIds.indexOf(match.id);
|
|
1206
|
+
if (matchIndex === -1)
|
|
1207
|
+
throw new Error(`Match with id ${match.id} not found in participant with id ${participantId}`);
|
|
1208
|
+
const participantSide = match.home === participantId ? 'home' : 'away';
|
|
1209
|
+
const isWinner = match.winner === participantSide;
|
|
1210
|
+
const isLooser = match.winner && match.winner !== participantSide;
|
|
1211
|
+
const isTie = match.status === 'completed' && !match.winner;
|
|
1212
|
+
let winsTilNow = isWinner ? 1 : 0;
|
|
1213
|
+
let lossesTilNow = isLooser ? 1 : 0;
|
|
1214
|
+
let tiesTilNow = isTie ? 1 : 0;
|
|
1215
|
+
for (let i = 0; i < matchIndex; i++) {
|
|
1216
|
+
const previousMatchId = participantBase.matchIds[i];
|
|
1217
|
+
const previousMatch = source.matches.find((m) => m.id === previousMatchId);
|
|
1218
|
+
const myPreviousMatchSide = previousMatch?.home === participantId ? 'home' : previousMatch?.away === participantId ? 'away' : null;
|
|
1219
|
+
if (!previousMatch || !myPreviousMatchSide)
|
|
1220
|
+
continue;
|
|
1221
|
+
const previousIsWinner = previousMatch.winner === myPreviousMatchSide;
|
|
1222
|
+
const previousIsLooser = previousMatch.winner && previousMatch.winner !== myPreviousMatchSide;
|
|
1223
|
+
const previousIsTie = previousMatch.status === 'completed' && !previousMatch.winner;
|
|
1224
|
+
if (previousIsWinner) {
|
|
1225
|
+
winsTilNow++;
|
|
1226
|
+
}
|
|
1227
|
+
else if (previousIsLooser) {
|
|
1228
|
+
lossesTilNow++;
|
|
1229
|
+
}
|
|
1230
|
+
else if (previousIsTie) {
|
|
1231
|
+
tiesTilNow++;
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
const hasElimination = source.mode === TOURNAMENT_MODE.SINGLE_ELIMINATION ||
|
|
1235
|
+
source.mode === TOURNAMENT_MODE.DOUBLE_ELIMINATION ||
|
|
1236
|
+
source.mode === TOURNAMENT_MODE.SWISS_WITH_ELIMINATION;
|
|
1237
|
+
// Means the current match is loss and it's the last match of the participant
|
|
1238
|
+
let isEliminated = false;
|
|
1239
|
+
// Always true for single elimination, never for e.g. groups, depends on the round for double elimination, depends on the loss count for swiss with elimination
|
|
1240
|
+
let isEliminationMatch = false;
|
|
1241
|
+
if (hasElimination) {
|
|
1242
|
+
switch (source.mode) {
|
|
1243
|
+
case TOURNAMENT_MODE.SINGLE_ELIMINATION: {
|
|
1244
|
+
isEliminationMatch = true;
|
|
1245
|
+
isEliminated = isLooser ?? false;
|
|
1246
|
+
break;
|
|
1247
|
+
}
|
|
1248
|
+
case TOURNAMENT_MODE.DOUBLE_ELIMINATION: {
|
|
1249
|
+
isEliminationMatch =
|
|
1250
|
+
roundBase.type === DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.LOWER_BRACKET ||
|
|
1251
|
+
roundBase.type === DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.REVERSE_FINAL ||
|
|
1252
|
+
roundBase.type === COMMON_BRACKET_ROUND_TYPE.THIRD_PLACE;
|
|
1253
|
+
// Final is only an elimination match if there is no reverse final or there is one and this participant came from the lower bracket
|
|
1254
|
+
if (roundBase.type === COMMON_BRACKET_ROUND_TYPE.FINAL) {
|
|
1255
|
+
const hasReverseFinal = source.rounds.some((r) => r.type === DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.REVERSE_FINAL);
|
|
1256
|
+
if (!hasReverseFinal)
|
|
1257
|
+
break;
|
|
1258
|
+
const currentMatchIndex = participantBase.matchIds.indexOf(match.id);
|
|
1259
|
+
if (currentMatchIndex === -1)
|
|
1260
|
+
throw new Error(`Match with id ${match.id} not found in participant with id ${participantId}`);
|
|
1261
|
+
if (currentMatchIndex === 0)
|
|
1262
|
+
break;
|
|
1263
|
+
const previousMatchIndex = currentMatchIndex - 1;
|
|
1264
|
+
const previousMatchId = participantBase.matchIds[previousMatchIndex];
|
|
1265
|
+
const previousMatch = source.matches.find((m) => m.id === previousMatchId);
|
|
1266
|
+
if (!previousMatch)
|
|
1267
|
+
throw new Error(`Previous match with id ${previousMatchId} not found`);
|
|
1268
|
+
const previousRound = rounds.getOrThrow(previousMatch.roundId);
|
|
1269
|
+
isEliminationMatch = previousRound.type === DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.LOWER_BRACKET;
|
|
1270
|
+
}
|
|
1271
|
+
isEliminated = (isEliminationMatch && isLooser) ?? false;
|
|
1272
|
+
break;
|
|
1273
|
+
}
|
|
1274
|
+
case TOURNAMENT_MODE.SWISS_WITH_ELIMINATION: {
|
|
1275
|
+
// In swiss you are eliminated after 3 losses, so we need to track the loss count
|
|
1276
|
+
isEliminationMatch = lossesTilNow >= 2;
|
|
1277
|
+
isEliminated = (isEliminationMatch && isLooser) ?? false;
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
const matchParticipantBase = {
|
|
1282
|
+
...participantBase,
|
|
1283
|
+
isEliminated,
|
|
1284
|
+
isEliminationMatch,
|
|
1285
|
+
lossCount: lossesTilNow,
|
|
1286
|
+
tieCount: tiesTilNow,
|
|
1287
|
+
winCount: winsTilNow,
|
|
1288
|
+
result: isWinner ? 'win' : isLooser ? 'loss' : isTie ? 'tie' : null,
|
|
1289
|
+
side: participantSide,
|
|
1290
|
+
};
|
|
1291
|
+
return matchParticipantBase;
|
|
1292
|
+
};
|
|
1293
|
+
|
|
1294
|
+
const createMatchesMapBase = (source, rounds, participants) => {
|
|
1295
|
+
const map = new BracketMap();
|
|
1296
|
+
for (const match of source.matches) {
|
|
1297
|
+
const genericRound = rounds.get(match.roundId);
|
|
1298
|
+
const splitRound = rounds.get(`${match.roundId}--half-1`);
|
|
1299
|
+
const splitRound2 = rounds.get(`${match.roundId}--half-2`);
|
|
1300
|
+
const genericRoundMatchIndex = genericRound?.matchIds.indexOf(match.id) ?? -1;
|
|
1301
|
+
const splitRoundMatchIndex = splitRound?.matchIds.indexOf(match.id) ?? -1;
|
|
1302
|
+
const splitRound2MatchIndex = splitRound2?.matchIds.indexOf(match.id) ?? -1;
|
|
1303
|
+
const roundToUse = genericRoundMatchIndex !== -1
|
|
1304
|
+
? genericRound
|
|
1305
|
+
: splitRoundMatchIndex !== -1
|
|
1306
|
+
? splitRound
|
|
1307
|
+
: splitRound2MatchIndex !== -1
|
|
1308
|
+
? splitRound2
|
|
1309
|
+
: null;
|
|
1310
|
+
if (!roundToUse)
|
|
1311
|
+
throw new Error(`Round for match with id ${match.id} not found`);
|
|
1312
|
+
const indexInRound = genericRoundMatchIndex !== -1
|
|
1313
|
+
? genericRoundMatchIndex
|
|
1314
|
+
: splitRoundMatchIndex !== -1
|
|
1315
|
+
? splitRoundMatchIndex
|
|
1316
|
+
: splitRound2MatchIndex !== -1
|
|
1317
|
+
? splitRound2MatchIndex
|
|
1318
|
+
: -1;
|
|
1319
|
+
if (indexInRound === -1)
|
|
1320
|
+
throw new Error(`Match with id ${match.id} not found in round with id ${roundToUse.id}`);
|
|
1321
|
+
const home = createNewMatchParticipantBase(source, match.home, match, rounds, roundToUse.id, participants);
|
|
1322
|
+
const away = createNewMatchParticipantBase(source, match.away, match, rounds, roundToUse.id, participants);
|
|
1323
|
+
const winner = match.winner === 'home' ? home : match.winner === 'away' ? away : null;
|
|
1324
|
+
const matchBase = {
|
|
1325
|
+
home,
|
|
1326
|
+
away,
|
|
1327
|
+
data: match.data,
|
|
1328
|
+
id: match.id,
|
|
1329
|
+
indexInRound,
|
|
1330
|
+
position: (indexInRound + 1),
|
|
1331
|
+
roundId: roundToUse?.id,
|
|
1332
|
+
status: match.status,
|
|
1333
|
+
winner,
|
|
1334
|
+
winnerSide: match.winner,
|
|
1335
|
+
};
|
|
1336
|
+
map.set(matchBase.id, matchBase);
|
|
1337
|
+
}
|
|
1338
|
+
return map;
|
|
1339
|
+
};
|
|
1340
|
+
|
|
1341
|
+
const createParticipantsMapBase = (source) => {
|
|
1342
|
+
const map = new BracketMap();
|
|
1343
|
+
const participantIds = new Set(source.matches
|
|
1344
|
+
.map((m) => [m.home, m.away])
|
|
1345
|
+
.flat()
|
|
1346
|
+
.filter((p) => !!p));
|
|
1347
|
+
for (const [index, participantId] of participantIds.entries()) {
|
|
1348
|
+
const participantBase = {
|
|
1349
|
+
id: participantId,
|
|
1350
|
+
shortId: `p${index}`,
|
|
1351
|
+
matchIds: source.matches
|
|
1352
|
+
.filter((m) => m.home === participantId || m.away === participantId)
|
|
1353
|
+
.map((m) => m.id),
|
|
1354
|
+
};
|
|
1355
|
+
map.set(participantId, participantBase);
|
|
1356
|
+
}
|
|
1357
|
+
return map;
|
|
1358
|
+
};
|
|
1359
|
+
|
|
1360
|
+
const createNewBracketBase = (source, options) => {
|
|
1361
|
+
if (!canRenderLayoutInTournamentMode(options.layout, source.mode)) {
|
|
1362
|
+
throw new Error(`Cannot render layout ${options.layout} in mode ${source.mode}`);
|
|
1363
|
+
}
|
|
1364
|
+
const participants = createParticipantsMapBase(source);
|
|
1365
|
+
const rounds = createRoundsMapBase(source, options);
|
|
1366
|
+
const matches = createMatchesMapBase(source, rounds, participants);
|
|
1367
|
+
const bracketData = {
|
|
1368
|
+
matches,
|
|
1369
|
+
rounds,
|
|
1370
|
+
participants,
|
|
1371
|
+
mode: source.mode,
|
|
1372
|
+
};
|
|
1373
|
+
return bracketData;
|
|
1064
1374
|
};
|
|
1375
|
+
|
|
1065
1376
|
const generateRoundTypeFromEthleteRoundType = (type, tournamentMode, roundMatchCount) => {
|
|
1066
1377
|
switch (type) {
|
|
1067
1378
|
case 'normal':
|
|
@@ -1122,1029 +1433,976 @@ const generateTournamentModeFormEthleteRounds = (source) => {
|
|
|
1122
1433
|
throw new Error(`Unsupported tournament mode: ${firstMatch.matchType}`);
|
|
1123
1434
|
}
|
|
1124
1435
|
};
|
|
1125
|
-
const
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
const getFirstRounds = (bracketData, roundTypeMap) => {
|
|
1143
|
-
if (bracketData.mode === TOURNAMENT_MODE.DOUBLE_ELIMINATION) {
|
|
1144
|
-
const upper = roundTypeMap.get(DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.UPPER_BRACKET)?.values().next().value;
|
|
1145
|
-
const lower = roundTypeMap.get(DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.LOWER_BRACKET)?.values().next().value;
|
|
1146
|
-
if (!upper || !lower)
|
|
1147
|
-
throw new Error('Upper or lower bracket is null');
|
|
1148
|
-
return {
|
|
1149
|
-
type: FIRST_ROUNDS_TYPE.DOUBLE,
|
|
1150
|
-
upper,
|
|
1151
|
-
lower,
|
|
1436
|
+
const generateBracketDataForEthlete = (source) => {
|
|
1437
|
+
const tournamentMode = generateTournamentModeFormEthleteRounds(source);
|
|
1438
|
+
const bracketData = {
|
|
1439
|
+
rounds: [],
|
|
1440
|
+
matches: [],
|
|
1441
|
+
mode: tournamentMode,
|
|
1442
|
+
};
|
|
1443
|
+
for (const currentItem of source) {
|
|
1444
|
+
if (bracketData.rounds.some((r) => r.id === currentItem.round.id)) {
|
|
1445
|
+
throw new Error(`Round with id ${currentItem.round.id} already exists in the bracket data.`);
|
|
1446
|
+
}
|
|
1447
|
+
const roundType = generateRoundTypeFromEthleteRoundType(currentItem.round.type, tournamentMode, currentItem.matches.length);
|
|
1448
|
+
const bracketRound = {
|
|
1449
|
+
type: roundType,
|
|
1450
|
+
id: currentItem.round.id,
|
|
1451
|
+
data: currentItem.round,
|
|
1452
|
+
name: currentItem.round.name || currentItem.round.type,
|
|
1152
1453
|
};
|
|
1454
|
+
bracketData.rounds.push(bracketRound);
|
|
1455
|
+
for (const match of currentItem.matches) {
|
|
1456
|
+
if (bracketData.matches.some((m) => m.id === match.id)) {
|
|
1457
|
+
throw new Error(`Match with id ${match.id} already exists in the bracket data.`);
|
|
1458
|
+
}
|
|
1459
|
+
const bracketMatch = {
|
|
1460
|
+
id: match.id,
|
|
1461
|
+
data: match,
|
|
1462
|
+
roundId: currentItem.round.id,
|
|
1463
|
+
home: match.home?.id || null,
|
|
1464
|
+
away: match.away?.id || null,
|
|
1465
|
+
winner: match.winningSide,
|
|
1466
|
+
status: match.status === 'published' ? 'completed' : 'pending',
|
|
1467
|
+
};
|
|
1468
|
+
bracketData.matches.push(bracketMatch);
|
|
1469
|
+
}
|
|
1153
1470
|
}
|
|
1154
|
-
|
|
1155
|
-
if (!first)
|
|
1156
|
-
throw new Error('First round is null');
|
|
1157
|
-
return {
|
|
1158
|
-
type: FIRST_ROUNDS_TYPE.SINGLE,
|
|
1159
|
-
first,
|
|
1160
|
-
};
|
|
1471
|
+
return bracketData;
|
|
1161
1472
|
};
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1473
|
+
|
|
1474
|
+
const generateTournamentModeFormGgData = (source) => {
|
|
1475
|
+
switch (source.mode) {
|
|
1476
|
+
// case 'groups':
|
|
1477
|
+
// return TOURNAMENT_MODE.GROUP;
|
|
1478
|
+
// case 'fifa_swiss': {
|
|
1479
|
+
// const lastRound = source[source.length - 1];
|
|
1480
|
+
// if (!lastRound) throw new Error('No last round found');
|
|
1481
|
+
// if (lastRound.matches.length !== firstRound.matches.length) {
|
|
1482
|
+
// return TOURNAMENT_MODE.SWISS_WITH_ELIMINATION;
|
|
1483
|
+
// } else {
|
|
1484
|
+
// return TOURNAMENT_MODE.SWISS;
|
|
1485
|
+
// }
|
|
1486
|
+
// }
|
|
1487
|
+
case 'double-elimination':
|
|
1488
|
+
return TOURNAMENT_MODE.DOUBLE_ELIMINATION;
|
|
1489
|
+
case 'single-elimination':
|
|
1490
|
+
return TOURNAMENT_MODE.SINGLE_ELIMINATION;
|
|
1491
|
+
default:
|
|
1492
|
+
throw new Error(`Unsupported tournament mode: ${source.mode}`);
|
|
1167
1493
|
}
|
|
1168
|
-
return matchPositionMaps;
|
|
1169
1494
|
};
|
|
1170
|
-
const
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1495
|
+
const generateRoundTypeFromGgMatch = (tournamentMode, bracketType, // 'winner' | 'looser' | null,
|
|
1496
|
+
stageNumber, matchCount) => {
|
|
1497
|
+
switch (tournamentMode) {
|
|
1498
|
+
case 'double-elimination': {
|
|
1499
|
+
switch (stageNumber) {
|
|
1500
|
+
case 1: {
|
|
1501
|
+
switch (bracketType) {
|
|
1502
|
+
case 'winner':
|
|
1503
|
+
return DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.UPPER_BRACKET;
|
|
1504
|
+
case 'looser':
|
|
1505
|
+
return DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.LOWER_BRACKET;
|
|
1506
|
+
default:
|
|
1507
|
+
throw new Error(`Unsupported bracket type: ${bracketType}`);
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
case 2: {
|
|
1511
|
+
return COMMON_BRACKET_ROUND_TYPE.FINAL;
|
|
1512
|
+
}
|
|
1513
|
+
case 3: {
|
|
1514
|
+
return DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.REVERSE_FINAL;
|
|
1515
|
+
}
|
|
1516
|
+
default: {
|
|
1517
|
+
throw new Error(`Unsupported stage number: ${stageNumber}`);
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1179
1520
|
}
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
const generateRoundRelations = (bracketData) => {
|
|
1184
|
-
const roundRelations = new Map();
|
|
1185
|
-
const allRounds = bracketData.roundIds.map((id) => bracketData.rounds.get(id)).filter((r) => !!r);
|
|
1186
|
-
const upperRounds = allRounds.filter((r) => r.type !== DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.LOWER_BRACKET);
|
|
1187
|
-
const lowerRounds = allRounds.filter((r) => r.type === DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.LOWER_BRACKET);
|
|
1188
|
-
const firstUpperRound = upperRounds[0] || null;
|
|
1189
|
-
const firstLowerRound = lowerRounds[0] || null;
|
|
1190
|
-
if (!firstUpperRound)
|
|
1191
|
-
throw new Error('firstUpperRound is null');
|
|
1192
|
-
const hasLowerRounds = lowerRounds.length > 0;
|
|
1193
|
-
const lastLowerRound = lowerRounds[lowerRounds.length - 1] || null;
|
|
1194
|
-
for (const [currentUpperRoundIndex, currentUpperRound] of upperRounds.entries()) {
|
|
1195
|
-
const isLeftToRight = currentUpperRound.mirrorRoundType === null ||
|
|
1196
|
-
currentUpperRound.mirrorRoundType === BRACKET_ROUND_MIRROR_TYPE.LEFT;
|
|
1197
|
-
const relativePreviousUpperRound = upperRounds[currentUpperRoundIndex - 1] || null;
|
|
1198
|
-
const relativeNextUpperRound = upperRounds[currentUpperRoundIndex + 1] || null;
|
|
1199
|
-
const previousUpperRound = isLeftToRight ? relativePreviousUpperRound : relativeNextUpperRound;
|
|
1200
|
-
const nextUpperRound = isLeftToRight ? relativeNextUpperRound : relativePreviousUpperRound;
|
|
1201
|
-
const currentLowerRound = lowerRounds[currentUpperRoundIndex] || null;
|
|
1202
|
-
const previousLowerRound = lowerRounds[currentUpperRoundIndex - 1] || null;
|
|
1203
|
-
const nextLowerRound = lowerRounds[currentUpperRoundIndex + 1] || null;
|
|
1204
|
-
const isLastUpperRound = !nextUpperRound ||
|
|
1205
|
-
(nextUpperRound.mirrorRoundType === BRACKET_ROUND_MIRROR_TYPE.RIGHT &&
|
|
1206
|
-
currentUpperRound.mirrorRoundType === null);
|
|
1207
|
-
const isFinal = currentUpperRound.type === COMMON_BRACKET_ROUND_TYPE.FINAL;
|
|
1208
|
-
if (isFinal && hasLowerRounds) {
|
|
1209
|
-
// two to one relation
|
|
1210
|
-
if (!lastLowerRound || !currentLowerRound || !previousUpperRound)
|
|
1211
|
-
throw new Error('lastLowerRound or currentLowerRound or previousUpperRound is null');
|
|
1212
|
-
// in a sync double elimination bracket, the final round has the same index as the last lower round
|
|
1213
|
-
// in an async one, there is always one more round in the lower bracket since we only display a section of the whole tournament
|
|
1214
|
-
const isAsyncBracket = currentLowerRound.id !== lastLowerRound.id;
|
|
1215
|
-
const finalLowerRound = isAsyncBracket ? nextLowerRound : currentLowerRound;
|
|
1216
|
-
if (!finalLowerRound)
|
|
1217
|
-
throw new Error('finalLowerRound is null');
|
|
1218
|
-
if (!firstLowerRound)
|
|
1219
|
-
throw new Error('firstLowerRound is null');
|
|
1220
|
-
if (finalLowerRound.id !== lastLowerRound.id)
|
|
1221
|
-
throw new Error('finalLowerRound is not the last lower round');
|
|
1222
|
-
// if we have a reverse final
|
|
1223
|
-
if (nextUpperRound) {
|
|
1224
|
-
// for the final
|
|
1225
|
-
roundRelations.set(currentUpperRound.id, {
|
|
1226
|
-
type: 'two-to-one',
|
|
1227
|
-
previousUpperRound: previousUpperRound,
|
|
1228
|
-
previousLowerRound: finalLowerRound,
|
|
1229
|
-
nextRound: nextUpperRound,
|
|
1230
|
-
currentRound: currentUpperRound,
|
|
1231
|
-
nextRoundMatchFactor: nextUpperRound.matchCount / currentUpperRound.matchCount,
|
|
1232
|
-
previousUpperRoundMatchFactor: previousUpperRound.matchCount / currentUpperRound.matchCount,
|
|
1233
|
-
previousLowerRoundMatchFactor: finalLowerRound.matchCount / currentUpperRound.matchCount,
|
|
1234
|
-
upperRootRoundMatchFactor: firstUpperRound.matchCount / currentUpperRound.matchCount,
|
|
1235
|
-
lowerRootRoundMatchFactor: firstLowerRound.matchCount / currentUpperRound.matchCount,
|
|
1236
|
-
});
|
|
1521
|
+
case 'single-elimination': {
|
|
1522
|
+
if (matchCount === 1) {
|
|
1523
|
+
return COMMON_BRACKET_ROUND_TYPE.FINAL;
|
|
1237
1524
|
}
|
|
1238
1525
|
else {
|
|
1239
|
-
|
|
1240
|
-
// for the final
|
|
1241
|
-
roundRelations.set(currentUpperRound.id, {
|
|
1242
|
-
type: 'two-to-nothing',
|
|
1243
|
-
previousUpperRound: previousUpperRound,
|
|
1244
|
-
previousLowerRound: finalLowerRound,
|
|
1245
|
-
currentRound: currentUpperRound,
|
|
1246
|
-
previousLowerRoundMatchFactor: finalLowerRound.matchCount / currentUpperRound.matchCount,
|
|
1247
|
-
previousUpperRoundMatchFactor: previousUpperRound.matchCount / currentUpperRound.matchCount,
|
|
1248
|
-
lowerRootRoundMatchFactor: firstLowerRound.matchCount / currentUpperRound.matchCount,
|
|
1249
|
-
upperRootRoundMatchFactor: firstUpperRound.matchCount / currentUpperRound.matchCount,
|
|
1250
|
-
});
|
|
1251
|
-
}
|
|
1252
|
-
if (isAsyncBracket) {
|
|
1253
|
-
// if this is an async bracket, we need to set the relations for the 2 last lower rounds since they will be skipped by the default one to one logic
|
|
1254
|
-
const preFinalLowerRound = lowerRounds[lowerRounds.length - 2] || null;
|
|
1255
|
-
const prePreFinalLowerRound = lowerRounds[lowerRounds.length - 3] || null;
|
|
1256
|
-
if (!preFinalLowerRound)
|
|
1257
|
-
throw new Error('preFinalLowerRound is null');
|
|
1258
|
-
if (!firstLowerRound)
|
|
1259
|
-
throw new Error('firstLowerRound is null');
|
|
1260
|
-
// for the last lower round
|
|
1261
|
-
roundRelations.set(finalLowerRound.id, {
|
|
1262
|
-
type: 'one-to-one',
|
|
1263
|
-
previousRound: preFinalLowerRound,
|
|
1264
|
-
nextRound: currentUpperRound,
|
|
1265
|
-
currentRound: finalLowerRound,
|
|
1266
|
-
nextRoundMatchFactor: currentUpperRound.matchCount / finalLowerRound.matchCount,
|
|
1267
|
-
previousRoundMatchFactor: preFinalLowerRound.matchCount / finalLowerRound.matchCount,
|
|
1268
|
-
rootRoundMatchFactor: firstLowerRound.matchCount / finalLowerRound.matchCount,
|
|
1269
|
-
});
|
|
1270
|
-
if (prePreFinalLowerRound) {
|
|
1271
|
-
// for the pre final lower round
|
|
1272
|
-
roundRelations.set(preFinalLowerRound.id, {
|
|
1273
|
-
type: 'one-to-one',
|
|
1274
|
-
previousRound: prePreFinalLowerRound,
|
|
1275
|
-
nextRound: finalLowerRound,
|
|
1276
|
-
currentRound: preFinalLowerRound,
|
|
1277
|
-
nextRoundMatchFactor: finalLowerRound.matchCount / preFinalLowerRound.matchCount,
|
|
1278
|
-
previousRoundMatchFactor: prePreFinalLowerRound.matchCount / preFinalLowerRound.matchCount,
|
|
1279
|
-
rootRoundMatchFactor: firstLowerRound.matchCount / preFinalLowerRound.matchCount,
|
|
1280
|
-
});
|
|
1281
|
-
}
|
|
1282
|
-
else {
|
|
1283
|
-
// for the first lower round
|
|
1284
|
-
roundRelations.set(preFinalLowerRound.id, {
|
|
1285
|
-
type: 'nothing-to-one',
|
|
1286
|
-
nextRound: finalLowerRound,
|
|
1287
|
-
currentRound: preFinalLowerRound,
|
|
1288
|
-
nextRoundMatchFactor: finalLowerRound.matchCount / preFinalLowerRound.matchCount,
|
|
1289
|
-
});
|
|
1290
|
-
}
|
|
1291
|
-
}
|
|
1292
|
-
else {
|
|
1293
|
-
// this is a sync bracket, we only need to set the relation for the last lower round
|
|
1294
|
-
if (!previousLowerRound)
|
|
1295
|
-
throw new Error('previousLowerRound is null');
|
|
1296
|
-
if (!firstLowerRound)
|
|
1297
|
-
throw new Error('firstLowerRound is null');
|
|
1298
|
-
// for the last lower round
|
|
1299
|
-
roundRelations.set(finalLowerRound.id, {
|
|
1300
|
-
type: 'one-to-one',
|
|
1301
|
-
previousRound: previousLowerRound,
|
|
1302
|
-
nextRound: currentUpperRound,
|
|
1303
|
-
currentRound: finalLowerRound,
|
|
1304
|
-
nextRoundMatchFactor: currentUpperRound.matchCount / finalLowerRound.matchCount,
|
|
1305
|
-
previousRoundMatchFactor: previousLowerRound.matchCount / finalLowerRound.matchCount,
|
|
1306
|
-
rootRoundMatchFactor: firstLowerRound.matchCount / finalLowerRound.matchCount,
|
|
1307
|
-
});
|
|
1308
|
-
}
|
|
1309
|
-
}
|
|
1310
|
-
else if (isLastUpperRound) {
|
|
1311
|
-
// one to nothing relation
|
|
1312
|
-
if (!previousUpperRound)
|
|
1313
|
-
throw new Error('previousUpperRound is null');
|
|
1314
|
-
roundRelations.set(currentUpperRound.id, {
|
|
1315
|
-
type: 'one-to-nothing',
|
|
1316
|
-
previousRound: previousUpperRound,
|
|
1317
|
-
currentRound: currentUpperRound,
|
|
1318
|
-
previousRoundMatchFactor: previousUpperRound.matchCount / currentUpperRound.matchCount,
|
|
1319
|
-
rootRoundMatchFactor: firstUpperRound.matchCount / currentUpperRound.matchCount,
|
|
1320
|
-
});
|
|
1321
|
-
}
|
|
1322
|
-
else if (currentUpperRound.isFirstRound) {
|
|
1323
|
-
// nothing to one relation
|
|
1324
|
-
if (!nextUpperRound)
|
|
1325
|
-
throw new Error('nextUpperRound is null');
|
|
1326
|
-
roundRelations.set(currentUpperRound.id, {
|
|
1327
|
-
type: 'nothing-to-one',
|
|
1328
|
-
nextRound: nextUpperRound,
|
|
1329
|
-
currentRound: currentUpperRound,
|
|
1330
|
-
nextRoundMatchFactor: nextUpperRound.matchCount / currentUpperRound.matchCount,
|
|
1331
|
-
});
|
|
1332
|
-
if (currentLowerRound) {
|
|
1333
|
-
if (!nextLowerRound)
|
|
1334
|
-
throw new Error('nextLowerRound is null');
|
|
1335
|
-
roundRelations.set(currentLowerRound.id, {
|
|
1336
|
-
type: 'nothing-to-one',
|
|
1337
|
-
nextRound: nextLowerRound,
|
|
1338
|
-
currentRound: currentLowerRound,
|
|
1339
|
-
nextRoundMatchFactor: nextLowerRound.matchCount / currentLowerRound.matchCount,
|
|
1340
|
-
});
|
|
1526
|
+
return SINGLE_ELIMINATION_BRACKET_ROUND_TYPE.SINGLE_ELIMINATION_BRACKET;
|
|
1341
1527
|
}
|
|
1342
1528
|
}
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
if (!previousUpperRound)
|
|
1346
|
-
throw new Error('previousUpperRound is null');
|
|
1347
|
-
if (!nextUpperRound)
|
|
1348
|
-
throw new Error('nextUpperRound is null');
|
|
1349
|
-
roundRelations.set(currentUpperRound.id, {
|
|
1350
|
-
type: 'one-to-one',
|
|
1351
|
-
previousRound: previousUpperRound,
|
|
1352
|
-
nextRound: nextUpperRound,
|
|
1353
|
-
currentRound: currentUpperRound,
|
|
1354
|
-
nextRoundMatchFactor: nextUpperRound.matchCount / currentUpperRound.matchCount,
|
|
1355
|
-
previousRoundMatchFactor: previousUpperRound.matchCount / currentUpperRound.matchCount,
|
|
1356
|
-
rootRoundMatchFactor: firstUpperRound.matchCount / currentUpperRound.matchCount,
|
|
1357
|
-
});
|
|
1358
|
-
// we only want to set lower rounds here until the special merging point of the final.
|
|
1359
|
-
// lower bracket rounds after and including the final will be set in the final round block
|
|
1360
|
-
if (currentLowerRound && currentUpperRound.type === DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.UPPER_BRACKET) {
|
|
1361
|
-
if (!previousLowerRound)
|
|
1362
|
-
throw new Error('previousLowerRound is null');
|
|
1363
|
-
if (!nextLowerRound)
|
|
1364
|
-
throw new Error('nextLowerRound is null');
|
|
1365
|
-
if (!firstLowerRound)
|
|
1366
|
-
throw new Error('firstLowerRound is null');
|
|
1367
|
-
roundRelations.set(currentLowerRound.id, {
|
|
1368
|
-
type: 'one-to-one',
|
|
1369
|
-
previousRound: previousLowerRound,
|
|
1370
|
-
nextRound: nextLowerRound,
|
|
1371
|
-
currentRound: currentLowerRound,
|
|
1372
|
-
nextRoundMatchFactor: nextLowerRound.matchCount / currentLowerRound.matchCount,
|
|
1373
|
-
previousRoundMatchFactor: previousLowerRound.matchCount / currentLowerRound.matchCount,
|
|
1374
|
-
rootRoundMatchFactor: firstLowerRound.matchCount / currentLowerRound.matchCount,
|
|
1375
|
-
});
|
|
1376
|
-
}
|
|
1529
|
+
default: {
|
|
1530
|
+
throw new Error(`Unsupported tournament mode: ${tournamentMode}`);
|
|
1377
1531
|
}
|
|
1378
1532
|
}
|
|
1379
|
-
return roundRelations;
|
|
1380
1533
|
};
|
|
1381
|
-
const
|
|
1382
|
-
const
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
// means left is nothing. right is one
|
|
1394
|
-
matchRelations.set(match.id, {
|
|
1395
|
-
type: 'nothing-to-one',
|
|
1396
|
-
currentMatch: match,
|
|
1397
|
-
currentRound: currentRelation.currentRound,
|
|
1398
|
-
nextRound: currentRelation.nextRound,
|
|
1399
|
-
nextMatch,
|
|
1400
|
-
});
|
|
1401
|
-
break;
|
|
1402
|
-
}
|
|
1403
|
-
case 'one-to-nothing': {
|
|
1404
|
-
const previousUpperMatch = matchPositionMaps
|
|
1405
|
-
.get(currentRelation.previousRound.id)
|
|
1406
|
-
?.get(previousUpperRoundMatchPosition);
|
|
1407
|
-
const previousLowerMatch = matchPositionMaps
|
|
1408
|
-
.get(currentRelation.previousRound.id)
|
|
1409
|
-
?.get(previousLowerRoundMatchPosition);
|
|
1410
|
-
if (!previousUpperMatch)
|
|
1411
|
-
throw new Error('Previous round match not found');
|
|
1412
|
-
if (previousUpperRoundMatchPosition !== previousLowerRoundMatchPosition) {
|
|
1413
|
-
// means left is two. right is one
|
|
1414
|
-
if (!previousLowerMatch)
|
|
1415
|
-
throw new Error('Previous lower round match not found');
|
|
1416
|
-
matchRelations.set(match.id, {
|
|
1417
|
-
type: 'two-to-nothing',
|
|
1418
|
-
currentMatch: match,
|
|
1419
|
-
currentRound: currentRelation.currentRound,
|
|
1420
|
-
previousUpperMatch,
|
|
1421
|
-
previousUpperRound: currentRelation.previousRound,
|
|
1422
|
-
previousLowerMatch,
|
|
1423
|
-
previousLowerRound: currentRelation.previousRound,
|
|
1424
|
-
});
|
|
1425
|
-
}
|
|
1426
|
-
else {
|
|
1427
|
-
// means left is one. right is nothing
|
|
1428
|
-
matchRelations.set(match.id, {
|
|
1429
|
-
type: 'one-to-nothing',
|
|
1430
|
-
currentMatch: match,
|
|
1431
|
-
currentRound: currentRelation.currentRound,
|
|
1432
|
-
previousMatch: previousUpperMatch,
|
|
1433
|
-
previousRound: currentRelation.previousRound,
|
|
1434
|
-
});
|
|
1435
|
-
}
|
|
1436
|
-
break;
|
|
1437
|
-
}
|
|
1438
|
-
case 'one-to-one': {
|
|
1439
|
-
const nextMatch = matchPositionMaps.get(currentRelation.nextRound.id)?.get(nextRoundMatchPosition);
|
|
1440
|
-
const previousUpperMatch = matchPositionMaps
|
|
1441
|
-
.get(currentRelation.previousRound.id)
|
|
1442
|
-
?.get(previousUpperRoundMatchPosition);
|
|
1443
|
-
const previousLowerMatch = matchPositionMaps
|
|
1444
|
-
.get(currentRelation.previousRound.id)
|
|
1445
|
-
?.get(previousLowerRoundMatchPosition);
|
|
1446
|
-
if (!nextMatch)
|
|
1447
|
-
throw new Error('Next round match not found');
|
|
1448
|
-
if (!previousUpperMatch)
|
|
1449
|
-
throw new Error(`Previous upper round match not found`);
|
|
1450
|
-
if (!previousLowerMatch)
|
|
1451
|
-
throw new Error('Previous lower round match not found');
|
|
1452
|
-
// can be either one to one or two to one
|
|
1453
|
-
const isLeftOne = previousUpperRoundMatchPosition === previousLowerRoundMatchPosition;
|
|
1454
|
-
if (isLeftOne) {
|
|
1455
|
-
// one-to-one
|
|
1456
|
-
matchRelations.set(match.id, {
|
|
1457
|
-
type: 'one-to-one',
|
|
1458
|
-
currentMatch: match,
|
|
1459
|
-
currentRound: currentRelation.currentRound,
|
|
1460
|
-
previousMatch: previousUpperMatch,
|
|
1461
|
-
previousRound: currentRelation.previousRound,
|
|
1462
|
-
nextMatch,
|
|
1463
|
-
nextRound: currentRelation.nextRound,
|
|
1464
|
-
});
|
|
1465
|
-
}
|
|
1466
|
-
else {
|
|
1467
|
-
// two-to-one
|
|
1468
|
-
matchRelations.set(match.id, {
|
|
1469
|
-
type: 'two-to-one',
|
|
1470
|
-
currentMatch: match,
|
|
1471
|
-
currentRound: currentRelation.currentRound,
|
|
1472
|
-
previousUpperMatch,
|
|
1473
|
-
previousUpperRound: currentRelation.previousRound,
|
|
1474
|
-
previousLowerMatch,
|
|
1475
|
-
previousLowerRound: currentRelation.previousRound,
|
|
1476
|
-
nextMatch,
|
|
1477
|
-
nextRound: currentRelation.nextRound,
|
|
1478
|
-
});
|
|
1479
|
-
}
|
|
1480
|
-
break;
|
|
1481
|
-
}
|
|
1482
|
-
case 'two-to-one': {
|
|
1483
|
-
const nextMatch = matchPositionMaps.get(currentRelation.nextRound.id)?.get(nextRoundMatchPosition);
|
|
1484
|
-
const previousUpperMatch = matchPositionMaps
|
|
1485
|
-
.get(currentRelation.previousUpperRound.id)
|
|
1486
|
-
?.get(previousUpperRoundMatchPosition);
|
|
1487
|
-
const previousLowerMatch = matchPositionMaps
|
|
1488
|
-
.get(currentRelation.previousLowerRound.id)
|
|
1489
|
-
?.get(previousLowerRoundMatchPosition);
|
|
1490
|
-
if (!nextMatch)
|
|
1491
|
-
throw new Error('Next round match not found');
|
|
1492
|
-
if (!previousUpperMatch)
|
|
1493
|
-
throw new Error(`Previous upper round match not found`);
|
|
1494
|
-
if (!previousLowerMatch)
|
|
1495
|
-
throw new Error('Previous lower round match not found');
|
|
1496
|
-
matchRelations.set(match.id, {
|
|
1497
|
-
type: 'two-to-one',
|
|
1498
|
-
currentMatch: match,
|
|
1499
|
-
currentRound: currentRelation.currentRound,
|
|
1500
|
-
previousUpperMatch,
|
|
1501
|
-
previousUpperRound: currentRelation.previousUpperRound,
|
|
1502
|
-
previousLowerMatch,
|
|
1503
|
-
previousLowerRound: currentRelation.previousLowerRound,
|
|
1504
|
-
nextMatch,
|
|
1505
|
-
nextRound: currentRelation.nextRound,
|
|
1506
|
-
});
|
|
1507
|
-
break;
|
|
1508
|
-
}
|
|
1509
|
-
case 'two-to-nothing': {
|
|
1510
|
-
const previousUpperMatch = matchPositionMaps
|
|
1511
|
-
.get(currentRelation.previousUpperRound.id)
|
|
1512
|
-
?.get(previousUpperRoundMatchPosition);
|
|
1513
|
-
const previousLowerMatch = matchPositionMaps
|
|
1514
|
-
.get(currentRelation.previousUpperRound.id)
|
|
1515
|
-
?.get(previousLowerRoundMatchPosition);
|
|
1516
|
-
if (!previousUpperMatch)
|
|
1517
|
-
throw new Error(`Previous upper round match not found`);
|
|
1518
|
-
if (!previousLowerMatch)
|
|
1519
|
-
throw new Error('Previous lower round match not found');
|
|
1520
|
-
matchRelations.set(match.id, {
|
|
1521
|
-
type: 'two-to-nothing',
|
|
1522
|
-
currentMatch: match,
|
|
1523
|
-
currentRound: currentRelation.currentRound,
|
|
1524
|
-
previousUpperMatch,
|
|
1525
|
-
previousUpperRound: currentRelation.previousUpperRound,
|
|
1526
|
-
previousLowerMatch,
|
|
1527
|
-
previousLowerRound: currentRelation.previousUpperRound,
|
|
1528
|
-
});
|
|
1529
|
-
break;
|
|
1530
|
-
}
|
|
1534
|
+
const generateBracketDataForGg = (source) => {
|
|
1535
|
+
const tournamentMode = generateTournamentModeFormGgData(source);
|
|
1536
|
+
const bracketData = {
|
|
1537
|
+
rounds: [],
|
|
1538
|
+
matches: [],
|
|
1539
|
+
mode: tournamentMode,
|
|
1540
|
+
};
|
|
1541
|
+
const matchesGrouped = new Map();
|
|
1542
|
+
for (const match of source.matches) {
|
|
1543
|
+
const roundId = `${match.roundNumber}-${match.stageNumber}-${match.bracketType || null}`;
|
|
1544
|
+
if (!matchesGrouped.has(roundId)) {
|
|
1545
|
+
matchesGrouped.set(roundId, []);
|
|
1531
1546
|
}
|
|
1547
|
+
matchesGrouped.get(roundId)?.push(match);
|
|
1532
1548
|
}
|
|
1533
|
-
|
|
1549
|
+
for (const [roundId, matches] of matchesGrouped.entries()) {
|
|
1550
|
+
const firstMatch = matches[0];
|
|
1551
|
+
if (!firstMatch)
|
|
1552
|
+
throw new Error('First match not found');
|
|
1553
|
+
const roundType = generateRoundTypeFromGgMatch(tournamentMode, firstMatch.bracketType, firstMatch.stageNumber, matches.length);
|
|
1554
|
+
const bracketRound = {
|
|
1555
|
+
type: roundType,
|
|
1556
|
+
id: roundId,
|
|
1557
|
+
data: null,
|
|
1558
|
+
name: firstMatch.roundTitle,
|
|
1559
|
+
};
|
|
1560
|
+
bracketData.rounds.push(bracketRound);
|
|
1561
|
+
for (const match of matches) {
|
|
1562
|
+
const bracketMatch = {
|
|
1563
|
+
id: match.id,
|
|
1564
|
+
data: match,
|
|
1565
|
+
roundId: roundId,
|
|
1566
|
+
home: match.homeMatchSide.participant?.id || null,
|
|
1567
|
+
away: match.awayMatchSide.participant?.id || null,
|
|
1568
|
+
winner: match.winningSide,
|
|
1569
|
+
status: match.status === 'completed' ? 'completed' : 'pending',
|
|
1570
|
+
};
|
|
1571
|
+
bracketData.matches.push(bracketMatch);
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
return bracketData;
|
|
1534
1575
|
};
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1576
|
+
|
|
1577
|
+
class NewBracketDefaultMatchComponent {
|
|
1578
|
+
constructor() {
|
|
1579
|
+
this.bracketRound = input.required();
|
|
1580
|
+
this.bracketMatch = input.required();
|
|
1581
|
+
}
|
|
1582
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: NewBracketDefaultMatchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1583
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.0.3", type: NewBracketDefaultMatchComponent, isStandalone: true, selector: "et-new-bracket-default-match", inputs: { bracketRound: { classPropertyName: "bracketRound", publicName: "bracketRound", isSignal: true, isRequired: true, transformFunction: null }, bracketMatch: { classPropertyName: "bracketMatch", publicName: "bracketMatch", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "et-new-bracket-default-match-host" }, ngImport: i0, template: ` {{ bracketMatch().id }} `, isInline: true, styles: [".et-new-bracket-default-match-host{display:block;padding:8px;border:1px solid yellow;inline-size:250px;block-size:75px;display:flex;justify-content:center;align-items:center;box-sizing:border-box;font-size:12px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
1584
|
+
}
|
|
1585
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: NewBracketDefaultMatchComponent, decorators: [{
|
|
1586
|
+
type: Component,
|
|
1587
|
+
args: [{ selector: 'et-new-bracket-default-match', template: ` {{ bracketMatch().id }} `, standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
|
|
1588
|
+
class: 'et-new-bracket-default-match-host',
|
|
1589
|
+
}, styles: [".et-new-bracket-default-match-host{display:block;padding:8px;border:1px solid yellow;inline-size:250px;block-size:75px;display:flex;justify-content:center;align-items:center;box-sizing:border-box;font-size:12px}\n"] }]
|
|
1590
|
+
}] });
|
|
1591
|
+
|
|
1592
|
+
class NewBracketDefaultRoundHeaderComponent {
|
|
1593
|
+
constructor() {
|
|
1594
|
+
this.bracketRound = input.required();
|
|
1595
|
+
}
|
|
1596
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: NewBracketDefaultRoundHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1597
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.0.3", type: NewBracketDefaultRoundHeaderComponent, isStandalone: true, selector: "et-new-bracket-default-round-header", inputs: { bracketRound: { classPropertyName: "bracketRound", publicName: "bracketRound", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "et-new-bracket-default-round-header-host" }, ngImport: i0, template: ` {{ bracketRound().name }} `, isInline: true, styles: [".et-new-bracket-default-round-header-host{display:block;padding:8px;border:1px solid green;inline-size:250px;block-size:50px;display:flex;justify-content:center;align-items:center;box-sizing:border-box}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
1598
|
+
}
|
|
1599
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: NewBracketDefaultRoundHeaderComponent, decorators: [{
|
|
1600
|
+
type: Component,
|
|
1601
|
+
args: [{ selector: 'et-new-bracket-default-round-header', template: ` {{ bracketRound().name }} `, standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
|
|
1602
|
+
class: 'et-new-bracket-default-round-header-host',
|
|
1603
|
+
}, styles: [".et-new-bracket-default-round-header-host{display:block;padding:8px;border:1px solid green;inline-size:250px;block-size:50px;display:flex;justify-content:center;align-items:center;box-sizing:border-box}\n"] }]
|
|
1604
|
+
}] });
|
|
1605
|
+
|
|
1606
|
+
const FALLBACK_MATCH_RELATION_POSITION = -1;
|
|
1607
|
+
const generateMatchPosition = (match, factor) => Math.ceil(match.position * factor);
|
|
1608
|
+
const generateMatchRelationPositions = (relation, match) => {
|
|
1609
|
+
switch (relation.type) {
|
|
1610
|
+
case 'nothing-to-one':
|
|
1538
1611
|
return {
|
|
1539
|
-
nextRoundMatchPosition: generateMatchPosition(match,
|
|
1540
|
-
previousUpperRoundMatchPosition:
|
|
1541
|
-
previousLowerRoundMatchPosition:
|
|
1612
|
+
nextRoundMatchPosition: generateMatchPosition(match, relation.nextRoundMatchFactor),
|
|
1613
|
+
previousUpperRoundMatchPosition: FALLBACK_MATCH_RELATION_POSITION,
|
|
1614
|
+
previousLowerRoundMatchPosition: FALLBACK_MATCH_RELATION_POSITION,
|
|
1542
1615
|
};
|
|
1543
|
-
}
|
|
1544
1616
|
case 'one-to-nothing': {
|
|
1545
|
-
const
|
|
1546
|
-
const doubleUpperMatchCountShift = previousRoundHasDoubleTheMatchCount ? 1 : 0;
|
|
1617
|
+
const double = relation.previousRoundMatchFactor === 2 ? 1 : 0;
|
|
1547
1618
|
return {
|
|
1548
|
-
nextRoundMatchPosition:
|
|
1549
|
-
previousUpperRoundMatchPosition: (generateMatchPosition(match,
|
|
1550
|
-
|
|
1551
|
-
previousLowerRoundMatchPosition: generateMatchPosition(match,
|
|
1619
|
+
nextRoundMatchPosition: FALLBACK_MATCH_RELATION_POSITION,
|
|
1620
|
+
previousUpperRoundMatchPosition: (generateMatchPosition(match, relation.previousRoundMatchFactor) -
|
|
1621
|
+
double),
|
|
1622
|
+
previousLowerRoundMatchPosition: generateMatchPosition(match, relation.previousRoundMatchFactor),
|
|
1552
1623
|
};
|
|
1553
1624
|
}
|
|
1554
1625
|
case 'one-to-one': {
|
|
1555
|
-
const
|
|
1556
|
-
const doubleUpperMatchCountShift = previousRoundHasDoubleTheMatchCount ? 1 : 0;
|
|
1626
|
+
const double = relation.previousRoundMatchFactor === 2 ? 1 : 0;
|
|
1557
1627
|
return {
|
|
1558
|
-
nextRoundMatchPosition: generateMatchPosition(match,
|
|
1559
|
-
previousUpperRoundMatchPosition: (generateMatchPosition(match,
|
|
1560
|
-
|
|
1561
|
-
previousLowerRoundMatchPosition: generateMatchPosition(match,
|
|
1628
|
+
nextRoundMatchPosition: generateMatchPosition(match, relation.nextRoundMatchFactor),
|
|
1629
|
+
previousUpperRoundMatchPosition: (generateMatchPosition(match, relation.previousRoundMatchFactor) -
|
|
1630
|
+
double),
|
|
1631
|
+
previousLowerRoundMatchPosition: generateMatchPosition(match, relation.previousRoundMatchFactor),
|
|
1562
1632
|
};
|
|
1563
1633
|
}
|
|
1564
|
-
case 'two-to-one':
|
|
1634
|
+
case 'two-to-one':
|
|
1565
1635
|
return {
|
|
1566
|
-
nextRoundMatchPosition: generateMatchPosition(match,
|
|
1567
|
-
previousUpperRoundMatchPosition: generateMatchPosition(match,
|
|
1568
|
-
previousLowerRoundMatchPosition: generateMatchPosition(match,
|
|
1636
|
+
nextRoundMatchPosition: generateMatchPosition(match, relation.nextRoundMatchFactor),
|
|
1637
|
+
previousUpperRoundMatchPosition: generateMatchPosition(match, relation.previousUpperRoundMatchFactor),
|
|
1638
|
+
previousLowerRoundMatchPosition: generateMatchPosition(match, relation.previousLowerRoundMatchFactor),
|
|
1569
1639
|
};
|
|
1570
|
-
|
|
1571
|
-
case 'two-to-nothing': {
|
|
1640
|
+
case 'two-to-nothing':
|
|
1572
1641
|
return {
|
|
1573
|
-
nextRoundMatchPosition:
|
|
1574
|
-
previousUpperRoundMatchPosition: generateMatchPosition(match,
|
|
1575
|
-
previousLowerRoundMatchPosition: generateMatchPosition(match,
|
|
1642
|
+
nextRoundMatchPosition: FALLBACK_MATCH_RELATION_POSITION,
|
|
1643
|
+
previousUpperRoundMatchPosition: generateMatchPosition(match, relation.previousUpperRoundMatchFactor),
|
|
1644
|
+
previousLowerRoundMatchPosition: generateMatchPosition(match, relation.previousLowerRoundMatchFactor),
|
|
1576
1645
|
};
|
|
1577
|
-
}
|
|
1578
1646
|
}
|
|
1579
1647
|
};
|
|
1580
|
-
const
|
|
1581
|
-
|
|
1648
|
+
const createNothingToOneRelation$1 = (params) => {
|
|
1649
|
+
const { match, relation, matchPositionMaps, nextRoundMatchPosition } = params;
|
|
1650
|
+
const nextMatch = matchPositionMaps.get(relation.nextRound.id)?.get(nextRoundMatchPosition);
|
|
1651
|
+
if (!nextMatch)
|
|
1652
|
+
throw new Error('Next round match not found');
|
|
1653
|
+
return {
|
|
1654
|
+
type: 'nothing-to-one',
|
|
1655
|
+
currentMatch: match,
|
|
1656
|
+
currentRound: relation.currentRound,
|
|
1657
|
+
nextMatch,
|
|
1658
|
+
nextRound: relation.nextRound,
|
|
1659
|
+
};
|
|
1582
1660
|
};
|
|
1583
|
-
const
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1661
|
+
const createOneToNothingOrTwoToNothingRelation = (params) => {
|
|
1662
|
+
const { match, relation, matchPositionMaps, previousUpperRoundMatchPosition, previousLowerRoundMatchPosition } = params;
|
|
1663
|
+
const previousUpperMatch = matchPositionMaps.get(relation.previousRound.id)?.get(previousUpperRoundMatchPosition);
|
|
1664
|
+
const previousLowerMatch = matchPositionMaps.get(relation.previousRound.id)?.get(previousLowerRoundMatchPosition);
|
|
1665
|
+
if (!previousUpperMatch)
|
|
1666
|
+
throw new Error('Previous round match not found');
|
|
1667
|
+
if (previousUpperRoundMatchPosition !== previousLowerRoundMatchPosition) {
|
|
1668
|
+
if (!previousLowerMatch)
|
|
1669
|
+
throw new Error('Previous lower round match not found');
|
|
1670
|
+
return {
|
|
1671
|
+
type: 'two-to-nothing',
|
|
1672
|
+
currentMatch: match,
|
|
1673
|
+
currentRound: relation.currentRound,
|
|
1674
|
+
previousUpperMatch,
|
|
1675
|
+
previousUpperRound: relation.previousRound,
|
|
1676
|
+
previousLowerMatch,
|
|
1677
|
+
previousLowerRound: relation.previousRound,
|
|
1678
|
+
};
|
|
1679
|
+
}
|
|
1680
|
+
else {
|
|
1681
|
+
return {
|
|
1682
|
+
type: 'one-to-nothing',
|
|
1683
|
+
currentMatch: match,
|
|
1684
|
+
currentRound: relation.currentRound,
|
|
1685
|
+
previousMatch: previousUpperMatch,
|
|
1686
|
+
previousRound: relation.previousRound,
|
|
1687
|
+
};
|
|
1688
|
+
}
|
|
1689
|
+
};
|
|
1690
|
+
const createOneToOneOrTwoToOneRelation = (params) => {
|
|
1691
|
+
const { match, relation, matchPositionMaps, nextRoundMatchPosition, previousUpperRoundMatchPosition, previousLowerRoundMatchPosition, } = params;
|
|
1692
|
+
const nextMatch = matchPositionMaps.get(relation.nextRound.id)?.get(nextRoundMatchPosition);
|
|
1693
|
+
const previousUpperMatch = matchPositionMaps.get(relation.previousRound.id)?.get(previousUpperRoundMatchPosition);
|
|
1694
|
+
const previousLowerMatch = matchPositionMaps.get(relation.previousRound.id)?.get(previousLowerRoundMatchPosition);
|
|
1695
|
+
if (!nextMatch)
|
|
1696
|
+
throw new Error('Next round match not found');
|
|
1697
|
+
if (!previousUpperMatch)
|
|
1698
|
+
throw new Error('Previous upper round match not found');
|
|
1699
|
+
if (!previousLowerMatch)
|
|
1700
|
+
throw new Error('Previous lower round match not found');
|
|
1701
|
+
if (previousUpperRoundMatchPosition === previousLowerRoundMatchPosition) {
|
|
1702
|
+
return {
|
|
1703
|
+
type: 'one-to-one',
|
|
1704
|
+
currentMatch: match,
|
|
1705
|
+
currentRound: relation.currentRound,
|
|
1706
|
+
previousMatch: previousUpperMatch,
|
|
1707
|
+
previousRound: relation.previousRound,
|
|
1708
|
+
nextMatch,
|
|
1709
|
+
nextRound: relation.nextRound,
|
|
1710
|
+
};
|
|
1711
|
+
}
|
|
1712
|
+
else {
|
|
1713
|
+
return {
|
|
1714
|
+
type: 'two-to-one',
|
|
1715
|
+
currentMatch: match,
|
|
1716
|
+
currentRound: relation.currentRound,
|
|
1717
|
+
previousUpperMatch,
|
|
1718
|
+
previousUpperRound: relation.previousRound,
|
|
1719
|
+
previousLowerMatch,
|
|
1720
|
+
previousLowerRound: relation.previousRound,
|
|
1721
|
+
nextMatch,
|
|
1722
|
+
nextRound: relation.nextRound,
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1725
|
+
};
|
|
1726
|
+
const createTwoToOneRelation$1 = (params) => {
|
|
1727
|
+
const { match, relation, matchPositionMaps, nextRoundMatchPosition, previousUpperRoundMatchPosition, previousLowerRoundMatchPosition, } = params;
|
|
1728
|
+
const nextMatch = matchPositionMaps.get(relation.nextRound.id)?.get(nextRoundMatchPosition);
|
|
1729
|
+
const previousUpperMatch = matchPositionMaps
|
|
1730
|
+
.get(relation.previousUpperRound.id)
|
|
1731
|
+
?.get(previousUpperRoundMatchPosition);
|
|
1732
|
+
const previousLowerMatch = matchPositionMaps
|
|
1733
|
+
.get(relation.previousLowerRound.id)
|
|
1734
|
+
?.get(previousLowerRoundMatchPosition);
|
|
1735
|
+
if (!nextMatch)
|
|
1736
|
+
throw new Error('Next round match not found');
|
|
1737
|
+
if (!previousUpperMatch)
|
|
1738
|
+
throw new Error('Previous upper round match not found');
|
|
1739
|
+
if (!previousLowerMatch)
|
|
1740
|
+
throw new Error('Previous lower round match not found');
|
|
1741
|
+
return {
|
|
1742
|
+
type: 'two-to-one',
|
|
1743
|
+
currentMatch: match,
|
|
1744
|
+
currentRound: relation.currentRound,
|
|
1745
|
+
previousUpperMatch,
|
|
1746
|
+
previousUpperRound: relation.previousUpperRound,
|
|
1747
|
+
previousLowerMatch,
|
|
1748
|
+
previousLowerRound: relation.previousLowerRound,
|
|
1749
|
+
nextMatch,
|
|
1750
|
+
nextRound: relation.nextRound,
|
|
1751
|
+
};
|
|
1752
|
+
};
|
|
1753
|
+
const createTwoToNothingRelation$1 = (params) => {
|
|
1754
|
+
const { match, relation, matchPositionMaps, previousUpperRoundMatchPosition, previousLowerRoundMatchPosition } = params;
|
|
1755
|
+
const previousUpperMatch = matchPositionMaps
|
|
1756
|
+
.get(relation.previousUpperRound.id)
|
|
1757
|
+
?.get(previousUpperRoundMatchPosition);
|
|
1758
|
+
const previousLowerMatch = matchPositionMaps
|
|
1759
|
+
.get(relation.previousUpperRound.id)
|
|
1760
|
+
?.get(previousLowerRoundMatchPosition);
|
|
1761
|
+
if (!previousUpperMatch)
|
|
1762
|
+
throw new Error('Previous upper round match not found');
|
|
1763
|
+
if (!previousLowerMatch)
|
|
1764
|
+
throw new Error('Previous lower round match not found');
|
|
1765
|
+
return {
|
|
1766
|
+
type: 'two-to-nothing',
|
|
1767
|
+
currentMatch: match,
|
|
1768
|
+
currentRound: relation.currentRound,
|
|
1769
|
+
previousUpperMatch,
|
|
1770
|
+
previousUpperRound: relation.previousUpperRound,
|
|
1771
|
+
previousLowerMatch,
|
|
1772
|
+
previousLowerRound: relation.previousUpperRound,
|
|
1773
|
+
};
|
|
1774
|
+
};
|
|
1775
|
+
const generateMatchRelationsNew = (bracketData) => {
|
|
1776
|
+
const matchRelations = [];
|
|
1777
|
+
const matchPositionMaps = new Map();
|
|
1778
|
+
for (const round of bracketData.rounds.values()) {
|
|
1779
|
+
const matchMap = new Map([...round.matches.values()].map((m) => [m.position, m]));
|
|
1780
|
+
matchPositionMaps.set(round.id, matchMap);
|
|
1781
|
+
}
|
|
1782
|
+
for (const match of bracketData.matches.values()) {
|
|
1783
|
+
const relation = match.round.relation;
|
|
1784
|
+
const { nextRoundMatchPosition, previousUpperRoundMatchPosition, previousLowerRoundMatchPosition } = generateMatchRelationPositions(relation, match);
|
|
1590
1785
|
switch (relation.type) {
|
|
1591
1786
|
case 'nothing-to-one':
|
|
1592
|
-
|
|
1787
|
+
matchRelations.push(createNothingToOneRelation$1({
|
|
1788
|
+
match,
|
|
1789
|
+
relation,
|
|
1790
|
+
matchPositionMaps,
|
|
1791
|
+
nextRoundMatchPosition,
|
|
1792
|
+
}));
|
|
1593
1793
|
break;
|
|
1594
1794
|
case 'one-to-nothing':
|
|
1595
|
-
|
|
1795
|
+
matchRelations.push(createOneToNothingOrTwoToNothingRelation({
|
|
1796
|
+
match,
|
|
1797
|
+
relation,
|
|
1798
|
+
matchPositionMaps,
|
|
1799
|
+
previousUpperRoundMatchPosition,
|
|
1800
|
+
previousLowerRoundMatchPosition,
|
|
1801
|
+
}));
|
|
1596
1802
|
break;
|
|
1597
1803
|
case 'one-to-one':
|
|
1598
|
-
|
|
1804
|
+
matchRelations.push(createOneToOneOrTwoToOneRelation({
|
|
1805
|
+
match,
|
|
1806
|
+
relation,
|
|
1807
|
+
matchPositionMaps,
|
|
1808
|
+
nextRoundMatchPosition,
|
|
1809
|
+
previousUpperRoundMatchPosition,
|
|
1810
|
+
previousLowerRoundMatchPosition,
|
|
1811
|
+
}));
|
|
1599
1812
|
break;
|
|
1600
1813
|
case 'two-to-one':
|
|
1601
|
-
|
|
1814
|
+
matchRelations.push(createTwoToOneRelation$1({
|
|
1815
|
+
match,
|
|
1816
|
+
relation,
|
|
1817
|
+
matchPositionMaps,
|
|
1818
|
+
nextRoundMatchPosition,
|
|
1819
|
+
previousUpperRoundMatchPosition,
|
|
1820
|
+
previousLowerRoundMatchPosition,
|
|
1821
|
+
}));
|
|
1602
1822
|
break;
|
|
1603
1823
|
case 'two-to-nothing':
|
|
1604
|
-
|
|
1824
|
+
matchRelations.push(createTwoToNothingRelation$1({
|
|
1825
|
+
match,
|
|
1826
|
+
relation,
|
|
1827
|
+
matchPositionMaps,
|
|
1828
|
+
previousUpperRoundMatchPosition,
|
|
1829
|
+
previousLowerRoundMatchPosition,
|
|
1830
|
+
}));
|
|
1605
1831
|
break;
|
|
1606
1832
|
}
|
|
1607
1833
|
}
|
|
1834
|
+
return matchRelations;
|
|
1608
1835
|
};
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
return
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
// Cache factorial calculations
|
|
1675
|
-
const getFactorial = (n) => {
|
|
1676
|
-
if (n <= 1)
|
|
1677
|
-
return 1;
|
|
1678
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1679
|
-
if (factorialCache.has(n))
|
|
1680
|
-
return factorialCache.get(n);
|
|
1681
|
-
const result = n * getFactorial(n - 1);
|
|
1682
|
-
factorialCache.set(n, result);
|
|
1683
|
-
return result;
|
|
1836
|
+
|
|
1837
|
+
const calculateMatchFactor = (numeratorRound, denominatorRound) => numeratorRound.matchCount / denominatorRound.matchCount;
|
|
1838
|
+
const createNothingToOneRelation = (params) => ({
|
|
1839
|
+
type: 'nothing-to-one',
|
|
1840
|
+
currentRound: params.currentRound,
|
|
1841
|
+
nextRound: params.nextRound,
|
|
1842
|
+
nextRoundMatchFactor: calculateMatchFactor(params.nextRound, params.currentRound),
|
|
1843
|
+
});
|
|
1844
|
+
const createOneToNothingRelation = (params) => ({
|
|
1845
|
+
type: 'one-to-nothing',
|
|
1846
|
+
currentRound: params.currentRound,
|
|
1847
|
+
previousRound: params.previousRound,
|
|
1848
|
+
previousRoundMatchFactor: calculateMatchFactor(params.previousRound, params.currentRound),
|
|
1849
|
+
rootRoundMatchFactor: calculateMatchFactor(params.rootRound, params.currentRound),
|
|
1850
|
+
});
|
|
1851
|
+
const createOneToOneRelation = (params) => ({
|
|
1852
|
+
type: 'one-to-one',
|
|
1853
|
+
currentRound: params.currentRound,
|
|
1854
|
+
previousRound: params.previousRound,
|
|
1855
|
+
nextRound: params.nextRound,
|
|
1856
|
+
nextRoundMatchFactor: calculateMatchFactor(params.nextRound, params.currentRound),
|
|
1857
|
+
previousRoundMatchFactor: calculateMatchFactor(params.previousRound, params.currentRound),
|
|
1858
|
+
rootRoundMatchFactor: calculateMatchFactor(params.rootRound, params.currentRound),
|
|
1859
|
+
});
|
|
1860
|
+
const createTwoToOneRelation = (params) => ({
|
|
1861
|
+
type: 'two-to-one',
|
|
1862
|
+
currentRound: params.currentRound,
|
|
1863
|
+
previousUpperRound: params.previousUpperRound,
|
|
1864
|
+
previousLowerRound: params.previousLowerRound,
|
|
1865
|
+
nextRound: params.nextRound,
|
|
1866
|
+
nextRoundMatchFactor: calculateMatchFactor(params.nextRound, params.currentRound),
|
|
1867
|
+
previousUpperRoundMatchFactor: calculateMatchFactor(params.previousUpperRound, params.currentRound),
|
|
1868
|
+
previousLowerRoundMatchFactor: calculateMatchFactor(params.previousLowerRound, params.currentRound),
|
|
1869
|
+
upperRootRoundMatchFactor: calculateMatchFactor(params.firstUpperRound, params.currentRound),
|
|
1870
|
+
lowerRootRoundMatchFactor: calculateMatchFactor(params.firstLowerRound, params.currentRound),
|
|
1871
|
+
});
|
|
1872
|
+
const createTwoToNothingRelation = (params) => ({
|
|
1873
|
+
type: 'two-to-nothing',
|
|
1874
|
+
currentRound: params.currentRound,
|
|
1875
|
+
previousUpperRound: params.previousUpperRound,
|
|
1876
|
+
previousLowerRound: params.previousLowerRound,
|
|
1877
|
+
previousUpperRoundMatchFactor: calculateMatchFactor(params.previousUpperRound, params.currentRound),
|
|
1878
|
+
previousLowerRoundMatchFactor: calculateMatchFactor(params.previousLowerRound, params.currentRound),
|
|
1879
|
+
upperRootRoundMatchFactor: calculateMatchFactor(params.firstUpperRound, params.currentRound),
|
|
1880
|
+
lowerRootRoundMatchFactor: calculateMatchFactor(params.firstLowerRound, params.currentRound),
|
|
1881
|
+
});
|
|
1882
|
+
const getNavigationContext = (params) => {
|
|
1883
|
+
const { upperRounds, currentUpperRoundIndex } = params;
|
|
1884
|
+
const currentUpperRound = upperRounds[currentUpperRoundIndex];
|
|
1885
|
+
if (!currentUpperRound)
|
|
1886
|
+
throw new Error('currentUpperRound is null');
|
|
1887
|
+
const isLeftToRight = !currentUpperRound.mirrorRoundType || currentUpperRound.mirrorRoundType === BRACKET_ROUND_MIRROR_TYPE.LEFT;
|
|
1888
|
+
const relativePrevious = upperRounds[currentUpperRoundIndex - 1] || null;
|
|
1889
|
+
const relativeNext = upperRounds[currentUpperRoundIndex + 1] || null;
|
|
1890
|
+
const previousUpperRound = isLeftToRight ? relativePrevious : relativeNext;
|
|
1891
|
+
const nextUpperRound = isLeftToRight ? relativeNext : relativePrevious;
|
|
1892
|
+
const isLastUpperRound = !nextUpperRound ||
|
|
1893
|
+
(nextUpperRound.mirrorRoundType === BRACKET_ROUND_MIRROR_TYPE.RIGHT && !currentUpperRound.mirrorRoundType);
|
|
1894
|
+
const isFinal = currentUpperRound.type === COMMON_BRACKET_ROUND_TYPE.FINAL;
|
|
1895
|
+
return {
|
|
1896
|
+
currentUpperRound,
|
|
1897
|
+
previousUpperRound,
|
|
1898
|
+
nextUpperRound,
|
|
1899
|
+
isLastUpperRound,
|
|
1900
|
+
isFinal,
|
|
1684
1901
|
};
|
|
1685
|
-
// Pre-calculate roundFactorial
|
|
1686
|
-
const roundFact = getFactorial(roundNumber);
|
|
1687
|
-
let totalCombinations = 0;
|
|
1688
|
-
const validGroups = [];
|
|
1689
|
-
// Single loop to gather valid groups and total combinations
|
|
1690
|
-
for (let wins = roundNumber; wins >= 0; wins--) {
|
|
1691
|
-
const losses = roundNumber - wins;
|
|
1692
|
-
const remainingGames = ADVANCE_WINS + ELIMINATE_LOSSES - (wins + losses) - 1;
|
|
1693
|
-
const notYetEliminated = losses < ELIMINATE_LOSSES;
|
|
1694
|
-
const canStillAdvance = wins < ADVANCE_WINS && remainingGames >= 0;
|
|
1695
|
-
if (!canStillAdvance || !notYetEliminated)
|
|
1696
|
-
continue;
|
|
1697
|
-
const combinations = roundFact / (getFactorial(wins) * getFactorial(losses));
|
|
1698
|
-
totalCombinations += combinations;
|
|
1699
|
-
validGroups.push({ wins, losses, combinations });
|
|
1700
|
-
}
|
|
1701
|
-
// Create final groups with calculated proportions
|
|
1702
|
-
return validGroups.map(({ wins, losses, combinations }) => ({
|
|
1703
|
-
id: `${wins}-${losses}`,
|
|
1704
|
-
name: `${wins}-${losses}`,
|
|
1705
|
-
matchesInGroup: Math.round((combinations / totalCombinations) * totalMatchesInRound),
|
|
1706
|
-
}));
|
|
1707
1902
|
};
|
|
1708
|
-
const
|
|
1709
|
-
|
|
1710
|
-
|
|
1903
|
+
const handleFinalRound = (params) => {
|
|
1904
|
+
const { relations, currentUpperRound, previousUpperRound, nextUpperRound, lowerRounds, currentUpperRoundIndex, firstUpperRound, firstLowerRound, lastLowerRound, } = params;
|
|
1905
|
+
const currentLowerRound = lowerRounds[currentUpperRoundIndex] || null;
|
|
1906
|
+
const nextLowerRound = lowerRounds[currentUpperRoundIndex + 1] || null;
|
|
1907
|
+
const previousLowerRound = lowerRounds[currentUpperRoundIndex - 1] || null;
|
|
1908
|
+
if (!currentLowerRound)
|
|
1909
|
+
throw new Error('currentLowerRound is null');
|
|
1910
|
+
const isAsyncBracket = currentLowerRound.id !== lastLowerRound.id;
|
|
1911
|
+
const finalLowerRound = isAsyncBracket ? nextLowerRound : currentLowerRound;
|
|
1912
|
+
if (!finalLowerRound)
|
|
1913
|
+
throw new Error('finalLowerRound is null');
|
|
1914
|
+
if (finalLowerRound.id !== lastLowerRound.id)
|
|
1915
|
+
throw new Error('finalLowerRound is not the last lower round');
|
|
1916
|
+
if (nextUpperRound) {
|
|
1917
|
+
relations.push(createTwoToOneRelation({
|
|
1918
|
+
currentRound: currentUpperRound,
|
|
1919
|
+
previousUpperRound,
|
|
1920
|
+
previousLowerRound: finalLowerRound,
|
|
1921
|
+
nextRound: nextUpperRound,
|
|
1922
|
+
firstUpperRound,
|
|
1923
|
+
firstLowerRound,
|
|
1924
|
+
}));
|
|
1711
1925
|
}
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
throw new Error('Match participant match not found');
|
|
1740
|
-
const wins = matchParticipantMatch.winCount;
|
|
1741
|
-
const losses = matchParticipantMatch.lossCount;
|
|
1742
|
-
const group = roundSwissData.groups.get(`${wins}-${losses}`);
|
|
1743
|
-
if (!group)
|
|
1744
|
-
throw new Error('Group not found for match: ' + match.id);
|
|
1745
|
-
group.matches.set(match.id, match);
|
|
1926
|
+
else {
|
|
1927
|
+
relations.push(createTwoToNothingRelation({
|
|
1928
|
+
currentRound: currentUpperRound,
|
|
1929
|
+
previousUpperRound,
|
|
1930
|
+
previousLowerRound: finalLowerRound,
|
|
1931
|
+
firstUpperRound,
|
|
1932
|
+
firstLowerRound,
|
|
1933
|
+
}));
|
|
1934
|
+
}
|
|
1935
|
+
if (isAsyncBracket) {
|
|
1936
|
+
const preFinalLowerRound = lowerRounds[lowerRounds.length - 2];
|
|
1937
|
+
const prePreFinalLowerRound = lowerRounds[lowerRounds.length - 3] || null;
|
|
1938
|
+
if (!preFinalLowerRound)
|
|
1939
|
+
throw new Error('preFinalLowerRound is null');
|
|
1940
|
+
relations.push(createOneToOneRelation({
|
|
1941
|
+
currentRound: finalLowerRound,
|
|
1942
|
+
previousRound: preFinalLowerRound,
|
|
1943
|
+
nextRound: currentUpperRound,
|
|
1944
|
+
rootRound: firstLowerRound,
|
|
1945
|
+
}));
|
|
1946
|
+
if (prePreFinalLowerRound) {
|
|
1947
|
+
relations.push(createOneToOneRelation({
|
|
1948
|
+
currentRound: preFinalLowerRound,
|
|
1949
|
+
previousRound: prePreFinalLowerRound,
|
|
1950
|
+
nextRound: finalLowerRound,
|
|
1951
|
+
rootRound: firstLowerRound,
|
|
1952
|
+
}));
|
|
1746
1953
|
}
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
for (const group of roundSwissData.groups.values()) {
|
|
1753
|
-
if (group.matches.size < group.allowedMatchCount) {
|
|
1754
|
-
group.matches.set(match.id, match);
|
|
1755
|
-
groupFound = true;
|
|
1756
|
-
break;
|
|
1757
|
-
}
|
|
1758
|
-
}
|
|
1759
|
-
if (!groupFound) {
|
|
1760
|
-
throw new Error('No group found for empty match');
|
|
1761
|
-
}
|
|
1954
|
+
else {
|
|
1955
|
+
relations.push(createNothingToOneRelation({
|
|
1956
|
+
currentRound: preFinalLowerRound,
|
|
1957
|
+
nextRound: finalLowerRound,
|
|
1958
|
+
}));
|
|
1762
1959
|
}
|
|
1763
|
-
roundNumber++;
|
|
1764
1960
|
}
|
|
1765
|
-
|
|
1961
|
+
else {
|
|
1962
|
+
if (!previousLowerRound)
|
|
1963
|
+
throw new Error('previousLowerRound is null');
|
|
1964
|
+
relations.push(createOneToOneRelation({
|
|
1965
|
+
currentRound: finalLowerRound,
|
|
1966
|
+
previousRound: previousLowerRound,
|
|
1967
|
+
nextRound: currentUpperRound,
|
|
1968
|
+
rootRound: firstLowerRound,
|
|
1969
|
+
}));
|
|
1970
|
+
}
|
|
1766
1971
|
};
|
|
1767
|
-
const
|
|
1768
|
-
const
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
type: roundType,
|
|
1781
|
-
id: currentItem.round.id,
|
|
1782
|
-
data: currentItem.round,
|
|
1783
|
-
name: currentItem.round.name || currentItem.round.type,
|
|
1784
|
-
};
|
|
1785
|
-
bracketData.rounds.push(bracketRound);
|
|
1786
|
-
for (const match of currentItem.matches) {
|
|
1787
|
-
if (bracketData.matches.some((m) => m.id === match.id)) {
|
|
1788
|
-
throw new Error(`Match with id ${match.id} already exists in the bracket data.`);
|
|
1789
|
-
}
|
|
1790
|
-
const bracketMatch = {
|
|
1791
|
-
id: match.id,
|
|
1792
|
-
data: match,
|
|
1793
|
-
roundId: currentItem.round.id,
|
|
1794
|
-
home: match.home?.id || null,
|
|
1795
|
-
away: match.away?.id || null,
|
|
1796
|
-
winner: match.winningSide,
|
|
1797
|
-
status: match.status === 'published' ? 'completed' : 'pending',
|
|
1798
|
-
};
|
|
1799
|
-
bracketData.matches.push(bracketMatch);
|
|
1800
|
-
}
|
|
1972
|
+
const handleFirstRound = (params) => {
|
|
1973
|
+
const { relations, currentUpperRound, nextUpperRound, lowerRounds, currentUpperRoundIndex } = params;
|
|
1974
|
+
relations.push(createNothingToOneRelation({
|
|
1975
|
+
currentRound: currentUpperRound,
|
|
1976
|
+
nextRound: nextUpperRound,
|
|
1977
|
+
}));
|
|
1978
|
+
const currentLowerRound = lowerRounds[currentUpperRoundIndex] || null;
|
|
1979
|
+
const nextLowerRound = lowerRounds[currentUpperRoundIndex + 1] || null;
|
|
1980
|
+
if (currentLowerRound && nextLowerRound) {
|
|
1981
|
+
relations.push(createNothingToOneRelation({
|
|
1982
|
+
currentRound: currentLowerRound,
|
|
1983
|
+
nextRound: nextLowerRound,
|
|
1984
|
+
}));
|
|
1801
1985
|
}
|
|
1802
|
-
return bracketData;
|
|
1803
1986
|
};
|
|
1804
|
-
const
|
|
1805
|
-
const
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1987
|
+
const handleRegularRound = (params) => {
|
|
1988
|
+
const { relations, currentUpperRound, previousUpperRound, nextUpperRound, lowerRounds, currentUpperRoundIndex, firstUpperRound, firstLowerRound, } = params;
|
|
1989
|
+
relations.push(createOneToOneRelation({
|
|
1990
|
+
currentRound: currentUpperRound,
|
|
1991
|
+
previousRound: previousUpperRound,
|
|
1992
|
+
nextRound: nextUpperRound,
|
|
1993
|
+
rootRound: firstUpperRound,
|
|
1994
|
+
}));
|
|
1995
|
+
const currentLowerRound = lowerRounds[currentUpperRoundIndex] || null;
|
|
1996
|
+
const previousLowerRound = lowerRounds[currentUpperRoundIndex - 1] || null;
|
|
1997
|
+
const nextLowerRound = lowerRounds[currentUpperRoundIndex + 1] || null;
|
|
1998
|
+
if (currentLowerRound &&
|
|
1999
|
+
currentUpperRound.type === DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.UPPER_BRACKET &&
|
|
2000
|
+
previousLowerRound &&
|
|
2001
|
+
nextLowerRound &&
|
|
2002
|
+
firstLowerRound) {
|
|
2003
|
+
relations.push(createOneToOneRelation({
|
|
2004
|
+
currentRound: currentLowerRound,
|
|
2005
|
+
previousRound: previousLowerRound,
|
|
2006
|
+
nextRound: nextLowerRound,
|
|
2007
|
+
rootRound: firstLowerRound,
|
|
2008
|
+
}));
|
|
2009
|
+
}
|
|
2010
|
+
};
|
|
2011
|
+
const generateRoundRelationsNew = (bracketData) => {
|
|
2012
|
+
const relations = [];
|
|
2013
|
+
const allRounds = [...bracketData.rounds.values()];
|
|
2014
|
+
const upperRounds = allRounds.filter((r) => r.type !== DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.LOWER_BRACKET);
|
|
2015
|
+
const lowerRounds = allRounds.filter((r) => r.type === DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.LOWER_BRACKET);
|
|
2016
|
+
const firstUpperRound = upperRounds[0];
|
|
2017
|
+
const firstLowerRound = lowerRounds[0] || null;
|
|
2018
|
+
const lastLowerRound = lowerRounds[lowerRounds.length - 1] || null;
|
|
2019
|
+
if (!firstUpperRound)
|
|
2020
|
+
throw new Error('No upper rounds found');
|
|
2021
|
+
const hasLowerRounds = lowerRounds.length > 0;
|
|
2022
|
+
for (const [currentUpperRoundIndex] of upperRounds.entries()) {
|
|
2023
|
+
const nav = getNavigationContext({
|
|
2024
|
+
upperRounds,
|
|
2025
|
+
currentUpperRoundIndex,
|
|
2026
|
+
});
|
|
2027
|
+
if (nav.isFinal && hasLowerRounds && lastLowerRound && firstLowerRound && nav.previousUpperRound) {
|
|
2028
|
+
handleFinalRound({
|
|
2029
|
+
relations,
|
|
2030
|
+
currentUpperRound: nav.currentUpperRound,
|
|
2031
|
+
previousUpperRound: nav.previousUpperRound,
|
|
2032
|
+
nextUpperRound: nav.nextUpperRound,
|
|
2033
|
+
lowerRounds,
|
|
2034
|
+
currentUpperRoundIndex,
|
|
2035
|
+
firstUpperRound,
|
|
2036
|
+
firstLowerRound,
|
|
2037
|
+
lastLowerRound,
|
|
1833
2038
|
});
|
|
1834
2039
|
}
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
2040
|
+
else if (nav.isLastUpperRound && nav.previousUpperRound) {
|
|
2041
|
+
relations.push(createOneToNothingRelation({
|
|
2042
|
+
currentRound: nav.currentUpperRound,
|
|
2043
|
+
previousRound: nav.previousUpperRound,
|
|
2044
|
+
rootRound: firstUpperRound,
|
|
2045
|
+
}));
|
|
2046
|
+
}
|
|
2047
|
+
else if (nav.currentUpperRound.isFirstRound && nav.nextUpperRound) {
|
|
2048
|
+
handleFirstRound({
|
|
2049
|
+
relations,
|
|
2050
|
+
currentUpperRound: nav.currentUpperRound,
|
|
2051
|
+
nextUpperRound: nav.nextUpperRound,
|
|
2052
|
+
lowerRounds,
|
|
2053
|
+
currentUpperRoundIndex,
|
|
1840
2054
|
});
|
|
1841
2055
|
}
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
2056
|
+
else if (nav.previousUpperRound && nav.nextUpperRound) {
|
|
2057
|
+
handleRegularRound({
|
|
2058
|
+
relations,
|
|
2059
|
+
currentUpperRound: nav.currentUpperRound,
|
|
2060
|
+
previousUpperRound: nav.previousUpperRound,
|
|
2061
|
+
nextUpperRound: nav.nextUpperRound,
|
|
2062
|
+
lowerRounds,
|
|
2063
|
+
currentUpperRoundIndex,
|
|
2064
|
+
firstUpperRound,
|
|
2065
|
+
firstLowerRound,
|
|
2066
|
+
});
|
|
1845
2067
|
}
|
|
1846
2068
|
}
|
|
1847
|
-
return
|
|
2069
|
+
return relations;
|
|
1848
2070
|
};
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
const
|
|
1854
|
-
const
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
let currentLowerBracketIndex = 0;
|
|
1864
|
-
const splitRoundsRest = [];
|
|
1865
|
-
for (const [roundIndex, round] of source.rounds.entries()) {
|
|
1866
|
-
const isLowerBracket = round.type === DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.LOWER_BRACKET;
|
|
1867
|
-
const matches = source.matches.filter((m) => m.roundId === round.id);
|
|
1868
|
-
const roundId = round.id;
|
|
1869
|
-
const shouldSplitRound = shouldSplitRoundsInTwo && matches.length % 2 === 0;
|
|
1870
|
-
const isFirstRound = roundIndex === 0;
|
|
1871
|
-
if (shouldSplitRound) {
|
|
1872
|
-
const firstHalfMatchesMaxIndex = matches.length / 2 - 1;
|
|
1873
|
-
const firstHalfRoundId = `${roundId}--half-1`;
|
|
1874
|
-
const secondHalfRoundId = `${roundId}--half-2`;
|
|
1875
|
-
const bracketRoundFirstHalf = {
|
|
1876
|
-
type: round.type,
|
|
1877
|
-
id: firstHalfRoundId,
|
|
1878
|
-
index: isLowerBracket ? currentLowerBracketIndex : currentUpperBracketIndex,
|
|
1879
|
-
data: round.data,
|
|
1880
|
-
position: ((isLowerBracket ? currentLowerBracketIndex : currentUpperBracketIndex) + 1),
|
|
1881
|
-
name: round.name,
|
|
1882
|
-
matchCount: matches.length / 2,
|
|
1883
|
-
matches: new Map(),
|
|
1884
|
-
mirrorRoundType: BRACKET_ROUND_MIRROR_TYPE.LEFT,
|
|
1885
|
-
isFirstRound,
|
|
1886
|
-
};
|
|
1887
|
-
const bracketRoundSecondHalf = {
|
|
1888
|
-
type: round.type,
|
|
1889
|
-
id: secondHalfRoundId,
|
|
1890
|
-
index: -1,
|
|
1891
|
-
data: round.data,
|
|
1892
|
-
position: -1,
|
|
1893
|
-
name: round.name,
|
|
1894
|
-
matchCount: matches.length / 2,
|
|
1895
|
-
matches: new Map(),
|
|
1896
|
-
mirrorRoundType: BRACKET_ROUND_MIRROR_TYPE.RIGHT,
|
|
1897
|
-
isFirstRound,
|
|
1898
|
-
};
|
|
1899
|
-
bracketData.roundIds.push(firstHalfRoundId);
|
|
1900
|
-
bracketData.rounds.set(firstHalfRoundId, bracketRoundFirstHalf);
|
|
1901
|
-
splitRoundsRest.unshift(bracketRoundSecondHalf);
|
|
1902
|
-
for (let index = 0; index <= firstHalfMatchesMaxIndex; index++) {
|
|
1903
|
-
const matchFirst = matches[index];
|
|
1904
|
-
const matchSecond = matches[firstHalfMatchesMaxIndex + 1 + index];
|
|
1905
|
-
if (!matchFirst || !matchSecond)
|
|
1906
|
-
throw new Error('Match not found');
|
|
1907
|
-
const participantCounter = bracketData.participants.size;
|
|
1908
|
-
const bracketMatchFirst = generateAndSetBracketMatchWithParticipants(matchFirst, {
|
|
1909
|
-
bracketData,
|
|
1910
|
-
bracketRound: bracketRoundFirstHalf,
|
|
1911
|
-
currentIndexInRound: index,
|
|
1912
|
-
participantCounter,
|
|
1913
|
-
});
|
|
1914
|
-
// The participant counter might have changed after the first half
|
|
1915
|
-
const updatedParticipantCounter = bracketData.participants.size;
|
|
1916
|
-
const bracketMatchSecond = generateAndSetBracketMatchWithParticipants(matchSecond, {
|
|
1917
|
-
bracketData,
|
|
1918
|
-
bracketRound: bracketRoundSecondHalf,
|
|
1919
|
-
currentIndexInRound: index,
|
|
1920
|
-
participantCounter: updatedParticipantCounter,
|
|
1921
|
-
});
|
|
1922
|
-
bracketData.matchIds.push(bracketMatchFirst.id, bracketMatchSecond.id);
|
|
1923
|
-
}
|
|
1924
|
-
}
|
|
1925
|
-
else {
|
|
1926
|
-
const bracketRound = {
|
|
1927
|
-
type: round.type,
|
|
1928
|
-
id: roundId,
|
|
1929
|
-
index: isLowerBracket ? currentLowerBracketIndex : currentUpperBracketIndex,
|
|
1930
|
-
data: round.data,
|
|
1931
|
-
position: ((isLowerBracket ? currentLowerBracketIndex : currentUpperBracketIndex) + 1),
|
|
1932
|
-
name: round.name,
|
|
1933
|
-
matchCount: matches.length,
|
|
1934
|
-
matches: new Map(),
|
|
1935
|
-
mirrorRoundType: null,
|
|
1936
|
-
isFirstRound,
|
|
1937
|
-
};
|
|
1938
|
-
bracketData.roundIds.push(roundId);
|
|
1939
|
-
bracketData.rounds.set(roundId, bracketRound);
|
|
1940
|
-
let currentIndexInRound = 0;
|
|
1941
|
-
for (const match of matches) {
|
|
1942
|
-
const participantCounter = bracketData.participants.size;
|
|
1943
|
-
const bracketMatch = generateAndSetBracketMatchWithParticipants(match, {
|
|
1944
|
-
bracketData,
|
|
1945
|
-
bracketRound,
|
|
1946
|
-
currentIndexInRound,
|
|
1947
|
-
participantCounter,
|
|
1948
|
-
});
|
|
1949
|
-
bracketData.matchIds.push(bracketMatch.id);
|
|
1950
|
-
currentIndexInRound++;
|
|
1951
|
-
}
|
|
2071
|
+
|
|
2072
|
+
const createNewBracket = (source, options) => {
|
|
2073
|
+
const bracketNewBase = createNewBracketBase(source, options);
|
|
2074
|
+
const rounds = new BracketMap();
|
|
2075
|
+
const roundsByType = new BracketMap();
|
|
2076
|
+
for (const roundBase of bracketNewBase.rounds.values()) {
|
|
2077
|
+
const newRound = {
|
|
2078
|
+
...roundBase,
|
|
2079
|
+
matches: new BracketMap(),
|
|
2080
|
+
relation: { type: 'dummy' },
|
|
2081
|
+
};
|
|
2082
|
+
rounds.set(roundBase.id, newRound);
|
|
2083
|
+
if (!roundsByType.has(roundBase.type)) {
|
|
2084
|
+
roundsByType.set(roundBase.type, new BracketMap());
|
|
1952
2085
|
}
|
|
1953
|
-
|
|
1954
|
-
|
|
2086
|
+
roundsByType.getOrThrow(roundBase.type).set(roundBase.id, newRound);
|
|
2087
|
+
}
|
|
2088
|
+
const participants = new BracketMap();
|
|
2089
|
+
for (const participantBase of bracketNewBase.participants.values()) {
|
|
2090
|
+
participants.set(participantBase.id, {
|
|
2091
|
+
...participantBase,
|
|
2092
|
+
matches: new BracketMap(),
|
|
2093
|
+
});
|
|
2094
|
+
}
|
|
2095
|
+
const matches = new BracketMap();
|
|
2096
|
+
for (const matchBase of bracketNewBase.matches.values()) {
|
|
2097
|
+
const round = rounds.getOrThrow(matchBase.roundId);
|
|
2098
|
+
const homeParticipant = matchBase.home
|
|
2099
|
+
? { ...matchBase.home, matches: new BracketMap() }
|
|
2100
|
+
: null;
|
|
2101
|
+
const awayParticipant = matchBase.away
|
|
2102
|
+
? { ...matchBase.away, matches: new BracketMap() }
|
|
2103
|
+
: null;
|
|
2104
|
+
const newMatch = {
|
|
2105
|
+
...matchBase,
|
|
2106
|
+
home: homeParticipant,
|
|
2107
|
+
away: awayParticipant,
|
|
2108
|
+
winner: null,
|
|
2109
|
+
round,
|
|
2110
|
+
relation: { type: 'dummy' },
|
|
2111
|
+
};
|
|
2112
|
+
if (matchBase.winner) {
|
|
2113
|
+
const winnerParticipant = homeParticipant?.id === matchBase.winner.id ? homeParticipant : awayParticipant;
|
|
2114
|
+
if (!winnerParticipant)
|
|
2115
|
+
throw new Error(`Winner participant with id ${matchBase.winner.id} not found in match base`);
|
|
2116
|
+
newMatch.winner = winnerParticipant;
|
|
2117
|
+
}
|
|
2118
|
+
matches.set(matchBase.id, newMatch);
|
|
2119
|
+
round.matches.set(matchBase.id, newMatch);
|
|
2120
|
+
if (homeParticipant) {
|
|
2121
|
+
const participant = participants.getOrThrow(homeParticipant.id);
|
|
2122
|
+
participant.matches.set(matchBase.id, {
|
|
2123
|
+
...newMatch,
|
|
2124
|
+
me: homeParticipant,
|
|
2125
|
+
opponent: awayParticipant,
|
|
2126
|
+
});
|
|
1955
2127
|
}
|
|
1956
|
-
|
|
1957
|
-
|
|
2128
|
+
if (awayParticipant) {
|
|
2129
|
+
const participant = participants.getOrThrow(awayParticipant.id);
|
|
2130
|
+
participant.matches.set(matchBase.id, {
|
|
2131
|
+
...newMatch,
|
|
2132
|
+
me: awayParticipant,
|
|
2133
|
+
opponent: homeParticipant,
|
|
2134
|
+
});
|
|
1958
2135
|
}
|
|
1959
2136
|
}
|
|
1960
|
-
|
|
1961
|
-
const
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
splitRound.index = lastRound.index + splitRoundIndex + 1;
|
|
1969
|
-
splitRound.position = (lastRound.position + splitRoundIndex + 1);
|
|
1970
|
-
bracketData.rounds.set(splitRound.id, splitRound);
|
|
1971
|
-
bracketData.roundIds.push(splitRound.id);
|
|
2137
|
+
for (const participant of participants.values()) {
|
|
2138
|
+
for (const match of participant.matches.values()) {
|
|
2139
|
+
if (match.home?.id === participant.id)
|
|
2140
|
+
match.home.matches = participant.matches;
|
|
2141
|
+
if (match.away?.id === participant.id)
|
|
2142
|
+
match.away.matches = participant.matches;
|
|
2143
|
+
if (match.winner?.id === participant.id)
|
|
2144
|
+
match.winner.matches = participant.matches;
|
|
1972
2145
|
}
|
|
1973
2146
|
}
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
// }
|
|
1989
|
-
case 'double-elimination':
|
|
1990
|
-
return TOURNAMENT_MODE.DOUBLE_ELIMINATION;
|
|
1991
|
-
case 'single-elimination':
|
|
1992
|
-
return TOURNAMENT_MODE.SINGLE_ELIMINATION;
|
|
1993
|
-
default:
|
|
1994
|
-
throw new Error(`Unsupported tournament mode: ${source.mode}`);
|
|
2147
|
+
const newBracket = {
|
|
2148
|
+
matches,
|
|
2149
|
+
participants,
|
|
2150
|
+
rounds,
|
|
2151
|
+
roundsByType,
|
|
2152
|
+
mode: bracketNewBase.mode,
|
|
2153
|
+
};
|
|
2154
|
+
const roundRelations = generateRoundRelationsNew(newBracket);
|
|
2155
|
+
for (const roundRelation of roundRelations) {
|
|
2156
|
+
roundRelation.currentRound.relation = roundRelation;
|
|
2157
|
+
}
|
|
2158
|
+
const matchRelations = generateMatchRelationsNew(newBracket);
|
|
2159
|
+
for (const matchRelation of matchRelations) {
|
|
2160
|
+
matchRelation.currentMatch.relation = matchRelation;
|
|
1995
2161
|
}
|
|
2162
|
+
return newBracket;
|
|
1996
2163
|
};
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2164
|
+
|
|
2165
|
+
const pad = (str, len) => str.padEnd(len, ' ');
|
|
2166
|
+
const color = (str, code) => `\x1b[${code}m${str}\x1b[0m`;
|
|
2167
|
+
const roundColor = (str) => color(str, 36); // cyan
|
|
2168
|
+
const arrowColor = (str) => color(str, 90); // gray
|
|
2169
|
+
const labelColor = (str) => color(str, 33); // yellow
|
|
2170
|
+
const factorColor = (str) => color(str, 32); // green
|
|
2171
|
+
const logRoundRelations = (bracketData) => {
|
|
2172
|
+
// Find max round name length for alignment
|
|
2173
|
+
let maxNameLen = 0;
|
|
2174
|
+
for (const round of bracketData.rounds.values()) {
|
|
2175
|
+
maxNameLen = Math.max(maxNameLen, round.name.length);
|
|
2176
|
+
}
|
|
2177
|
+
const colWidth = maxNameLen + 2;
|
|
2178
|
+
// Build rows
|
|
2179
|
+
const rows = [];
|
|
2180
|
+
for (const round of bracketData.rounds.values()) {
|
|
2181
|
+
const relation = round.relation;
|
|
2182
|
+
switch (relation.type) {
|
|
2183
|
+
case 'nothing-to-one':
|
|
2184
|
+
rows.push([
|
|
2185
|
+
labelColor(pad('START', colWidth)),
|
|
2186
|
+
arrowColor('──▶'),
|
|
2187
|
+
roundColor(pad(round.name, colWidth)),
|
|
2188
|
+
arrowColor('──▶'),
|
|
2189
|
+
roundColor(pad(relation.nextRound.name, colWidth)),
|
|
2190
|
+
factorColor(`[${relation.nextRoundMatchFactor}]`),
|
|
2191
|
+
]);
|
|
2192
|
+
break;
|
|
2193
|
+
case 'one-to-nothing':
|
|
2194
|
+
rows.push([
|
|
2195
|
+
roundColor(pad(relation.previousRound.name, colWidth)),
|
|
2196
|
+
arrowColor('──▶'),
|
|
2197
|
+
roundColor(pad(round.name, colWidth)),
|
|
2198
|
+
arrowColor('──▶'),
|
|
2199
|
+
labelColor(pad('END', colWidth)),
|
|
2200
|
+
factorColor(`[${relation.previousRoundMatchFactor}]`),
|
|
2201
|
+
]);
|
|
2202
|
+
break;
|
|
2203
|
+
case 'one-to-one':
|
|
2204
|
+
rows.push([
|
|
2205
|
+
roundColor(pad(relation.previousRound.name, colWidth)),
|
|
2206
|
+
arrowColor('──▶'),
|
|
2207
|
+
roundColor(pad(round.name, colWidth)),
|
|
2208
|
+
arrowColor('──▶'),
|
|
2209
|
+
roundColor(pad(relation.nextRound.name, colWidth)),
|
|
2210
|
+
factorColor(`[Prev: ${relation.previousRoundMatchFactor}, Next: ${relation.nextRoundMatchFactor}]`),
|
|
2211
|
+
]);
|
|
2212
|
+
break;
|
|
2213
|
+
case 'two-to-one':
|
|
2214
|
+
rows.push([
|
|
2215
|
+
roundColor(pad(relation.previousUpperRound.name, colWidth)),
|
|
2216
|
+
arrowColor(' │ '),
|
|
2217
|
+
roundColor(pad(relation.previousLowerRound.name, colWidth)),
|
|
2218
|
+
arrowColor('──▶'),
|
|
2219
|
+
roundColor(pad(round.name, colWidth)),
|
|
2220
|
+
arrowColor('──▶'),
|
|
2221
|
+
roundColor(pad(relation.nextRound.name, colWidth)),
|
|
2222
|
+
factorColor(`[Upper: ${relation.previousUpperRoundMatchFactor}, Lower: ${relation.previousLowerRoundMatchFactor}, Next: ${relation.nextRoundMatchFactor}]`),
|
|
2223
|
+
]);
|
|
2224
|
+
break;
|
|
2225
|
+
case 'two-to-nothing':
|
|
2226
|
+
rows.push([
|
|
2227
|
+
roundColor(pad(relation.previousUpperRound.name, colWidth)),
|
|
2228
|
+
arrowColor(' │'),
|
|
2229
|
+
roundColor(pad(relation.previousLowerRound.name, colWidth)),
|
|
2230
|
+
arrowColor(' ▼'),
|
|
2231
|
+
roundColor(pad(round.name, colWidth)),
|
|
2232
|
+
arrowColor('──▶'),
|
|
2233
|
+
labelColor(pad('END', colWidth)),
|
|
2234
|
+
factorColor(`[Upper: ${relation.previousUpperRoundMatchFactor}, Lower: ${relation.previousLowerRoundMatchFactor}]`),
|
|
2235
|
+
]);
|
|
2236
|
+
break;
|
|
2030
2237
|
}
|
|
2031
|
-
|
|
2032
|
-
|
|
2238
|
+
}
|
|
2239
|
+
// Print header
|
|
2240
|
+
const divider = (label) => {
|
|
2241
|
+
// eslint-disable-next-line no-control-regex
|
|
2242
|
+
const width = rows[0]?.reduce((w, col) => w + col.replace(/\x1b\[[0-9;]*m/g, '').length + 2, 0) || 60;
|
|
2243
|
+
return `\n${'='.repeat(width)}\n${labelColor(label)}\n${'='.repeat(width)}\n`;
|
|
2244
|
+
};
|
|
2245
|
+
console.log(divider('Bracket Structure'));
|
|
2246
|
+
// Print rows
|
|
2247
|
+
for (const row of rows) {
|
|
2248
|
+
console.log(row.join(' '));
|
|
2249
|
+
}
|
|
2250
|
+
console.log();
|
|
2251
|
+
};
|
|
2252
|
+
|
|
2253
|
+
const FIRST_ROUNDS_TYPE = {
|
|
2254
|
+
SINGLE: 'single',
|
|
2255
|
+
DOUBLE: 'double',
|
|
2256
|
+
};
|
|
2257
|
+
const getFirstRounds = (bracketData) => {
|
|
2258
|
+
if (bracketData.mode === TOURNAMENT_MODE.DOUBLE_ELIMINATION) {
|
|
2259
|
+
const upper = bracketData.roundsByType.getOrThrow(DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.UPPER_BRACKET).first();
|
|
2260
|
+
const lower = bracketData.roundsByType.getOrThrow(DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.LOWER_BRACKET).first();
|
|
2261
|
+
if (!upper || !lower) {
|
|
2262
|
+
throw new Error('Upper or lower first round is null');
|
|
2033
2263
|
}
|
|
2264
|
+
return {
|
|
2265
|
+
type: FIRST_ROUNDS_TYPE.DOUBLE,
|
|
2266
|
+
upper,
|
|
2267
|
+
lower,
|
|
2268
|
+
};
|
|
2034
2269
|
}
|
|
2270
|
+
const first = bracketData.rounds.first();
|
|
2271
|
+
if (!first)
|
|
2272
|
+
throw new Error('First round is null');
|
|
2273
|
+
return {
|
|
2274
|
+
type: FIRST_ROUNDS_TYPE.SINGLE,
|
|
2275
|
+
first,
|
|
2276
|
+
};
|
|
2035
2277
|
};
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2278
|
+
|
|
2279
|
+
const factorialCache = new Map();
|
|
2280
|
+
const getAvailableSwissGroupsForRound = (roundNumber, totalMatchesInRound) => {
|
|
2281
|
+
const ADVANCE_WINS = 3;
|
|
2282
|
+
const ELIMINATE_LOSSES = 3;
|
|
2283
|
+
// Cache factorial calculations
|
|
2284
|
+
const getFactorial = (n) => {
|
|
2285
|
+
if (n <= 1)
|
|
2286
|
+
return 1;
|
|
2287
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2288
|
+
if (factorialCache.has(n))
|
|
2289
|
+
return factorialCache.get(n);
|
|
2290
|
+
const result = n * getFactorial(n - 1);
|
|
2291
|
+
factorialCache.set(n, result);
|
|
2292
|
+
return result;
|
|
2042
2293
|
};
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2294
|
+
// Pre-calculate roundFactorial
|
|
2295
|
+
const roundFactorial = getFactorial(roundNumber);
|
|
2296
|
+
let totalCombinations = 0;
|
|
2297
|
+
const validGroups = [];
|
|
2298
|
+
// Single loop to gather valid groups and total combinations
|
|
2299
|
+
for (let wins = roundNumber; wins >= 0; wins--) {
|
|
2300
|
+
const losses = roundNumber - wins;
|
|
2301
|
+
const remainingGames = ADVANCE_WINS + ELIMINATE_LOSSES - (wins + losses) - 1;
|
|
2302
|
+
const notYetEliminated = losses < ELIMINATE_LOSSES;
|
|
2303
|
+
const canStillAdvance = wins < ADVANCE_WINS && remainingGames >= 0;
|
|
2304
|
+
if (!canStillAdvance || !notYetEliminated)
|
|
2305
|
+
continue;
|
|
2306
|
+
const combinations = roundFactorial / (getFactorial(wins) * getFactorial(losses));
|
|
2307
|
+
totalCombinations += combinations;
|
|
2308
|
+
validGroups.push({ wins, losses, combinations });
|
|
2050
2309
|
}
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2310
|
+
// Create final groups with calculated proportions
|
|
2311
|
+
return validGroups.map(({ wins, losses, combinations }) => ({
|
|
2312
|
+
id: `${wins}-${losses}`,
|
|
2313
|
+
name: `${wins}-${losses}`,
|
|
2314
|
+
matchesInGroup: Math.round((combinations / totalCombinations) * totalMatchesInRound),
|
|
2315
|
+
}));
|
|
2316
|
+
};
|
|
2317
|
+
const generateBracketRoundSwissGroupMaps = (bracketData) => {
|
|
2318
|
+
if (bracketData.mode !== TOURNAMENT_MODE.SWISS_WITH_ELIMINATION) {
|
|
2319
|
+
return null;
|
|
2320
|
+
}
|
|
2321
|
+
const roundsWithSwissGroups = new Map();
|
|
2322
|
+
let roundNumber = 0;
|
|
2323
|
+
for (const bracketRound of bracketData.rounds.values()) {
|
|
2324
|
+
const availableGroups = getAvailableSwissGroupsForRound(roundNumber, bracketRound.matchCount);
|
|
2325
|
+
const roundSwissData = {
|
|
2326
|
+
groups: new Map(),
|
|
2061
2327
|
};
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
home: match.homeMatchSide.participant?.id || null,
|
|
2069
|
-
away: match.awayMatchSide.participant?.id || null,
|
|
2070
|
-
winner: match.winningSide,
|
|
2071
|
-
status: match.status === 'completed' ? 'completed' : 'pending',
|
|
2328
|
+
for (const group of availableGroups) {
|
|
2329
|
+
const subGroup = {
|
|
2330
|
+
id: group.id,
|
|
2331
|
+
name: group.name,
|
|
2332
|
+
matches: new BracketMap(),
|
|
2333
|
+
allowedMatchCount: group.matchesInGroup,
|
|
2072
2334
|
};
|
|
2073
|
-
|
|
2335
|
+
roundSwissData.groups.set(group.id, subGroup);
|
|
2336
|
+
}
|
|
2337
|
+
const emptyMatchIds = [];
|
|
2338
|
+
for (const match of bracketRound.matches.values()) {
|
|
2339
|
+
const anyParticipant = match.home || match.away;
|
|
2340
|
+
if (!anyParticipant) {
|
|
2341
|
+
emptyMatchIds.push(match.id);
|
|
2342
|
+
continue;
|
|
2343
|
+
}
|
|
2344
|
+
const wins = anyParticipant.winCount;
|
|
2345
|
+
const losses = anyParticipant.lossCount;
|
|
2346
|
+
const group = roundSwissData.groups.get(`${wins}-${losses}`);
|
|
2347
|
+
if (!group)
|
|
2348
|
+
throw new Error('Group not found for match: ' + match.id);
|
|
2349
|
+
group.matches.set(match.id, match);
|
|
2350
|
+
}
|
|
2351
|
+
for (const emptyMatchId of emptyMatchIds) {
|
|
2352
|
+
const match = bracketRound.matches.getOrThrow(emptyMatchId);
|
|
2353
|
+
let groupFound = false;
|
|
2354
|
+
for (const group of roundSwissData.groups.values()) {
|
|
2355
|
+
if (group.matches.size < group.allowedMatchCount) {
|
|
2356
|
+
group.matches.set(match.id, match);
|
|
2357
|
+
groupFound = true;
|
|
2358
|
+
break;
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
if (!groupFound) {
|
|
2362
|
+
throw new Error('No group found for empty match');
|
|
2363
|
+
}
|
|
2074
2364
|
}
|
|
2365
|
+
roundNumber++;
|
|
2075
2366
|
}
|
|
2076
|
-
return
|
|
2367
|
+
return roundsWithSwissGroups;
|
|
2077
2368
|
};
|
|
2078
2369
|
|
|
2079
2370
|
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}" />`;
|
|
2080
|
-
const
|
|
2081
|
-
const
|
|
2082
|
-
|
|
2083
|
-
const
|
|
2084
|
-
const
|
|
2085
|
-
const startingCurveAmount = options.lineStartingCurveAmount;
|
|
2086
|
-
const endingCurveAmount = options.lineEndingCurveAmount;
|
|
2087
|
-
const totalSpace = toInline - fromInline;
|
|
2088
|
-
const totalSpaceMinusCurve = totalSpace - startingCurveAmount - endingCurveAmount;
|
|
2089
|
-
const lineLength = totalSpaceMinusCurve / 2;
|
|
2090
|
-
const straightLeftEnd = fromInline + lineLength;
|
|
2091
|
-
const straightRightStart = toInline - lineLength;
|
|
2092
|
-
// first 90 degree curve down
|
|
2093
|
-
const firstCurveStartX = straightLeftEnd;
|
|
2094
|
-
const firstCurveEndX = straightLeftEnd + startingCurveAmount;
|
|
2095
|
-
const firstCurveEndY = direction === 'down' ? fromBlock + startingCurveAmount : fromBlock - startingCurveAmount;
|
|
2096
|
-
const firstCurveBezierX = straightLeftEnd + startingCurveAmount;
|
|
2097
|
-
const firstCurveBezierY = fromBlock;
|
|
2098
|
-
// second 90 degree curve to the right
|
|
2099
|
-
const secondCurveStartY = direction === 'down' ? toBlock - endingCurveAmount : toBlock + endingCurveAmount;
|
|
2100
|
-
const secondCurveEndX = straightRightStart;
|
|
2101
|
-
const secondCurveEndY = toBlock;
|
|
2102
|
-
const secondCurveBezierX = straightRightStart - endingCurveAmount;
|
|
2103
|
-
const secondCurveBezierY = toBlock;
|
|
2104
|
-
const pathStr = `
|
|
2105
|
-
M ${fromInline} ${fromBlock}
|
|
2106
|
-
H ${firstCurveStartX}
|
|
2107
|
-
Q ${firstCurveBezierX} ${firstCurveBezierY}, ${firstCurveEndX} ${firstCurveEndY}
|
|
2108
|
-
V ${secondCurveStartY}
|
|
2109
|
-
Q ${secondCurveBezierX} ${secondCurveBezierY}, ${secondCurveEndX} ${secondCurveEndY}
|
|
2110
|
-
H ${toInline}
|
|
2111
|
-
`;
|
|
2112
|
-
return path(pathStr, options.path);
|
|
2113
|
-
};
|
|
2114
|
-
const curveInverted = (from, to, direction, options) => {
|
|
2115
|
-
const fromInline = from.inline.start;
|
|
2371
|
+
const curvePath = (from, to, direction, options) => {
|
|
2372
|
+
const inverted = options.inverted ?? false;
|
|
2373
|
+
// Inline/block coordinates depending on direction and inversion
|
|
2374
|
+
const fromInline = inverted ? from.inline.start : from.inline.end;
|
|
2375
|
+
const toInline = inverted ? to.inline.end : to.inline.start;
|
|
2116
2376
|
const fromBlock = from.block.center;
|
|
2117
|
-
const toInline = to.inline.end;
|
|
2118
2377
|
const toBlock = to.block.center;
|
|
2119
|
-
|
|
2120
|
-
const
|
|
2121
|
-
const
|
|
2122
|
-
const
|
|
2123
|
-
const
|
|
2124
|
-
|
|
2125
|
-
const
|
|
2126
|
-
const
|
|
2127
|
-
|
|
2128
|
-
const
|
|
2129
|
-
const
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
const
|
|
2133
|
-
const secondCurveEndX = straightLeftStart;
|
|
2378
|
+
// Curve parameters
|
|
2379
|
+
const startCurve = options.lineStartingCurveAmount;
|
|
2380
|
+
const endCurve = options.lineEndingCurveAmount;
|
|
2381
|
+
const totalInline = Math.abs(toInline - fromInline);
|
|
2382
|
+
const straightLength = (totalInline - startCurve - endCurve) / 2;
|
|
2383
|
+
// Calculate key points for the path
|
|
2384
|
+
const straightEnd = inverted ? fromInline - straightLength : fromInline + straightLength;
|
|
2385
|
+
const straightStart = inverted ? toInline + straightLength : toInline - straightLength;
|
|
2386
|
+
// First curve (from start)
|
|
2387
|
+
const firstCurveEndX = inverted ? straightEnd - startCurve : straightEnd + startCurve;
|
|
2388
|
+
const firstCurveEndY = direction === 'down' ? fromBlock + startCurve : fromBlock - startCurve;
|
|
2389
|
+
// Second curve (to end)
|
|
2390
|
+
const secondCurveStartY = direction === 'down' ? toBlock - endCurve : toBlock + endCurve;
|
|
2391
|
+
const secondCurveEndX = straightStart;
|
|
2134
2392
|
const secondCurveEndY = toBlock;
|
|
2135
|
-
const secondCurveBezierX =
|
|
2136
|
-
|
|
2137
|
-
const
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
return path(
|
|
2393
|
+
const secondCurveBezierX = inverted ? straightStart + endCurve : straightStart - endCurve;
|
|
2394
|
+
// SVG path string
|
|
2395
|
+
const d = [
|
|
2396
|
+
`M ${fromInline} ${fromBlock}`,
|
|
2397
|
+
`H ${straightEnd}`,
|
|
2398
|
+
`Q ${firstCurveEndX} ${fromBlock}, ${firstCurveEndX} ${firstCurveEndY}`,
|
|
2399
|
+
`V ${secondCurveStartY}`,
|
|
2400
|
+
`Q ${secondCurveBezierX} ${toBlock}, ${secondCurveEndX} ${secondCurveEndY}`,
|
|
2401
|
+
`H ${toInline}`,
|
|
2402
|
+
].join(' ');
|
|
2403
|
+
return path(d, options.path);
|
|
2146
2404
|
};
|
|
2147
|
-
const
|
|
2405
|
+
const linePath = (from, to, options) => {
|
|
2148
2406
|
return path(`M ${from.inline.end} ${from.block.center} L ${to.inline.start} ${to.block.center}`, options.path);
|
|
2149
2407
|
};
|
|
2150
2408
|
const getMatchPosition = (roundItem, matchId, staticMeasurements) => {
|
|
@@ -2236,24 +2494,25 @@ const drawMan = (items, firstRounds, dimensions) => {
|
|
|
2236
2494
|
case 'one-to-one': {
|
|
2237
2495
|
const previousMatchPosition = getMatchPosition(items.get(item.matchRelation.previousRound.id), item.matchRelation.previousMatch.id, staticMeasurements);
|
|
2238
2496
|
// draw a straight line
|
|
2239
|
-
svgParts.push(
|
|
2497
|
+
svgParts.push(linePath(previousMatchPosition, currentMatchPosition, { path: pathOptions }));
|
|
2240
2498
|
break;
|
|
2241
2499
|
}
|
|
2242
2500
|
case 'two-to-nothing':
|
|
2243
2501
|
case 'two-to-one': {
|
|
2244
2502
|
const previousUpper = getMatchPosition(items.get(item.matchRelation.previousUpperRound.id), item.matchRelation.previousUpperMatch.id, staticMeasurements);
|
|
2245
2503
|
const previousLower = getMatchPosition(items.get(item.matchRelation.previousLowerRound.id), item.matchRelation.previousLowerMatch.id, staticMeasurements);
|
|
2504
|
+
const invertCurve = item.roundRelation.currentRound.mirrorRoundType === BRACKET_ROUND_MIRROR_TYPE.RIGHT;
|
|
2246
2505
|
const curveOptions = {
|
|
2247
2506
|
...dimensions.curve,
|
|
2507
|
+
inverted: invertCurve,
|
|
2248
2508
|
path: { ...dimensions.path, className: '' },
|
|
2249
2509
|
};
|
|
2250
|
-
const curveFn = item.roundRelation.currentRound.mirrorRoundType === BRACKET_ROUND_MIRROR_TYPE.RIGHT ? curveInverted : curve;
|
|
2251
2510
|
// draw two lines that merge into one in the middle
|
|
2252
|
-
svgParts.push(
|
|
2511
|
+
svgParts.push(curvePath(previousUpper, currentMatchPosition, 'down', {
|
|
2253
2512
|
...curveOptions,
|
|
2254
2513
|
path: { ...curveOptions.path, className: item.matchRelation.previousUpperMatch.winner?.shortId || '' },
|
|
2255
2514
|
}));
|
|
2256
|
-
svgParts.push(
|
|
2515
|
+
svgParts.push(curvePath(previousLower, currentMatchPosition, 'up', {
|
|
2257
2516
|
...curveOptions,
|
|
2258
2517
|
path: { ...curveOptions.path, className: item.matchRelation.previousLowerMatch.winner?.shortId || '' },
|
|
2259
2518
|
}));
|
|
@@ -2262,7 +2521,7 @@ const drawMan = (items, firstRounds, dimensions) => {
|
|
|
2262
2521
|
item.matchRelation.nextRound.mirrorRoundType === null) {
|
|
2263
2522
|
// draw a straight line for the special case of connecting the final match to the mirrored semi final match
|
|
2264
2523
|
const next = getMatchPosition(items.get(item.matchRelation.nextRound.id), item.matchRelation.nextMatch.id, staticMeasurements);
|
|
2265
|
-
svgParts.push(
|
|
2524
|
+
svgParts.push(linePath(next, currentMatchPosition, { path: pathOptions }));
|
|
2266
2525
|
}
|
|
2267
2526
|
break;
|
|
2268
2527
|
}
|
|
@@ -2272,14 +2531,18 @@ const drawMan = (items, firstRounds, dimensions) => {
|
|
|
2272
2531
|
return svgParts.join('');
|
|
2273
2532
|
};
|
|
2274
2533
|
|
|
2275
|
-
const generateColumnCount = (bracketData
|
|
2276
|
-
const thirdPlaceRoundSize =
|
|
2534
|
+
const generateColumnCount = (bracketData) => {
|
|
2535
|
+
const thirdPlaceRoundSize = bracketData.roundsByType.get(COMMON_BRACKET_ROUND_TYPE.THIRD_PLACE)?.size || 0;
|
|
2277
2536
|
switch (bracketData.mode) {
|
|
2278
2537
|
case TOURNAMENT_MODE.DOUBLE_ELIMINATION: {
|
|
2279
|
-
const upperBracketSize =
|
|
2280
|
-
const lowerBracketSize =
|
|
2538
|
+
const upperBracketSize = bracketData.roundsByType.get(DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.UPPER_BRACKET)?.size || 0;
|
|
2539
|
+
const lowerBracketSize = bracketData.roundsByType.get(DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.LOWER_BRACKET)?.size || 0;
|
|
2281
2540
|
// the total columns are the bigger number of winner bracket rounds vs looser bracket rounds + all other rounds excluding third place
|
|
2282
|
-
return bracketData.rounds.size -
|
|
2541
|
+
return (bracketData.rounds.size -
|
|
2542
|
+
upperBracketSize -
|
|
2543
|
+
lowerBracketSize -
|
|
2544
|
+
thirdPlaceRoundSize +
|
|
2545
|
+
Math.max(upperBracketSize, lowerBracketSize));
|
|
2283
2546
|
}
|
|
2284
2547
|
default: {
|
|
2285
2548
|
// the total columns are the amount of rounds excluding third place
|
|
@@ -2287,13 +2550,15 @@ const generateColumnCount = (bracketData, roundTypeMap) => {
|
|
|
2287
2550
|
}
|
|
2288
2551
|
}
|
|
2289
2552
|
};
|
|
2290
|
-
const generateRowCount = (bracketData,
|
|
2553
|
+
const generateRowCount = (bracketData, options) => {
|
|
2291
2554
|
switch (bracketData.mode) {
|
|
2292
2555
|
case TOURNAMENT_MODE.DOUBLE_ELIMINATION: {
|
|
2293
|
-
const upperBracketSize =
|
|
2294
|
-
.
|
|
2295
|
-
|
|
2296
|
-
|
|
2556
|
+
const upperBracketSize = bracketData.roundsByType
|
|
2557
|
+
.getOrThrow(DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.UPPER_BRACKET)
|
|
2558
|
+
.first()?.matchCount;
|
|
2559
|
+
const lowerBracketSize = bracketData.roundsByType
|
|
2560
|
+
.getOrThrow(DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.LOWER_BRACKET)
|
|
2561
|
+
?.first()?.matchCount;
|
|
2297
2562
|
const roundHeadersCount = options.includeRoundHeaders ? 2 : 0;
|
|
2298
2563
|
if (upperBracketSize === undefined || lowerBracketSize === undefined) {
|
|
2299
2564
|
throw new Error('Upper or lower bracket size is undefined');
|
|
@@ -2312,42 +2577,14 @@ const generateRowCount = (bracketData, roundTypeMap, options) => {
|
|
|
2312
2577
|
}
|
|
2313
2578
|
}
|
|
2314
2579
|
};
|
|
2315
|
-
const generateBracketGridDefinitions = (bracketData,
|
|
2316
|
-
const columnCount = generateColumnCount(bracketData
|
|
2317
|
-
const rowCount = generateRowCount(bracketData,
|
|
2580
|
+
const generateBracketGridDefinitions = (bracketData, options) => {
|
|
2581
|
+
const columnCount = generateColumnCount(bracketData);
|
|
2582
|
+
const rowCount = generateRowCount(bracketData, options);
|
|
2318
2583
|
return { columnCount, rowCount };
|
|
2319
2584
|
};
|
|
2320
2585
|
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
this.bracketRound = input.required();
|
|
2324
|
-
this.bracketMatch = input.required();
|
|
2325
|
-
}
|
|
2326
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: NewBracketDefaultMatchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2327
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.0.3", type: NewBracketDefaultMatchComponent, isStandalone: true, selector: "et-new-bracket-default-match", inputs: { bracketRound: { classPropertyName: "bracketRound", publicName: "bracketRound", isSignal: true, isRequired: true, transformFunction: null }, bracketMatch: { classPropertyName: "bracketMatch", publicName: "bracketMatch", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "et-new-bracket-default-match-host" }, ngImport: i0, template: ` {{ bracketMatch().id }} `, isInline: true, styles: [".et-new-bracket-default-match-host{display:block;padding:8px;border:1px solid yellow;inline-size:250px;block-size:75px;display:flex;justify-content:center;align-items:center;box-sizing:border-box;font-size:12px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
2328
|
-
}
|
|
2329
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: NewBracketDefaultMatchComponent, decorators: [{
|
|
2330
|
-
type: Component,
|
|
2331
|
-
args: [{ selector: 'et-new-bracket-default-match', template: ` {{ bracketMatch().id }} `, standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
|
|
2332
|
-
class: 'et-new-bracket-default-match-host',
|
|
2333
|
-
}, styles: [".et-new-bracket-default-match-host{display:block;padding:8px;border:1px solid yellow;inline-size:250px;block-size:75px;display:flex;justify-content:center;align-items:center;box-sizing:border-box;font-size:12px}\n"] }]
|
|
2334
|
-
}] });
|
|
2335
|
-
|
|
2336
|
-
class NewBracketDefaultRoundHeaderComponent {
|
|
2337
|
-
constructor() {
|
|
2338
|
-
this.bracketRound = input.required();
|
|
2339
|
-
}
|
|
2340
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: NewBracketDefaultRoundHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2341
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.0.3", type: NewBracketDefaultRoundHeaderComponent, isStandalone: true, selector: "et-new-bracket-default-round-header", inputs: { bracketRound: { classPropertyName: "bracketRound", publicName: "bracketRound", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "et-new-bracket-default-round-header-host" }, ngImport: i0, template: ` {{ bracketRound().name }} `, isInline: true, styles: [".et-new-bracket-default-round-header-host{display:block;padding:8px;border:1px solid green;inline-size:250px;block-size:50px;display:flex;justify-content:center;align-items:center;box-sizing:border-box}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
2342
|
-
}
|
|
2343
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: NewBracketDefaultRoundHeaderComponent, decorators: [{
|
|
2344
|
-
type: Component,
|
|
2345
|
-
args: [{ selector: 'et-new-bracket-default-round-header', template: ` {{ bracketRound().name }} `, standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
|
|
2346
|
-
class: 'et-new-bracket-default-round-header-host',
|
|
2347
|
-
}, styles: [".et-new-bracket-default-round-header-host{display:block;padding:8px;border:1px solid green;inline-size:250px;block-size:50px;display:flex;justify-content:center;align-items:center;box-sizing:border-box}\n"] }]
|
|
2348
|
-
}] });
|
|
2349
|
-
|
|
2350
|
-
const generateBracketGridItems = (bracketData, roundTypeMap, swissGroups, roundRelations, matchRelations, options) => {
|
|
2586
|
+
const generateBracketGridItems = (data) => {
|
|
2587
|
+
const { bracketData, swissGroups, options } = data;
|
|
2351
2588
|
const roundHeaderComponent = options.headerComponent ?? NewBracketDefaultRoundHeaderComponent;
|
|
2352
2589
|
const matchComponent = options.matchComponent ?? NewBracketDefaultMatchComponent;
|
|
2353
2590
|
const finalMatchComponent = options.finalMatchComponent ?? matchComponent;
|
|
@@ -2357,32 +2594,33 @@ const generateBracketGridItems = (bracketData, roundTypeMap, swissGroups, roundR
|
|
|
2357
2594
|
matchComponent: matchComponent,
|
|
2358
2595
|
finalMatchComponent: finalMatchComponent,
|
|
2359
2596
|
};
|
|
2597
|
+
const config = {
|
|
2598
|
+
bracketData,
|
|
2599
|
+
swissGroups,
|
|
2600
|
+
options: optionsWithDefaults,
|
|
2601
|
+
};
|
|
2360
2602
|
switch (bracketData.mode) {
|
|
2361
2603
|
case TOURNAMENT_MODE.DOUBLE_ELIMINATION:
|
|
2362
|
-
return generateDoubleEliminationGridPlacements(
|
|
2604
|
+
return generateDoubleEliminationGridPlacements(config);
|
|
2363
2605
|
case TOURNAMENT_MODE.SWISS_WITH_ELIMINATION: {
|
|
2364
|
-
|
|
2365
|
-
throw new Error('Swiss groups are required for swiss with elimination mode');
|
|
2366
|
-
return generateSwissWithEliminationGridPlacements(bracketData, roundTypeMap, swissGroups, roundRelations, matchRelations, optionsWithDefaults);
|
|
2606
|
+
return generateSwissWithEliminationGridPlacements(config);
|
|
2367
2607
|
}
|
|
2368
2608
|
default:
|
|
2369
|
-
return generateGenericGridPlacements(
|
|
2609
|
+
return generateGenericGridPlacements(config);
|
|
2370
2610
|
}
|
|
2371
2611
|
};
|
|
2372
|
-
const generateGenericGridPlacements = (
|
|
2612
|
+
const generateGenericGridPlacements = (config) => {
|
|
2373
2613
|
const gridItems = new Map();
|
|
2374
|
-
const
|
|
2614
|
+
const { bracketData, options } = config;
|
|
2615
|
+
const firstRound = bracketData.rounds.first();
|
|
2375
2616
|
if (!firstRound) {
|
|
2376
2617
|
throw new Error('First round is missing');
|
|
2377
2618
|
}
|
|
2378
2619
|
for (const round of bracketData.rounds.values()) {
|
|
2379
|
-
const roundRelation = roundRelations.get(round.id);
|
|
2380
2620
|
let currentMatchRow = 1;
|
|
2381
|
-
const columnStart = round.
|
|
2621
|
+
const columnStart = round.logicalIndex + 1;
|
|
2382
2622
|
const columnEnd = columnStart + 1;
|
|
2383
|
-
|
|
2384
|
-
throw new Error('Round relation is missing');
|
|
2385
|
-
}
|
|
2623
|
+
const roundRelation = round.relation;
|
|
2386
2624
|
if (roundRelation.type === 'two-to-nothing' || roundRelation.type === 'two-to-one') {
|
|
2387
2625
|
throw new Error(`Invalid relation type ${roundRelation.type}`);
|
|
2388
2626
|
}
|
|
@@ -2391,6 +2629,8 @@ const generateGenericGridPlacements = (bracketData, roundTypeMap, roundRelations
|
|
|
2391
2629
|
layoutId: `${round.id}-layout`,
|
|
2392
2630
|
columnStart,
|
|
2393
2631
|
columnEnd,
|
|
2632
|
+
rowStart: 1,
|
|
2633
|
+
rowEnd: firstRound.matchCount + 1,
|
|
2394
2634
|
roundRelation,
|
|
2395
2635
|
items: new Map(),
|
|
2396
2636
|
};
|
|
@@ -2413,10 +2653,7 @@ const generateGenericGridPlacements = (bracketData, roundTypeMap, roundRelations
|
|
|
2413
2653
|
const rootRoundMatchFactor = roundRelation.type !== 'nothing-to-one' ? roundRelation.rootRoundMatchFactor : null;
|
|
2414
2654
|
const matchHeight = rootRoundMatchFactor ? rootRoundMatchFactor : 1;
|
|
2415
2655
|
for (const match of round.matches.values()) {
|
|
2416
|
-
const matchRelation =
|
|
2417
|
-
if (!matchRelation) {
|
|
2418
|
-
throw new Error('Match relation is missing');
|
|
2419
|
-
}
|
|
2656
|
+
const matchRelation = match.relation;
|
|
2420
2657
|
const baseRow = match.indexInRound * matchHeight;
|
|
2421
2658
|
const participantShortIds = [match.home?.shortId, match.away?.shortId].filter((id) => !!id).join(' ');
|
|
2422
2659
|
const matchItem = {
|
|
@@ -2440,11 +2677,131 @@ const generateGenericGridPlacements = (bracketData, roundTypeMap, roundRelations
|
|
|
2440
2677
|
}
|
|
2441
2678
|
return gridItems;
|
|
2442
2679
|
};
|
|
2443
|
-
const generateDoubleEliminationGridPlacements = (
|
|
2680
|
+
const generateDoubleEliminationGridPlacements = (config) => {
|
|
2444
2681
|
const gridItems = new Map();
|
|
2682
|
+
const { bracketData, options } = config;
|
|
2683
|
+
const upperBracketRounds = bracketData.roundsByType.getOrThrow(DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.UPPER_BRACKET);
|
|
2684
|
+
const lowerBracketRounds = bracketData.roundsByType.getOrThrow(DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.LOWER_BRACKET);
|
|
2685
|
+
const hasReverseFinal = !!bracketData.roundsByType.get(DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.REVERSE_FINAL);
|
|
2686
|
+
const firstUpperRound = upperBracketRounds.first();
|
|
2687
|
+
const firstLowerRound = lowerBracketRounds.first();
|
|
2688
|
+
const headerRowMod = options.includeRoundHeaders ? 1 : 0;
|
|
2689
|
+
if (!firstUpperRound) {
|
|
2690
|
+
throw new Error('First round is missing');
|
|
2691
|
+
}
|
|
2692
|
+
if (!firstLowerRound) {
|
|
2693
|
+
throw new Error('First lower round is missing');
|
|
2694
|
+
}
|
|
2695
|
+
const roundDelta = lowerBracketRounds.size - upperBracketRounds.size;
|
|
2696
|
+
const isAsyncBracket = roundDelta === 2;
|
|
2697
|
+
let currentUpperColumn = 1;
|
|
2698
|
+
for (const round of bracketData.rounds.values()) {
|
|
2699
|
+
let currentMatchRow = 1;
|
|
2700
|
+
const columnStart = round.logicalIndex + 1;
|
|
2701
|
+
const columnEnd = columnStart + 1;
|
|
2702
|
+
const roundRelation = round.relation;
|
|
2703
|
+
const isUpperBracket = round.type === DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.UPPER_BRACKET;
|
|
2704
|
+
const isUpperNeitherStartNorEnd = !round.isFirstOfType && !round.isLastOfType && isUpperBracket;
|
|
2705
|
+
const upperRowSpan = isUpperNeitherStartNorEnd || (isAsyncBracket && round.isLastOfType) ? 2 : 1;
|
|
2706
|
+
const isLowerBracket = round.type === DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.LOWER_BRACKET;
|
|
2707
|
+
const upperBracketRowStart = 1;
|
|
2708
|
+
const upperBracketRowEnd = firstUpperRound.matchCount + headerRowMod + 1;
|
|
2709
|
+
const lowerBracketRowStart = upperBracketRowEnd;
|
|
2710
|
+
const lowerBracketRowEnd = lowerBracketRowStart + headerRowMod + firstLowerRound.matchCount;
|
|
2711
|
+
const fullBracketRowStart = 1;
|
|
2712
|
+
const fullBracketRowEnd = lowerBracketRowEnd;
|
|
2713
|
+
const roundRowStart = isUpperBracket
|
|
2714
|
+
? upperBracketRowStart
|
|
2715
|
+
: isLowerBracket
|
|
2716
|
+
? lowerBracketRowStart
|
|
2717
|
+
: fullBracketRowStart;
|
|
2718
|
+
const roundRowEnd = isUpperBracket ? upperBracketRowEnd : isLowerBracket ? lowerBracketRowEnd : fullBracketRowEnd;
|
|
2719
|
+
const upperBracketColumnStart = currentUpperColumn;
|
|
2720
|
+
const upperBracketColumnEnd = currentUpperColumn + upperRowSpan;
|
|
2721
|
+
currentUpperColumn += upperRowSpan;
|
|
2722
|
+
const lowerBracketColumnStart = columnStart;
|
|
2723
|
+
const lowerBracketColumnEnd = columnEnd;
|
|
2724
|
+
const fullBracketColumnStart = columnStart;
|
|
2725
|
+
const fullBracketColumnEnd = columnEnd;
|
|
2726
|
+
const roundColumnStart = isUpperBracket
|
|
2727
|
+
? upperBracketColumnStart
|
|
2728
|
+
: isLowerBracket
|
|
2729
|
+
? lowerBracketColumnStart
|
|
2730
|
+
: fullBracketColumnStart;
|
|
2731
|
+
const roundColumnEnd = isUpperBracket
|
|
2732
|
+
? upperBracketColumnEnd
|
|
2733
|
+
: isLowerBracket
|
|
2734
|
+
? lowerBracketColumnEnd
|
|
2735
|
+
: fullBracketColumnEnd;
|
|
2736
|
+
const roundItem = {
|
|
2737
|
+
id: round.id,
|
|
2738
|
+
layoutId: `${round.id}-layout`,
|
|
2739
|
+
columnStart: roundColumnStart,
|
|
2740
|
+
columnEnd: roundColumnEnd,
|
|
2741
|
+
rowStart: roundRowStart,
|
|
2742
|
+
rowEnd: roundRowEnd,
|
|
2743
|
+
roundRelation,
|
|
2744
|
+
items: new Map(),
|
|
2745
|
+
};
|
|
2746
|
+
if (options.includeRoundHeaders) {
|
|
2747
|
+
const roundHeaderItem = {
|
|
2748
|
+
type: 'round',
|
|
2749
|
+
id: round.id,
|
|
2750
|
+
layoutId: `${round.id}-layout`,
|
|
2751
|
+
rowStart: currentMatchRow,
|
|
2752
|
+
rowEnd: ++currentMatchRow,
|
|
2753
|
+
component: options.headerComponent,
|
|
2754
|
+
className: 'et-bracket-new-item et-bracket-round-header-container',
|
|
2755
|
+
roundRelation,
|
|
2756
|
+
data: {
|
|
2757
|
+
bracketRound: round,
|
|
2758
|
+
},
|
|
2759
|
+
};
|
|
2760
|
+
roundItem.items.set(round.id, roundHeaderItem);
|
|
2761
|
+
}
|
|
2762
|
+
const rootRoundMatchFactor = roundRelation.type !== 'nothing-to-one' &&
|
|
2763
|
+
roundRelation.type !== 'two-to-nothing' &&
|
|
2764
|
+
roundRelation.type !== 'two-to-one'
|
|
2765
|
+
? roundRelation.rootRoundMatchFactor
|
|
2766
|
+
: roundRelation.type === 'two-to-nothing' || roundRelation.type === 'two-to-one'
|
|
2767
|
+
? roundRelation.upperRootRoundMatchFactor
|
|
2768
|
+
: null;
|
|
2769
|
+
const matchHeight = rootRoundMatchFactor ? rootRoundMatchFactor : 1;
|
|
2770
|
+
for (const match of round.matches.values()) {
|
|
2771
|
+
const matchRelation = match.relation;
|
|
2772
|
+
const baseRow = match.indexInRound * matchHeight;
|
|
2773
|
+
const participantShortIds = [match.home?.shortId, match.away?.shortId].filter((id) => !!id).join(' ');
|
|
2774
|
+
const matchItem = {
|
|
2775
|
+
type: 'match',
|
|
2776
|
+
id: match.id,
|
|
2777
|
+
layoutId: `${match.id}-layout`,
|
|
2778
|
+
rowStart: baseRow + currentMatchRow,
|
|
2779
|
+
rowEnd: baseRow + currentMatchRow + matchHeight,
|
|
2780
|
+
component: hasReverseFinal
|
|
2781
|
+
? round.type === DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE.REVERSE_FINAL
|
|
2782
|
+
? options.finalMatchComponent
|
|
2783
|
+
: options.matchComponent
|
|
2784
|
+
: round.type === COMMON_BRACKET_ROUND_TYPE.FINAL
|
|
2785
|
+
? options.finalMatchComponent
|
|
2786
|
+
: options.matchComponent,
|
|
2787
|
+
matchRelation,
|
|
2788
|
+
roundRelation,
|
|
2789
|
+
className: `et-bracket-new-item et-bracket-match-container ${participantShortIds}`,
|
|
2790
|
+
data: {
|
|
2791
|
+
bracketRound: round,
|
|
2792
|
+
bracketMatch: match,
|
|
2793
|
+
},
|
|
2794
|
+
};
|
|
2795
|
+
roundItem.items.set(match.id, matchItem);
|
|
2796
|
+
}
|
|
2797
|
+
gridItems.set(round.id, roundItem);
|
|
2798
|
+
}
|
|
2445
2799
|
return gridItems;
|
|
2446
2800
|
};
|
|
2447
|
-
const generateSwissWithEliminationGridPlacements = (
|
|
2801
|
+
const generateSwissWithEliminationGridPlacements = (config) => {
|
|
2802
|
+
const { swissGroups } = config;
|
|
2803
|
+
if (!swissGroups)
|
|
2804
|
+
throw new Error('Swiss groups are required for swiss with elimination mode');
|
|
2448
2805
|
const gridItems = new Map();
|
|
2449
2806
|
return gridItems;
|
|
2450
2807
|
};
|
|
@@ -2468,17 +2825,16 @@ const createJourneyHighlight = (bracketData) => {
|
|
|
2468
2825
|
};
|
|
2469
2826
|
|
|
2470
2827
|
class NewBracketComponent {
|
|
2471
|
-
#domSanitizer;
|
|
2472
|
-
#elementId;
|
|
2473
2828
|
constructor() {
|
|
2474
|
-
this
|
|
2475
|
-
this
|
|
2829
|
+
this.domSanitizer = inject(DomSanitizer);
|
|
2830
|
+
this.elementId = createComponentId('et-new-bracket');
|
|
2476
2831
|
this.source = input.required();
|
|
2477
2832
|
this.columnWidth = input(250, { transform: numberAttribute });
|
|
2478
2833
|
this.matchHeight = input(75, { transform: numberAttribute });
|
|
2479
2834
|
this.roundHeaderHeight = input(50, { transform: numberAttribute });
|
|
2480
2835
|
this.columnGap = input(60, { transform: numberAttribute });
|
|
2481
2836
|
this.rowGap = input(30, { transform: numberAttribute });
|
|
2837
|
+
this.upperLowerGap = input(50, { transform: numberAttribute });
|
|
2482
2838
|
this.lineStartingCurveAmount = input(10, { transform: numberAttribute });
|
|
2483
2839
|
this.lineEndingCurveAmount = input(0, { transform: numberAttribute });
|
|
2484
2840
|
this.lineWidth = input(2, { transform: numberAttribute });
|
|
@@ -2490,50 +2846,59 @@ class NewBracketComponent {
|
|
|
2490
2846
|
this.roundHeaderComponent = input();
|
|
2491
2847
|
this.matchComponent = input();
|
|
2492
2848
|
this.finalMatchComponent = input();
|
|
2493
|
-
this.bracketData = computed(() =>
|
|
2494
|
-
this.
|
|
2495
|
-
this.
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
finalMatchComponent: this.finalMatchComponent(),
|
|
2849
|
+
this.bracketData = computed(() => createNewBracket(this.source(), { layout: this.layout() }));
|
|
2850
|
+
this.swissGroups = computed(() => generateBracketRoundSwissGroupMaps(this.bracketData()));
|
|
2851
|
+
this.items = computed(() => generateBracketGridItems({
|
|
2852
|
+
bracketData: this.bracketData(),
|
|
2853
|
+
swissGroups: this.swissGroups(),
|
|
2854
|
+
options: {
|
|
2855
|
+
includeRoundHeaders: !this.hideRoundHeaders(),
|
|
2856
|
+
headerComponent: this.roundHeaderComponent(),
|
|
2857
|
+
matchComponent: this.matchComponent(),
|
|
2858
|
+
finalMatchComponent: this.finalMatchComponent(),
|
|
2859
|
+
},
|
|
2505
2860
|
}));
|
|
2506
|
-
this.definitions = computed(() => generateBracketGridDefinitions(this.bracketData(),
|
|
2861
|
+
this.definitions = computed(() => generateBracketGridDefinitions(this.bracketData(), {
|
|
2507
2862
|
includeRoundHeaders: !this.hideRoundHeaders(),
|
|
2508
2863
|
}));
|
|
2509
|
-
this.firstRounds = computed(() => getFirstRounds(this.bracketData()
|
|
2510
|
-
this.drawManData = computed(() =>
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2864
|
+
this.firstRounds = computed(() => getFirstRounds(this.bracketData()));
|
|
2865
|
+
this.drawManData = computed(() => {
|
|
2866
|
+
if (this.bracketData().mode !== TOURNAMENT_MODE.SINGLE_ELIMINATION)
|
|
2867
|
+
return '';
|
|
2868
|
+
return this.domSanitizer.bypassSecurityTrustHtml(drawMan(this.items(), this.firstRounds(), {
|
|
2869
|
+
columnGap: this.columnGap(),
|
|
2870
|
+
upperLowerGap: this.bracketData().mode === TOURNAMENT_MODE.DOUBLE_ELIMINATION ? this.upperLowerGap() : 0,
|
|
2871
|
+
columnWidth: this.columnWidth(),
|
|
2872
|
+
matchHeight: this.matchHeight(),
|
|
2873
|
+
roundHeaderHeight: this.hideRoundHeaders() ? 0 : this.roundHeaderHeight(),
|
|
2874
|
+
rowGap: this.rowGap(),
|
|
2875
|
+
gridDefinitions: this.definitions(),
|
|
2876
|
+
curve: {
|
|
2877
|
+
lineEndingCurveAmount: this.lineEndingCurveAmount(),
|
|
2878
|
+
lineStartingCurveAmount: this.lineStartingCurveAmount(),
|
|
2879
|
+
},
|
|
2880
|
+
path: {
|
|
2881
|
+
dashArray: this.lineDashArray(),
|
|
2882
|
+
dashOffset: this.lineDashOffset(),
|
|
2883
|
+
width: this.lineWidth(),
|
|
2884
|
+
},
|
|
2885
|
+
}));
|
|
2886
|
+
});
|
|
2527
2887
|
this.journeyHighlight = computed(() => this.disableJourneyHighlight() ? null : createJourneyHighlight(this.bracketData()));
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2888
|
+
this.setupJourneyHighlight();
|
|
2889
|
+
effect(() => {
|
|
2890
|
+
logRoundRelations(this.bracketData());
|
|
2891
|
+
console.log(this.bracketData());
|
|
2892
|
+
});
|
|
2893
|
+
}
|
|
2894
|
+
setupJourneyHighlight() {
|
|
2531
2895
|
const renderer = inject(Renderer2);
|
|
2532
|
-
const
|
|
2896
|
+
const doc = inject(DOCUMENT);
|
|
2897
|
+
const styleId = `et-new-bracket-journey-highlight--${this.elementId}`;
|
|
2533
2898
|
let oldStyleEl = null;
|
|
2534
2899
|
effect(() => {
|
|
2535
2900
|
const newHighlightStyle = this.journeyHighlight();
|
|
2536
|
-
const head =
|
|
2901
|
+
const head = doc.head;
|
|
2537
2902
|
if (oldStyleEl) {
|
|
2538
2903
|
renderer.removeChild(head, oldStyleEl);
|
|
2539
2904
|
}
|
|
@@ -2547,31 +2912,31 @@ class NewBracketComponent {
|
|
|
2547
2912
|
else {
|
|
2548
2913
|
oldStyleEl = null;
|
|
2549
2914
|
}
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2915
|
+
return () => {
|
|
2916
|
+
if (oldStyleEl) {
|
|
2917
|
+
renderer.removeChild(head, oldStyleEl);
|
|
2918
|
+
oldStyleEl = null;
|
|
2919
|
+
}
|
|
2920
|
+
};
|
|
2555
2921
|
});
|
|
2556
2922
|
}
|
|
2557
2923
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: NewBracketComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2558
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.3", 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
|
|
2924
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.3", 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 }, upperLowerGap: { classPropertyName: "upperLowerGap", publicName: "upperLowerGap", 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.--_ulg.px]=\"upperLowerGap()\"\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\n [style.--_c]=\"round.columnStart + ' / ' + round.columnEnd\"\n [style.--_r]=\"round.rowStart + ' / ' + round.rowEnd\"\n class=\"et-bracket-new-round\"\n >\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;column-gap:var(--_cg);row-gap:var(--_ulg);grid-auto-columns:var(--_cw);--bracket-line-color: red}.et-bracket-new-round{display:grid;grid-column:var(--_c);grid-row:var(--_r);list-style:none;margin:0;padding:0;row-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/var(--_brc);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"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
2559
2925
|
}
|
|
2560
2926
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: NewBracketComponent, decorators: [{
|
|
2561
2927
|
type: Component,
|
|
2562
2928
|
args: [{ selector: 'et-new-bracket', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
|
|
2563
2929
|
class: 'et-new-bracket-host',
|
|
2564
|
-
}, 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
|
|
2930
|
+
}, 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.--_ulg.px]=\"upperLowerGap()\"\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\n [style.--_c]=\"round.columnStart + ' / ' + round.columnEnd\"\n [style.--_r]=\"round.rowStart + ' / ' + round.rowEnd\"\n class=\"et-bracket-new-round\"\n >\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;column-gap:var(--_cg);row-gap:var(--_ulg);grid-auto-columns:var(--_cw);--bracket-line-color: red}.et-bracket-new-round{display:grid;grid-column:var(--_c);grid-row:var(--_r);list-style:none;margin:0;padding:0;row-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/var(--_brc);inline-size:100%;block-size:100%}.et-bracket-new-svg path{color:var(--bracket-line-color);transition:opacity .2s}\n"] }]
|
|
2565
2931
|
}], ctorParameters: () => [] });
|
|
2566
2932
|
|
|
2567
2933
|
var index = /*#__PURE__*/Object.freeze({
|
|
2568
2934
|
__proto__: null,
|
|
2569
2935
|
BRACKET_DATA_LAYOUT: BRACKET_DATA_LAYOUT,
|
|
2570
2936
|
BRACKET_ROUND_MIRROR_TYPE: BRACKET_ROUND_MIRROR_TYPE,
|
|
2937
|
+
BracketMap: BracketMap,
|
|
2571
2938
|
COMMON_BRACKET_ROUND_TYPE: COMMON_BRACKET_ROUND_TYPE,
|
|
2572
2939
|
DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE: DOUBLE_ELIMINATION_BRACKET_ROUND_TYPE,
|
|
2573
|
-
FALLBACK_MATCH_POSITION: FALLBACK_MATCH_POSITION,
|
|
2574
|
-
FIRST_ROUNDS_TYPE: FIRST_ROUNDS_TYPE,
|
|
2575
2940
|
GROUP_BRACKET_ROUND_TYPE: GROUP_BRACKET_ROUND_TYPE,
|
|
2576
2941
|
NewBracketComponent: NewBracketComponent,
|
|
2577
2942
|
NewBracketDefaultMatchComponent: NewBracketDefaultMatchComponent,
|
|
@@ -2580,27 +2945,17 @@ var index = /*#__PURE__*/Object.freeze({
|
|
|
2580
2945
|
SWISS_BRACKET_ROUND_TYPE: SWISS_BRACKET_ROUND_TYPE,
|
|
2581
2946
|
TOURNAMENT_MODE: TOURNAMENT_MODE,
|
|
2582
2947
|
canRenderLayoutInTournamentMode: canRenderLayoutInTournamentMode,
|
|
2583
|
-
|
|
2584
|
-
|
|
2948
|
+
createMatchesMapBase: createMatchesMapBase,
|
|
2949
|
+
createNewBracketBase: createNewBracketBase,
|
|
2950
|
+
createNewMatchParticipantBase: createNewMatchParticipantBase,
|
|
2951
|
+
createParticipantsMapBase: createParticipantsMapBase,
|
|
2952
|
+
createRoundsMapBase: createRoundsMapBase,
|
|
2585
2953
|
generateBracketDataForEthlete: generateBracketDataForEthlete,
|
|
2586
2954
|
generateBracketDataForGg: generateBracketDataForGg,
|
|
2587
|
-
generateBracketGridDefinitions: generateBracketGridDefinitions,
|
|
2588
|
-
generateBracketGridItems: generateBracketGridItems,
|
|
2589
|
-
generateBracketRoundSwissGroupMaps: generateBracketRoundSwissGroupMaps,
|
|
2590
|
-
generateBracketRoundTypeMap: generateBracketRoundTypeMap,
|
|
2591
|
-
generateMatchParticipantMap: generateMatchParticipantMap,
|
|
2592
|
-
generateMatchPosition: generateMatchPosition,
|
|
2593
|
-
generateMatchPositionMaps: generateMatchPositionMaps,
|
|
2594
|
-
generateMatchRelationPositions: generateMatchRelationPositions,
|
|
2595
|
-
generateMatchRelations: generateMatchRelations,
|
|
2596
|
-
generateRoundRelations: generateRoundRelations,
|
|
2597
2955
|
generateRoundTypeFromEthleteRoundType: generateRoundTypeFromEthleteRoundType,
|
|
2598
2956
|
generateRoundTypeFromGgMatch: generateRoundTypeFromGgMatch,
|
|
2599
2957
|
generateTournamentModeFormEthleteRounds: generateTournamentModeFormEthleteRounds,
|
|
2600
|
-
generateTournamentModeFormGgData: generateTournamentModeFormGgData
|
|
2601
|
-
getAvailableSwissGroupsForRound: getAvailableSwissGroupsForRound,
|
|
2602
|
-
getFirstRounds: getFirstRounds,
|
|
2603
|
-
logRoundRelations: logRoundRelations
|
|
2958
|
+
generateTournamentModeFormGgData: generateTournamentModeFormGgData
|
|
2604
2959
|
});
|
|
2605
2960
|
|
|
2606
2961
|
class ButtonDirective {
|
|
@@ -8902,7 +9257,7 @@ class SliderComponent {
|
|
|
8902
9257
|
constructor() {
|
|
8903
9258
|
this._elementRef = inject(ElementRef);
|
|
8904
9259
|
this._dirService = inject(Directionality);
|
|
8905
|
-
this._document = inject(DOCUMENT);
|
|
9260
|
+
this._document = inject(DOCUMENT$1);
|
|
8906
9261
|
this._destroy$ = createDestroy();
|
|
8907
9262
|
this._mouseDown$ = fromEvent(this._elementRef.nativeElement, 'mousedown', { passive: false });
|
|
8908
9263
|
this._touchStart$ = fromEvent(this._elementRef.nativeElement, 'touchstart', {
|
|
@@ -9589,7 +9944,7 @@ class BottomSheetContainerBaseComponent extends CdkDialogContainer {
|
|
|
9589
9944
|
this._trapFocus();
|
|
9590
9945
|
}
|
|
9591
9946
|
}
|
|
9592
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: BottomSheetContainerBaseComponent, deps: [{ token: i0.ElementRef }, { token: i1$1.FocusTrapFactory }, { token: DOCUMENT, optional: true }, { token: BOTTOM_SHEET_CONFIG }, { token: i1$1.InteractivityChecker }, { token: i0.NgZone }, { token: i2$1.OverlayRef }, { token: i1$1.FocusMonitor }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
9947
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: BottomSheetContainerBaseComponent, deps: [{ token: i0.ElementRef }, { token: i1$1.FocusTrapFactory }, { token: DOCUMENT$1, optional: true }, { token: BOTTOM_SHEET_CONFIG }, { token: i1$1.InteractivityChecker }, { token: i0.NgZone }, { token: i2$1.OverlayRef }, { token: i1$1.FocusMonitor }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
9593
9948
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.0.3", type: BottomSheetContainerBaseComponent, isStandalone: true, selector: "et-bottom-sheet-container-base", usesInheritance: true, ngImport: i0, template: '', isInline: true }); }
|
|
9594
9949
|
}
|
|
9595
9950
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: BottomSheetContainerBaseComponent, decorators: [{
|
|
@@ -9602,7 +9957,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.3", ngImpor
|
|
|
9602
9957
|
type: Optional
|
|
9603
9958
|
}, {
|
|
9604
9959
|
type: Inject,
|
|
9605
|
-
args: [DOCUMENT]
|
|
9960
|
+
args: [DOCUMENT$1]
|
|
9606
9961
|
}] }, { type: undefined, decorators: [{
|
|
9607
9962
|
type: Inject,
|
|
9608
9963
|
args: [BOTTOM_SHEET_CONFIG]
|
|
@@ -9619,7 +9974,7 @@ class BottomSheetContainerComponent extends BottomSheetContainerBaseComponent {
|
|
|
9619
9974
|
constructor() {
|
|
9620
9975
|
const elementRef = inject(ElementRef);
|
|
9621
9976
|
const focusTrapFactory = inject(FocusTrapFactory);
|
|
9622
|
-
const document = inject(DOCUMENT);
|
|
9977
|
+
const document = inject(DOCUMENT$1);
|
|
9623
9978
|
const bottomSheetConfig = inject(BOTTOM_SHEET_CONFIG);
|
|
9624
9979
|
const checker = inject(InteractivityChecker);
|
|
9625
9980
|
const ngZone = inject(NgZone);
|
|
@@ -10290,7 +10645,7 @@ class DialogContainerBaseComponent extends CdkDialogContainer {
|
|
|
10290
10645
|
this._trapFocus();
|
|
10291
10646
|
}
|
|
10292
10647
|
}
|
|
10293
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: DialogContainerBaseComponent, deps: [{ token: i0.ElementRef }, { token: i1$1.FocusTrapFactory }, { token: DOCUMENT, optional: true }, { token: DIALOG_CONFIG }, { token: i1$1.InteractivityChecker }, { token: i0.NgZone }, { token: i2$1.OverlayRef }, { token: i1$1.FocusMonitor }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
10648
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: DialogContainerBaseComponent, deps: [{ token: i0.ElementRef }, { token: i1$1.FocusTrapFactory }, { token: DOCUMENT$1, optional: true }, { token: DIALOG_CONFIG }, { token: i1$1.InteractivityChecker }, { token: i0.NgZone }, { token: i2$1.OverlayRef }, { token: i1$1.FocusMonitor }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
10294
10649
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.0.3", type: DialogContainerBaseComponent, isStandalone: true, selector: "et-dialog-container-base", usesInheritance: true, ngImport: i0, template: '', isInline: true }); }
|
|
10295
10650
|
}
|
|
10296
10651
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: DialogContainerBaseComponent, decorators: [{
|
|
@@ -10303,7 +10658,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.3", ngImpor
|
|
|
10303
10658
|
type: Optional
|
|
10304
10659
|
}, {
|
|
10305
10660
|
type: Inject,
|
|
10306
|
-
args: [DOCUMENT]
|
|
10661
|
+
args: [DOCUMENT$1]
|
|
10307
10662
|
}] }, { type: undefined, decorators: [{
|
|
10308
10663
|
type: Inject,
|
|
10309
10664
|
args: [DIALOG_CONFIG]
|
|
@@ -10320,7 +10675,7 @@ class DialogContainerComponent extends DialogContainerBaseComponent {
|
|
|
10320
10675
|
constructor() {
|
|
10321
10676
|
const elementRef = inject(ElementRef);
|
|
10322
10677
|
const focusTrapFactory = inject(FocusTrapFactory);
|
|
10323
|
-
const document = inject(DOCUMENT);
|
|
10678
|
+
const document = inject(DOCUMENT$1);
|
|
10324
10679
|
const dialogConfig = inject(DIALOG_CONFIG);
|
|
10325
10680
|
const checker = inject(InteractivityChecker);
|
|
10326
10681
|
const ngZone = inject(NgZone);
|
|
@@ -13661,7 +14016,7 @@ class OverlayContainerComponent extends CdkDialogContainer {
|
|
|
13661
14016
|
return super._ariaLabelledBy;
|
|
13662
14017
|
}
|
|
13663
14018
|
constructor() {
|
|
13664
|
-
super(inject(ElementRef), inject(FocusTrapFactory), inject(DOCUMENT), inject(OVERLAY_CONFIG), inject(InteractivityChecker), inject(NgZone), inject(OverlayRef$1), inject(FocusMonitor));
|
|
14019
|
+
super(inject(ElementRef), inject(FocusTrapFactory), inject(DOCUMENT$1), inject(OVERLAY_CONFIG), inject(InteractivityChecker), inject(NgZone), inject(OverlayRef$1), inject(FocusMonitor));
|
|
13665
14020
|
this._swipeHandlerService = inject(SwipeHandlerService);
|
|
13666
14021
|
this._dragToDismissStop$ = new Subject();
|
|
13667
14022
|
this._themeProvider = inject(THEME_PROVIDER);
|
|
@@ -16528,7 +16883,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.3", ngImpor
|
|
|
16528
16883
|
class PaginationHeadService {
|
|
16529
16884
|
constructor() {
|
|
16530
16885
|
this._config = null;
|
|
16531
|
-
this._document = inject(DOCUMENT);
|
|
16886
|
+
this._document = inject(DOCUMENT$1);
|
|
16532
16887
|
this._titleService = inject(Title);
|
|
16533
16888
|
this._router = inject(Router);
|
|
16534
16889
|
this._head = this._document.getElementsByTagName('head')[0];
|
|
@@ -18411,7 +18766,7 @@ class InlineTabBodyHostDirective extends CdkPortalOutlet {
|
|
|
18411
18766
|
constructor() {
|
|
18412
18767
|
const componentFactoryResolver = inject(ComponentFactoryResolver);
|
|
18413
18768
|
const viewContainerRef = inject(ViewContainerRef);
|
|
18414
|
-
const _document = inject(DOCUMENT);
|
|
18769
|
+
const _document = inject(DOCUMENT$1);
|
|
18415
18770
|
super(componentFactoryResolver, viewContainerRef, _document);
|
|
18416
18771
|
}
|
|
18417
18772
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: InlineTabBodyHostDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|