Round state and functionality complete.

master
Ben Burlingham 9 years ago
parent 7506974691
commit a1dec26fc5
  1. 7
      index.html
  2. 7
      js/constants.js
  3. 16
      js/diagram.js
  4. 60
      js/main.js
  5. 110
      js/ui.js
  6. 1
      res/diagram.css
  7. 14
      res/rounds.css
  8. 1
      res/sort.css

@ -11,7 +11,7 @@
}
body {
font: 10px sans-serif;
font-family: sans-serif;
padding: 20px;
}
@ -29,7 +29,6 @@ body {
</style>
<script src="http://d3js.org/d3.v4.0.0-alpha.50.min.js"></script>
<script src="http://d3js.org/d3-chord.v0.0.min.js"></script>
<script src='js/constants.js'></script>
<script src='js/matrices.js'></script>
<script src='js/diagram.js'></script>
<script src='js/ui.js'></script>
@ -72,17 +71,19 @@ body {
https://openfootball.github.io/questions.html
<h5>TODO</h5>
fix setState to not remove everything
1974 two germanies?
embiggen current event flag
move styling out of index.html
move fetch into main
add sort metric below team name
hide rounds
round persistence
sort persistence
better layout
better colors
webpack 2 / css modules?
add three.js sketchup clone to a list somewhere
separate three.js swell map below post or something
tweet it!
</div>
</body>

@ -1,7 +0,0 @@
const CONSTANTS = {
SORT_TYPES: {
GOALS: 'goals',
COUNTRY: 'country',
POPULATION: 'population',
},
};

@ -197,9 +197,19 @@ const Diagram = {
.data(function(chords) { return chords; })
.enter().append("path")
.attr("d", ribbon)
.style("fill", function(d) { return color(d.target.index); })
.style("stroke", function(d) { return d3.rgb(color(d.target.index)).darker(); })
.attr("class", "ribbon")
.attr("data-round-id", (d) => {
const t1 = data.tourneys[eventKey].teams[d.source.index];
const t2 = data.tourneys[eventKey].teams[d.target.index];
const game = data.tourneys[eventKey].games.find(v => {
return (v.t1 === t1.tId || v.t1 === t2.tId) && (v.t2 === t1.tId || v.t2 === t2.tId);
});
return game.rId;
})
.style("fill", (d) => color(d.target.index))
.style("stroke", (d) => d3.rgb(color(d.target.index)).darker())
.classed("ribbon", true)
.append("title")
.text(function(d) {
const t1 = data.tourneys[eventKey].teams[d.source.index];

@ -10,6 +10,21 @@ const fetch = (url) => new Promise((resolve, reject) => {
});
const main = {
SORT_TYPES: {
GOALS: 'goals',
COUNTRY: 'country',
POPULATION: 'population',
},
ROUND_TYPES: {
PRELIM: 'prelims',
ROUNDOF16: 'round-of-16',
QUARTERFINAL: 'quarterfinals',
SEMIFINAL: 'semifinals',
CONSOLATION: 'consolation',
FINAL: 'finals',
},
changeEvent: (e) => {
const target = UI.findRoot(e.target);
main.setState({ eventKey: target.getAttribute(UI.DATA.EVENT) });
@ -17,9 +32,17 @@ const main = {
},
changeRound: (e) => {
const state = main.getState();
const target = UI.findRoot(e.target);
main.setState({ round: target.getAttribute(UI.DATA.ROUND) });
main.updateUI();
const rounds = state.rounds ? state.rounds.split(',') : [];
const r = target.getAttribute(UI.DATA.ROUND)
const i = rounds.indexOf(r);
(i === -1) ? rounds.push(r) : rounds.splice(i, 1);
UI.toggleRoundRibbons(rounds, main.ROUND_TYPES, main.json.rounds);
UI.toggleRoundItem(target);
main.setState({ rounds });
},
changeSort: (e) => {
@ -29,34 +52,40 @@ const main = {
},
getRounds: (eventKey) => {
const reducer = (acc, v) => (acc.indexOf(v.rId) === -1 ? acc.concat(v.rId) : acc);
const roundIds = main.json.tourneys[eventKey].games.reduce(reducer, []);
return roundIds.map(v => ({
roundId: v,
roundName: UI.getRoundName(main.json.rounds[v]),
})
);
const rounds = {};
main.json.tourneys[eventKey].games.forEach(game => {
const name = UI.getRoundName(main.json.rounds[game.rId]);
if (rounds[name] === undefined) {
rounds[name] = [];
}
rounds[name].push({
id: game.rId,
name: name,
});
});
return rounds;
},
generateUI: () => {
const state = main.getState();
UI.buildEventsPane(main.changeEvent);
UI.buildSortPane(main.changeSort, CONSTANTS.SORT_TYPES);
UI.buildRoundsPane(main.changeRound);
UI.buildSortPane(main.changeSort, main.SORT_TYPES);
UI.buildRoundsPane(main.changeRound, main.ROUND_TYPES);
},
updateUI: () => {
const state = main.getState();
const rounds = main.getRounds(state.eventKey);
UI.updateEventsPane(state.eventKey);
// UI.updateRoundsPane(rounds);
UI.updateSortPane(state.sort);
const matrix = Matrices.buildMatrix(main.json, state.eventKey);
Diagram.clear();
Diagram.build(main.json, state.eventKey, state.sort, CONSTANTS.SORT_TYPES, matrix);
Diagram.build(main.json, state.eventKey, state.sort, main.SORT_TYPES, matrix);
},
initJSON: (strData) => {
@ -89,6 +118,7 @@ const main = {
}
if (state.rounds === undefined) {
state.rounds = Object.values(main.ROUND_TYPES);
}
main.setState(state);
@ -99,7 +129,7 @@ const main = {
const url = window.location.href.split('?')[0];
const eventKey = next.eventKey || prev.eventKey;
const rounds = next.rounds || prev.rounds || null;
const rounds = next.rounds || prev.rounds;
const sort = next.sort || prev.sort || null;
history.pushState(null, null, `${url}?eventKey=${eventKey}&sort=${sort}&rounds=${rounds}`);

@ -11,6 +11,12 @@ const UI = {
ITEM: 'sort-item',
TEXT: 'sort-text',
},
ROUND: {
ITEM: 'round-item',
HIDE: 'round-hide',
TEXT: 'round-text',
},
},
DATA: {
@ -20,6 +26,11 @@ const UI = {
SORT: 'data-sort-key',
},
I18N: {
HIDE: 'hide',
SHOW: 'show',
},
findRoot: (node) => {
while (node !== document.body && node.getAttribute(UI.DATA.ROOT) === null) {
node = node.parentNode;
@ -36,6 +47,23 @@ const UI = {
});
},
toggleRoundItem: (target) => {
const hide = target.querySelector(`.${UI.CLASSNAMES.ROUND.HIDE}`);
hide.innerHTML = (hide.innerHTML === UI.I18N.HIDE ? UI.I18N.SHOW : UI.I18N.HIDE);
},
toggleRoundRibbons: (roundsToShow, ROUND_TYPES, allRounds) => {
const ribbons = document.querySelectorAll('.ribbon');
ribbons.forEach(ribbon => {
const name = allRounds[ribbon.getAttribute("data-round-id")];
const type = UI.getRoundType(name, ROUND_TYPES);
roundsToShow.indexOf(type) === -1
? ribbon.style.display = 'none'
: ribbon.style.display = 'inline';
});
},
updateEventsPane: (eventKey) => {
const eventItems = document.querySelectorAll(`.${UI.CLASSNAMES.EVENT.ITEM}`);
UI.clearActive(eventItems);
@ -140,7 +168,6 @@ const UI = {
item.addEventListener('click', onClick);
item.setAttribute(UI.DATA.SORT, sort.value);
item.setAttribute(UI.DATA.ROOT, true);
item.addEventListener('click', onClick);
text.className = UI.CLASSNAMES.SORT.TEXT;
text.innerHTML = sort.text;
@ -150,33 +177,44 @@ const UI = {
});
},
buildRoundsPane: (onClick) => {
// const roundsDiv = document.querySelector('.rounds');
//
// while (roundsDiv.hasChildNodes()) {
// roundsDiv.firstChild.remove();
// }
//
// rounds.forEach(round => {
// const item = document.createElement('div');
// item.className = 'round-item';
// item.setAttribute('data-round-id', round.roundId);
//
// const hide = document.createElement('div');
// hide.className = 'round-hide';
// hide.innerHTML = 'hide';
//
// const text = document.createElement('div');
// text.className = 'round-text';
// text.innerHTML = round.roundName;
//
// item.appendChild(text);
// item.appendChild(hide);
// roundsDiv.appendChild(item);
// });
buildRoundsPane: (onClick, ROUND_TYPES) => {
const roundsList = [
{ text: 'Preliminaries', value: ROUND_TYPES.PRELIM },
{ text: 'Round of 16', value: ROUND_TYPES.ROUNDOF16 },
{ text: 'Quarterfinals', value: ROUND_TYPES.QUARTERFINAL },
{ text: 'Semifinals', value: ROUND_TYPES.SEMIFINAL },
{ text: 'Consolation', value: ROUND_TYPES.CONSOLATION },
{ text: 'Final', value: ROUND_TYPES.FINAL },
];
const roundsDiv = document.querySelector('.rounds');
while (roundsDiv.hasChildNodes()) {
roundsDiv.firstChild.remove();
}
roundsList.forEach(round => {
const item = document.createElement('div');
item.className = UI.CLASSNAMES.ROUND.ITEM;
item.addEventListener('click', onClick);
item.setAttribute(UI.DATA.ROUND, round.value);
item.setAttribute(UI.DATA.ROOT, true);
const hide = document.createElement('div');
hide.className = UI.CLASSNAMES.ROUND.HIDE;
hide.innerHTML = 'hide';
const text = document.createElement('div');
text.className = UI.CLASSNAMES.ROUND.TEXT;
text.innerHTML = round.text;
item.appendChild(text);
item.appendChild(hide);
roundsDiv.appendChild(item);
});
},
getRoundName: (name) => {
getRoundType: (name, ROUND_TYPES) => {
const name2 = name.match(/^Matchday/) ? 'First round' : name;
switch (name2) {
@ -187,27 +225,25 @@ const UI = {
case 'Group-2 Play-off':
case 'Group-3 Play-off':
case 'Group-4 Play-off':
return "Preliminaries";
return ROUND_TYPES.PRELIM;
case 'Round of 16':
return "Round of 16";
case 'Quarter-finals':
case 'Quarter-finals replays':
return "Quarter Finals";
case 'Semi-finals':
return "Semi Finals";
return ROUND_TYPES.ROUNDOF16;
case 'Third place match':
case 'Third-place match':
case 'Match for third place':
case 'Third place play-off':
case 'Third-place play-off':
return "Third Place Match";
return ROUND_TYPES.CONSOLATION;
case 'Quarterfinals':
return "Quarterfinals";
case 'Quarter-finals':
case 'Quarter-finals replays':
return ROUND_TYPES.QUARTERFINAL;
case 'Semifinals':
return "Semifinals";
case 'Semi-finals':
return ROUND_TYPES.SEMIFINAL;
case 'Final':
case 'Finals':
return "Final";
return ROUND_TYPES.FINAL;
}
console.error(`Unknown round name: ${name}`);

@ -1,4 +1,5 @@
.diagram {
font-size: 10px;
height: 700px;
left: 200px;
position: absolute;

@ -1,7 +1,6 @@
.rounds {
bottom: 0;
color: #000;
height: 500px;
position: absolute;
right: 0;
text-align: right;
@ -10,10 +9,23 @@
}
.round-item {
cursor: pointer;
font-size: 13px;
line-height: 34px;
padding: 0 10px;
}
.round-item:hover {
background: yellow;
}
.round-hide {
display: inline-block;
font-size: 9px;
margin-left: 20px;
text-transform: uppercase;
}
.round-text {
display: inline-block;
}

@ -13,6 +13,7 @@
cursor: pointer;
font-size: 13px;
line-height: 34px;
padding: 0 10px;
}
.sort-item:hover {

Loading…
Cancel
Save