From a1dec26fc5fd0ef3bcab49800409ca1619749377 Mon Sep 17 00:00:00 2001 From: Ben Burlingham Date: Sat, 5 Nov 2016 17:37:36 -0700 Subject: [PATCH] Round state and functionality complete. --- index.html | 7 +-- js/constants.js | 7 --- js/diagram.js | 16 +++++-- js/main.js | 60 +++++++++++++++++++------- js/ui.js | 110 ++++++++++++++++++++++++++++++++---------------- res/diagram.css | 1 + res/rounds.css | 14 +++++- res/sort.css | 1 + 8 files changed, 150 insertions(+), 66 deletions(-) delete mode 100644 js/constants.js diff --git a/index.html b/index.html index 81171fd..6354310 100644 --- a/index.html +++ b/index.html @@ -11,7 +11,7 @@ } body { - font: 10px sans-serif; + font-family: sans-serif; padding: 20px; } @@ -29,7 +29,6 @@ body { - @@ -72,17 +71,19 @@ body { https://openfootball.github.io/questions.html
TODO
+ 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! diff --git a/js/constants.js b/js/constants.js deleted file mode 100644 index c0551b2..0000000 --- a/js/constants.js +++ /dev/null @@ -1,7 +0,0 @@ -const CONSTANTS = { - SORT_TYPES: { - GOALS: 'goals', - COUNTRY: 'country', - POPULATION: 'population', - }, -}; diff --git a/js/diagram.js b/js/diagram.js index bc7d37c..e3b134a 100644 --- a/js/diagram.js +++ b/js/diagram.js @@ -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]; diff --git a/js/main.js b/js/main.js index 66f946f..d523454 100644 --- a/js/main.js +++ b/js/main.js @@ -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}`); diff --git a/js/ui.js b/js/ui.js index 8bd1d5f..843fff3 100644 --- a/js/ui.js +++ b/js/ui.js @@ -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}`); diff --git a/res/diagram.css b/res/diagram.css index ab7fd96..023b621 100644 --- a/res/diagram.css +++ b/res/diagram.css @@ -1,4 +1,5 @@ .diagram { + font-size: 10px; height: 700px; left: 200px; position: absolute; diff --git a/res/rounds.css b/res/rounds.css index 71f697c..a46f29f 100644 --- a/res/rounds.css +++ b/res/rounds.css @@ -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; } diff --git a/res/sort.css b/res/sort.css index ecbe3c6..9b3667d 100644 --- a/res/sort.css +++ b/res/sort.css @@ -13,6 +13,7 @@ cursor: pointer; font-size: 13px; line-height: 34px; + padding: 0 10px; } .sort-item:hover {