Implement keypads module in React

This commit is contained in:
Daniel Sinn
2019-11-26 02:37:06 -05:00
parent 2fb6fd8446
commit 3c9ea427d8
38 changed files with 188 additions and 85 deletions
-5
View File
@@ -13,11 +13,6 @@
<h1>Keep Talking and Nobody Explodes</h1>
<section id="sectionKeypads">
<h2>Keypads</h2>
<button class="jsResetSection">Reset</button>
</section>
<section id="sectionSimon">
<h2>Simon Says</h2>
-48
View File
@@ -39,54 +39,6 @@ $('section').each(function () {
id && $('#navList').append($('<li></li>').append($a));
});
(function () {
/**
* Keypads
*/
var columns = [
['balloon', 'at', 'upsidedowny', 'squigglyn', 'squidknife', 'hookn', 'leftc'],
['euro', 'balloon', 'leftc', 'cursive', 'hollowstar', 'hookn', 'questionmark'],
['copyright', 'pumpkin', 'cursive', 'doublek', 'meltedthree', 'upsidedowny', 'hollowstar'],
['six', 'paragraph', 'bt', 'squidknife', 'doublek', 'questionmark', 'smileyface'],
['pitchfork', 'smileyface', 'bt', 'rightc', 'paragraph', 'dragon', 'filledstar'],
['six', 'euro', 'tracks', 'ae', 'pitchfork', 'nwithhat', 'omega']
],
$section = $('#sectionKeypads'),
$imageLists = {}; // name -> $('<img />')[]
$.each(columns, function (i, column) {
var $column = $('<ul data-count="0"></ul>');
$section.append($column);
$.each(column, function (i, name) {
var $img = $('<img data-name="' + name + '" src="keypads/' + name + '.png" width="64" height="64" />');
$column.append($('<li></li>').append($img));
name in $imageLists
? $imageLists[name].push($img)
: $imageLists[name] = [$img];
});
});
$section.on('click', 'img', function () {
var $img = $(this),
name = $img.data('name'),
endState = !$img.hasClass('selected'),
offset = endState ? 1 : -1;
$.each($imageLists[name], function (i, $img) {
var $column = $img.parent().parent();
$img.toggleClass('selected', endState);
$column.attr('data-count', parseInt($column.attr('data-count'), 10) + offset);
});
});
$section.find('.jsResetSection').on('click', function () {
// Also need to reset the indices after removing .selected
$section.find('ul').attr('data-count', 0);
});
})();
(function () {
/**
* Simon

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

-26
View File
@@ -93,10 +93,6 @@ h2 {
margin: 0;
}
.jsResetSection {
display: block;
}
nav {
background-color: white;
border-left: 1px solid black;
@@ -112,28 +108,6 @@ nav {
padding: 0.1em 0.5em;
}
#sectionKeypads ul {
display: inline-block;
list-style-type: none;
margin-right: 2em;
padding-left: 0;
}
#sectionKeypads li {
border: 1px solid black;
border-bottom: 0;
cursor: pointer;
line-height: 0;
}
#sectionKeypads li:last-child {
border-bottom: 1px solid black;
}
#sectionKeypads .selected {
background-color: #ff6;
}
#sectionKeypads ul[data-count='4'] .selected {
background-color: lightgreen;
}
#simonInputs {
float: left;
margin-right: 5em;
+2
View File
@@ -1,6 +1,7 @@
import React from 'react';
import './App.css';
import ButtonModule from "./components/ButtonModule";
import KeypadsModule from "./components/KeypadsModule";
import WiresModule from "./components/WiresModule";
function App() {
@@ -12,6 +13,7 @@ function App() {
<WiresModule id="sectionWires" />
<ButtonModule id="sectionButton" />
<KeypadsModule id="sectionKeypads" />
</div>
);
}
+2 -2
View File
@@ -94,9 +94,9 @@ export default class ButtonModule extends KtaneModule {
)
}
resetState() {
getInitialState() {
// @TODO Put string literals into constants
this.state = {
return {
colour: "blue",
text: "Abort"
};
+137
View File
@@ -0,0 +1,137 @@
import KtaneModule from "./KtaneModule";
import React from "react";
import ResetButton from "./ResetButton";
import "../css/KeypadsModule.css";
export default class KeypadsModule extends KtaneModule {
constructor(props) {
super(props);
this.resetState = this.resetState.bind(this);
this.toggleHighlight = this.toggleHighlight.bind(this);
}
computeKeyColumnMap() {
const map = {};
this.state.columns.forEach((column, index) => {
column.keys.forEach(key => {
if (!map.hasOwnProperty(key)) {
map[key] = [];
}
map[key].push(index);
});
});
return map;
}
getInitialState() {
return {
columns: [
{
highlightCount: 0,
keys: ["balloon", "at", "upsidedowny", "squigglyn", "squidknife", "hookn", "leftc"]
},
{
highlightCount: 0,
keys: ["euro", "balloon", "leftc", "cursive", "hollowstar", "hookn", "questionmark"]
},
{
highlightCount: 0,
keys: ["copyright", "pumpkin", "cursive", "doublek", "meltedthree", "upsidedowny", "hollowstar"]
},
{
highlightCount: 0,
keys: ["six", "paragraph", "bt", "squidknife", "doublek", "questionmark", "smileyface"]
},
{
highlightCount: 0,
keys: ["pitchfork", "smileyface", "bt", "rightc", "paragraph", "dragon", "filledstar"]
},
{
highlightCount: 0,
keys: ["six", "euro", "tracks", "ae", "pitchfork", "nwithhat", "omega"]
}
],
highlights: {
ae: false,
at: false,
balloon: false,
bt: false,
copyright: false,
cursive: false,
doublek: false,
dragon: false,
euro: false,
filledstar: false,
hollowstar: false,
hookn: false,
leftc: false,
meltedthree: false,
paragraph: false,
pumpkin: false,
questionmark: false,
rightc: false,
six: false,
smileyface: false,
squidknife: false,
squigglyn: false,
upsidedowny: false
}
};
}
getTitle() {
return "Keypads";
}
mainRender() {
return (
<>
<ResetButton onClick={this.resetState} />
{
this.state.columns.map((column, index) => {
return <ul className={column.highlightCount === 4 ? "matching" : ""} key={index}>
{
column.keys.map(key => {
return (
<li
className={this.state.highlights[key] ? "selected" : ""}
data-key={key}
key={key}
onClick={this.toggleHighlight}
>
<img alt={key} height="64" width="64" src={`keypads/${key}.png`}/>
</li>
);
})
}
</ul>;
})
}
</>
)
}
resetState() {
this.setState(this.getInitialState());
}
toggleHighlight(event) {
this.keyColumnMap = this.keyColumnMap || this.computeKeyColumnMap();
const key = event.currentTarget.dataset.key;
this.setState(state => {
const step = state.highlights[key] ? -1 : 1;
this.keyColumnMap[key].forEach(columnIndex => {
state.columns[columnIndex].highlightCount += step;
});
state.highlights[key] = !state.highlights[key];
return state;
});
}
}
+8 -2
View File
@@ -5,7 +5,13 @@ class KtaneModule extends Component {
constructor(props) {
super(props);
this.resetState();
this.state = this.getInitialState();
this.resetState = this.resetState.bind(this);
}
getInitialState() {
throw new Error("getInitialState() not implemented.");
}
getTitle() {
@@ -27,7 +33,7 @@ class KtaneModule extends Component {
}
resetState() {
throw new Error("mainRender() not implemented.");
this.setState(this.getInitialState());
}
}
+13
View File
@@ -0,0 +1,13 @@
import PropTypes from "prop-types";
import React, {Component} from "react";
import "../css/ResetButton.css";
export default class ResetButton extends Component {
render() {
return <button className="jsResetSection" onClick={this.props.onClick}>Reset</button>;
}
}
ResetButton.propTypes = {
onClick: PropTypes.func
};
+2 -2
View File
@@ -174,8 +174,8 @@ export default class WiresModule extends KtaneModule {
)
}
resetState() {
this.state = {
getInitialState() {
return {
// @TODO Convert this.allColours to a constant that is available here, i.e., before the constructor's super call
counts: ["red", "yellow", "blue", "white", "black"].reduce((counts, colour) => {
counts[colour] = 0;
+21
View File
@@ -0,0 +1,21 @@
#sectionKeypads ul {
display: inline-block;
list-style-type: none;
margin-right: 2em;
padding-left: 0;
}
#sectionKeypads li {
border: 1px solid black;
border-bottom: 0;
cursor: pointer;
line-height: 0;
}
#sectionKeypads li:last-child {
border-bottom: 1px solid black;
}
#sectionKeypads .selected {
background-color: #ff6;
}
#sectionKeypads .matching .selected {
background-color: lightgreen;
}
+3
View File
@@ -0,0 +1,3 @@
.jsResetSection {
display: block;
}