You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
padelclub_backend/tournaments/templates/tournaments/tournament_bracket.html

288 lines
9.1 KiB

{% extends 'tournaments/base.html' %}
{% block head_title %}Matchs du {{ tournament.display_name }}{% endblock %}
{% block first_title %}{{ tournament.event.display_name }}{% endblock %}
{% block second_title %}{{ tournament.display_name }}{% endblock %}
{% block content %}
{% if tournament.display_matches %}
{% include 'tournaments/navigation_tournament.html' %}
<div class="butterfly-bracket" id="bracket"></div>
<div id="match-templates" style="display: none;">
{% for match_group in match_groups %}
{% if match_group.matches %}
{% for match in match_group.matches %}
<div data-match-round="{{ forloop.parentloop.counter0 }}"
data-match-index="{{ forloop.counter0 }}"
data-disabled="{{ match.disabled|lower }}"
class="match-template">
{% include 'tournaments/bracket_match_cell.html' %}
</div>
{% endfor %}
{% endif %}
{% endfor %}
</div>
<script>
function renderBracket() {
const bracket = document.getElementById('bracket');
const matchTemplates = document.getElementById('match-templates').children;
const rounds = [];
const matchPositions = [];
// Group matches by round
Array.from(matchTemplates).forEach(template => {
const roundIndex = parseInt(template.dataset.matchRound);
if (!rounds[roundIndex]) {
rounds[roundIndex] = [];
}
rounds[roundIndex].push(template);
});
// First create a test match to get natural height
const firstMatch = document.createElement('div');
firstMatch.className = 'butterfly-match';
firstMatch.innerHTML = `<div class="match-content">${rounds[0][0].innerHTML}</div>`;
bracket.appendChild(firstMatch);
const matchHeight = firstMatch.offsetHeight;
const matchSpacing = 10;
const baseDistance = matchHeight + matchSpacing;
bracket.innerHTML = '';
const roundCount = rounds.length;
const finalRoundIndex = (roundCount - 1) / 2;
rounds.forEach((roundMatches, roundIndex) => {
const roundDiv = document.createElement('div');
roundDiv.className = 'butterfly-round';
roundDiv.style.setProperty('--match-width', `${365}px`);
matchPositions[roundIndex] = [];
roundMatches.forEach((matchTemplate, matchIndex) => {
const matchDiv = document.createElement('div');
matchDiv.className = 'butterfly-match';
matchDiv.style.position = 'absolute';
const isDisabled = matchTemplate.dataset.disabled === 'true';
let top;
let left;
let right;
let nextMatchDistance = baseDistance;
// Check if this round has only one match
const isSingleMatchRound = roundMatches.length === 1;
if (isSingleMatchRound) {
matchDiv.classList.add('single-match-round');
nextMatchDistance = 0;
}
if (roundIndex > finalRoundIndex) {
matchDiv.classList.add('reverse-bracket');
top = matchPositions[roundCount - roundIndex - 1][matchIndex];
nextMatchDistance = baseDistance * Math.pow(2, rounds.length - roundIndex - 1);
} else {
nextMatchDistance = baseDistance * Math.pow(2, roundIndex);
}
if (roundIndex === 0) {
top = matchIndex * (matchHeight + matchSpacing);
} else if (roundIndex == finalRoundIndex) {
let lgth = matchPositions[0].length / 2;
let index = lgth + matchIndex - 1;
// If index goes negative, use 0 instead
top = matchPositions[0][index < 0 ? 0 : index];
nextMatchDistance = baseDistance;
} else if (roundIndex < finalRoundIndex) {
const parentIndex1 = matchIndex * 2;
const parentIndex2 = parentIndex1 + 1;
const parentPos1 = matchPositions[roundIndex - 1][parentIndex1];
const parentPos2 = matchPositions[roundIndex - 1][parentIndex2];
top = (parentPos1 + parentPos2) / 2;
nextMatchDistance = baseDistance * Math.pow(2, roundIndex);
}
if (roundIndex >= finalRoundIndex - 1) {
if (roundCount > 5) {
matchDiv.style.transform = `translateX(-50%)`;
if (roundIndex >= finalRoundIndex + 2) {
matchDiv.style.transform = `translateX(-100%)`;
}
if (roundIndex == finalRoundIndex - 1) {
matchDiv.classList.add('inward');
}
if (roundIndex == finalRoundIndex + 1) {
matchDiv.classList.add('outward');
}
}
}
if (roundIndex === finalRoundIndex - 2 || roundIndex === finalRoundIndex + 2) {
nextMatchDistance = (nextMatchDistance - baseDistance);
}
else if (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;
matchDiv.innerHTML = `
<div class="incoming-line ${isDisabled ? 'disabled' : ''}"></div>
<div class="match-content ${isDisabled ? 'disabled' : ''}">${matchTemplate.innerHTML}</div>
`;
if (roundIndex == finalRoundIndex - 1) {
const matchDiv2 = document.createElement('div');
matchDiv2.className = 'butterfly-match';
matchDiv2.style.position = 'absolute';
if (roundCount > 5) {
matchDiv2.style.transform = `translateX(-50%)`;
}
matchDiv2.classList.add('inward');
matchDiv2.classList.add('semi-final');
matchDiv2.style.setProperty('--next-match-distance', `${baseDistance}px`);
matchDiv2.style.top = `${top}px`;
matchDiv2.innerHTML = `
`;
roundDiv.appendChild(matchDiv2);
}
roundDiv.appendChild(matchDiv);
});
bracket.appendChild(roundDiv);
});
}
renderBracket();
</script>
<style>
.butterfly-match.same-level::before {
display: none;
}
/* Adjust styling for matches with single parent */
.match-content.disabled {
visibility: hidden;
}
.incoming-line.disabled,
.butterfly-match:has(.match-content.disabled)::after,
.butterfly-match:has(.match-content.disabled)::before {
visibility: hidden;
}
.butterfly-bracket {
display: flex;
gap: 40px; /* Increased to account for horizontal lines (20px on each side) */
position: relative;
}
.butterfly-round {
position: relative;
width: var(--match-width); /* 300px for match + 20px on each side for lines */
flex-shrink: 0; /* Prevents rounds from shrinking */
}
.butterfly-match {
position: absolute;
width: 100%;
}
/* Horizontal line after match */
.butterfly-match::after {
content: "";
position: absolute;
left: 100%; /* Start from end of match cell */
top: 50%;
width: 20px;
height: 2px;
background: #666;
}
.semi-final::after {
content: "";
position: absolute;
left: calc(100% + 20px); /* After horizontal line */
top: -4px;
width: 2px;
height: calc(var(--next-match-distance));
background: #666;
}
/* Vertical line connecting pair of matches */
.butterfly-match:nth-child(2n+1)::before {
content: "";
position: absolute;
left: calc(100% + 20px); /* After horizontal line */
top: 50%;
width: 2px;
height: calc((var(--next-match-distance)) / 2);
background: #666;
}
.butterfly-match:nth-child(2n)::before {
content: "";
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: #666;
}
/* Vertical line connecting pair of matches */
.butterfly-match.reverse-bracket:nth-child(2n+1)::before {
content: "";
position: absolute;
left: calc(0% - 20px); /* After horizontal line */
top: calc(50% + 2px);
width: 2px;
height: calc((var(--next-match-distance)) / 2);
background: #666;
}
.butterfly-match.reverse-bracket:nth-child(2n)::before {
content: "";
position: absolute;
left: calc(0% - 20px);
bottom: 50%; /* Account for half of horizontal line height */
width: 2px;
height: calc((var(--next-match-distance)) / 2); /* Add half of horizontal line height */
background: #666;
}
/* Horizontal line to next round match */
.butterfly-match .incoming-line {
position: absolute;
left: -20px;
top: 50%;
width: 20px;
height: 2px;
background: #666;
}
.inward .incoming-line {
display: none;
}
.butterfly-match.outward::after {
display: none;
}
.butterfly-round:last-child .butterfly-match::after,
.butterfly-round:first-child .incoming-line {
display: none;
}
</style>
{% endif %}
{% endblock %}