Merge branch 'finals' into development
This commit is contained in:
@@ -330,6 +330,9 @@
|
||||
max-width: 96rem;
|
||||
}
|
||||
}
|
||||
.mx-1 {
|
||||
margin-inline: calc(var(--spacing) * 1);
|
||||
}
|
||||
.mx-auto {
|
||||
margin-inline: auto;
|
||||
}
|
||||
@@ -454,6 +457,9 @@
|
||||
width: calc(var(--spacing) * 5);
|
||||
height: calc(var(--spacing) * 5);
|
||||
}
|
||||
.h-0 {
|
||||
height: calc(var(--spacing) * 0);
|
||||
}
|
||||
.h-1 {
|
||||
height: calc(var(--spacing) * 1);
|
||||
}
|
||||
@@ -475,6 +481,9 @@
|
||||
.h-9 {
|
||||
height: calc(var(--spacing) * 9);
|
||||
}
|
||||
.h-10 {
|
||||
height: calc(var(--spacing) * 10);
|
||||
}
|
||||
.h-12 {
|
||||
height: calc(var(--spacing) * 12);
|
||||
}
|
||||
@@ -493,6 +502,9 @@
|
||||
.h-screen {
|
||||
height: 100vh;
|
||||
}
|
||||
.max-h-40 {
|
||||
max-height: calc(var(--spacing) * 40);
|
||||
}
|
||||
.max-h-60 {
|
||||
max-height: calc(var(--spacing) * 60);
|
||||
}
|
||||
@@ -556,9 +568,15 @@
|
||||
.w-14 {
|
||||
width: calc(var(--spacing) * 14);
|
||||
}
|
||||
.w-16 {
|
||||
width: calc(var(--spacing) * 16);
|
||||
}
|
||||
.w-20 {
|
||||
width: calc(var(--spacing) * 20);
|
||||
}
|
||||
.w-24 {
|
||||
width: calc(var(--spacing) * 24);
|
||||
}
|
||||
.w-26 {
|
||||
width: calc(var(--spacing) * 26);
|
||||
}
|
||||
@@ -631,6 +649,12 @@
|
||||
.min-w-0 {
|
||||
min-width: calc(var(--spacing) * 0);
|
||||
}
|
||||
.min-w-\[500px\] {
|
||||
min-width: 500px;
|
||||
}
|
||||
.min-w-\[700px\] {
|
||||
min-width: 700px;
|
||||
}
|
||||
.flex-1 {
|
||||
flex: 1;
|
||||
}
|
||||
@@ -674,6 +698,9 @@
|
||||
--tw-scale-z: 100%;
|
||||
scale: var(--tw-scale-x) var(--tw-scale-y);
|
||||
}
|
||||
.rotate-180 {
|
||||
rotate: 180deg;
|
||||
}
|
||||
.transform {
|
||||
transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
|
||||
}
|
||||
@@ -959,6 +986,10 @@
|
||||
border-top-style: var(--tw-border-style);
|
||||
border-top-width: 1px;
|
||||
}
|
||||
.border-t-2 {
|
||||
border-top-style: var(--tw-border-style);
|
||||
border-top-width: 2px;
|
||||
}
|
||||
.border-b {
|
||||
border-bottom-style: var(--tw-border-style);
|
||||
border-bottom-width: 1px;
|
||||
@@ -992,9 +1023,6 @@
|
||||
.border-overlay0 {
|
||||
border-color: var(--overlay0);
|
||||
}
|
||||
.border-peach {
|
||||
border-color: var(--peach);
|
||||
}
|
||||
.border-peach\/50 {
|
||||
border-color: var(--peach);
|
||||
@supports (color: color-mix(in lab, red, red)) {
|
||||
@@ -1088,6 +1116,12 @@
|
||||
.bg-green {
|
||||
background-color: var(--green);
|
||||
}
|
||||
.bg-green\/5 {
|
||||
background-color: var(--green);
|
||||
@supports (color: color-mix(in lab, red, red)) {
|
||||
background-color: color-mix(in oklab, var(--green) 5%, transparent);
|
||||
}
|
||||
}
|
||||
.bg-green\/10 {
|
||||
background-color: var(--green);
|
||||
@supports (color: color-mix(in lab, red, red)) {
|
||||
@@ -1112,9 +1146,6 @@
|
||||
.bg-mauve {
|
||||
background-color: var(--mauve);
|
||||
}
|
||||
.bg-overlay0 {
|
||||
background-color: var(--overlay0);
|
||||
}
|
||||
.bg-overlay0\/10 {
|
||||
background-color: var(--overlay0);
|
||||
@supports (color: color-mix(in lab, red, red)) {
|
||||
@@ -1588,6 +1619,11 @@
|
||||
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
||||
transition-duration: var(--tw-duration, var(--default-transition-duration));
|
||||
}
|
||||
.transition-transform {
|
||||
transition-property: transform, translate, scale, rotate;
|
||||
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
||||
transition-duration: var(--tw-duration, var(--default-transition-duration));
|
||||
}
|
||||
.duration-150 {
|
||||
--tw-duration: 150ms;
|
||||
transition-duration: 150ms;
|
||||
@@ -1865,6 +1901,16 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.hover\:bg-surface0\/50 {
|
||||
&:hover {
|
||||
@media (hover: hover) {
|
||||
background-color: var(--surface0);
|
||||
@supports (color: color-mix(in lab, red, red)) {
|
||||
background-color: color-mix(in oklab, var(--surface0) 50%, transparent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.hover\:bg-surface1 {
|
||||
&:hover {
|
||||
@media (hover: hover) {
|
||||
@@ -2416,6 +2462,16 @@
|
||||
gap: calc(var(--spacing) * 12);
|
||||
}
|
||||
}
|
||||
.lg\:divide-x {
|
||||
@media (width >= 64rem) {
|
||||
:where(& > :not(:last-child)) {
|
||||
--tw-divide-x-reverse: 0;
|
||||
border-inline-style: var(--tw-border-style);
|
||||
border-inline-start-width: calc(1px * var(--tw-divide-x-reverse));
|
||||
border-inline-end-width: calc(1px * calc(1 - var(--tw-divide-x-reverse)));
|
||||
}
|
||||
}
|
||||
}
|
||||
.lg\:px-8 {
|
||||
@media (width >= 64rem) {
|
||||
padding-inline: calc(var(--spacing) * 8);
|
||||
|
||||
152
internal/embedfs/web/js/bracket-lines.js
Normal file
152
internal/embedfs/web/js/bracket-lines.js
Normal file
@@ -0,0 +1,152 @@
|
||||
// bracket-lines.js
|
||||
// Draws smooth SVG bezier connector lines between series cards in a playoff bracket.
|
||||
// Lines connect from the bottom-center of a source card to the top-center of a
|
||||
// destination card. Winner paths are solid green, loser paths are dashed red.
|
||||
//
|
||||
// Usage: Add data-bracket-lines to a container element. Inside, series cards
|
||||
// should have data-series="N" attributes. The container needs a data-connections
|
||||
// attribute with a JSON array of connection objects:
|
||||
// [{"from": 1, "to": 3, "type": "winner"}, ...]
|
||||
//
|
||||
// Optional toSide field ("left" or "right") makes the line arrive at the
|
||||
// left or right edge (vertically centered) of the destination card instead
|
||||
// of the top-center.
|
||||
//
|
||||
// An SVG element with data-bracket-svg inside the container is used for drawing.
|
||||
|
||||
(function () {
|
||||
var WINNER_COLOR = "var(--green)";
|
||||
var LOSER_COLOR = "var(--red)";
|
||||
var STROKE_WIDTH = 2;
|
||||
var DASH_ARRAY = "6 3";
|
||||
|
||||
// Curvature control: how far the control points extend
|
||||
// as a fraction of the total distance between cards
|
||||
var CURVE_FACTOR = 0.4;
|
||||
|
||||
function drawBracketLines(container) {
|
||||
var svg = container.querySelector("[data-bracket-svg]");
|
||||
if (!svg) return;
|
||||
|
||||
var connectionsAttr = container.getAttribute("data-connections");
|
||||
if (!connectionsAttr) return;
|
||||
|
||||
var connections;
|
||||
try {
|
||||
connections = JSON.parse(connectionsAttr);
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear existing paths
|
||||
while (svg.firstChild) {
|
||||
svg.removeChild(svg.firstChild);
|
||||
}
|
||||
|
||||
// Get container position for relative coordinates
|
||||
var containerRect = container.getBoundingClientRect();
|
||||
|
||||
// Size SVG to match container
|
||||
svg.setAttribute("width", containerRect.width);
|
||||
svg.setAttribute("height", containerRect.height);
|
||||
|
||||
connections.forEach(function (conn) {
|
||||
var fromCard = container.querySelector(
|
||||
'[data-series="' + conn.from + '"]',
|
||||
);
|
||||
var toCard = container.querySelector('[data-series="' + conn.to + '"]');
|
||||
if (!fromCard || !toCard) return;
|
||||
|
||||
var fromRect = fromCard.getBoundingClientRect();
|
||||
var toRect = toCard.getBoundingClientRect();
|
||||
|
||||
// Start: bottom-center of source card
|
||||
var x1 = fromRect.left + fromRect.width / 2 - containerRect.left;
|
||||
var y1 = fromRect.bottom - containerRect.top;
|
||||
|
||||
var x2, y2, d;
|
||||
|
||||
if (conn.toSide === "left") {
|
||||
// End: left edge, vertically centered
|
||||
x2 = toRect.left - containerRect.left;
|
||||
y2 = toRect.top + toRect.height / 2 - containerRect.top;
|
||||
|
||||
// Bezier: go down first, then curve into the left side
|
||||
var dy = y2 - y1;
|
||||
var dx = x2 - x1;
|
||||
d =
|
||||
"M " + x1 + " " + y1 +
|
||||
" C " + x1 + " " + (y1 + dy * 0.5) +
|
||||
", " + (x2 + dx * 0.2) + " " + y2 +
|
||||
", " + x2 + " " + y2;
|
||||
} else if (conn.toSide === "right") {
|
||||
// End: right edge, vertically centered
|
||||
x2 = toRect.right - containerRect.left;
|
||||
y2 = toRect.top + toRect.height / 2 - containerRect.top;
|
||||
|
||||
// Bezier: go down first, then curve into the right side
|
||||
var dy = y2 - y1;
|
||||
var dx = x2 - x1;
|
||||
d =
|
||||
"M " + x1 + " " + y1 +
|
||||
" C " + x1 + " " + (y1 + dy * 0.5) +
|
||||
", " + (x2 + dx * 0.2) + " " + y2 +
|
||||
", " + x2 + " " + y2;
|
||||
} else {
|
||||
// Default: end at top-center of destination card
|
||||
x2 = toRect.left + toRect.width / 2 - containerRect.left;
|
||||
y2 = toRect.top - containerRect.top;
|
||||
|
||||
var dy = y2 - y1;
|
||||
d =
|
||||
"M " + x1 + " " + y1 +
|
||||
" C " + x1 + " " + (y1 + dy * CURVE_FACTOR) +
|
||||
", " + x2 + " " + (y2 - dy * CURVE_FACTOR) +
|
||||
", " + x2 + " " + y2;
|
||||
}
|
||||
|
||||
var path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
||||
path.setAttribute("d", d);
|
||||
path.setAttribute("fill", "none");
|
||||
path.setAttribute("stroke-width", STROKE_WIDTH);
|
||||
|
||||
if (conn.type === "winner") {
|
||||
path.setAttribute("stroke", WINNER_COLOR);
|
||||
} else {
|
||||
path.setAttribute("stroke", LOSER_COLOR);
|
||||
path.setAttribute("stroke-dasharray", DASH_ARRAY);
|
||||
}
|
||||
|
||||
svg.appendChild(path);
|
||||
});
|
||||
}
|
||||
|
||||
function drawAllBrackets() {
|
||||
var containers = document.querySelectorAll("[data-bracket-lines]");
|
||||
containers.forEach(drawBracketLines);
|
||||
}
|
||||
|
||||
// Draw on initial load
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", drawAllBrackets);
|
||||
} else {
|
||||
// DOM already loaded (e.g. script loaded via HTMX swap)
|
||||
drawAllBrackets();
|
||||
}
|
||||
|
||||
// Redraw on window resize (debounced)
|
||||
var resizeTimer;
|
||||
window.addEventListener("resize", function () {
|
||||
clearTimeout(resizeTimer);
|
||||
resizeTimer = setTimeout(drawAllBrackets, 100);
|
||||
});
|
||||
|
||||
// Redraw after HTMX swaps
|
||||
document.addEventListener("htmx:afterSwap", function () {
|
||||
// Small delay to let the DOM settle
|
||||
setTimeout(drawAllBrackets, 50);
|
||||
});
|
||||
|
||||
// Expose for manual triggering if needed
|
||||
window.drawBracketLines = drawAllBrackets;
|
||||
})();
|
||||
Reference in New Issue
Block a user