bracket-vue-tool
Version:
A flexible Vue 3 component for creating interactive tournament brackets (single & double elimination) with zero runtime dependencies.
2 lines (1 loc) • 33.8 kB
JavaScript
(function(x,t){typeof exports=="object"&&typeof module<"u"?t(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],t):(x=typeof globalThis<"u"?globalThis:x||self,t(x.BracketVueTool={},x.Vue))})(this,function(x,t){"use strict";const I={SINGLE_ELIMINATION:"single_elimination",DOUBLE_ELIMINATION:"double_elimination",SWISS:"swiss",ROUND_ROBIN:"round_robin"},w="TBD",S={ONE:"teamOne",TWO:"teamTwo"},E={CAN_SELECT_TEAM:"can_select_team",CAN_EDIT_DATE:"can_edit_date",CAN_EDIT_SCOPE:"can_edit_scope",CAN_EDIT_ROUND_NAME:"can_edit_round_name",CAN_EDIT_BEST_OF:"can_edit_best_of"},D=(r,n)=>{const a=r.__vccOpts||r;for(const[e,i]of n)a[e]=i;return a},G={__name:"TeamSelect",props:{team:{type:Object,required:!0},teamPosition:{type:String,required:!0},availableTeams:{type:Array,required:!0},selectedTeams:{type:Array,required:!0},canEdit:{type:Boolean,required:!0},highlightedTeam:{type:Number,default:null},permissions:{type:Object,default:()=>({[E.CAN_SELECT_TEAM]:!0})},isWinner:{type:Boolean,default:!1},isLoser:{type:Boolean,default:!1},shouldHighlight:{type:Boolean,default:!1}},emits:["update:team","highlight-team","unhighlight-team"],setup(r,{expose:n,emit:a}){n();const e=r,i=a,o=t.ref(e.team.name),s=t.computed(()=>{var m;return o.value===w?null:((m=e.availableTeams.find(T=>T.name===o.value))==null?void 0:m.logo)||null});t.watch(()=>e.team,m=>{o.value=m.name},{immediate:!0}),t.onMounted(()=>{console.log("TeamSelect mounted:",{team:e.team,availableTeams:e.availableTeams})});const l=m=>m===w?!1:e.selectedTeams.includes(m)&&m!==e.team.name||m===e.team.name&&e.team.name!==w,d=t.computed(()=>e.availableTeams?e.availableTeams.filter(m=>m.name===w||m.name===e.team.name?!0:!l(m.name)):[]),h={props:e,emit:i,selectedTeam:o,selectedTeamLogo:s,isTeamSelected:l,availableTeamsForSelection:d,highlightTeam:()=>{e.team.name!==w&&i("highlight-team",e.team.name)},unhighlightTeam:()=>{i("unhighlight-team")},updateTeam:()=>{const m=e.availableTeams.find(T=>T.name===o.value);console.log("Updating team:",{selectedTeam:o.value,selectedTeamData:m}),i("update:team",{position:e.teamPosition,team:{id:(m==null?void 0:m.id)||null,name:o.value,logo:(m==null?void 0:m.logo)||null,score:0}})},ref:t.ref,computed:t.computed,onMounted:t.onMounted,watch:t.watch,get TBD(){return w},get PERMISSIONS(){return E}};return Object.defineProperty(h,"__isScriptSetup",{enumerable:!1,value:!0}),h}},v={key:0,class:"flex items-center gap-2"},K=["src","alt"],Y=["value","disabled"],Q={key:1,class:"flex items-center gap-2"},X=["src","alt"],Z={class:"text-gray-900 dark:text-white"};function $(r,n,a,e,i,o){return t.openBlock(),t.createElementBlock("div",{class:t.normalizeClass(["flex-grow p-2.5 hover:bg-gray-200/30 dark:hover:bg-gray-950/20",{"hover:bg-green-500/20 dark:hover:bg-green-500/20":a.isWinner,"hover:bg-red-500/20 dark:hover:bg-red-500/20":a.isLoser,"bg-green-500/20 dark:bg-green-500/20":a.shouldHighlight&&a.isWinner,"bg-red-500/20 dark:bg-red-500/20":a.shouldHighlight&&a.isLoser}]),onMouseenter:e.highlightTeam,onMouseleave:e.unhighlightTeam},[a.canEdit&&a.permissions[e.PERMISSIONS.CAN_SELECT_TEAM]?(t.openBlock(),t.createElementBlock("div",v,[e.selectedTeamLogo?(t.openBlock(),t.createElementBlock("img",{key:0,src:e.selectedTeamLogo,alt:e.selectedTeam,class:"w-6 h-6 rounded-full"},null,8,K)):t.createCommentVNode("v-if",!0),t.withDirectives(t.createElementVNode("select",{"onUpdate:modelValue":n[0]||(n[0]=s=>e.selectedTeam=s),class:"form-input max-w-sm block w-full p-2 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500",onChange:e.updateTeam},[n[1]||(n[1]=t.createElementVNode("option",{value:"TBD"},"TBD",-1)),(t.openBlock(!0),t.createElementBlock(t.Fragment,null,t.renderList(e.availableTeamsForSelection,s=>(t.openBlock(),t.createElementBlock("option",{key:s.id,value:s.name,disabled:e.isTeamSelected(s.name)},t.toDisplayString(s.name),9,Y))),128))],544),[[t.vModelSelect,e.selectedTeam]])])):(t.openBlock(),t.createElementBlock("div",Q,[a.team.logo?(t.openBlock(),t.createElementBlock("img",{key:0,src:a.team.logo,alt:a.team.name,class:"w-6 h-6 rounded-full"},null,8,X)):t.createCommentVNode("v-if",!0),t.createElementVNode("span",Z,t.toDisplayString(a.team.name),1)]))],34)}const ee=D(G,[["render",$],["__file","/app/src/components/team/TeamSelect.vue"]]),te={__name:"TeamScoreInput",props:{team:{type:Object,required:!0},teamPosition:{type:String,required:!0},canEditScore:{type:Boolean,required:!0},isFirstTeam:{type:Boolean,default:!1}},emits:["update:score"],setup(r,{expose:n,emit:a}){n();const e=r,i=a,o=t.ref(!1),s=t.ref(e.team.score??0),l=()=>{e.canEditScore&&(o.value=!0)},d=()=>{const c=parseInt(s.value)||0;i("update:score",{position:e.teamPosition,score:c})};t.watch(()=>e.team,c=>{s.value=c.score??0},{deep:!0});const f={props:e,emit:i,isEditing:o,score:s,selectScore:l,updateScore:d,ref:t.ref,watch:t.watch};return Object.defineProperty(f,"__isScriptSetup",{enumerable:!1,value:!0}),f}},ae={key:1,class:"text-white"};function ne(r,n,a,e,i,o){return t.openBlock(),t.createElementBlock("div",{class:t.normalizeClass(["p-2.5 bg-orange-500 dark:bg-orange-600 cursor-pointer min-w-10 text-center",{"border-b border-orange-600 dark:border-orange-700":a.isFirstTeam}]),onClick:e.selectScore},[e.isEditing?t.withDirectives((t.openBlock(),t.createElementBlock("input",{key:0,"onUpdate:modelValue":n[0]||(n[0]=s=>e.score=s),type:"number",class:"form-input",required:"",min:"0",onChange:e.updateScore,onBlur:n[1]||(n[1]=s=>e.isEditing=!1)},null,544)),[[t.vModelText,e.score]]):(t.openBlock(),t.createElementBlock("span",ae,t.toDisplayString(a.team.score),1))],2)}const oe=D(te,[["render",ne],["__file","/app/src/components/team/TeamScoreInput.vue"]]),re={__name:"TeamRow",props:{team:{type:Object,required:!0},teamPosition:{type:String,required:!0},availableTeams:{type:Array,required:!0},selectedTeams:{type:Array,required:!0},canEdit:{type:Boolean,required:!0},canEditScore:{type:Boolean,required:!0},isWinner:{type:Boolean,default:!1},isLoser:{type:Boolean,default:!1},shouldHighlight:{type:Boolean,default:!1},isFirstTeam:{type:Boolean,default:!1},highlightedTeam:{type:Number,default:null},permissions:{type:Object,default:()=>({[E.CAN_SELECT_TEAM]:!0,[E.CAN_EDIT_SCOPE]:!0})}},emits:["update:team","update:score","highlight-team","unhighlight-team"],setup(r,{expose:n}){n();const e={props:r,TeamSelect:ee,TeamScoreInput:oe,get PERMISSIONS(){return E}};return Object.defineProperty(e,"__isScriptSetup",{enumerable:!1,value:!0}),e}},ie={class:"flex"};function se(r,n,a,e,i,o){return t.openBlock(),t.createElementBlock("div",ie,[t.createVNode(e.TeamSelect,{team:a.team,"team-position":a.teamPosition,"available-teams":a.availableTeams,"selected-teams":a.selectedTeams,"highlighted-team":a.highlightedTeam,permissions:a.permissions,"can-edit":a.canEdit,"is-winner":a.isWinner,"is-loser":a.isLoser,"should-highlight":a.shouldHighlight,"is-first-team":a.isFirstTeam,"onUpdate:team":n[0]||(n[0]=s=>r.$emit("update:team",s)),onHighlightTeam:n[1]||(n[1]=s=>r.$emit("highlight-team",s)),onUnhighlightTeam:n[2]||(n[2]=s=>r.$emit("unhighlight-team"))},null,8,["team","team-position","available-teams","selected-teams","highlighted-team","permissions","can-edit","is-winner","is-loser","should-highlight","is-first-team"]),t.createVNode(e.TeamScoreInput,{team:a.team,"team-position":a.teamPosition,"can-edit-score":a.canEditScore,"is-first-team":a.isFirstTeam,"onUpdate:score":n[3]||(n[3]=s=>r.$emit("update:score",s))},null,8,["team","team-position","can-edit-score","is-first-team"])])}const le=D(re,[["render",se],["__file","/app/src/components/team/TeamRow.vue"]]),ce={__name:"BracketMatch",props:{match:{type:Object,default:()=>({teamOne:{name:w,score:0},teamTwo:{name:w,score:0},winner:null,date:new Date().toISOString().split("T")[0]})},index:{type:Number,required:!0},totalMatches:{type:Number,required:!0},availableTeams:{type:Array,required:!0},selectedTeams:{type:Array,required:!0},roundIndex:{type:Number,required:!0},highlightedTeam:{type:String,default:null},permissions:{type:Object,default:()=>({[E.CAN_SELECT_TEAM]:!0,[E.CAN_EDIT_DATE]:!0,[E.CAN_EDIT_SCOPE]:!0})}},emits:["update:match","highlight-team","unhighlight-team","click-match"],setup(r,{expose:n,emit:a}){n();const e=r,i=a,o=t.computed(()=>e.roundIndex===0&&e.permissions[E.CAN_SELECT_TEAM]),s=t.computed(()=>e.match[S.ONE].name!==w&&e.match[S.TWO].name!==w&&e.permissions[E.CAN_EDIT_SCOPE]),B={props:e,emit:i,canEdit:o,canEditScore:s,isWinner:g=>e.match.winner===g,isLoser:g=>e.match.winner&&e.match.winner!==g,shouldHighlight:g=>{const _=e.match[g].name;return e.highlightedTeam===_},highlightTeam:g=>{i("highlight-team",g)},unhighlightTeam:()=>{i("unhighlight-team")},updateTeam:({position:g,team:_})=>{const O={...e.match,[g]:_};i("update:match",O)},updateScore:({position:g,score:_})=>{const O={...e.match,[g]:{...e.match[g],score:_}},b=O[S.ONE].score,k=O[S.TWO].score;b>k&&(O.winner=S.ONE),k>b&&(O.winner=S.TWO),b===k&&(O.winner=null),i("update:match",O)},formatDateTimeForInput:g=>g?new Date(g).toISOString().slice(0,16):"",updateDate:g=>{const _={...e.match,date:g.target.value};i("update:match",_)},formatDate:g=>g?new Date(g).toLocaleDateString("uk-UA",{day:"2-digit",month:"2-digit",year:"numeric"}):"",onMatchClick:g=>{const _=g.target.tagName.toLowerCase();_==="input"||_==="select"||_==="option"||_==="button"||g.target.closest("input,select,option,button")||!(!e.permissions[E.CAN_SELECT_TEAM]&&!e.permissions[E.CAN_EDIT_DATE]&&!e.permissions[E.CAN_EDIT_SCOPE])||i("click-match",{match:e.match,roundIndex:e.roundIndex,matchIndex:e.index,id:e.match.id??null})},computed:t.computed,TeamRow:le,get TBD(){return w},get TEAM_POSITION(){return S},get PERMISSIONS(){return E}};return Object.defineProperty(B,"__isScriptSetup",{enumerable:!1,value:!0}),B}},de={class:"flex flex-col w-full"},me={class:"px-3 py-1 text-xs text-gray-500 dark:text-gray-400 flex items-center justify-center"},he=["value","disabled"],ue={key:0,class:"absolute top-1/2 left-full w-2.5 h-[calc(100%+10px)] border-2 border-gray-300 dark:border-gray-600 border-l-0 rounded-r flex items-center z-10 -mt-[-10px] ml-[15px] mx-2 transition-colors duration-200"};function ge(r,n,a,e,i,o){return t.openBlock(),t.createElementBlock("div",{class:t.normalizeClass(["relative text-[0.8em] flex items-center",{group:a.index%2==0&&a.totalMatches>1}])},[t.createElementVNode("div",de,[t.createElementVNode("div",me,[t.createElementVNode("input",{type:"datetime-local",value:e.formatDateTimeForInput(a.match.date),class:"form-input ml-[15px] border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-900 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500",disabled:!a.permissions[e.PERMISSIONS.CAN_EDIT_DATE],onInput:e.updateDate},null,40,he)]),t.createElementVNode("div",{class:"my-1.5 ml-2.5 bg-white rounded overflow-hidden w-full min-w-[200px] shadow-sm ring-1 ring-gray-950/5 dark:bg-gray-900 dark:ring-white/10",onClick:e.onMatchClick},[t.createVNode(e.TeamRow,{team:a.match.teamOne,"team-position":e.TEAM_POSITION.ONE,"available-teams":a.availableTeams,"selected-teams":a.selectedTeams,"can-edit":e.canEdit,"can-edit-score":e.canEditScore,"is-winner":e.isWinner(e.TEAM_POSITION.ONE),"is-loser":e.isLoser(e.TEAM_POSITION.ONE),"should-highlight":e.shouldHighlight(e.TEAM_POSITION.ONE),"is-first-team":!0,"can-select-team":r.canSelectTeam,"highlighted-team":a.highlightedTeam,permissions:a.permissions,"onUpdate:team":e.updateTeam,"onUpdate:score":e.updateScore,onHighlightTeam:e.highlightTeam,onUnhighlightTeam:e.unhighlightTeam},null,8,["team","team-position","available-teams","selected-teams","can-edit","can-edit-score","is-winner","is-loser","should-highlight","can-select-team","highlighted-team","permissions"]),t.createVNode(e.TeamRow,{team:a.match.teamTwo,"team-position":e.TEAM_POSITION.TWO,"available-teams":a.availableTeams,"selected-teams":a.selectedTeams,"can-edit":e.canEdit,"can-edit-score":e.canEditScore,"is-winner":e.isWinner(e.TEAM_POSITION.TWO),"is-loser":e.isLoser(e.TEAM_POSITION.TWO),"should-highlight":e.shouldHighlight(e.TEAM_POSITION.TWO),"can-select-team":r.canSelectTeam,"highlighted-team":a.highlightedTeam,permissions:a.permissions,"onUpdate:team":e.updateTeam,"onUpdate:score":e.updateScore,onHighlightTeam:e.highlightTeam,onUnhighlightTeam:e.unhighlightTeam},null,8,["team","team-position","available-teams","selected-teams","can-edit","can-edit-score","is-winner","is-loser","should-highlight","can-select-team","highlighted-team","permissions"])])]),a.index%2==0&&a.totalMatches>1?(t.openBlock(),t.createElementBlock("div",ue,n[0]||(n[0]=[t.createElementVNode("span",{class:"w-2.5 h-0.5 bg-gray-300 dark:bg-gray-600 translate-x-full block"},null,-1)]))):t.createCommentVNode("v-if",!0)],2)}const fe=D(ce,[["render",ge],["__file","/app/src/components/bracket/BracketMatch.vue"]]),pe={__name:"BracketColumn",props:{column:{type:Object,required:!0},columnIndex:{type:Number,required:!0},availableTeams:{type:Array,required:!0},selectedTeams:{type:Array,required:!0},highlightedTeam:{type:String,default:null},permissions:{type:Object,default:()=>({[E.CAN_SELECT_TEAM]:!0})}},emits:["update:match","highlight-team","unhighlight-team","click-match"],setup(r,{expose:n,emit:a}){n();const e=r,i=a,l={props:e,emit:i,updateMatch:(d,f)=>{i("update:match",e.columnIndex,d,f)},onClickMatch:(d,f)=>{i("click-match",{...f,roundIndex:e.columnIndex,matchIndex:d})},BracketMatch:fe,get PERMISSIONS(){return E}};return Object.defineProperty(l,"__isScriptSetup",{enumerable:!1,value:!0}),l}},_e={class:"flex-1 px-5 pb-2.5 grid grid-cols-[min-content_auto]"},Te={class:"text-[0.7em] text-gray-900 dark:text-white flex justify-end items-center opacity-50 mt-[23px]"};function ye(r,n,a,e,i,o){return t.openBlock(),t.createElementBlock("div",_e,[(t.openBlock(!0),t.createElementBlock(t.Fragment,null,t.renderList(a.column.matches,(s,l)=>(t.openBlock(),t.createElementBlock(t.Fragment,{key:s.id},[t.createElementVNode("div",Te,t.toDisplayString(s.number),1),t.createVNode(e.BracketMatch,{match:s,index:l,"total-matches":a.column.matches.length,"round-index":a.columnIndex,"available-teams":a.availableTeams,"selected-teams":a.selectedTeams,"highlighted-team":a.highlightedTeam,permissions:a.permissions,"onUpdate:match":d=>e.updateMatch(l,d),onHighlightTeam:n[0]||(n[0]=d=>r.$emit("highlight-team",d)),onUnhighlightTeam:n[1]||(n[1]=d=>r.$emit("unhighlight-team")),onClickMatch:d=>e.onClickMatch(l,d)},null,8,["match","index","total-matches","round-index","available-teams","selected-teams","highlighted-team","permissions","onUpdate:match","onClickMatch"])],64))),128))])}const V=D(pe,[["render",ye],["__file","/app/src/components/bracket/BracketColumn.vue"]]),be={__name:"BracketRoundHeaders",props:{columns:{type:Array,required:!0},permissions:{type:Object,required:!0}},emits:["update:columns"],setup(r,{expose:n,emit:a}){n();const e=r,i=a,o=[1,3,5,7,9],s=t.ref(e.columns.map(c=>c.name));t.watch(()=>e.columns,c=>{s.value=c.map(u=>u.name)},{deep:!0});const f={props:e,emit:i,bestOfValues:o,localColumnNames:s,updateColumnName:(c,u)=>{s.value[c]=u;const h=[...e.columns];h[c]={...h[c],name:u},i("update:columns",h)},updateColumnBestOf:(c,u)=>{const h=[...e.columns];h[c]={...h[c],bestOf:Number(u)},i("update:columns",h)},ref:t.ref,watch:t.watch,get PERMISSIONS(){return E}};return Object.defineProperty(f,"__isScriptSetup",{enumerable:!1,value:!0}),f}},Ee={class:"flex justify-between px-5"},Se={class:"flex flex-col items-center gap-2"},ke={class:"mt-2"},we={class:"flex items-center rounded-md bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-600"},Ne={class:"join"},Oe=["onUpdate:modelValue","disabled","onBlur"],Be=["value","disabled","onChange"],xe=["value"];function De(r,n,a,e,i,o){return t.openBlock(),t.createElementBlock("div",Ee,[(t.openBlock(!0),t.createElementBlock(t.Fragment,null,t.renderList(a.columns,(s,l)=>(t.openBlock(),t.createElementBlock("div",{key:s.name,class:"flex-1 text-center text-sm text-gray-400 py-2 rounded overflow-hidden"},[t.createElementVNode("div",Se,[t.createElementVNode("div",ke,[t.createElementVNode("div",we,[t.createElementVNode("div",Ne,[t.withDirectives(t.createElementVNode("input",{"onUpdate:modelValue":d=>e.localColumnNames[l]=d,disabled:!a.permissions[e.PERMISSIONS.CAN_EDIT_ROUND_NAME],type:"text",class:"input rounded-md border-transparent focus:border-gray-500 focus:bg-white join-item text-sm text-gray-700 dark:bg-gray-900 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500",onBlur:d=>e.updateColumnName(l,d.target.value)},null,40,Oe),[[t.vModelText,e.localColumnNames[l]]]),t.createElementVNode("select",{value:s.bestOf,disabled:!a.permissions[e.PERMISSIONS.CAN_EDIT_BEST_OF],class:"select rounded-md border-transparent focus:border-gray-500 focus:bg-white join-item text-sm text-gray-700 dark:bg-gray-900 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500",onChange:d=>e.updateColumnBestOf(l,d.target.value)},[(t.openBlock(),t.createElementBlock(t.Fragment,null,t.renderList(e.bestOfValues,d=>t.createElementVNode("option",{key:d,value:d}," Best of "+t.toDisplayString(d),9,xe)),64))],40,Be)])])])])]))),128))])}const U=D(be,[["render",De],["__file","/app/src/components/bracket/BracketRoundHeaders.vue"]]),Ie={__name:"BracketSection",props:{title:{type:String,required:!0},columns:{type:Array,required:!0},availableTeams:{type:Array,required:!0},selectedTeams:{type:Array,required:!0},highlightedTeam:{type:String,default:null},permissions:{type:Object,required:!0},bordered:{type:Boolean,default:!1},onMatchUpdate:{type:Function,required:!0},onColumnsUpdate:{type:Function,required:!0},onHighlight:{type:Function,required:!0},onUnhighlight:{type:Function,required:!0},onClickMatch:{type:Function,required:!1,default:()=>{}}},setup(r,{expose:n}){n();const e={props:r,BracketColumn:V,BracketRoundHeaders:U};return Object.defineProperty(e,"__isScriptSetup",{enumerable:!1,value:!0}),e}},Me={class:"text-xl font-bold text-gray-800 dark:text-white mb-4"},Ce={class:"flex flex-col"},Ae={class:"overflow-x-auto"},Le={class:"min-w-max"},Ve={class:"flex flex-1 p-5"};function Ue(r,n,a,e,i,o){return t.openBlock(),t.createElementBlock("div",{class:t.normalizeClass(["flex flex-col mt-8",{"border-t-2 border-gray-300 dark:border-gray-600 pt-8":a.bordered}])},[t.createElementVNode("div",Me,t.toDisplayString(a.title),1),t.createElementVNode("div",Ce,[t.createElementVNode("div",Ae,[t.createElementVNode("div",Le,[t.createVNode(e.BracketRoundHeaders,{columns:a.columns,permissions:a.permissions,"onUpdate:columns":a.onColumnsUpdate},null,8,["columns","permissions","onUpdate:columns"]),t.createElementVNode("div",Ve,[(t.openBlock(!0),t.createElementBlock(t.Fragment,null,t.renderList(a.columns,(s,l)=>(t.openBlock(),t.createBlock(e.BracketColumn,{key:s.id,column:s,"column-index":l,"available-teams":a.availableTeams,"selected-teams":a.selectedTeams,"highlighted-team":a.highlightedTeam,permissions:a.permissions,"onUpdate:match":a.onMatchUpdate,onHighlightTeam:a.onHighlight,onUnhighlightTeam:a.onUnhighlight,onClickMatch:a.onClickMatch},null,8,["column","column-index","available-teams","selected-teams","highlighted-team","permissions","onUpdate:match","onHighlightTeam","onUnhighlightTeam","onClickMatch"]))),128))])])])])],2)}const qe=D(Ie,[["render",Ue],["__file","/app/src/components/bracket/BracketSection.vue"]]),q=()=>({id:null,name:w,logo:null,score:0}),W=r=>({id:`match-${r}`,number:r,[S.ONE]:q(),[S.TWO]:q(),winner:null,date:null});function j(r,n){return Array.from({length:r},(a,e)=>n(e))}const We=(r,n=3)=>{const a=Math.log2(r);let e=1;return Array.from({length:a},(i,o)=>{const s=Math.pow(2,a-o-1),l=j(s,()=>W(e++));return{id:`upper-round-${o+1}`,name:`Round ${o+1}`,matches:l,bestOf:n}})},M=(r,n)=>{const a=r-1;let e=1;return Array.from({length:a},(i,o)=>{const s=Math.pow(2,r-o-2),l=j(s,()=>W(e++));return{id:`lower-round-${o+1}`,name:`Lower Round ${o+1}`,matches:l,bestOf:n}})},je={__name:"StandingsTable",props:{standings:{type:Array,required:!0},format:{type:String,required:!0},tournamentFormat:{type:Object,required:!0}},setup(r,{expose:n}){n();const a={};return Object.defineProperty(a,"__isScriptSetup",{enumerable:!1,value:!0}),a}},Pe={class:"flex flex-col mt-8 border-t-2 border-gray-300 dark:border-gray-600 pt-8"},Re={class:"relative overflow-x-auto shadow-md sm:rounded-lg mt-8"},Fe={class:"w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400"},He={class:"text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400"},ze={key:0,scope:"col",class:"px-6 py-3"},Je={scope:"row",class:"px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white"},Ge={class:"px-6 py-4"},ve={class:"px-6 py-4"},Ke={class:"px-6 py-4"},Ye={key:0,class:"px-6 py-4"},Qe={class:"px-6 py-4"};function Xe(r,n,a,e,i,o){return t.openBlock(),t.createElementBlock("div",Pe,[n[5]||(n[5]=t.createElementVNode("div",{class:"text-xl font-bold text-gray-800 dark:text-white mb-4"}," Standings ",-1)),t.createElementVNode("div",Re,[t.createElementVNode("table",Fe,[t.createElementVNode("thead",He,[t.createElementVNode("tr",null,[n[0]||(n[0]=t.createElementVNode("th",{scope:"col",class:"px-6 py-3"},"Place",-1)),n[1]||(n[1]=t.createElementVNode("th",{scope:"col",class:"px-6 py-3"},"Team",-1)),n[2]||(n[2]=t.createElementVNode("th",{scope:"col",class:"px-6 py-3"},"W-L-T",-1)),n[3]||(n[3]=t.createElementVNode("th",{scope:"col",class:"px-6 py-3"},"Points",-1)),a.format===a.tournamentFormat.SWISS?(t.openBlock(),t.createElementBlock("th",ze," Buchholz ")):t.createCommentVNode("v-if",!0),n[4]||(n[4]=t.createElementVNode("th",{scope:"col",class:"px-6 py-3"},"Pts Diff",-1))])]),t.createElementVNode("tbody",null,[(t.openBlock(!0),t.createElementBlock(t.Fragment,null,t.renderList(a.standings,s=>(t.openBlock(),t.createElementBlock("tr",{key:s.id,class:"bg-white border-b dark:bg-gray-800 dark:border-gray-700 border-gray-200 hover:bg-gray-50 dark:hover:bg-gray-600"},[t.createElementVNode("th",Je,t.toDisplayString(s.place),1),t.createElementVNode("td",Ge,t.toDisplayString(s.name),1),t.createElementVNode("td",ve,t.toDisplayString(s.wins)+"-"+t.toDisplayString(s.losses)+"-"+t.toDisplayString(s.ties),1),t.createElementVNode("td",Ke,t.toDisplayString(s.score),1),a.format===a.tournamentFormat.SWISS?(t.openBlock(),t.createElementBlock("td",Ye,t.toDisplayString(s.buchholz),1)):t.createCommentVNode("v-if",!0),t.createElementVNode("td",Qe,t.toDisplayString(s.ptsDiff),1)]))),128))])])])])}const Ze=D(je,[["render",Xe],["__file","/app/src/components/StandingsTable.vue"]]);function $e(r){const n={},a={};r.forEach(i=>{i.matches.forEach(o=>{if(["teamOne","teamTwo"].forEach(s=>{const l=o[s];l&&(n[l.id]||(n[l.id]={...l,wins:0,losses:0,ties:0,score:0,ptsDiff:0,buchholz:0}),a[l.id]||(a[l.id]=[]))}),o.teamOne&&o.teamTwo){const s=o.teamOne,l=o.teamTwo,d=s.score||0,f=l.score||0;o.winner==="teamOne"?(n[s.id].wins++,n[s.id].score+=1,n[l.id].losses++):o.winner==="teamTwo"?(n[l.id].wins++,n[l.id].score+=1,n[s.id].losses++):o.winner===null&&(d>0||f>0)&&(n[s.id].ties++,n[l.id].ties++,n[s.id].score+=.5,n[l.id].score+=.5),n[s.id].ptsDiff+=d-f,n[l.id].ptsDiff+=f-d,a[s.id].push(l.id),a[l.id].push(s.id)}else o.teamOne&&!o.teamTwo?(n[o.teamOne.id].wins++,n[o.teamOne.id].score+=1):!o.teamOne&&o.teamTwo&&(n[o.teamTwo.id].wins++,n[o.teamTwo.id].score+=1)})}),Object.values(n).forEach(i=>{i.buchholz=(a[i.id]||[]).reduce((o,s)=>{var l;return o+(((l=n[s])==null?void 0:l.score)||0)},0)});const e=Object.values(n).sort((i,o)=>o.score-i.score||o.wins-i.wins||o.buchholz-i.buchholz||o.ptsDiff-i.ptsDiff);return e.forEach((i,o)=>{i.place=o+1}),e}function A(r,n){return r.filter(a=>!!a.id&&a.name!==n)}function L(r,n,a,e,i,o){const s=n.score||0,l=a.score||0;e===i?(r[n.id].wins++,r[n.id].score+=1,r[a.id].losses++):e===o?(r[a.id].wins++,r[a.id].score+=1,r[n.id].losses++):e===null&&(s>0||l>0)&&(r[n.id].ties++,r[a.id].ties++,r[n.id].score+=.5,r[a.id].score+=.5),r[n.id].ptsDiff+=s-l,r[a.id].ptsDiff+=l-s}function P({upperColumns:r,lowerColumns:n,format:a,TOURNAMENT_FORMAT:e,TEAM_POSITION:i,TBD:o}){switch(a){case e.SWISS:return A(et(r),o);case e.ROUND_ROBIN:return A(tt(r,i),o);case e.SINGLE_ELIMINATION:case e.DOUBLE_ELIMINATION:default:return A(at(r,n,a,e,i),o)}}function et(r){return $e(r)}function tt(r,n){const a={};for(const e of r)for(const i of e.matches){for(const l of[n.ONE,n.TWO]){const d=i[l];!d||!d.id||a[d.id]||(a[d.id]={...d,wins:0,losses:0,ties:0,score:0,ptsDiff:0})}const o=i[n.ONE],s=i[n.TWO];o&&s&&o.id&&s.id&&L(a,o,s,i.winner,n.ONE,n.TWO)}return R(a,["score","wins","ptsDiff"])}function at(r,n,a,e,i){const o={};for(const s of r)for(const l of s.matches){for(const c of[i.ONE,i.TWO]){const u=l[c];!u||!u.id||o[u.id]||(o[u.id]={...u,wins:0,losses:0,ties:0,score:0,ptsDiff:0})}const d=l[i.ONE],f=l[i.TWO];d&&f&&d.id&&f.id&&L(o,d,f,l.winner,i.ONE,i.TWO)}if(a===e.DOUBLE_ELIMINATION&&n)for(const s of n)for(const l of s.matches){for(const c of[i.ONE,i.TWO]){const u=l[c];!u||!u.id||o[u.id]||(o[u.id]={...u,wins:0,losses:0,ties:0,score:0,ptsDiff:0})}const d=l[i.ONE],f=l[i.TWO];d&&f&&d.id&&f.id&&L(o,d,f,l.winner,i.ONE,i.TWO)}return R(o,["score","wins","ptsDiff"])}function R(r,n){const a=Object.values(r).sort((e,i)=>{for(const o of n)if(i[o]!==e[o])return i[o]-e[o];return 0});return a.forEach((e,i)=>{e.place=i+1}),a}function nt(r,n=[]){const a={};r.forEach(c=>{a[c.score]||(a[c.score]=[]),a[c.score].push(c)});const e=Object.keys(a).map(Number).sort((c,u)=>u-c),i=[],o=new Set,s=r.map(c=>c.id),l=new Set(n.map(([c,u])=>`${c}-${u}`));function d(c){const u=[],h=new Set;for(let m=0;m<c.length;m++){if(h.has(c[m].id))continue;let T=!1;for(let p=m+1;p<c.length;p++)if(!h.has(c[p].id)&&!l.has(`${c[m].id}-${c[p].id}`)&&!l.has(`${c[p].id}-${c[m].id}`)){u.push([c[m],c[p]]),h.add(c[m].id),h.add(c[p].id),o.add(c[m].id),o.add(c[p].id),T=!0;break}if(!T){for(let p of e)if(p!==c[m].score){for(let y of a[p]||[])if(!o.has(y.id)&&!l.has(`${c[m].id}-${y.id}`)&&!l.has(`${y.id}-${c[m].id}`))return u.push([c[m],y]),h.add(c[m].id),h.add(y.id),o.add(c[m].id),o.add(y.id),u}}}return u}for(let c of e){const u=a[c].filter(T=>!o.has(T.id)),h=d(u);i.push(...h);const m=u.filter(T=>!o.has(T.id));if(m.length===1){let T=!1;for(let p of e)if(p!==c){for(let y of a[p]||[])if(!o.has(y.id)&&!l.has(`${m[0].id}-${y.id}`)&&!l.has(`${y.id}-${m[0].id}`)){i.push([m[0],y]),o.add(m[0].id),o.add(y.id),T=!0;break}if(T)break}T||(i.push([m[0],null]),o.add(m[0].id))}}const f=s.filter(c=>!o.has(c));for(let c of f){const u=r.find(h=>h.id===c);i.push([u,null])}return i}function F({upperColumns:r,lowerColumns:n,props:a,emit:e,TOURNAMENT_FORMAT:i,TEAM_POSITION:o,TBD:s}){function l(h,m,T){if(r.value[h]&&r.value[h].matches){if(r.value[h].matches[m]=T,T.winner&&h<r.value.length-1){const p=h+1,y=Math.floor(m/2);if(r.value[p]&&r.value[p].matches[y]){const N=r.value[p].matches[y],B=m%2===0?o.ONE:o.TWO,g=T[T.winner];r.value[p].matches[y]={...N,[B]:{id:g.id,name:g.name,logo:g.logo,score:0}}}}if(a.format===i.DOUBLE_ELIMINATION&&T.winner){const p=T[T.winner===o.ONE?o.TWO:o.ONE];if(p.name!==s){const y=Math.floor(h/2),N=Math.floor(m/2);if(n.value[y]&&n.value[y].matches[N]){const B=n.value[y].matches[N],g=m%2===0?o.ONE:o.TWO;n.value[y].matches[N]={...B,[g]:{id:p.id,name:p.name,logo:p.logo,score:0}}}}}c()}if(a.format===i.SWISS&&r.value[h].matches.every(N=>N.winner)&&h<r.value.length-1){const N={};for(let b=0;b<=h;b++)r.value[b].matches.forEach(k=>{["teamOne","teamTwo"].forEach(ct=>{const C=k[ct];C&&(N[C.id]||(N[C.id]={...C,score:0}))}),k.winner&&k.teamOne&&k.teamTwo&&(k.winner==="teamOne"&&(N[k.teamOne.id].score+=1),k.winner==="teamTwo"&&(N[k.teamTwo.id].score+=1))});const B=[];for(let b=0;b<=h;b++)r.value[b].matches.forEach(k=>{k.teamOne&&k.teamTwo&&B.push([k.teamOne.id,k.teamTwo.id])});const g=Object.values(N),_=nt(g,B),O=r.value[h+1];O.matches=_.map((b,k)=>({id:`swiss-match-${h+2}-${k+1}`,number:k+1,teamOne:b[0],teamTwo:b[1],winner:null,date:null}))}}function d(h){r.value=h,c()}function f(h){n.value=h,c()}function c(){e("update:state",{upper:r.value,lower:a.format===i.DOUBLE_ELIMINATION?n.value:null})}function u(){if(a.initialState){if(Array.isArray(a.initialState)){if(r.value=JSON.parse(JSON.stringify(a.initialState)),a.format===i.DOUBLE_ELIMINATION){n.value=M(r.value.length,a.defaultBestOf);return}if(a.format===i.SWISS){n.value=[];return}return}r.value=JSON.parse(JSON.stringify(a.initialState.upper||[])),n.value=JSON.parse(JSON.stringify(a.initialState.lower||[]))}}return{updateUpperMatch:l,updateUpperColumns:d,updateLowerState:f,emitTournamentState:c,initializeTournament:u}}class ot{async getTournament(n){throw new Error("Not implemented")}async saveTournament(n){throw new Error("Not implemented")}async updateMatch(n,a,e){throw new Error("Not implemented")}async listTournaments(){throw new Error("Not implemented")}async deleteTournament(n){throw new Error("Not implemented")}}const H="tournaments";class z extends ot{_getAll(){return JSON.parse(localStorage.getItem(H)||"{}")}_setAll(n){localStorage.setItem(H,JSON.stringify(n))}async getTournament(n){return this._getAll()[n]||null}async saveTournament(n){const a=this._getAll();a[n.id]=n,this._setAll(a)}async updateMatch(n,a,e){const i=this._getAll(),o=i[n];if(!o)throw new Error("Tournament not found");let s=!1;for(const l of o.rounds)for(let d=0;d<l.matches.length;d++)l.matches[d].id===a&&(l.matches[d]={...l.matches[d],...e},s=!0);if(!s)throw new Error("Match not found");i[n]=o,this._setAll(i)}async listTournaments(){const n=this._getAll();return Object.values(n).map(a=>({id:a.id,name:a.name}))}async deleteTournament(n){const a=this._getAll();delete a[n],this._setAll(a)}}const rt={__name:"TournamentBracket",props:{initialState:{type:Object,required:!0},availableTeams:{type:Array,required:!0},defaultBestOf:{type:Number,required:!0},format:{type:String,required:!0},permissions:{type:Object,default:()=>({[E.CAN_SELECT_TEAM]:!0,[E.CAN_EDIT_ROUND_NAME]:!0,[E.CAN_EDIT_BEST_OF]:!0})}},emits:["update:state","click-match"],setup(r,{expose:n,emit:a}){n();const e=a,i=r,o=t.ref([]),s=t.ref([]),l=t.ref(null),d=t.computed(()=>{const _=new Set;return o.value.forEach(O=>{O.matches.forEach(b=>{b[S.ONE].name!==w&&_.add(b[S.ONE].name),b[S.TWO].name!==w&&_.add(b[S.TWO].name)})}),i.format===I.DOUBLE_ELIMINATION&&s.value.forEach(O=>{O.matches.forEach(b=>{b[S.ONE].name!==w&&_.add(b[S.ONE].name),b[S.TWO].name!==w&&_.add(b[S.TWO].name)})}),Array.from(_)}),f=t.computed(()=>P({upperColumns:o.value,lowerColumns:s.value,format:i.format,TOURNAMENT_FORMAT:I,TEAM_POSITION:S,TBD:w})),c=_=>{l.value=_},u=()=>{l.value=null},h=_=>{e("click-match",_)},m=new z,{updateUpperMatch:T,updateUpperColumns:p,updateLowerState:y,emitTournamentState:N,initializeTournament:B}=F({upperColumns:o,lowerColumns:s,props:i,emit:e,TOURNAMENT_FORMAT:I,TEAM_POSITION:S,TBD:w});t.watch(()=>i.initialState,()=>{B()},{deep:!0}),t.watch(()=>i.format,_=>{_===I.DOUBLE_ELIMINATION&&(!s.value||s.value.length===0)&&(s.value=M(o.value.length,i.defaultBestOf),N())}),t.onMounted(()=>{B()});const g={emit:e,props:i,upperColumns:o,lowerColumns:s,highlightedTeam:l,selectedTeams:d,standingsData:f,highlightTeam:c,unhighlightTeam:u,onMatchClick:h,storage:m,updateUpperMatch:T,updateUpperColumns:p,updateLowerState:y,emitTournamentState:N,initializeTournament:B,ref:t.ref,onMounted:t.onMounted,watch:t.watch,computed:t.computed,BracketColumn:V,BracketRoundHeaders:U,BracketSection:qe,get createLowerBracketStructure(){return M},get TOURNAMENT_FORMAT(){return I},get TBD(){return w},get TEAM_POSITION(){return S},get PERMISSIONS(){return E},StandingsTable:Ze,get useStandings(){return P},get useBracket(){return F},get LocalStorageTournament(){return z}};return Object.defineProperty(g,"__isScriptSetup",{enumerable:!1,value:!0}),g}},it={class:"flex flex-col"};function st(r,n,a,e,i,o){return t.openBlock(),t.createElementBlock("div",it,[t.createVNode(e.BracketSection,{title:"Upper Bracket",columns:e.upperColumns,"available-teams":a.availableTeams,"selected-teams":e.selectedTeams,"highlighted-team":e.highlightedTeam,permissions:a.permissions,"on-match-update":e.updateUpperMatch,"on-columns-update":e.updateUpperColumns,"on-highlight":e.highlightTeam,"on-unhighlight":e.unhighlightTeam,"on-click-match":e.onMatchClick},null,8,["columns","available-teams","selected-teams","highlighted-team","permissions","on-match-update","on-columns-update"]),a.format===e.TOURNAMENT_FORMAT.DOUBLE_ELIMINATION?(t.openBlock(),t.createBlock(e.BracketSection,{key:0,title:"Lower Bracket",columns:e.lowerColumns,"available-teams":a.availableTeams,"selected-teams":e.selectedTeams,"highlighted-team":e.highlightedTeam,permissions:a.permissions,bordered:"","on-match-update":e.updateLowerState,"on-columns-update":e.updateLowerState,"on-highlight":e.highlightTeam,"on-unhighlight":e.unhighlightTeam},null,8,["columns","available-teams","selected-teams","highlighted-team","permissions","on-match-update","on-columns-update"])):t.createCommentVNode("v-if",!0),t.createVNode(e.StandingsTable,{standings:e.standingsData,format:a.format,"tournament-format":e.TOURNAMENT_FORMAT},null,8,["standings","format","tournament-format"])])}const J=D(rt,[["render",st],["__file","/app/src/components/TournamentBracket.vue"]]),lt=r=>{r.component("TournamentBracket",J)};x.PERMISSIONS=E,x.TournamentBracket=J,x.createLowerBracketStructure=M,x.createTournamentState=We,x.install=lt,Object.defineProperty(x,Symbol.toStringTag,{value:"Module"})});