diff --git a/.htdocs/index.html b/.htdocs/index.html new file mode 100644 index 0000000..e69de29 diff --git a/dzielenie.html b/dzielenie.html new file mode 100644 index 0000000..4e8f7d1 --- /dev/null +++ b/dzielenie.html @@ -0,0 +1,102 @@ + + + + + + Nauka Dzielenia + + + + +
+ +
+

÷ Nauka Dzielenia

+

Wybierz dzielnik do ćwiczenia

+
+
+ + + + + + + + + + + +
+
+
+ +
+ +
+ + +
+
+ 0/20 + ✔ 0 + +
+
+ +
+
?
+
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ + + +
+
+ +
+
+
+
+ + +
+
+
🎉
+

Koniec!

+

+ + +
+
+
+ + + + + + diff --git a/dzielenie.js b/dzielenie.js new file mode 100644 index 0000000..483d4f7 --- /dev/null +++ b/dzielenie.js @@ -0,0 +1,130 @@ +// Nauka Dzielenia +;(function () { + const st = { divisor: null, total: 20, solved: 0, score: 0, current: null, buf: '' } + + const selectScreen = document.getElementById('select-screen') + const playScreen = document.getElementById('play-screen') + const summaryScreen = document.getElementById('summary-screen') + const problemEl = document.getElementById('problem') + const answerEl = document.getElementById('answer') + const feedbackEl = document.getElementById('feedback') + const progressInner = document.getElementById('progress-inner') + const progressLabel = document.getElementById('progress-label') + const scoreLabel = document.getElementById('score-label') + const summaryText = document.getElementById('summary-text') + const totalInput = document.getElementById('total-input') + + // divisor selection + document.getElementById('table-grid').addEventListener('click', e => { + const btn = e.target.closest('.table-btn') + if (!btn) return + document.querySelectorAll('.table-btn').forEach(b => b.classList.remove('active')) + btn.classList.add('active') + st.divisor = parseInt(btn.dataset.val, 10) // 0 = all + }) + + document.getElementById('start-btn').addEventListener('click', () => { + st.total = Math.max(5, Math.min(100, parseInt(totalInput.value, 10) || 20)) + st.solved = 0 + st.score = 0 + show(playScreen) + nextProblem() + }) + + document.getElementById('back-btn').addEventListener('click', () => show(selectScreen)) + document.getElementById('again-btn').addEventListener('click', () => { + st.solved = 0; st.score = 0; show(playScreen); nextProblem() + }) + document.getElementById('change-btn').addEventListener('click', () => show(selectScreen)) + + // keypad + document.querySelectorAll('.key').forEach(k => { + k.addEventListener('click', () => { + const v = k.textContent.trim() + if (!/^[0-9]$/.test(v)) return + if (st.buf.length >= 6) return + st.buf += v; answerEl.textContent = st.buf + }) + }) + document.getElementById('clear').addEventListener('click', () => { st.buf = ''; answerEl.textContent = '' }) + document.getElementById('backspace').addEventListener('click', () => { st.buf = st.buf.slice(0,-1); answerEl.textContent = st.buf || ' ' }) + document.getElementById('submit').addEventListener('click', submit) + + window.addEventListener('keydown', e => { + if (!playScreen.classList.contains('hidden')) { + if (/^[0-9]$/.test(e.key) && st.buf.length < 6) { st.buf += e.key; answerEl.textContent = st.buf } + else if (e.key === 'Backspace') { st.buf = st.buf.slice(0,-1); answerEl.textContent = st.buf || ' ' } + else if (e.key === 'Enter') submit() + } + }) + + function randInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min } + + function nextProblem() { + const b = st.divisor || randInt(1, 10) // divisor + const answer = randInt(1, 10) // quotient (always integer) + const a = b * answer // dividend + st.current = { a, b, answer } + problemEl.textContent = `${a} ÷ ${b} = ?` + feedbackEl.textContent = '' + st.buf = ''; answerEl.textContent = ' ' + updateBar() + } + + function submit() { + if (!st.buf.trim()) return + const given = parseInt(st.buf, 10) + st.solved++ + if (given === st.current.answer) { + st.score++ + feedbackEl.textContent = '✔ dobrze!' + feedbackEl.style.color = '#16a34a' + } else { + feedbackEl.textContent = `✖ ${st.current.a} ÷ ${st.current.b} = ${st.current.answer}` + feedbackEl.style.color = '#dc2626' + } + st.buf = ''; answerEl.textContent = ' ' + updateBar() + if (st.solved >= st.total) { + setTimeout(showSummary, 700) + } else { + setTimeout(nextProblem, 600) + } + } + + function updateBar() { + const pct = Math.round((st.solved / st.total) * 100) + progressInner.style.width = pct + '%' + progressLabel.textContent = `${st.solved}/${st.total}` + scoreLabel.textContent = `✔ ${st.score}` + } + + function showSummary() { + const pct = Math.round((st.score / st.total) * 100) + summaryText.textContent = `${st.score} / ${st.total} poprawnie (${pct}%)` + show(summaryScreen) + } + + function show(screen) { + [selectScreen, playScreen, summaryScreen].forEach(s => s.classList.add('hidden')) + screen.classList.remove('hidden') + } + + // commit SHA + document.addEventListener('DOMContentLoaded', async () => { + const el = document.getElementById('commit-sha') + if (!el) return + let sha = (window.COMMIT_SHA || '').toString().trim() + if (!sha) { + try { + const res = await fetch('/version.sha', { cache: 'no-cache' }) + if (res.ok) { + const txt = await res.text() + const first = txt.split(/\r?\n/).find(l => l.trim().length > 0) + if (first) sha = first.trim() + } + } catch (e) {} + } + if (sha) el.textContent = sha.slice(0, 8) + }) +})() diff --git a/index.html b/index.html index cd33201..03239fe 100644 --- a/index.html +++ b/index.html @@ -7,6 +7,14 @@ +

