fix bracket

shop
Raz 8 months ago
parent 14abf81de5
commit 67a2a3bdce
  1. 6
      tournaments/models/match.py
  2. 2
      tournaments/models/round.py
  3. 117
      tournaments/static/tournaments/css/tournament_bracket.css
  4. 259
      tournaments/static/tournaments/js/tournament_bracket.js
  5. 1
      tournaments/templates/tournaments/broadcast/broadcasted_bracket.html
  6. 4
      tournaments/templates/tournaments/navigation_tournament.html
  7. 1
      tournaments/templates/tournaments/tournament_bracket.html

@ -412,7 +412,7 @@ class Match(models.Model):
ended = self.end_date is not None
live_format = "Format " + FederalMatchCategory(self.format).format_label_short
livematch = LiveMatch(title, date, time_indication, court, self.started(), ended, group_stage_name, live_format, self.start_date, self.court_index, self.disabled)
livematch = LiveMatch(self.index, title, date, time_indication, court, self.started(), ended, group_stage_name, live_format, self.start_date, self.court_index, self.disabled)
for team in self.live_teams():
livematch.add_team(team)
@ -473,7 +473,8 @@ class Team:
}
class LiveMatch:
def __init__(self, title, date, time_indication, court, started, ended, group_stage_name, format, start_date, court_index, disabled):
def __init__(self, index, title, date, time_indication, court, started, ended, group_stage_name, format, start_date, court_index, disabled):
self.index = index
self.title = title
self.date = date
self.teams = []
@ -495,6 +496,7 @@ class LiveMatch:
def to_dict(self):
return {
"index": self.index,
"title": self.title,
"date": self.date,
"teams": [team.to_dict() for team in self.teams],

@ -118,7 +118,9 @@ class Round(models.Model):
# Only filter out the pair if both matches are disabled
if current_match.disabled and pair_match and pair_match.disabled:
# Skip one of the matches in the pair
if next_round_matches.filter(index=current_match.index // 2).exists():
filtered_matches.append(current_match)
filtered_matches.append(pair_match)
pass
else:
# Keep the current match

@ -23,6 +23,7 @@
}
.incoming-line.disabled,
.outgoing-line.disabled,
.butterfly-match:has(.match-content.disabled)::after,
.butterfly-match:has(.match-content.disabled)::before {
visibility: hidden;
@ -104,90 +105,89 @@
}
/* Horizontal line after match */
.butterfly-match::after {
.outgoing-line {
content: "";
position: absolute;
left: 100%; /* Start from end of match cell */
top: 50%;
width: 20px;
height: 2px;
background: #707070;
background: orange;
}
.semi-final::after {
.butterfly-match::before {
content: "";
position: absolute;
left: calc(100% + 20px); /* After horizontal line */
left: calc(0% - 20px);
width: 2px;
height: calc((var(--next-match-distance)) / 2);
background: #707070;
top: calc(50% - (var(--next-match-distance) / 2));
height: calc(var(--next-match-distance) + 2px);
background: pink;
}
/* Vertical line connecting pair of matches */
.butterfly-match:nth-child(2n)::before {
.butterfly-match.reverse-bracket::before {
content: "";
position: absolute;
left: calc(100% + 20px); /* After horizontal line */
top: 50%;
left: calc(100% + 20px);
width: 2px;
height: calc((var(--next-match-distance)) / 2);
background: #707070;
top: calc(50% - (var(--next-match-distance) / 2));
height: calc(var(--next-match-distance) + 2px);
background: red;
}
.butterfly-match:nth-child(2n + 1)::before {
content: "";
/* Horizontal line to next round match */
.incoming-line {
position: absolute;
left: calc(100% + 20px);
bottom: calc(50% - 2px); /* Account for half of horizontal line height */
width: 2px;
height: calc(
((var(--next-match-distance)) / 2)
); /* Add half of horizontal line height */
background: #707070;
left: -20px;
top: 50%;
width: 20px;
height: 2px;
background: blue;
}
/* Vertical line connecting pair of matches */
.butterfly-match.reverse-bracket:nth-child(2n)::before {
content: "";
/* Horizontal line to next round match */
.butterfly-match .outgoing-line-downward {
position: absolute;
left: calc(0% - 20px); /* After horizontal line */
top: 50%;
right: -20px;
top: 50%; /* Start from middle of match */
width: 2px;
height: calc((var(--next-match-distance)) / 2);
background: #707070;
height: 120px;
background: black;
}
.butterfly-match.reverse-bracket:nth-child(2n + 1)::before {
content: "";
/* Horizontal line to next round match */
.butterfly-match .outgoing-line-upward {
position: absolute;
left: calc(0% - 20px);
bottom: 50%; /* Account for half of horizontal line height */
right: -20px;
bottom: 50%;
width: 2px;
height: calc(
(var(--next-match-distance)) / 2
); /* Add half of horizontal line height */
background: #707070;
height: 120px;
background: black;
}
/* Horizontal line to next round match */
.butterfly-match .incoming-line {
.butterfly-match.reverse-bracket .outgoing-line-downward {
position: absolute;
left: -20px;
top: 50%;
width: 20px;
height: 2px;
background: #707070;
}
.inward .incoming-line {
display: none;
top: 50%; /* Start from middle of match */
width: 2px;
height: 120px;
background: black;
}
.butterfly-match.outward::after {
display: none;
/* Horizontal line to next round match */
.butterfly-match.reverse-bracket .outgoing-line-upward {
position: absolute;
left: -20px;
bottom: 50%;
width: 2px;
height: 120px;
background: black;
}
.butterfly-round:last-child .butterfly-match::after,
.butterfly-round:last-child .butterfly-match.reverse-bracket::before,
.butterfly-round:last-child .outgoing-line,
.butterfly-round:first-child .incoming-line {
display: none;
}
@ -205,16 +205,25 @@
border-radius: 24px;
}
/* Broadcast mode styling for all lines */
.broadcast-mode .butterfly-match::after,
.broadcast-mode .butterfly-match:nth-child(2n)::before,
.broadcast-mode .butterfly-match:nth-child(2n + 1)::before,
.broadcast-mode .butterfly-match.reverse-bracket:nth-child(2n)::before,
.broadcast-mode .butterfly-match.reverse-bracket:nth-child(2n + 1)::before,
.broadcast-mode .butterfly-match .incoming-line,
.broadcast-mode .semi-final::after {
.broadcast-mode .butterfly-match::before,
.broadcast-mode .butterfly-match.reverse-bracket::before,
.broadcast-mode .incoming-line,
.broadcast-mode .outgoing-line,
.broadcast-mode .outgoing-line-upward,
.broadcast-mode .outgoing-line-downward {
background-color: black; /* Bright yellow - change to your preferred color */
}
/* Broadcast mode styling for all lines */
.butterfly-match::before,
.butterfly-match.reverse-bracket::before,
.incoming-line,
.outgoing-line,
.outgoing-line-upward,
.outgoing-line-downward {
background-color: #707070 !important; /* Bright yellow - change to your preferred color */
}
.bubble.match-running {
position: relative;
}

@ -42,13 +42,20 @@ function renderBracket(options) {
}
const padding = 50 * roundTotalCount; // Account for some padding/margin
const availableWidth = screenWidth - padding;
let responsiveMatchWidth = Math.min(
responsiveMatchWidth = Math.min(
365,
Math.max(365, Math.floor(availableWidth / roundTotalCount)),
);
if (isBroadcast) {
responsiveMatchWidth = Math.min(
365,
Math.floor(availableWidth / roundTotalCount),
);
}
rounds.forEach((roundMatches, roundIndex) => {
if (rounds[0].length <= 2) {
if (rounds[0].length <= 2 && doubleButterflyMode) {
minimumMatchDistance = 2;
nextMatchDistance = baseDistance * 2;
}
@ -105,29 +112,39 @@ function renderBracket(options) {
}
}
roundDiv.appendChild(matchesContainer);
matchPositions[roundIndex] = [];
if (matchPositions[roundIndex] == undefined) {
matchPositions[roundIndex] = {};
}
matchDisabled[roundIndex] = []; // Initialize array for this round
roundMatches.forEach((matchTemplate, matchIndex) => {
const matchTitle = matchTemplate.dataset.matchTitle;
const matchRealIndex = matchTemplate.dataset.matchRealIndex;
const matchDiv = document.createElement("div");
matchDiv.className = "butterfly-match";
matchDiv.style.position = "absolute";
const isDisabled = matchTemplate.dataset.disabled === "true";
matchDisabled[roundIndex][matchIndex] = isDisabled;
matchDisabled[roundIndex][matchRealIndex] = isDisabled;
let isIncomingLineIsDisabled = isDisabled;
let isOutgoingLineIsDisabled = isDisabled;
let top;
let left;
let right;
const currentMatchesCount = roundMatches.length;
if (roundIndex > finalRoundIndex) {
matchDiv.classList.add("reverse-bracket");
top = matchPositions[roundCount - roundIndex - 1][matchIndex];
if (roundIndex <= finalRoundIndex + 2) {
const values = Object.values(
matchPositions[roundCount - roundIndex - 1],
);
top = values[matchIndex];
} else {
top = matchPositions[roundIndex][matchRealIndex];
console.log(matchTitle, top);
}
}
if (roundIndex === 0) {
nextMatchDistance = 0;
if (roundCount > 1) {
const nextMatchesCount = rounds[roundIndex + 1].length;
@ -144,33 +161,34 @@ function renderBracket(options) {
top = top + (matchHeight + matchSpacing) / 2;
}
} else if (roundIndex === roundCount - 1 && doubleButterflyMode == true) {
const nextMatchesCount = rounds[roundIndex - 1].length;
if (currentMatchesCount == nextMatchesCount) {
nextMatchDistance = 0;
}
} else if (roundIndex == finalRoundIndex) {
const values = Object.values(matchPositions[roundIndex - 1]);
const parentPos1 = values[0];
const parentPos2 = values[1];
if (doubleButterflyMode == true) {
let lgth = matchPositions[0].length / 2;
let index = lgth + matchIndex - 1;
// If index goes negative, use 0 instead
if (displayLoserFinal == true) {
if (matchIndex == 0) {
top = matchPositions[roundIndex - 1][0] - baseDistance / 2;
top = parentPos1 - baseDistance / 2;
} else {
top = matchPositions[roundIndex - 1][0] + baseDistance / 2;
top = parentPos1 + baseDistance / 2;
}
nextMatchDistance = baseDistance;
nextMatchDistance = 0;
} else {
top = matchPositions[roundIndex - 1][0];
top = parentPos1;
nextMatchDistance = 0;
}
} else {
const parentIndex1 = matchIndex * 2;
const parentIndex2 = parentIndex1 + 1;
const parentPos1 = matchPositions[roundIndex - 1][parentIndex1];
const parentPos2 = matchPositions[roundIndex - 1][parentIndex2];
top = (parentPos1 + parentPos2) / 2;
if (matchIndex == 0) {
nextMatchDistance = parentPos2 - parentPos1;
} else {
nextMatchDistance = 0;
}
if (displayLoserFinal == true) {
if (matchIndex == 1) {
@ -180,82 +198,115 @@ function renderBracket(options) {
}
}
} else if (roundIndex < finalRoundIndex) {
const previousMatchesCount = rounds[roundIndex - 1].length;
const nextMatchesCount = rounds[roundIndex + 1].length;
const parentIndex1 = matchRealIndex * 2 + 1;
const parentIndex2 = matchRealIndex * 2 + 2;
const parentPos1 = matchPositions[roundIndex - 1][parentIndex1];
const parentPos2 = matchPositions[roundIndex - 1][parentIndex2];
const parent1Disable = matchDisabled[roundIndex - 1][parentIndex1];
const parent2Disable = matchDisabled[roundIndex - 1][parentIndex2];
if (
currentMatchesCount == nextMatchesCount &&
displayLoserFinal == false
(parent1Disable == undefined || parent1Disable == true) &&
(parent2Disable == undefined || parent2Disable == true)
) {
nextMatchDistance = 0;
} else if (matchPositions.length > roundIndex - 1) {
nextMatchDistance =
matchPositions[roundIndex - 1][1] -
matchPositions[roundIndex - 1][0];
nextMatchDistance =
nextMatchDistance * (previousMatchesCount / currentMatchesCount);
}
if (currentMatchesCount == previousMatchesCount) {
if (matchDisabled[roundIndex - 1][matchIndex] == true) {
isIncomingLineIsDisabled = true;
}
top = matchPositions[roundIndex - 1][matchIndex];
} else {
const parentIndex1 = matchIndex * 2;
const parentIndex2 = parentIndex1 + 1;
const parentPos1 = matchPositions[roundIndex - 1][parentIndex1];
const parentPos2 = matchPositions[roundIndex - 1][parentIndex2];
if (
matchPositions[roundIndex - 1][parentIndex1] != undefined &&
matchPositions[roundIndex - 1][parentIndex2] != undefined
) {
top = (parentPos1 + parentPos2) / 2;
if (parent1Disable && parent2Disable) {
nextMatchDistance = 0;
} else {
nextMatchDistance = parentPos2 - parentPos1;
}
} else if (matchPositions[roundIndex - 1][parentIndex1] != undefined) {
nextMatchDistance = 0;
top = matchPositions[roundIndex - 1][parentIndex1];
} else if (matchPositions[roundIndex - 1][parentIndex2] != undefined) {
nextMatchDistance = 0;
top = matchPositions[roundIndex - 1][parentIndex2];
} else {
nextMatchDistance = 0;
top = 0;
}
} else if (roundIndex < roundCount) {
const nextMatchesCount = rounds[roundIndex - 1].length;
const previousMatchesCount = rounds[roundIndex + 1].length;
const parentIndex1 = matchRealIndex * 2 + 1;
const parentIndex2 = matchRealIndex * 2 + 2;
const parentMatch1 = rounds[roundIndex + 1].find(
(match) => parseInt(match.dataset.matchRealIndex) === parentIndex1,
);
const parentMatch2 = rounds[roundIndex + 1].find(
(match) => parseInt(match.dataset.matchRealIndex) === parentIndex2,
);
if (currentMatchesCount == nextMatchesCount) {
if (matchPositions[roundIndex + 1] == undefined) {
matchPositions[roundIndex + 1] = {};
}
if (
parentMatch1 != undefined &&
parentMatch2 != undefined &&
parentMatch1.dataset.disabled == "false" &&
parentMatch2.dataset.disabled == "false"
) {
console.log(
roundIndex,
matchTitle,
parentMatch1.dataset.matchTitle,
parentMatch2.dataset.matchTitle,
parentMatch1.dataset.disabled,
parentMatch2.dataset.disabled,
top,
);
nextMatchDistance = baseDistance;
matchPositions[roundIndex + 1][parentIndex1] = top - baseDistance / 2;
matchPositions[roundIndex + 1][parentIndex2] = top + baseDistance / 2;
console.log(matchPositions[roundIndex + 1]);
// } else if (parentMatch1 != undefined) {
// matchPositions[roundIndex + 1][parentIndex1] = top;
// nextMatchDistance = 0;
// } else if (parentMatch2 != undefined) {
// matchPositions[roundIndex + 1][parentIndex1] = top;
// nextMatchDistance = 0;
} else if (
parentMatch2 != undefined &&
parentMatch2.dataset.disabled == "false"
) {
nextMatchDistance = 0;
} else if (matchPositions.length > roundCount - roundIndex - 1 - 1) {
nextMatchDistance =
matchPositions[roundCount - roundIndex - 1 - 1][1] -
matchPositions[roundCount - roundIndex - 1 - 1][0];
nextMatchDistance =
nextMatchDistance * (previousMatchesCount / currentMatchesCount);
matchPositions[roundIndex + 1][parentIndex2] = top;
} else if (
parentMatch1 != undefined &&
parentMatch1.dataset.disabled == "false"
) {
nextMatchDistance = 0;
matchPositions[roundIndex + 1][parentIndex1] = top;
} else {
isOutgoingLineIsDisabled = true;
}
}
if (roundCount > 5 && doubleButterflyMode == true) {
if (roundIndex >= finalRoundIndex - 2) {
if (roundIndex == finalRoundIndex - 1) {
matchDiv.classList.add("inward");
matchDiv.classList.add("reverse-bracket");
isIncomingLineIsDisabled = true;
nextMatchDistance = nextMatchDistance / 2;
}
if (roundIndex == finalRoundIndex + 1) {
matchDiv.classList.add("outward");
}
if (
roundIndex === finalRoundIndex - 2 ||
roundIndex === finalRoundIndex + 2
) {
nextMatchDistance = nextMatchDistance - baseDistance;
matchDiv.classList.remove("reverse-bracket");
isOutgoingLineIsDisabled = true;
nextMatchDistance = nextMatchDistance;
}
}
}
if (displayLoserFinal == true) {
if (
doubleButterflyMode == true &&
(roundIndex == finalRoundIndex - 1 ||
roundIndex == finalRoundIndex + 1)
) {
nextMatchDistance = baseDistance;
}
}
matchDiv.style.setProperty(
"--next-match-distance",
`${nextMatchDistance}px`,
);
matchDiv.style.top = `${top}px`;
matchPositions[roundIndex][matchIndex] = top;
matchPositions[roundIndex][matchRealIndex] = top;
if (matchIndex === 0) {
// // Add logo for final round
// if (roundIndex == finalRoundIndex) {
@ -270,7 +321,14 @@ function renderBracket(options) {
// }
// Position title above the first match
if (
roundIndex < finalRoundIndex - 1 ||
roundIndex > finalRoundIndex + 1
) {
titleDiv.style.top = `${-80}px`; // Adjust the 60px offset as needed
} else {
titleDiv.style.top = `${top - 80}px`; // Adjust the 60px offset as needed
}
titleDiv.style.position = "absolute";
if (roundCount >= 5 && doubleButterflyMode == true) {
if (roundIndex == finalRoundIndex - 1) {
@ -285,6 +343,7 @@ function renderBracket(options) {
if (
roundIndex == finalRoundIndex &&
matchIndex === 1 &&
matchDisabled[roundIndex][matchRealIndex] == false &&
displayLoserFinal == true &&
doubleButterflyMode == false
) {
@ -306,25 +365,53 @@ function renderBracket(options) {
matchDiv.innerHTML = `
<div class="incoming-line ${isIncomingLineIsDisabled ? "disabled" : ""}"></div>
<div class="match-content ${isDisabled ? "disabled" : ""}">${matchTemplate.innerHTML}</div>
<div class="outgoing-line ${isOutgoingLineIsDisabled ? "disabled" : ""}"></div>
`;
if (
roundIndex == finalRoundIndex - 1 &&
displayLoserFinal == true &&
doubleButterflyMode == true
) {
const matchDiv2 = document.createElement("div");
matchDiv2.className = "butterfly-match";
matchDiv2.classList.add("inward");
matchDiv2.classList.add("semi-final");
matchDiv2.style.setProperty(
"--next-match-distance",
`${baseDistance}px`,
);
matchDiv2.style.top = `${top}px`;
matchDiv2.innerHTML = `<div class="match-content">${rounds[0][0].innerHTML}</div>`;
matchesContainer.appendChild(matchDiv2); // Append to matchesContainer instead of roundDiv
if (roundCount > 5 && doubleButterflyMode == true) {
if (roundIndex >= finalRoundIndex - 2) {
if (roundIndex === finalRoundIndex - 2) {
if (matchIndex === 0) {
const outgoingLine = document.createElement("div");
outgoingLine.className = "outgoing-line-downward";
matchDiv.appendChild(outgoingLine);
} else {
const outgoingLine = document.createElement("div");
outgoingLine.className = "outgoing-line-upward";
matchDiv.appendChild(outgoingLine);
}
}
if (roundIndex === finalRoundIndex + 2) {
if (matchIndex === 0) {
const outgoingLine = document.createElement("div");
outgoingLine.className = "outgoing-line-downward";
matchDiv.appendChild(outgoingLine);
} else {
const outgoingLine = document.createElement("div");
outgoingLine.className = "outgoing-line-upward";
matchDiv.appendChild(outgoingLine);
}
}
}
}
// if (
// roundIndex == finalRoundIndex - 1 &&
// displayLoserFinal == true &&
// doubleButterflyMode == true
// ) {
// const matchDiv2 = document.createElement("div");
// matchDiv2.className = "butterfly-match";
// matchDiv2.classList.add("inward");
// matchDiv2.classList.add("semi-final");
// matchDiv2.style.setProperty(
// "--next-match-distance",
// `${baseDistance}px`,
// );
// matchDiv2.style.top = `${top}px`;
// matchDiv2.innerHTML = `<div class="match-content">${rounds[0][0].innerHTML}</div>`;
// matchesContainer.appendChild(matchDiv2); // Append to matchesContainer instead of roundDiv
// }
matchesContainer.appendChild(matchDiv); // Append to matchesContainer instead of roundDiv
});

@ -126,6 +126,7 @@
template.dataset.matchFormat = match.format;
template.dataset.matchTitle = match.title;
template.dataset.roundId = group.round_id;
template.dataset.matchRealIndex = match.index;
// Create the match content using our HTML generator
template.innerHTML = `<div class="bubble broadcast-bracket-match ${(!match.ended && match.started) ? 'match-running' : ''}">${createMatchHTML(match)}</div>`;

@ -2,9 +2,9 @@
<a href="{% url 'tournament-info' tournament.id %}" class="topmargin5 orange">Informations</a>
<!-- {% if tournament.display_matches %}
{% if tournament.display_matches %}
<a href="{% url 'tournament-bracket' tournament.id %}" class="topmargin5 orange">Tableau</a>
{% endif %} -->
{% endif %}
{% if tournament.display_matches or tournament.display_group_stages %}
<a href="{% url 'tournament' tournament.id %}" class="topmargin5 orange">Matches</a>

@ -26,6 +26,7 @@
data-match-group-name="{{ match_group.name }}"
data-match-format="{{ match.format }}"
data-match-title="{{ match.title }}"
data-match-real-index="{{ match.index }}"
data-round-id="{{ match_group.round_id }}"
class="match-template">
{% include 'tournaments/bracket_match_cell.html' %}

Loading…
Cancel
Save