JSFiddle - React, Tailwind, and code Playground
by Josh Pullen
CSS
body {
width: 100vw;
height: 100vh;
margin: 0;
display: flex;
font-family: sans-serif;
}
.grid {
display: grid;
width: 100vmin;
height: 100vmin;
grid-template-rows: repeat(10, 1fr);
grid-template-columns: repeat(10, 1fr);
}
.grid > button {
border: none;
}
.homeInfo {
flex-grow: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 24px;
text-align: center;
}
.winText {
color: rgb(6, 138, 72);
margin-top: 16px;
font-weight: bold;
}
.reset {
position: absolute;
bottom: 8px;
}
.number {
flex-grow: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 96px;
text-align: center;
}
.inputs {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.inputRow {
display: flex;
align-items: center;
font-size: 32px;
padding: 8px;
margin: 2px 8px;
border-radius: 4px;
}
.inputRow > input {
width: 80px;
font-size: 24px;
border-radius: 4px;
padding: 8px;
border: none;
background: rgba(0, 0, 0, 0.1);
text-align: center;
color: inherit;
}
.inputRow > span {
margin: 0 12px;
}
.inputRow--yes {
background: rgb(6, 138, 72);
color: #fff;
}
.inputRow--duplicate {
background: #f4ff91;
}
/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Firefox */
input[type=number] {
-moz-appearance: textfield;
}
JavaScript
import { html, render, useState } from "https://unpkg.com/htm/preact/standalone.module.js";
let defaultInputState = [];
defaultInputState[0] = [];
for (let i = 2; i <= 100; i++) {
const pairCount = factorPairs(i).length;
const inputRows = new Array(pairCount).fill().map(() => ["", ""]);
defaultInputState[i - 1] = inputRows;
}
function factorPairs(n) {
let result = [];
for (let i = 1; i <= Math.sqrt(n); i++) {
if (n % i === 0) {
result.push([i, n / i]);
}
}
return result;
}
function validatePairs(n, pairs) {
let result = [];
let usedDivisors = [];
for (let i = 0; i < pairs.length; i++) {
const pair = pairs[i];
let valid = (pair[0] * pair[1] === n) ? "yes" : "no";
if (isNaN(pair[0])) valid = "no";
if (isNaN(pair[1])) valid = "no";
if (pair[0] % 1 !== 0) valid = "no";
if (pair[1] % 1 !== 0) valid = "no";
if (valid === "yes") {
if (usedDivisors.includes(pair[0])) valid = "duplicate";
if (usedDivisors.includes(pair[1])) valid = "duplicate";
}
result.push(valid);
if (valid === "yes") {
usedDivisors.push(pair[0]);
usedDivisors.push(pair[1]);
}
}
return result;
}
function App() {
const [screen, setScreen] = useState({ name: "home" });
const [inputs, setInputs] = useState(defaultInputState);
const setInput = (num, pairIndex, inputIndex, value) => {
setInputs([
...inputs.slice(0, num - 1),
[
...inputs[num - 1].slice(0, pairIndex),
[
...inputs[num - 1][pairIndex].slice(0, inputIndex),
value,
...inputs[num - 1][pairIndex].slice(inputIndex + 1)
],
...inputs[num - 1].slice(pairIndex + 1)
],
...inputs.slice(num)
]);
}
const validationResults = inputs.map((pairs, i) => (
validatePairs(i + 1, pairs.map(pair => pair.map(n => n === "" ? NaN : Number(n))))
));
const overallProgress = validationResults.flat().reduce((a, b) => a + (b === "yes"), 0);
const...