UNPKG

@weball/ui-kit

Version:

UI Kit library with flexible fixture and tournament components for sports applications

1,258 lines (1,234 loc) 84.3 kB
import { jsx, jsxs, Fragment } from 'react/jsx-runtime'; import React, { forwardRef, useRef, useState, useEffect, useMemo } from 'react'; import { Tooltip, Modal, InputNumber } from 'antd'; /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */ var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __rest(s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; } function __spreadArray(to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); } typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; var Box = forwardRef(function (_a, ref) { var children = _a.children, _b = _a.className, className = _b === void 0 ? '' : _b, position = _a.position, width = _a.width, height = _a.height, borderWidth = _a.borderWidth, borderTopWidth = _a.borderTopWidth, borderLeftWidth = _a.borderLeftWidth, borderRightWidth = _a.borderRightWidth, borderBottomWidth = _a.borderBottomWidth, borderStyle = _a.borderStyle, borderTopRightRadius = _a.borderTopRightRadius, borderBottomRightRadius = _a.borderBottomRightRadius, borderTopLeftRadius = _a.borderTopLeftRadius, borderBottomLeftRadius = _a.borderBottomLeftRadius, borderColor = _a.borderColor, backgroundColor = _a.backgroundColor, px = _a.px, py = _a.py, gap = _a.gap, color = _a.color, fontSize = _a.fontSize, fontWeight = _a.fontWeight, textAlign = _a.textAlign, pointerEvents = _a.pointerEvents, style = _a.style, props = __rest(_a, ["children", "className", "position", "width", "height", "borderWidth", "borderTopWidth", "borderLeftWidth", "borderRightWidth", "borderBottomWidth", "borderStyle", "borderTopRightRadius", "borderBottomRightRadius", "borderTopLeftRadius", "borderBottomLeftRadius", "borderColor", "backgroundColor", "px", "py", "gap", "color", "fontSize", "fontWeight", "textAlign", "pointerEvents", "style"]); var computedStyle = __assign(__assign(__assign(__assign(__assign(__assign(__assign(__assign({ display: 'block', boxSizing: 'border-box' }, style), { width: typeof width === 'number' ? "".concat(width, "px") : width, height: typeof height === 'number' ? "".concat(height, "px") : height }), borderWidth && { borderWidth: "".concat(borderWidth, "px"), borderStyle: borderStyle || 'solid', borderColor: borderColor || 'currentColor' }), borderTopWidth !== undefined && { borderTopWidth: "".concat(borderTopWidth, "px"), borderTopStyle: borderStyle || 'solid', borderTopColor: borderColor || 'currentColor' }), borderLeftWidth !== undefined && { borderLeftWidth: "".concat(borderLeftWidth, "px"), borderLeftStyle: borderLeftWidth === 0 ? 'none' : borderStyle || 'solid', borderLeftColor: borderLeftWidth === 0 ? 'transparent' : borderColor || 'currentColor' }), borderRightWidth !== undefined && { borderRightWidth: "".concat(borderRightWidth, "px"), borderRightStyle: borderRightWidth === 0 ? 'none' : borderStyle || 'solid', borderRightColor: borderRightWidth === 0 ? 'transparent' : borderColor || 'currentColor' }), borderBottomWidth !== undefined && { borderBottomWidth: "".concat(borderBottomWidth, "px"), borderBottomStyle: borderBottomWidth === 0 ? 'none' : borderStyle || 'solid', borderBottomColor: borderBottomWidth === 0 ? 'transparent' : borderColor || 'currentColor' }), { // Border radius borderTopRightRadius: borderTopRightRadius ? "".concat(borderTopRightRadius, "px") : undefined, borderBottomRightRadius: borderBottomRightRadius ? "".concat(borderBottomRightRadius, "px") : undefined, borderTopLeftRadius: borderTopLeftRadius ? "".concat(borderTopLeftRadius, "px") : undefined, borderBottomLeftRadius: borderBottomLeftRadius ? "".concat(borderBottomLeftRadius, "px") : undefined, // Background backgroundColor: backgroundColor, // Spacing - Chakra UI behavior paddingLeft: px ? "".concat(px * 4, "px") : undefined, paddingRight: px ? "".concat(px * 4, "px") : undefined, paddingTop: py ? "".concat(py * 4, "px") : undefined, paddingBottom: py ? "".concat(py * 4, "px") : undefined, gap: gap ? "".concat(gap * 4, "px") : undefined, // Layout position: position, // Typography color: color, fontSize: fontSize, fontWeight: fontWeight, textAlign: textAlign, // Interaction pointerEvents: pointerEvents }); return jsx("div", __assign({ ref: ref, className: className, style: computedStyle }, props, { children: children })); }); Box.displayName = 'Box'; var Flex = forwardRef(function (_a, ref) { var children = _a.children, _b = _a.className, className = _b === void 0 ? '' : _b, direction = _a.direction, alignItems = _a.alignItems, justifyContent = _a.justifyContent, gap = _a.gap, width = _a.width, height = _a.height, position = _a.position, px = _a.px, py = _a.py, mt = _a.mt, borderRadius = _a.borderRadius, flexDirection = _a.flexDirection, color = _a.color, fontWeight = _a.fontWeight, cursor = _a.cursor, zIndex = _a.zIndex, backgroundColor = _a.backgroundColor, style = _a.style, props = __rest(_a, ["children", "className", "direction", "alignItems", "justifyContent", "gap", "width", "height", "position", "px", "py", "mt", "borderRadius", "flexDirection", "color", "fontWeight", "cursor", "zIndex", "backgroundColor", "style"]); var baseClasses = 'flex'; var directionClasses = { 'row': 'flex-row', 'column': 'flex-col', 'row-reverse': 'flex-row-reverse', 'column-reverse': 'flex-col-reverse' }; var alignClasses = { 'flex-start': 'items-start', 'flex-end': 'items-end', 'center': 'items-center', 'baseline': 'items-baseline', 'stretch': 'items-stretch' }; var justifyClasses = { 'flex-start': 'justify-start', 'flex-end': 'justify-end', 'center': 'justify-center', 'space-between': 'justify-between', 'space-around': 'justify-around', 'space-evenly': 'justify-evenly' }; var finalDirection = direction || flexDirection; var directionClass = finalDirection ? directionClasses[finalDirection] : ''; var alignClass = alignItems ? alignClasses[alignItems] : ''; var justifyClass = justifyContent ? justifyClasses[justifyContent] : ''; var positionClass = position || ''; var computedStyle = __assign(__assign({}, style), { width: typeof width === 'number' ? "".concat(width, "px") : width, height: typeof height === 'number' ? "".concat(height, "px") : height, gap: gap ? "".concat(gap * 4, "px") : undefined, paddingLeft: px ? "".concat(px * 4, "px") : undefined, paddingRight: px ? "".concat(px * 4, "px") : undefined, paddingTop: py ? "".concat(py * 4, "px") : undefined, paddingBottom: py ? "".concat(py * 4, "px") : undefined, marginTop: mt ? "".concat(mt * 4, "px") : undefined, borderRadius: borderRadius ? "".concat(borderRadius, "px") : undefined, position: position, color: color, fontWeight: fontWeight, cursor: cursor, zIndex: zIndex, backgroundColor: backgroundColor }); return jsx("div", __assign({ ref: ref, className: "".concat(baseClasses, " ").concat(directionClass, " ").concat(alignClass, " ").concat(justifyClass, " ").concat(positionClass, " ").concat(className), style: computedStyle }, props, { children: children })); }); Flex.displayName = 'Flex'; var Text = forwardRef(function (_a, ref) { var children = _a.children, _b = _a.className, className = _b === void 0 ? '' : _b, fontSize = _a.fontSize, fontWeight = _a.fontWeight, color = _a.color, textAlign = _a.textAlign, px = _a.px, py = _a.py, height = _a.height, position = _a.position, pointerEvents = _a.pointerEvents, _c = _a.as, as = _c === void 0 ? 'p' : _c, style = _a.style, onClick = _a.onClick, width = _a.width; __rest(_a, ["children", "className", "fontSize", "fontWeight", "color", "textAlign", "px", "py", "height", "position", "pointerEvents", "as", "style", "onClick", "width"]); var computedStyle = __assign(__assign({}, style), { fontSize: fontSize, fontWeight: fontWeight, color: color, textAlign: textAlign, paddingLeft: px ? "".concat(px * 4, "px") : undefined, paddingRight: px ? "".concat(px * 4, "px") : undefined, paddingTop: py ? "".concat(py * 4, "px") : undefined, paddingBottom: py ? "".concat(py * 4, "px") : undefined, height: typeof height === 'number' ? "".concat(height * 4, "px") : height, position: position, pointerEvents: pointerEvents, width: width }); var commonProps = { ref: ref, className: className, style: computedStyle, onClick: onClick }; switch (as) { case 'span': return jsx("span", __assign({}, commonProps, { children: children })); case 'div': return jsx("div", __assign({}, commonProps, { children: children })); case 'h1': return jsx("h1", __assign({}, commonProps, { children: children })); case 'h2': return jsx("h2", __assign({}, commonProps, { children: children })); case 'h3': return jsx("h3", __assign({}, commonProps, { children: children })); case 'h4': return jsx("h4", __assign({}, commonProps, { children: children })); case 'h5': return jsx("h5", __assign({}, commonProps, { children: children })); case 'h6': return jsx("h6", __assign({}, commonProps, { children: children })); default: return jsx("p", __assign({}, commonProps, { children: children })); } }); Text.displayName = 'Text'; var Image = function (_a) { var src = _a.src, _b = _a.alt, alt = _b === void 0 ? '' : _b, width = _a.width, height = _a.height, _c = _a.className, className = _c === void 0 ? '' : _c, style = _a.style, onError = _a.onError, onLoad = _a.onLoad, props = __rest(_a, ["src", "alt", "width", "height", "className", "style", "onError", "onLoad"]); var computedStyle = __assign(__assign({}, style), { width: width, height: height }); return jsx("img", __assign({ src: src, alt: alt, className: className, style: computedStyle, onError: onError, onLoad: onLoad }, props)); }; var Collapse = function (_a) { var children = _a.children, isOpen = _a.in, _b = _a.animateOpacity, animateOpacity = _b === void 0 ? false : _b, _c = _a.className, className = _c === void 0 ? '' : _c, style = _a.style; var contentRef = useRef(null); var _d = useState(isOpen ? 'auto' : 0), height = _d[0], setHeight = _d[1]; useEffect(function () { if (contentRef.current) { var contentHeight = contentRef.current.scrollHeight; setHeight(isOpen ? contentHeight : 0); } }, [isOpen]); var computedStyle = __assign(__assign({}, style), { height: height === 'auto' ? 'auto' : "".concat(height, "px"), overflow: 'hidden', transition: "height 0.3s ease-in-out".concat(animateOpacity ? ', opacity 0.3s ease-in-out' : ''), opacity: animateOpacity ? isOpen ? 1 : 0 : 1 }); return jsx("div", { ref: contentRef, className: className, style: computedStyle, children: children }); }; var Divider = function (_a) { var _b = _a.className, className = _b === void 0 ? '' : _b, style = _a.style, _c = _a.orientation, orientation = _c === void 0 ? 'horizontal' : _c, _d = _a.color, color = _d === void 0 ? '#e2e8f0' : _d; var computedStyle = __assign(__assign(__assign({}, style), { backgroundColor: color, border: 'none' }), orientation === 'horizontal' ? { width: '100%', height: '1px' } : { width: '1px', height: '100%' }); return jsx("hr", { className: className, style: computedStyle }); }; var FIXTURE_BRACE_WIDTH = 10; var FIXTURE_LINE_WIDTH = 10; var FIXTURE_FINAL_LINE_WIDTH = 50; var FIXTURE_NODE_HEIGHT = 80; var FIXTURE_NODE_WIDTH = 170; var FIXTURE_HEIGHT_BETWEEN_NODES = 10; var FIXTURE_HEIGHT_BETWEEN_GROUPS = 35; var FIXTURE_WIDTH_BETWEEN_WINGS = 70; var FIXTURE_STAGE_SIZE = 14; var FIXTURE_WINNER_STAGE_SIZE = 40; var FIXTURE_WINNER_WIDTH = 170; var FIXTURE_WINNER_HEIGHT = 60; var FIXTURE_SCROLL_SIZE = 30; var SRC_IMG = "https://img.freepik.com/vector-gratis/balon-futbol-aislado_1284-41812.jpg?size=338&ext=jpg&ga=GA1.1.1788614524.1719187200&semt=ais_user"; function getOrderedMatchesByParentChildrenCount(tree, stages, fromStage) { var _a; // 1) Recolectamos datos en un array auxiliar var matches = []; var parentIdCounter = 1; function traverse(node, parentId) { var _a; // Si no hay hijos if (!node.children || node.children.length === 0) { node.matchesPlanning.forEach(function (m) { var _a; if ((_a = m.tournamentMatches) === null || _a === void 0 ? void 0 : _a.length) { matches.push({ match: m, parentChildrenCount: 0, parentId: parentId }); } }); return 0; } var childrenCount = node.children.length; // Recolectamos del nodo actual (_a = node.matchesPlanning) === null || _a === void 0 ? void 0 : _a.forEach(function (m) { matches.push({ match: m, parentChildrenCount: childrenCount, parentId: parentId }); }); // Recorremos recursivamente for (var _i = 0, _b = node.children; _i < _b.length; _i++) { var child = _b[_i]; traverse(child, parentIdCounter); parentIdCounter++; } return childrenCount; } // 2) Iniciar la recursividad traverse(tree); // 3) Orden descendente según hijos matches.sort(function (a, b) { return b.parentChildrenCount - a.parentChildrenCount; }); // 4) Variables para la lógica de "stageNumberFromFinal" y "parentRank" var currentRank = 1; var previousParentId = (_a = matches[0]) === null || _a === void 0 ? void 0 : _a.parentId; var lastStageNumberFromFinal; var parentRankCounter = 1; // 5) Creamos el array de WBFixtureNode (sin mutar match) var wbFixtureNodes = matches.map(function (item, index) { // Si cambió el parentId, subimos la “ronda” if (item.parentId !== previousParentId) { currentRank++; previousParentId = item.parentId; } var stageNumber = stages - currentRank + 1; // Si cambió la ronda, reseteamos parentRankCounter a 1 if (stageNumber !== lastStageNumberFromFinal) { lastStageNumberFromFinal = stageNumber; parentRankCounter = 1; } // Construimos un nuevo objeto con las props que SÍ tiene FixtureVisualizerMatch // (por ejemplo, 'id', 'score', 'participants', etc.), // y agregamos las 5 propiedades que definiste en WBFixtureNode. var parentRank = parentRankCounter; var wbNode = __assign(__assign({}, item.match), { stageNumberFromFinal: stageNumber, nodeNumber: index + 1, parentRank: parentRank, groupNumber: parentRank // O la lógica que tú quieras }); parentRankCounter++; return wbNode; }); return wbFixtureNodes.filter(function (m) { return m.stageNumberFromFinal >= (0); }); } function getShortestNameClubInscription(clubInscription, slice) { var _a, _b, _c; if (slice === void 0) { slice = 11; } return ((clubInscription === null || clubInscription === void 0 ? void 0 : clubInscription.tableName) || (clubInscription === null || clubInscription === void 0 ? void 0 : clubInscription.name) || ((_a = clubInscription === null || clubInscription === void 0 ? void 0 : clubInscription.club) === null || _a === void 0 ? void 0 : _a.tableShortName) || ((_b = clubInscription === null || clubInscription === void 0 ? void 0 : clubInscription.club) === null || _b === void 0 ? void 0 : _b.tableName) || ((_c = clubInscription === null || clubInscription === void 0 ? void 0 : clubInscription.club) === null || _c === void 0 ? void 0 : _c.name) || "").slice(0, slice); } var WbFixtureNodeClub = function (props) { var _a, _b, _c, _d, _e, _f, _g; var onClickMatch = props.onClickMatch, visualizerMatch = props.match, local = props.local, nodeSelected = props.nodeSelected, _h = props.editMode, editMode = _h === void 0 ? false : _h; var match = visualizerMatch; var _j = useMemo(function () { var _a, _b, _c, _d, _e; if (!match) return {}; var hasOnlyOneMAtch = ((_a = match === null || match === void 0 ? void 0 : match.tournamentMatches) === null || _a === void 0 ? void 0 : _a.length) === 1; if (hasOnlyOneMAtch) { return { clubScore: local ? (_b = match === null || match === void 0 ? void 0 : match.tournamentMatches) === null || _b === void 0 ? void 0 : _b[0].scoreHome : (_c = match === null || match === void 0 ? void 0 : match.tournamentMatches) === null || _c === void 0 ? void 0 : _c[0].scoreAway, clubPenaltyScore: local ? (_d = match === null || match === void 0 ? void 0 : match.tournamentMatches) === null || _d === void 0 ? void 0 : _d[0].scoreHomePenalty : (_e = match === null || match === void 0 ? void 0 : match.tournamentMatches) === null || _e === void 0 ? void 0 : _e[0].scoreAwayPenalty }; } return { clubScore: local ? match === null || match === void 0 ? void 0 : match.valueScoreHome : match.valueScoreAway, clubPenaltyScore: local ? match === null || match === void 0 ? void 0 : match.valueScoreHomePenalty : match === null || match === void 0 ? void 0 : match.valueScoreAwayPenalty }; }, [match, local]), clubScore = _j.clubScore, clubPenaltyScore = _j.clubPenaltyScore; var hasLost = match.teamWon && (local ? ((_a = match.teamWon) === null || _a === void 0 ? void 0 : _a.id) !== ((_b = match.clubHome) === null || _b === void 0 ? void 0 : _b.id) : ((_c = match.teamWon) === null || _c === void 0 ? void 0 : _c.id) !== ((_d = match.clubAway) === null || _d === void 0 ? void 0 : _d.id)); var club = local ? match.clubHome : match.clubAway; var vacancy = local ? match === null || match === void 0 ? void 0 : match.vacancyHome : match === null || match === void 0 ? void 0 : match.vacancyAway; if (!match) return null; return jsxs(Flex, { direction: "row", alignItems: "center", justifyContent: "space-between", style: { opacity: nodeSelected ? 1 : hasLost ? 0.5 : 1, backgroundColor: nodeSelected ? "gray" : "unset" }, children: [jsxs(Flex, { direction: "row", alignItems: "center", className: "flex-1 max-w-[80%] hover:bg-gray-300 transition-all duration-400", gap: 2, onClick: editMode ? function () { return onClickMatch(match, local ? "local" : "visit"); } : undefined, children: [((_e = club === null || club === void 0 ? void 0 : club.clubInscription) === null || _e === void 0 ? void 0 : _e.logo) ? jsx(Fragment, { children: jsx(Image, { src: ((_f = club === null || club === void 0 ? void 0 : club.clubInscription) === null || _f === void 0 ? void 0 : _f.logo) || SRC_IMG, width: "20px", height: "20px" }) }) : jsx("div", { className: "block h-4 aspect-square rounded-md", style: { backgroundColor: ((_g = club === null || club === void 0 ? void 0 : club.clubInscription) === null || _g === void 0 ? void 0 : _g.color) || (vacancy === null || vacancy === void 0 ? void 0 : vacancy.color) } }), jsx(Tooltip, { title: getShortestNameClubInscription(club === null || club === void 0 ? void 0 : club.clubInscription) || (vacancy === null || vacancy === void 0 ? void 0 : vacancy.name), mouseEnterDelay: 0.1, mouseLeaveDelay: 0.1, placement: "topLeft", trigger: "hover", overlayClassName: "custom-tooltip", children: jsx(Text, { className: "text-[10px] whitespace-nowrap overflow-hidden text-ellipsis ", fontWeight: "bold", children: getShortestNameClubInscription(club === null || club === void 0 ? void 0 : club.clubInscription) || (vacancy === null || vacancy === void 0 ? void 0 : vacancy.name) || "" }) })] }), jsx("div", { className: "transition-all duration-400 min-w-[40px] flex flex-col items-center justify-center ".concat(editMode ? "hover:bg-gray-300 cursor-pointer" : "cursor-default"), onClick: editMode ? function () { return onClickMatch(match, local ? "resultLocal" : "resultVisit"); } : undefined, children: jsxs("div", { className: "flex items-center gap-1", children: [jsx(Text, { fontWeight: "bold", height: 6, children: clubScore }), clubPenaltyScore !== undefined && clubPenaltyScore !== null && jsxs(Text, { fontSize: "10px", className: "text-gray-500", children: ["(", clubPenaltyScore, ")"] })] }) })] }); }; /** * WbFixtureResult component - displays tournament match results with team information * * @example * ```tsx * // Basic usage with minimum required properties * <WbFixtureResult * tournamentMatch={{ * id: 1, * scoreHome: 2, * scoreAway: 1, * category: { categoryInstance: { name: "Final" } }, * matchInfo: { vacancyHome: null, vacancyAway: null } * }} * fixtureMatch={{ * id: 1, * clubHome: { clubInscription: { logo: "...", name: "Team A", color: "#fff" } }, * clubAway: { clubInscription: { logo: "...", name: "Team B", color: "#000" } } * }} * /> * * // Usage with extended data * <WbFixtureResult * tournamentMatch={{ * id: 1, * scoreHome: 2, * scoreAway: 1, * customField: "my custom data", * category: { categoryInstance: { name: "Final", customCategoryData: "..." } }, * matchInfo: { vacancyHome: null, vacancyAway: null, customMatchInfo: "..." } * }} * fixtureMatch={{ * id: 1, * clubHome: { clubInscription: { logo: "...", name: "Team A", color: "#fff", customClubData: "..." } }, * clubAway: { clubInscription: { logo: "...", name: "Team B", color: "#000" } }, * customFixtureData: "my custom fixture data" * }} * /> * ``` */ var WbFixtureResult = function (props) { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z; var tournamentMatch = props.tournamentMatch, fixtureMatch = props.fixtureMatch; tournamentMatch.scoreHomePenalty !== undefined && tournamentMatch.scoreHomePenalty !== null || tournamentMatch.scoreAwayPenalty !== undefined && tournamentMatch.scoreAwayPenalty !== null; if (!tournamentMatch) return null; return jsxs(Flex, { flexDirection: "column", gap: 2, px: 4, py: 3, borderRadius: 12, width: FIXTURE_NODE_WIDTH + 10 + "px", height: FIXTURE_NODE_HEIGHT + 20 + "px", maxWidth: "100%", children: [jsx(Text, { className: "text-white text-xs p-0", children: (_b = (_a = tournamentMatch.category) === null || _a === void 0 ? void 0 : _a.categoryInstance) === null || _b === void 0 ? void 0 : _b.name }), jsxs(Flex, { direction: "row", alignItems: "center", justifyContent: "space-between", children: [jsxs(Flex, { direction: "row", alignItems: "center", className: "flex-1 max-w-[80%] text-white transition-all duration-400", gap: 2, children: [((_d = (_c = fixtureMatch.clubHome) === null || _c === void 0 ? void 0 : _c.clubInscription) === null || _d === void 0 ? void 0 : _d.logo) ? jsx(Fragment, { children: jsx(Image, { src: ((_f = (_e = fixtureMatch.clubHome) === null || _e === void 0 ? void 0 : _e.clubInscription) === null || _f === void 0 ? void 0 : _f.logo) || SRC_IMG, width: "20px", height: "20px" }) }) : jsx("div", { className: "block h-4 aspect-square rounded-md", style: { backgroundColor: ((_h = (_g = fixtureMatch.clubHome) === null || _g === void 0 ? void 0 : _g.clubInscription) === null || _h === void 0 ? void 0 : _h.color) || ((_j = tournamentMatch.matchInfo.vacancyHome) === null || _j === void 0 ? void 0 : _j.color) } }), jsx(Tooltip, { title: getShortestNameClubInscription((_k = fixtureMatch === null || fixtureMatch === void 0 ? void 0 : fixtureMatch.clubHome) === null || _k === void 0 ? void 0 : _k.clubInscription) || ((_l = tournamentMatch.matchInfo.vacancyHome) === null || _l === void 0 ? void 0 : _l.name) || "", mouseEnterDelay: 0.1, mouseLeaveDelay: 0.1, placement: "topLeft", trigger: "hover", children: jsx(Text, { className: "text-[10px] whitespace-nowrap overflow-hidden text-ellipsis ", fontWeight: "bold", children: getShortestNameClubInscription((_m = fixtureMatch === null || fixtureMatch === void 0 ? void 0 : fixtureMatch.clubHome) === null || _m === void 0 ? void 0 : _m.clubInscription) || ((_o = tournamentMatch.matchInfo.vacancyHome) === null || _o === void 0 ? void 0 : _o.name) || "" }) })] }), jsx("div", { className: "text-white transition-all duration-400 min-w-[40px] flex flex-col items-center justify-center", children: jsxs("div", { className: "flex items-center gap-1", children: [jsx(Text, { fontWeight: "bold", height: 6, children: tournamentMatch.scoreHome || 0 }), tournamentMatch.scoreHomePenalty !== undefined && tournamentMatch.scoreHomePenalty !== null && jsxs(Text, { fontSize: "10px", className: "text-gray-300", children: ["(", tournamentMatch.scoreHomePenalty, ")"] })] }) })] }), jsxs(Flex, { direction: "row", alignItems: "center", justifyContent: "space-between", children: [jsxs(Flex, { direction: "row", alignItems: "center", className: "flex-1 max-w-[80%] text-white transition-all duration-400", gap: 2, children: [((_q = (_p = fixtureMatch.clubAway) === null || _p === void 0 ? void 0 : _p.clubInscription) === null || _q === void 0 ? void 0 : _q.logo) ? jsx(Fragment, { children: jsx(Image, { src: ((_s = (_r = fixtureMatch.clubAway) === null || _r === void 0 ? void 0 : _r.clubInscription) === null || _s === void 0 ? void 0 : _s.logo) || SRC_IMG, width: "20px", height: "20px" }) }) : jsx("div", { className: "block h-4 aspect-square rounded-md", style: { backgroundColor: ((_u = (_t = fixtureMatch.clubAway) === null || _t === void 0 ? void 0 : _t.clubInscription) === null || _u === void 0 ? void 0 : _u.color) || ((_v = tournamentMatch.matchInfo.vacancyAway) === null || _v === void 0 ? void 0 : _v.color) } }), jsx(Tooltip, { title: getShortestNameClubInscription((_w = fixtureMatch.clubAway) === null || _w === void 0 ? void 0 : _w.clubInscription) || ((_x = tournamentMatch.matchInfo.vacancyAway) === null || _x === void 0 ? void 0 : _x.name) || "", mouseEnterDelay: 0.1, mouseLeaveDelay: 0.1, placement: "topLeft", trigger: "hover", children: jsx(Text, { className: "text-[10px] whitespace-nowrap overflow-hidden text-ellipsis ", fontWeight: "bold", children: getShortestNameClubInscription((_y = fixtureMatch.clubAway) === null || _y === void 0 ? void 0 : _y.clubInscription) || ((_z = tournamentMatch.matchInfo.vacancyAway) === null || _z === void 0 ? void 0 : _z.name) || "" }) })] }), jsx("div", { className: "text-white transition-all duration-400 min-w-[40px] flex flex-col items-center justify-center", children: jsxs("div", { className: "flex items-center gap-1", children: [jsx(Text, { fontWeight: "bold", height: 6, children: tournamentMatch.scoreAway || 0 }), tournamentMatch.scoreAwayPenalty !== undefined && tournamentMatch.scoreAwayPenalty !== null && jsxs(Text, { fontSize: "10px", className: "text-gray-300", children: ["(", tournamentMatch.scoreAwayPenalty, ")"] })] }) })] })] }); }; var WbColors = { light: { backgroundGrey: "#e9e9e9", inputBorder: "#00000040", darkGrey: "#464646", grey: "#707070"}}; /** * Helper function to convert an array of fixtures into a FixtureVisualizer object * * @param fixtures - Array of fixture data * @param options - Optional configuration * @returns FixtureVisualizer object * * @example * ```typescript * const fixtures = [/* your fixture array *\/]; * const fixtureRoot = createFixtureRoot(fixtures); * * // Use in your component: * // <WbFixture fixtureVisualizerRoot={fixtureRoot} /> * ``` */ function createFixtureRoot(fixtures, options) { var _a, _b; if (options === void 0) { options = {}; } return { id: (_a = options.id) !== null && _a !== void 0 ? _a : 1, children: [], matchesPlanning: (_b = options.matchesPlanning) !== null && _b !== void 0 ? _b : [], fixtures: fixtures }; } // eslint-disable-next-line react/display-name var WbFixtureNode = React.forwardRef(function (props, ref) { var _a, _b, _c; var match = props.match, onClickMatch = props.onClickMatch, nodeSelected = props.nodeSelected, _d = props.editMode, editMode = _d === void 0 ? false : _d; var _e = useState(false), showDetails = _e[0], setShowDetails = _e[1]; var handleClickMatch = function (match, position) { var _a, _b; if (position === "resultLocal" || position === "resultVisit") { // Only allow editing results if editMode is enabled if (editMode && ((_a = match === null || match === void 0 ? void 0 : match.tournamentMatches) === null || _a === void 0 ? void 0 : _a.length) && ((_b = match === null || match === void 0 ? void 0 : match.tournamentMatches) === null || _b === void 0 ? void 0 : _b.length) >= 1) { setEditingResult({ position: position, visible: true }); } return; } onClickMatch(match, position); setShowDetails(function (prev) { return !prev; }); }; var handleContainerClick = function () { // Only trigger expandable when not in edit mode if (!editMode) { setShowDetails(function (prev) { return !prev; }); } }; var _f = useState({ position: "resultLocal", visible: false }), editingResult = _f[0], setEditingResult = _f[1]; var _g = useState(null), inputScore = _g[0], setInputScore = _g[1]; return jsxs(Fragment, { children: [jsxs(Flex, { direction: "column", cursor: "pointer", ref: ref, children: [jsxs(Flex, { flexDirection: "column", gap: 2, px: 4, py: 3, borderRadius: 12, width: FIXTURE_NODE_WIDTH + "px", height: FIXTURE_NODE_HEIGHT + "px", onClick: handleContainerClick, style: { backgroundColor: nodeSelected ? "gray" : WbColors.light.backgroundGrey }, children: [jsx(WbFixtureNodeClub, { nodeSelected: nodeSelected, local: true, onClickMatch: handleClickMatch, match: match, editMode: editMode }), jsx(WbFixtureNodeClub, { nodeSelected: nodeSelected, local: false, onClickMatch: handleClickMatch, match: match, editMode: editMode })] }), jsx(Collapse, { in: showDetails, animateOpacity: true, style: { zIndex: 999 }, children: ((_a = match === null || match === void 0 ? void 0 : match.tournamentMatches) === null || _a === void 0 ? void 0 : _a.length) && ((_b = match === null || match === void 0 ? void 0 : match.tournamentMatches) === null || _b === void 0 ? void 0 : _b.length) > 1 && jsx(Flex, { direction: "column", gap: 1, mt: 1, py: 2, borderRadius: 8, zIndex: 999, backgroundColor: WbColors.light.darkGrey, width: FIXTURE_NODE_WIDTH + "px", children: (_c = match.tournamentMatches) === null || _c === void 0 ? void 0 : _c.map(function (tm, index) { return jsxs(React.Fragment, { children: [jsx(WbFixtureResult, { fixtureMatch: match, tournamentMatch: tm }), index !== match.tournamentMatches.length - 1 && jsx(Divider, {})] }, tm.id); }) }) })] }), jsx(Modal, { title: editingResult.position === "resultLocal" ? "Editar resultado Local" : "Editar resultado Visitante", open: editingResult.visible, onOk: function () { setEditingResult(__assign(__assign({}, editingResult), { visible: false })); if (inputScore !== null) { var scoreHome = editingResult.position === "resultLocal" ? +inputScore : match.valueScoreHome ? +match.valueScoreHome : 0; var scoreAway = editingResult.position === "resultVisit" ? +inputScore : match.valueScoreAway ? +match.valueScoreAway : 0; props.onResultSaved(match.id, scoreHome, scoreAway); } setInputScore(null); }, onCancel: function () { setEditingResult(__assign(__assign({}, editingResult), { visible: false })); setInputScore(null); }, okText: "Guardar", cancelText: "Cancelar", children: jsx(InputNumber, { autoFocus: true, min: 0, style: { width: "100%" }, value: inputScore !== null && inputScore !== void 0 ? inputScore : undefined, onChange: function (value) { return setInputScore(value !== null && value !== void 0 ? value : null); }, placeholder: "Ingres\u00E1 el nuevo score" }) })] }); }); var WbStages = ["Final", "Semi Final", "Cuartos de Final", "Octavos de Final", "16avos de Final", "32avos de Final", "64avos de Final", "128avos de Final", "256avos de Final", "512avos de Final", "1024avos de Final"]; var WbAvatar = function (_a) { var _b = _a.padding, padding = _b === void 0 ? 12 : _b, props = __rest(_a, ["padding"]); var sizeStyle = typeof props.size === 'number' ? "".concat(props.size, "px") : props.size; if (props.new) return jsx("div", { className: "flex items-center justify-center rounded-full overflow-hidden bg-white", style: { height: sizeStyle, width: sizeStyle, padding: padding }, children: jsx("img", { src: props.src, alt: "", className: "object-contain" }) }); return jsx("div", { className: "flex items-center justify-center rounded-full overflow-hidden", style: { width: sizeStyle, height: sizeStyle, backgroundColor: "white", borderColor: props.borderColor || "white", borderWidth: "2px", borderStyle: "solid" }, children: jsx("img", { src: props.src, alt: "", className: "w-full h-full object-cover rounded-full" }) }); }; var KEY_PREFIX = { BRACE: "brace-", LINE: "line-", STAGE: "stage-" }; // ====================================================== // 2) Componente principal: WbFixture // ====================================================== /** * WbFixture component - displays tournament bracket/fixture visualization * * @template TFixtureData - Type for fixture visualizer data (can contain any additional properties) * @template TCupWinnerData - Type for cup winner data (can contain any additional properties) * * @example * ```tsx * // Basic usage with minimum required properties * <WbFixture * fixtureVisualizerRoot={{ * id: 1, * children: [], * matchesPlanning: [ * { * id: 1, * clubHome: { clubInscription: { logo: "...", name: "Team A", color: "#fff" } }, * clubAway: { clubInscription: { logo: "...", name: "Team B", color: "#000" } } * } * ] * }} * cupWinner={{ id: 1, logo: "...", name: "Winner", color: "#gold" }} * onClickNode={(match, position) => console.log(match, position)} * /> * * // Usage with extended custom data * <WbFixture * fixtureVisualizerRoot={{ * id: 1, * children: [], * matchesPlanning: [], * customTournamentName: "World Cup 2024", * metadata: { season: "2024", region: "Global" } * }} * cupWinner={{ * id: 1, * logo: "...", * name: "Winner", * color: "#gold", * stats: { wins: 15, goals: 45 }, * country: "Argentina" * }} * /> * ``` */ var WbFixture = function (props) { var _a, _b; var fixtureVisualizerRoot = props.fixtureVisualizerRoot, cupWinner = props.cupWinner, cupLogo = props.cupLogo, nodeSelected = props.nodeSelected, onClickNode = props.onClickNode, onResultSaved = props.onResultSaved, _c = props.editMode, editMode = _c === void 0 ? false : _c, _d = props.responsive, responsive = _d === void 0 ? true : _d; __rest(props, ["fixtureVisualizerRoot", "cupWinner", "cupLogo", "nodeSelected", "onClickNode", "onResultSaved", "editMode", "responsive"]); // Estado con la lista final de nodos a renderizar (partidos) var _e = useState([]), matches = _e[0], setMatches = _e[1]; // Cantidad de líneas que se dibujan entre partidos var _f = useState(0), linesQuantity = _f[0], setLinesQuantity = _f[1]; // Cantidad de “stages” (rondas) var _g = useState(0), rootStageNumberToShow = _g[0], setRootStageNumberToShow = _g[1]; // Cantidad de textos de etapas que dibujaremos var _h = useState(0), stagesTextQuantity = _h[0], setStagesTextQuantity = _h[1]; // Campeón detectado automáticamente cuando cupWinner no está presente var _j = useState(null), detectedWinner = _j[0], setDetectedWinner = _j[1]; // Referencia al contenedor principal var containerRef = useRef(null); // Referencias para cada parte del fixture var refStages = useRef([]); var refmatches = useRef([]); var refBraces = useRef([]); var refLines = useRef([]); var refFinalStage = useRef(null); var refWinner = useRef(null); // Estado para responsive scaling var _k = useState(1), scale = _k[0], setScale = _k[1]; var _l = useState(false), isResponsiveActive = _l[0], setIsResponsiveActive = _l[1]; var _m = useState({ width: 0, height: 0 }), containerDimensions = _m[0], setContainerDimensions = _m[1]; // Refs para debouncing y control var scaleTimeoutRef = useRef(null); var lastCalculatedScale = useRef(1); var resizeObserverRef = useRef(null); // ------------------------------------------------------ // Función para detectar al campeón automáticamente // ------------------------------------------------------ var detectChampion = function (matches) { var _a, _b, _c, _d; if (!matches || matches.length === 0) return null; var winner = null; // Buscar la final (partido con stageNumberFromFinal === 1) var finalMatchVisualizer = matches.find(function (match) { return match.stageNumberFromFinal === 1; }); if (!finalMatchVisualizer) return null; var hasMoreThantOneMatch = finalMatchVisualizer.tournamentMatches && finalMatchVisualizer.tournamentMatches.length > 1; if (hasMoreThantOneMatch) { winner = finalMatchVisualizer.clubWon; } else { var finalMatch = (_a = finalMatchVisualizer.tournamentMatches) === null || _a === void 0 ? void 0 : _a[0]; // Aplicar la misma lógica de determinación del ganador if (finalMatch.scoreHome > finalMatch.scoreAway) { winner = finalMatchVisualizer.clubHome; } else if (finalMatch.scoreHome < finalMatch.scoreAway) { winner = finalMatchVisualizer.clubAway; } else { // Empate en tiempo regular, revisar penalties if (!finalMatch.scoreHomePenalty && !finalMatch.scoreAwayPenalty) { // Sin penalties, no hay ganador definido return null; } else { if (finalMatch.scoreHomePenalty > finalMatch.scoreAwayPenalty) { winner = finalMatchVisualizer.clubHome; } else if (finalMatch.scoreHomePenalty < finalMatch.scoreAwayPenalty) { winner = finalMatchVisualizer.clubAway; } else { // Empate en penalties también, no hay ganador definido return null; } } } } // Retornar el ganador en el formato esperado por cupWinner if (winner === null || winner === void 0 ? void 0 : winner.clubInscription) { return { id: winner.id, name: ((_c = (_b = winner.clubInscription) === null || _b === void 0 ? void 0 : _b.club) === null || _c === void 0 ? void 0 : _c.name) || winner.clubInscription.name, logo: winner.clubInscription.logo || ((_d = winner.clubInscription.club) === null || _d === void 0 ? void 0 : _d.logo), color: winner.clubInscription.color }; } return null; }; // ------------------------------------------------------ // useEffect: detectar campeón cuando cambian los matches // ------------------------------------------------------ useEffect(function () { if (!cupWinner && matches.length > 0) { var champion = detectChampion(matches); setDetectedWinner(champion); } else { setDetectedWinner(null); } }, [matches, cupWinner]); // ------------------------------------------------------ // useEffect: cuando cambia el fixtureVisualizerRoot // ------------------------------------------------------ useEffect(function () { if (!fixtureVisualizerRoot) return; // Interpretamos 'stages' como la cantidad de hijos directos de la raíz var stages = fixtureVisualizerRoot.children.length; var maxChildren = 8; setRootStageNumberToShow(stages); setStagesTextQuantity((stages - 1) * 2); // Obtenemos y ordenamos los partidos var orderedMatches = getOrderedMatchesByParentChildrenCount(fixtureVisualizerRoot, stages); // Calculamos ancho y alto del contenedor var containerWidth = stages * FIXTURE_NODE_WIDTH + (stages - 1) * FIXTURE_BRACE_WIDTH + (stages - 2) * FIXTURE_LINE_WIDTH + FIXTURE_FINAL_LINE_WIDTH + (FIXTURE_WINNER_WIDTH - FIXTURE_NODE_WIDTH) / 2; var containerHeight = maxChildren * FIXTURE_NODE_HEIGHT + (maxChildren - 1) * FIXTURE_HEIGHT_BETWEEN_NODES + (maxChildren / 2 - 1) * FIXTURE_HEIGHT_BETWEEN_GROUPS + (FIXTURE_STAGE_SIZE + FIXTURE_HEIGHT_BETWEEN_NODES) * 2 + FIXTURE_SCROLL_SIZE; // Store calculated dimensions for responsive handling setContainerDimensions({ width: containerWidth, height: containerHeight }); if (containerRef.current) { // Solo establecer dimensiones fijas si no es responsive if (!responsive) { containerRef.current.style.width = containerWidth + "px"; containerRef.current.style.height = containerHeight + "px"; } else { // En modo responsive, establecer mínimas dimensiones containerRef.current.style.minWidth = containerWidth + "px"; containerRef.current.style.minHeight = containerHeight + "px"; } } // Cantidad de “llaves” (líneas) var auxLinesQuantity = Math.pow(2, stages - 1) - 1; setLinesQuantity(auxLinesQuantity); // Mapeamos los FixtureVisualizerMatch -> FixtureNode // (para que WbFixtureNode los reciba con la misma estructura esperada) setMatches(orderedMatches); // eslint-disable-next-line react-hooks/exhaustive-deps }, [fixtureVisualizerRoot]); // ------------------------------------------------------ // useEffect: responsive scaling - SIMPLIFIED // ------------------------------------------------------ useEffect(function () { if (!responsive) { setScale(1); setIsResponsiveActive(false); return; } var calculateAndApplyScale = function () { var container = containerRef.current; if (!container) return; // Get parent container dimensions var parentElement = container.parentElement; if (!parentElement) return; var parentWidth = parentElement.clientWidth; // Calculate total fixture width based on stages var totalFixtureWidth = rootStageNumberToShow * (FIXTURE_NODE_WIDTH + FIXTURE_BRACE_WIDTH) + FIXTURE_WINNER_WIDTH; var newScale = 1; var shouldBeResponsive = false; if (parentWidth > 0 && totalFixtureWidth > parentWidth) { newScale = Math.max(0.3, Math.min(1, (parentWidth - 40) / totalFixtureWidth)); // 40px margin shouldBeResponsive = true; } // Only update if there's a significant change (prevents render loops) var scaleDifference = Math.abs(newScale - lastCalculatedScale.current); if (scaleDifference > 0.01) { lastCalculatedScale.current = newScale; // Clear any existing timeout if (scaleTimeoutRef.current) { clearTimeout(scaleTimeoutRef.current); } // Debounce the update scaleTimeoutRef.current = setTimeout(function () { setScale(newScale); setIsResponsiveActive(shouldBeResponsive); }, 50); } }; // Initial calculation calculateAndApplyScale(); // Setup ResizeObserver with proper cleanup if (containerRef.current) { resizeObserverRef.current = new ResizeObserver(function () { // Throttle resize events if (scaleTimeoutRef.current) { clearTimeout(scaleTimeoutRef.current); } scaleTimeoutRef.current = setTimeout(calculateAndApplyScale, 100); }); // Observe the parent container, not the fixture itself var parentElement = containerRef.current.parentElement; if (parentElement) { resizeObserverRef.current.observe(parentElement); } } // Cleanup function return function () { if (resizeObserverRef.current) { resizeObserverRef.current.disconnect(); resizeObserverRef.current = null; } if (scaleTimeoutRef.current) { clearTimeout(scaleTimeoutRef.current); scaleTimeoutRef.current = null; } }; }, [responsive, rootStageNumberToShow]); // Removed scale from dependencies to prevent loops // ------------------------------------------------------ // useEffect: posicionar y dibujar nodos/lines una vez // que `matches` y las refs están listas // ------------------------------------------------------ useEffect(function () { var _a; if (!matches || matches.length === 0) return; if (!refmatches.current || refmatches.current.length !== matches.length) return; var linesEndPositions = []; var braceTop = 0; var lineIndex = 0; var lastNodeTop = 0; var lastNodeLeft = 0; var lastStageIndex = 0; var lastStageNumber = matches[0].stageNumberFromFinal; for (var i = 0; i < matches.length; i++) { // ------------------------------ // ETAPA NUEVA: Texto al final de la fase anterior // ------------------------------ if (matches[i].stageNumberFromFinal !== lastStageNumber) { if (refStages.current[lastStageIndex]) { var stageTop = lastNodeTop + FIXTURE_NODE_HEIGHT + FIXTURE_HEIGHT_BETWEEN_NODES; refStages.current[lastStageIndex].innerText = WbStages[lastStageNumber - 1]; refStages.current[lastStageIndex].style.top = stageTop + "px"; refStages.current[lastStageIndex].style.left = lastNodeLeft + "px"; } lastStageIndex++; } // ------------------------------ // POSICIONAMIENTO DE LA PRIMERA FASE