Files
Poker/public/assets/key.js
T
agomizi1cmandClaude Opus 5.5 c3fbfdd12e Rebuild the UI in an Apple style with spring-driven motion
Replace the look of every page (key, lobby, create, table, range, review)
with light/dark semantic colors, translucent materials, iOS-style grouped
lists and system typography. Add fluid.js, a small spring engine with
interruptible, velocity-aware animations, and use it for draggable bottom
sheets, sliding segmented controls, a custom bet slider with rubber-banding,
swipe-to-delete in the lobby, and dealt/flipped cards. Seats and cards are
now reused between renders so animations are not cut off. Honors reduced
motion, reduced transparency and increased contrast.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 21:44:21 +09:00

98 lines
2.9 KiB
JavaScript

(function () {
'use strict';
const form = document.getElementById('form');
const box = document.getElementById('digits');
const inputs = [...box.querySelectorAll('input')];
const errorEl = document.getElementById('error');
const submitBtn = document.getElementById('submit');
let busy = false;
// /key?admin … 入室済みの人が管理者キーを入れるための画面
if (new URLSearchParams(location.search).has('admin')) {
document.getElementById('key-title').textContent = '管理者キーを入力';
document.getElementById('key-hint').textContent = '管理者用の6桁の数字';
}
const value = () => inputs.map((i) => i.value).join('');
// 数字が入った枠は小さく弾む(空に戻ったら印を外す)
function markFilled() {
for (const i of inputs) i.classList.toggle('filled', Boolean(i.value));
}
function fill(digits, from) {
let i = from;
for (const d of digits) {
if (i >= inputs.length) break;
inputs[i++].value = d;
}
markFilled();
inputs[Math.min(i, inputs.length - 1)].focus();
if (value().length === 6) submit();
}
inputs.forEach((input, idx) => {
input.addEventListener('input', () => {
const digits = input.value.replace(/\D/g, '');
input.value = '';
if (digits) fill(digits, idx);
else markFilled();
errorEl.textContent = '';
});
input.addEventListener('keydown', (e) => {
if (e.key === 'Backspace' && !input.value && idx > 0) {
inputs[idx - 1].value = '';
markFilled();
inputs[idx - 1].focus();
e.preventDefault();
} else if (e.key === 'ArrowLeft' && idx > 0) {
inputs[idx - 1].focus();
} else if (e.key === 'ArrowRight' && idx < inputs.length - 1) {
inputs[idx + 1].focus();
}
});
input.addEventListener('paste', (e) => {
const digits = (e.clipboardData.getData('text') || '').replace(/\D/g, '');
if (!digits) return;
e.preventDefault();
fill(digits, idx);
});
input.addEventListener('focus', () => input.select());
});
async function submit() {
if (busy) return;
const key = value();
if (key.length !== 6) {
errorEl.textContent = '6桁すべて入力してください。';
return;
}
busy = true;
submitBtn.disabled = true;
try {
await App.api('POST', '/api/auth', { key });
const next = new URLSearchParams(location.search).get('next');
location.replace(App.safeNext(next));
} catch (err) {
errorEl.textContent = err.message;
// 揺れと振動は同じ瞬間に(どちらも「違う」の合図)
Fluid.shake(box);
Fluid.haptic([18, 50, 18]);
inputs.forEach((i) => { i.value = ''; });
markFilled();
inputs[0].focus();
} finally {
busy = false;
submitBtn.disabled = false;
}
}
form.addEventListener('submit', (e) => {
e.preventDefault();
submit();
});
inputs[0].focus();
})();