console.clear(); const elToggle = document.querySelector('#toggle'); const elApp = document.querySelector('#app'); const elBody = document.body; // const machine = { // initial: 'off', // states: { // off: {}, // pending: {}, // on: {} // } // } const TIMEOUT = 1000; elBody.style.setProperty('--timeout', TIMEOUT); const transition = (state, event) => { switch (state) { case 'off': return 'pending'; case 'pending': switch (event.type) { case 'ALL_ON': return 'on'; case 'ALL_OFF': return 'off'; default: return state; } case 'on': return 'pending'; default: return 'off'; } } let current = elApp.dataset.state; function activate(state) { elApp.dataset.state = current; elBody.dataset.toggleState = current; document.querySelectorAll(`[data-active]`) .forEach(el => delete el.dataset.active); document.querySelectorAll(`[data-for="${current}"]`) .forEach(el => el.dataset.active = true); } activate(current); function send(event) { const prevState = current; elApp.dataset.prevState = prevState; current = transition(current, event); if (prevState === current) { return; } activate(current); switch (current) { case 'off': elToggle.indeterminate = false; elToggle.checked = false; elToggle.readOnly = false; break; case 'pending': elToggle.readOnly = true; elToggle.indeterminate = true; setTimeout(() => { if (prevState === 'on') { send({ type: 'ALL_OFF' }) } else { send({ type: 'ALL_ON' }) } }, TIMEOUT); break; case 'on': elToggle.readOnly = false; elToggle.checked = true; elToggle.indeterminate = false; break; default: break; } } elToggle.addEventListener('click', e => { e.preventDefault(); send({ type: 'TOGGLE' }); });