Matma Trening

@@ -145,6 +153,7 @@
Version: (loading)
+ diff --git a/mnozenie.html b/mnozenie.html new file mode 100644 index 0000000..3da5fff --- /dev/null +++ b/mnozenie.html @@ -0,0 +1,102 @@ + + + + + + Nauka Mnożenia + + + + +
+ +
+

× Nauka Mnożenia

+

Wybierz tabliczkę do ćwiczenia

+
+
+ + + + + + + + + + + +
+
+
+ +
+ +
+ + +
+
+ 0/20 + ✔ 0 + +
+
+ +
+
?
+
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ + + +
+
+ +
+
+
+
+ + +
+
+
🎉
+

Koniec!

+

+ + +
+
+
+ +
Commit:
+ + + + diff --git a/mnozenie.js b/mnozenie.js new file mode 100644 index 0000000..1a10d58 --- /dev/null +++ b/mnozenie.js @@ -0,0 +1,129 @@ +// Nauka Mnożenia +;(function () { + const st = { table: null, total: 20, solved: 0, score: 0, current: null, buf: '' } + + const selectScreen = document.getElementById('select-screen') + const playScreen = document.getElementById('play-screen') + const summaryScreen = document.getElementById('summary-screen') + const problemEl = document.getElementById('problem') + const answerEl = document.getElementById('answer') + const feedbackEl = document.getElementById('feedback') + const progressInner = document.getElementById('progress-inner') + const progressLabel = document.getElementById('progress-label') + const scoreLabel = document.getElementById('score-label') + const summaryText = document.getElementById('summary-text') + const totalInput = document.getElementById('total-input') + + // table selection + document.getElementById('table-grid').addEventListener('click', e => { + const btn = e.target.closest('.table-btn') + if (!btn) return + document.querySelectorAll('.table-btn').forEach(b => b.classList.remove('active')) + btn.classList.add('active') + st.table = parseInt(btn.dataset.val, 10) // 0 = all + }) + + document.getElementById('start-btn').addEventListener('click', () => { + st.total = Math.max(5, Math.min(100, parseInt(totalInput.value, 10) || 20)) + st.solved = 0 + st.score = 0 + show(playScreen) + nextProblem() + }) + + document.getElementById('back-btn').addEventListener('click', () => show(selectScreen)) + document.getElementById('again-btn').addEventListener('click', () => { + st.solved = 0; st.score = 0; show(playScreen); nextProblem() + }) + document.getElementById('change-btn').addEventListener('click', () => show(selectScreen)) + + // keypad + document.querySelectorAll('.key').forEach(k => { + k.addEventListener('click', () => { + const v = k.textContent.trim() + if (!/^[0-9]$/.test(v)) return + if (st.buf.length >= 6) return + st.buf += v; answerEl.textContent = st.buf + }) + }) + document.getElementById('clear').addEventListener('click', () => { st.buf = ''; answerEl.textContent = '' }) + document.getElementById('backspace').addEventListener('click', () => { st.buf = st.buf.slice(0,-1); answerEl.textContent = st.buf || ' ' }) + document.getElementById('submit').addEventListener('click', submit) + + window.addEventListener('keydown', e => { + if (!playScreen.classList.contains('hidden')) { + if (/^[0-9]$/.test(e.key) && st.buf.length < 6) { st.buf += e.key; answerEl.textContent = st.buf } + else if (e.key === 'Backspace') { st.buf = st.buf.slice(0,-1); answerEl.textContent = st.buf || ' ' } + else if (e.key === 'Enter') submit() + } + }) + + function randInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min } + + function nextProblem() { + const a = st.table || randInt(1, 10) + const b = randInt(1, 10) + st.current = { a, b, answer: a * b } + problemEl.textContent = `${a} × ${b} = ?` + feedbackEl.textContent = '' + st.buf = ''; answerEl.textContent = ' ' + updateBar() + } + + function submit() { + if (!st.buf.trim()) return + const given = parseInt(st.buf, 10) + st.solved++ + if (given === st.current.answer) { + st.score++ + feedbackEl.textContent = '✔ dobrze!' + feedbackEl.style.color = '#16a34a' + } else { + feedbackEl.textContent = `✖ ${st.current.a} × ${st.current.b} = ${st.current.answer}` + feedbackEl.style.color = '#dc2626' + } + st.buf = ''; answerEl.textContent = ' ' + updateBar() + if (st.solved >= st.total) { + setTimeout(showSummary, 700) + } else { + setTimeout(nextProblem, 600) + } + } + + function updateBar() { + const pct = Math.round((st.solved / st.total) * 100) + progressInner.style.width = pct + '%' + progressLabel.textContent = `${st.solved}/${st.total}` + scoreLabel.textContent = `✔ ${st.score}` + } + + function showSummary() { + const pct = Math.round((st.score / st.total) * 100) + summaryText.textContent = `${st.score} / ${st.total} poprawnie (${pct}%)` + show(summaryScreen) + } + + function show(screen) { + [selectScreen, playScreen, summaryScreen].forEach(s => s.classList.add('hidden')) + screen.classList.remove('hidden') + } + + // commit SHA + document.addEventListener('DOMContentLoaded', async () => { + const el = document.getElementById('commit-sha') + if (!el) return + let sha = (window.COMMIT_SHA || '').toString().trim() + if (!sha) { + try { + const res = await fetch('/version.sha', { cache: 'no-cache' }) + if (res.ok) { + const txt = await res.text() + const first = txt.split(/\r?\n/).find(l => l.trim().length > 0) + if (first) sha = first.trim() + } + } catch (e) {} + } + if (sha) el.textContent = sha.slice(0, 8) + }) +})() diff --git a/nav.js b/nav.js new file mode 100644 index 0000000..afeb52c --- /dev/null +++ b/nav.js @@ -0,0 +1,18 @@ +// Shared hamburger menu toggle +(function () { + document.addEventListener('DOMContentLoaded', () => { + const btn = document.getElementById('hamburger-btn') + const menu = document.getElementById('hamburger-menu') + if (!btn || !menu) return + btn.addEventListener('click', (e) => { + e.stopPropagation() + menu.classList.toggle('open') + }) + document.addEventListener('click', () => menu.classList.remove('open')) + // mark active link + const current = location.pathname.split('/').pop() || 'index.html' + menu.querySelectorAll('a').forEach(a => { + if (a.getAttribute('href') === current) a.classList.add('active') + }) + }) +})() diff --git a/styles.css b/styles.css index a76670b..7298880 100644 --- a/styles.css +++ b/styles.css @@ -65,6 +65,29 @@ body{ .app-footer{font-size:12px;color:var(--muted);text-align:center;padding:10px 0;width:100%;max-width:420px} +/* ── Hamburger nav ── */ +.nav-wrap{position:relative;width:100%;max-width:420px;display:flex;justify-content:flex-end;padding:6px 8px 0} +.hamburger-btn{background:var(--card);border:1px solid #e6e9ef;border-radius:10px;padding:8px 12px;font-size:20px;cursor:pointer;line-height:1;box-shadow:0 2px 8px rgba(16,24,40,0.07)} +.hamburger-menu{display:none;position:absolute;top:46px;right:8px;background:var(--card);border:1px solid #e6e9ef;border-radius:14px;box-shadow:0 8px 24px rgba(16,24,40,0.13);z-index:100;min-width:210px;overflow:hidden} +.hamburger-menu.open{display:block} +.hamburger-menu a{display:flex;align-items:center;gap:10px;padding:14px 18px;text-decoration:none;color:#111;font-size:16px;border-bottom:1px solid #f0f0f3} +.hamburger-menu a:last-child{border-bottom:none} +.hamburger-menu a:hover,.hamburger-menu a.active{background:#f0f4ff;color:var(--accent)} +.hamburger-menu a .nav-icon{font-size:20px;width:24px;text-align:center} + +/* ── Shared learn-page styles ── */ +.learn-title{margin:20px 16px 0;font-size:22px;font-weight:700;text-align:center} +.learn-subtitle{text-align:center;color:var(--muted);font-size:14px;margin:4px 16px 12px} +.table-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:8px;padding:0 4px} +.table-btn{padding:14px 6px;border-radius:10px;border:1px solid #e6e9ef;background:white;font-size:18px;font-weight:600;cursor:pointer} +.table-btn.active{background:var(--accent);color:white;border-color:var(--accent)} +.table-btn.all{grid-column:span 5;font-size:15px} +.start-btn{display:block;width:calc(100% - 32px);margin:14px 16px;padding:16px;border-radius:12px;background:var(--accent);color:white;font-size:18px;font-weight:700;border:0;cursor:pointer} +.learn-problem{font-size:60px;font-weight:700;text-align:center;margin:12px 0 4px} +.learn-score-bar{display:flex;justify-content:space-between;align-items:center;padding:10px 18px;font-size:15px;color:var(--muted)} +.learn-score-bar strong{font-size:18px;color:#111} +.learn-feedback{height:24px;text-align:center;font-size:16px;color:var(--muted)} + @media (orientation:portrait){ .screen{max-height:94vh} }