@usssa/component-library 1.0.0-alpha.263 → 1.0.0-alpha.264
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/components/core/UBracket.vue +316 -24
- package/src/utils/bracket.json +8 -0
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@ import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
|
|
|
5
5
|
import { UBtnIcon, UBtnStd, UChip, UMenuItem, USheet } from '../../components'
|
|
6
6
|
import { useScreenType } from '../../composables/useScreenType'
|
|
7
7
|
|
|
8
|
-
const emit = defineEmits(['open-expand'])
|
|
8
|
+
const emit = defineEmits(['open-expand', 'schedule-click'])
|
|
9
9
|
|
|
10
10
|
defineOptions({
|
|
11
11
|
name: 'UBracket',
|
|
@@ -52,10 +52,15 @@ const props = defineProps({
|
|
|
52
52
|
teamSheetApplyLabel: { type: String, default: 'Select Team' },
|
|
53
53
|
teamSheetCancelLabel: { type: String, default: 'Cancel' },
|
|
54
54
|
teamSheetHeading: { type: String, default: 'Select Team' },
|
|
55
|
+
venueSheetHeading: { type: String, default: 'Venue Detail' },
|
|
55
56
|
vGap: {
|
|
56
57
|
type: Number,
|
|
57
58
|
default: 50,
|
|
58
59
|
},
|
|
60
|
+
viewMatchupDetailText: {
|
|
61
|
+
type: String,
|
|
62
|
+
default: 'View Matchup Details',
|
|
63
|
+
},
|
|
59
64
|
zoomInTooltip: {
|
|
60
65
|
type: String,
|
|
61
66
|
default: 'Zoom In',
|
|
@@ -99,6 +104,7 @@ const tempSelectedTeamId = ref('')
|
|
|
99
104
|
const teamSheet = ref([])
|
|
100
105
|
const tooltipState = ref({ visible: false, x: 0, y: 0, text: '' })
|
|
101
106
|
const zoomRef = ref(null)
|
|
107
|
+
const locationSheet = ref([])
|
|
102
108
|
|
|
103
109
|
const selectedTeamName = computed({
|
|
104
110
|
get() {
|
|
@@ -838,44 +844,131 @@ const renderBracket = () => {
|
|
|
838
844
|
const destPos = getNodePosition(destId)
|
|
839
845
|
|
|
840
846
|
if (pos1 && pos2 && destPos) {
|
|
841
|
-
//
|
|
842
|
-
const
|
|
843
|
-
|
|
844
|
-
const
|
|
847
|
+
// Use the leftmost node's x for alignment
|
|
848
|
+
const nodeX1 = pos1.x
|
|
849
|
+
const nodeX2 = pos2.x
|
|
850
|
+
const nodeY1 = pos1.y
|
|
851
|
+
const nodeY2 = pos2.y
|
|
852
|
+
|
|
853
|
+
const startX = Math.min(nodeX1, nodeX2)
|
|
854
|
+
const topY = Math.min(nodeY1, nodeY2)
|
|
855
|
+
const bottomY = Math.max(nodeY1, nodeY2) + nodeHeight
|
|
856
|
+
// Move details upward by 12px so it doesn't touch the node
|
|
857
|
+
const centerY = (topY + bottomY) / 2 - 12
|
|
845
858
|
|
|
846
|
-
const
|
|
859
|
+
const detailsGroup = g.append('g').attr('transform', `translate(${startX}, ${centerY})`)
|
|
847
860
|
|
|
848
|
-
//
|
|
849
|
-
|
|
861
|
+
// Remove the white background rect (unwanted)
|
|
862
|
+
// detailsGroup.append('rect') ... (removed)
|
|
863
|
+
|
|
864
|
+
// Add the date text
|
|
865
|
+
detailsGroup
|
|
850
866
|
.append('text')
|
|
851
867
|
.attr('class', 'match-date text-overline-xs text-description')
|
|
852
|
-
.attr('x',
|
|
853
|
-
.attr('y',
|
|
854
|
-
.attr('text-anchor', '
|
|
868
|
+
.attr('x', 0)
|
|
869
|
+
.attr('y', -6)
|
|
870
|
+
.attr('text-anchor', 'start')
|
|
855
871
|
.attr('dominant-baseline', 'middle')
|
|
856
872
|
.text(game.date)
|
|
857
873
|
|
|
858
|
-
// Add
|
|
859
|
-
|
|
874
|
+
// Add venue text below date text from the 'venue' key
|
|
875
|
+
if (game.venue) {
|
|
876
|
+
// We'll use a <foreignObject> with a div, and check for overflow after rendering
|
|
877
|
+
const venueFO = detailsGroup
|
|
878
|
+
.append('foreignObject')
|
|
879
|
+
.attr('x', 0)
|
|
880
|
+
.attr('y', 2)
|
|
881
|
+
.attr('width', 200)
|
|
882
|
+
.attr('height', 30)
|
|
883
|
+
.append('xhtml:div')
|
|
884
|
+
.attr('class', 'text-body-xxs text-description match-venue venue-ellipsis')
|
|
885
|
+
.style('word-break', 'break-word')
|
|
886
|
+
.style('white-space', 'normal')
|
|
887
|
+
.style('text-align', 'start')
|
|
888
|
+
.style('width', '100%')
|
|
889
|
+
.style('overflow', 'hidden')
|
|
890
|
+
.style('display', '-webkit-box')
|
|
891
|
+
.style('-webkit-line-clamp', 2)
|
|
892
|
+
.style('-webkit-box-orient', 'vertical')
|
|
893
|
+
.text(game.venue);
|
|
894
|
+
|
|
895
|
+
// After nextTick, check for vertical overflow and add tooltip if needed
|
|
896
|
+
nextTick(() => {
|
|
897
|
+
// venueFO is a D3 selection, so .node() gives us the div
|
|
898
|
+
const div = venueFO.node();
|
|
899
|
+
if (div && div.scrollHeight > div.clientHeight + 1) {
|
|
900
|
+
// Add ellipsis via CSS (already set), and tooltip on hover
|
|
901
|
+
d3.select(div)
|
|
902
|
+
.on('mouseover', function (event) {
|
|
903
|
+
const rect = this.getBoundingClientRect()
|
|
904
|
+
const svgRect = svgRef.value.getBoundingClientRect()
|
|
905
|
+
tooltipState.value = {
|
|
906
|
+
visible: true,
|
|
907
|
+
x: rect.left - svgRect.left + rect.width / 2,
|
|
908
|
+
y: rect.top - svgRect.top - 40,
|
|
909
|
+
text: game.venue,
|
|
910
|
+
}
|
|
911
|
+
})
|
|
912
|
+
.on('mouseout', function () {
|
|
913
|
+
tooltipState.value = { ...tooltipState.value, visible: false }
|
|
914
|
+
})
|
|
915
|
+
}
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
const locationIcon = detailsGroup
|
|
860
920
|
.append('image')
|
|
861
921
|
.attr('href', '/icons/field-location.svg')
|
|
862
|
-
.attr('x',
|
|
863
|
-
.attr('y', -
|
|
922
|
+
.attr('x', 200)
|
|
923
|
+
.attr('y', -8)
|
|
864
924
|
.attr('width', 24)
|
|
865
925
|
.attr('height', 24)
|
|
866
926
|
.attr('class', 'location-icon')
|
|
867
927
|
.style('cursor', 'pointer')
|
|
868
928
|
|
|
929
|
+
const scheduleIcon = detailsGroup
|
|
930
|
+
.append('image')
|
|
931
|
+
.attr('href', '/icons/scoreboard.svg')
|
|
932
|
+
.attr('x', 240)
|
|
933
|
+
.attr('y', -8)
|
|
934
|
+
.attr('width', 24)
|
|
935
|
+
.attr('height', 24)
|
|
936
|
+
.attr('class', 'info-icon')
|
|
937
|
+
.style('cursor', 'pointer')
|
|
938
|
+
|
|
869
939
|
locationIcon.on('click', function (event) {
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
940
|
+
if ($screen.value.isMobile) {
|
|
941
|
+
locationSheet.value.push({ open: true, height: 150 })
|
|
942
|
+
} else {
|
|
943
|
+
const iconRect = this.getBoundingClientRect()
|
|
944
|
+
const svgRect = svgRef.value.getBoundingClientRect()
|
|
945
|
+
tooltipState.value = {
|
|
946
|
+
visible: true,
|
|
947
|
+
x: iconRect.left - svgRect.left + iconRect.width / 2,
|
|
948
|
+
y: iconRect.top - svgRect.top - 40,
|
|
949
|
+
text: game.location || 'Dallas, TX',
|
|
950
|
+
}
|
|
877
951
|
}
|
|
878
952
|
})
|
|
953
|
+
|
|
954
|
+
// Tooltip on hover for schedule icon
|
|
955
|
+
scheduleIcon
|
|
956
|
+
.on('mouseover', function (event) {
|
|
957
|
+
const iconRect = this.getBoundingClientRect()
|
|
958
|
+
const svgRect = svgRef.value.getBoundingClientRect()
|
|
959
|
+
tooltipState.value = {
|
|
960
|
+
visible: true,
|
|
961
|
+
x: iconRect.left - svgRect.left + iconRect.width / 2,
|
|
962
|
+
y: iconRect.top - svgRect.top - 40,
|
|
963
|
+
text: props.viewMatchupDetailText,
|
|
964
|
+
}
|
|
965
|
+
})
|
|
966
|
+
.on('mouseout', function () {
|
|
967
|
+
tooltipState.value = { ...tooltipState.value, visible: false }
|
|
968
|
+
})
|
|
969
|
+
.on('click', function (event) {
|
|
970
|
+
emit('schedule-click', game)
|
|
971
|
+
})
|
|
879
972
|
}
|
|
880
973
|
})
|
|
881
974
|
|
|
@@ -903,6 +996,39 @@ const renderBracket = () => {
|
|
|
903
996
|
(winnerNodePosition.x !== 0 || winnerNodePosition.y !== 0)
|
|
904
997
|
) {
|
|
905
998
|
const { x: mx, y: my } = winnerNodePosition
|
|
999
|
+
|
|
1000
|
+
let labelPlaced = false
|
|
1001
|
+
|
|
1002
|
+
const sourcePositions = (game.team || [])
|
|
1003
|
+
.map((source) => {
|
|
1004
|
+
if (
|
|
1005
|
+
typeof source === 'object' &&
|
|
1006
|
+
source.teamName &&
|
|
1007
|
+
source.position
|
|
1008
|
+
) {
|
|
1009
|
+
const id = `${source.teamName}_${source.position.columnNo}_${source.position.rowNo}`
|
|
1010
|
+
return getNodePosition(id)
|
|
1011
|
+
}
|
|
1012
|
+
return null
|
|
1013
|
+
})
|
|
1014
|
+
.filter(Boolean)
|
|
1015
|
+
|
|
1016
|
+
let triX = 0, triY = 0
|
|
1017
|
+
if (sourcePositions.length === 2) {
|
|
1018
|
+
if (mx > sourcePositions[0].x) {
|
|
1019
|
+
// Winner to the right
|
|
1020
|
+
triX = (sourcePositions[0].x + nodeWidth + mx - edgeMargin) / 2
|
|
1021
|
+
} else {
|
|
1022
|
+
// Winner to the left
|
|
1023
|
+
triX = (sourcePositions[0].x - edgeMargin + mx + nodeWidth) / 2
|
|
1024
|
+
}
|
|
1025
|
+
// Always use the vertical center of the destination node
|
|
1026
|
+
triY = my + nodeHeight / 2
|
|
1027
|
+
} else if (sourcePositions.length === 1) {
|
|
1028
|
+
triX = (sourcePositions[0].x + mx) / 2
|
|
1029
|
+
triY = my + nodeHeight / 2
|
|
1030
|
+
}
|
|
1031
|
+
|
|
906
1032
|
game.team.forEach((source) => {
|
|
907
1033
|
let sourceNodeId
|
|
908
1034
|
if (
|
|
@@ -945,6 +1071,47 @@ const renderBracket = () => {
|
|
|
945
1071
|
.attr('d', path.toString())
|
|
946
1072
|
.attr('stroke-linecap', 'round')
|
|
947
1073
|
})
|
|
1074
|
+
|
|
1075
|
+
// Place the gameAliasName at the trisection point (only once per edge group)
|
|
1076
|
+
if (game.gameAliasName && !labelPlaced && sourcePositions.length) {
|
|
1077
|
+
const aliasText = game.gameAliasName.replace(/-/g, '').trim();
|
|
1078
|
+
const padding = 6 // px, padding inside the square
|
|
1079
|
+
const tempText = g.append('text')
|
|
1080
|
+
.attr('class', 'text-caption-xs game-number')
|
|
1081
|
+
.attr('x', triX)
|
|
1082
|
+
.attr('y', triY)
|
|
1083
|
+
.attr('text-anchor', 'middle')
|
|
1084
|
+
.attr('dominant-baseline', 'middle')
|
|
1085
|
+
.style('visibility', 'hidden')
|
|
1086
|
+
.text(aliasText)
|
|
1087
|
+
const bbox = tempText.node().getBBox()
|
|
1088
|
+
tempText.remove()
|
|
1089
|
+
const size = Math.max(bbox.width, bbox.height) + padding
|
|
1090
|
+
|
|
1091
|
+
// Draw white square background with edge color border
|
|
1092
|
+
g.append('rect')
|
|
1093
|
+
.attr('x', triX - size / 2)
|
|
1094
|
+
.attr('y', triY - size / 2)
|
|
1095
|
+
.attr('width', size)
|
|
1096
|
+
.attr('height', size)
|
|
1097
|
+
.attr('rx', 4)
|
|
1098
|
+
.attr('fill', '#fff')
|
|
1099
|
+
.attr('stroke', getComputedStyle(document.documentElement)
|
|
1100
|
+
.getPropertyValue('--q-color-neutral-4') || '#b9e0ff')
|
|
1101
|
+
.attr('stroke-width', 4)
|
|
1102
|
+
.attr('class', 'edge-alias-bg')
|
|
1103
|
+
|
|
1104
|
+
// Draw the label text
|
|
1105
|
+
g.append('text')
|
|
1106
|
+
.attr('class', 'text-caption-xs text-description game-alias-label')
|
|
1107
|
+
.attr('x', triX)
|
|
1108
|
+
.attr('y', triY)
|
|
1109
|
+
.attr('text-anchor', 'middle')
|
|
1110
|
+
.attr('dominant-baseline', 'middle')
|
|
1111
|
+
.style('pointer-events', 'none')
|
|
1112
|
+
.text(aliasText)
|
|
1113
|
+
labelPlaced = true
|
|
1114
|
+
}
|
|
948
1115
|
}
|
|
949
1116
|
}
|
|
950
1117
|
|
|
@@ -969,6 +1136,32 @@ const renderBracket = () => {
|
|
|
969
1136
|
(loserNodePosition.x !== 0 || loserNodePosition.y !== 0)
|
|
970
1137
|
) {
|
|
971
1138
|
const { x: lx, y: ly } = loserNodePosition
|
|
1139
|
+
|
|
1140
|
+
let labelPlaced = false
|
|
1141
|
+
|
|
1142
|
+
const sourcePositions = (game.team || [])
|
|
1143
|
+
.map((source) => {
|
|
1144
|
+
if (
|
|
1145
|
+
typeof source === 'object' &&
|
|
1146
|
+
source.teamName &&
|
|
1147
|
+
source.position
|
|
1148
|
+
) {
|
|
1149
|
+
const id = `${source.teamName}_${source.position.columnNo}_${source.position.rowNo}`
|
|
1150
|
+
return getNodePosition(id)
|
|
1151
|
+
}
|
|
1152
|
+
return null
|
|
1153
|
+
})
|
|
1154
|
+
.filter(Boolean)
|
|
1155
|
+
|
|
1156
|
+
let triX = 0, triY = 0
|
|
1157
|
+
if (sourcePositions.length === 2) {
|
|
1158
|
+
triX = (sourcePositions[0].x - edgeMargin + lx + nodeWidth) / 2
|
|
1159
|
+
triY = ly + nodeHeight / 2
|
|
1160
|
+
} else if (sourcePositions.length === 1) {
|
|
1161
|
+
triX = (sourcePositions[0].x + lx) / 2
|
|
1162
|
+
triY = ly + nodeHeight / 2
|
|
1163
|
+
}
|
|
1164
|
+
|
|
972
1165
|
game.team.forEach((source) => {
|
|
973
1166
|
let sourceNodeId
|
|
974
1167
|
if (
|
|
@@ -999,18 +1192,74 @@ const renderBracket = () => {
|
|
|
999
1192
|
.attr('d', path.toString())
|
|
1000
1193
|
.attr('stroke-linecap', 'round') // Changed to rounded end
|
|
1001
1194
|
})
|
|
1195
|
+
|
|
1196
|
+
// Place the gameAliasName at the trisection point (only once per edge group)
|
|
1197
|
+
if (game.gameAliasName && !labelPlaced && sourcePositions.length) {
|
|
1198
|
+
// Remove hyphens from gameAliasName
|
|
1199
|
+
const aliasText = game.gameAliasName.replace(/-/g, '').trim();
|
|
1200
|
+
// Calculate text size for the background rect
|
|
1201
|
+
const fontSize = 13 // px, matches .edge-alias-label font-size
|
|
1202
|
+
const padding = 6 // px, padding inside the square
|
|
1203
|
+
// Temporary text element to measure width
|
|
1204
|
+
const tempText = g.append('text')
|
|
1205
|
+
.attr('class', 'text-caption-xs text-description')
|
|
1206
|
+
.attr('x', triX)
|
|
1207
|
+
.attr('y', triY)
|
|
1208
|
+
.attr('text-anchor', 'middle')
|
|
1209
|
+
.attr('dominant-baseline', 'middle')
|
|
1210
|
+
.style('visibility', 'hidden')
|
|
1211
|
+
.text(aliasText)
|
|
1212
|
+
const bbox = tempText.node().getBBox()
|
|
1213
|
+
tempText.remove()
|
|
1214
|
+
const size = Math.max(bbox.width, bbox.height) + padding
|
|
1215
|
+
|
|
1216
|
+
// Draw white square background with edge color border
|
|
1217
|
+
g.append('rect')
|
|
1218
|
+
.attr('x', triX - size / 2)
|
|
1219
|
+
.attr('y', triY - size / 2)
|
|
1220
|
+
.attr('width', size)
|
|
1221
|
+
.attr('height', size)
|
|
1222
|
+
.attr('rx', 5)
|
|
1223
|
+
.attr('fill', '#fff')
|
|
1224
|
+
.attr('stroke', getComputedStyle(document.documentElement)
|
|
1225
|
+
.getPropertyValue('--q-color-neutral-4') || '#b9e0ff')
|
|
1226
|
+
.attr('stroke-width', 4)
|
|
1227
|
+
.attr('class', 'edge-alias-bg')
|
|
1228
|
+
|
|
1229
|
+
// Draw the label text
|
|
1230
|
+
g.append('text')
|
|
1231
|
+
.attr('class', 'text-caption-xs text-description game-alias-label')
|
|
1232
|
+
.attr('x', triX)
|
|
1233
|
+
.attr('y', triY)
|
|
1234
|
+
.attr('text-anchor', 'middle')
|
|
1235
|
+
.attr('dominant-baseline', 'middle')
|
|
1236
|
+
.style('pointer-events', 'none')
|
|
1237
|
+
.text(aliasText)
|
|
1238
|
+
labelPlaced = true
|
|
1239
|
+
}
|
|
1002
1240
|
}
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1241
|
+
} // <-- FIX: close the LOSER edges block here
|
|
1242
|
+
|
|
1243
|
+
}) // <-- close games.value.forEach
|
|
1005
1244
|
|
|
1006
1245
|
// After all edges are drawn, move highlighted edges to top
|
|
1007
1246
|
nextTick(() => {
|
|
1008
1247
|
const gEl = gRef.value
|
|
1009
1248
|
if (gEl) {
|
|
1249
|
+
// Move highlighted edges to top
|
|
1010
1250
|
const highlightedEdges = gEl.querySelectorAll('.edge-path.highlighted')
|
|
1011
1251
|
highlightedEdges.forEach((el) => {
|
|
1012
1252
|
gEl.appendChild(el)
|
|
1013
1253
|
})
|
|
1254
|
+
// Move game-alias boxes (rects and texts) to top after edges
|
|
1255
|
+
const aliasRects = gEl.querySelectorAll('.edge-alias-bg')
|
|
1256
|
+
const aliasLabels = gEl.querySelectorAll('.game-alias-label')
|
|
1257
|
+
aliasRects.forEach((el) => {
|
|
1258
|
+
gEl.appendChild(el)
|
|
1259
|
+
})
|
|
1260
|
+
aliasLabels.forEach((el) => {
|
|
1261
|
+
gEl.appendChild(el)
|
|
1262
|
+
})
|
|
1014
1263
|
}
|
|
1015
1264
|
})
|
|
1016
1265
|
}
|
|
@@ -1709,6 +1958,26 @@ watch(currentTransform, (newVal) => {
|
|
|
1709
1958
|
/>
|
|
1710
1959
|
</template>
|
|
1711
1960
|
</USheet>
|
|
1961
|
+
<USheet
|
|
1962
|
+
v-model:dialogs="locationSheet"
|
|
1963
|
+
dialog-class="bracket-sheet"
|
|
1964
|
+
:heading="venueSheetHeading"
|
|
1965
|
+
show-action-buttons
|
|
1966
|
+
>
|
|
1967
|
+
<template #content>
|
|
1968
|
+
<div class="text-body-xl text-dark">
|
|
1969
|
+
Dallas, TX
|
|
1970
|
+
</div>
|
|
1971
|
+
</template>
|
|
1972
|
+
<template #action_primary_one>
|
|
1973
|
+
<UBtnStd
|
|
1974
|
+
class="full-width"
|
|
1975
|
+
color="primary"
|
|
1976
|
+
label="Close"
|
|
1977
|
+
@onClick="locationSheet = []"
|
|
1978
|
+
/>
|
|
1979
|
+
</template>
|
|
1980
|
+
</USheet>
|
|
1712
1981
|
</template>
|
|
1713
1982
|
|
|
1714
1983
|
<style scoped lang="sass">
|
|
@@ -1825,6 +2094,15 @@ svg
|
|
|
1825
2094
|
:deep(.match-date)
|
|
1826
2095
|
fill: #566176
|
|
1827
2096
|
|
|
2097
|
+
:deep(.match-venue)
|
|
2098
|
+
fill: #566176
|
|
2099
|
+
|
|
2100
|
+
:deep(.game-number)
|
|
2101
|
+
fill: #566176
|
|
2102
|
+
|
|
2103
|
+
:deep(.game-alias-label)
|
|
2104
|
+
fill: #566176
|
|
2105
|
+
|
|
1828
2106
|
:deep(.edge-path)
|
|
1829
2107
|
fill: none !important
|
|
1830
2108
|
stroke: $neutral-4
|
|
@@ -1836,6 +2114,7 @@ svg
|
|
|
1836
2114
|
opacity: 1
|
|
1837
2115
|
|
|
1838
2116
|
:deep(.dot)
|
|
2117
|
+
|
|
1839
2118
|
opacity: 0.6
|
|
1840
2119
|
|
|
1841
2120
|
:deep(.remove-team .button-label)
|
|
@@ -1886,4 +2165,17 @@ svg
|
|
|
1886
2165
|
pointer-events: auto
|
|
1887
2166
|
box-shadow: 0 2px 8px rgba(0,0,0,0.15)
|
|
1888
2167
|
overflow: hidden
|
|
2168
|
+
|
|
2169
|
+
:deep(.edge-alias-bg)
|
|
2170
|
+
shape-rendering: inherit
|
|
2171
|
+
stroke: $neutral-4 !important
|
|
2172
|
+
stroke-width: 2 !important
|
|
2173
|
+
fill: #fff !important
|
|
2174
|
+
|
|
2175
|
+
:deep(.venue-ellipsis)
|
|
2176
|
+
overflow: hidden
|
|
2177
|
+
text-overflow: ellipsis
|
|
2178
|
+
display: -webkit-box
|
|
2179
|
+
-webkit-line-clamp: 2
|
|
2180
|
+
-webkit-box-orient: vertical
|
|
1889
2181
|
</style>
|
package/src/utils/bracket.json
CHANGED
|
@@ -47,6 +47,8 @@
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
],
|
|
50
|
+
"date": "Sun. Feb 23, 2024 09:00 AM",
|
|
51
|
+
"venue": "Truckee High School 11725 Donner Pass Rd, Truckee, CA 96161",
|
|
50
52
|
"position": {
|
|
51
53
|
"columnNo": "1",
|
|
52
54
|
"rowNo": "4"
|
|
@@ -123,6 +125,8 @@
|
|
|
123
125
|
}
|
|
124
126
|
}
|
|
125
127
|
],
|
|
128
|
+
"date": "Sun. Feb 23, 2024 09:00 AM",
|
|
129
|
+
"venue": "Truckee High School 11725 Donner Pass Rd, Truckee, CA 96161",
|
|
126
130
|
"position": {
|
|
127
131
|
"columnNo": "1",
|
|
128
132
|
"rowNo": "8"
|
|
@@ -664,6 +668,8 @@
|
|
|
664
668
|
}
|
|
665
669
|
}
|
|
666
670
|
],
|
|
671
|
+
"date": "Sun. Feb 23, 2024 09:00 AM",
|
|
672
|
+
"venue": "Truckee High School 11725 Donner Pass Rd, Truckee, CA 96161",
|
|
667
673
|
"position": {
|
|
668
674
|
"columnNo": "2",
|
|
669
675
|
"rowNo": "11"
|
|
@@ -901,6 +907,8 @@
|
|
|
901
907
|
}
|
|
902
908
|
}
|
|
903
909
|
],
|
|
910
|
+
"date": "Sun. Feb 23, 2024 09:00 AM",
|
|
911
|
+
"venue": "Truckee High School 11725 Donner Pass Rd, Truckee, CA 96161",
|
|
904
912
|
"position": {
|
|
905
913
|
"columnNo": "3",
|
|
906
914
|
"rowNo": "9"
|