プレゼンタイマー
https://gyazo.com/b2dfebf5c7388205624a6e1515aa376c
code:install
code:style.css
@import "/api/code/pokutuna/プレゼンタイマー/style.css";
code:script.js
import "/api/code/pokutuna/プレゼンタイマー/script.js";
---.icon
code:style.css
.presentation-timer {
position: fixed;
left: 0;
bottom: 0;
z-index: 9999;
text-decoration: none;
padding: 8px;
font-size: 32px;
font-weight: 800;
display: flex;
frex-direction: row;
justify-content: center;
align-items: center;
}
.presentation-timer .timer-buttons {
margin-left: 0.2em;
display: flex;
}
.presentation-timer .timer-buttons i {
font-size: 90%;
margin: 0 2px;
font-style: normal;
font-family: FontAwesome;
}
code:script.js
import { h, Component, render, useReducer, useEffect } from '/api/code/pokutuna/preact-bundle/script.js';
import htm from '/api/code/pokutuna/htm@3.0.4/script.js';
import format from "/api/code/pokutuna/date-fns%2Fformat@2.14.0/script.js";
const html = htm.bind(h);
function reducer(state, action) {
switch (action.type) {
case "reset":
return { ...state, startAt: new Date(), now: new Date(), pause: false, showButtons: false };
case "pause":
return { ...state, pause: true }
case "start":
const ajusted = new Date() - (state.now - state.startAt);
return { ...state, startAt: ajusted, now: new Date(), pause: false };
case "tick":
return state.pause ? state : { ...state, now: new Date() };
case "buttons":
return { ...state, showButtons: action.show };
}
}
こういうぐらいの用途なら useReducer が輝くよね、普段はなんか足りないというか非同期どうするねんとか middleware 無いから自分で wrap するかとか、こう使って欲しいという React からのメッセージがあまり感じられない。もっぱら state と dispatch を引き回したいがために使っている。
とか、いきなり日記を挟んで文芸的プログラミングできる
code:script.js
function formatTimer(state) {
const diff = state.now - state.startAt;
const offset = new Date().getTimezoneOffset() * 60 * 1000;
const fmt = diff >= 60 * 60 * 1000 ? 'HH:mm:ss' : 'mm:ss';
return format(diff + offset, fmt) ;
}
const TimerButtons = (props) => {
const { state, dispatch } = props ;
return (state.showButtons || state.pause) && html`
<span class="timer-buttons">
${ state.pause ? html`
<i class="fas fa-play-circle"
onClick=${() => dispatch({ type: "start" })}></i>
<i class="fas fa-minus-circle"
onClick=${() => dispatch({ type: "reset" })}></i>
: html
<i class="fas fa-pause-circle"
onClick=${() => dispatch({ type: "pause" })}></i>
`}
</span>
`
}
const PresentationTimer = () => {
startAt: new Date(),
now: new Date(),
pause: false,
showButtons: false,
});
useEffect(() => {
const tid = setInterval(() => dispatch({ type: "tick" }), 500);
return () => clearInterval(tid);
return html`
<ins class="presentation-timer"
onMouseEnter=${() => dispatch({ type: "buttons", show: true })}
onMouseLeave=${() => dispatch({ type: "buttons", show: false })}>
<span>${formatTimer(state)}</span>
<${TimerButtons} state=${state} dispatch=${dispatch} />
</ins>
`
};
// start
(() => {
const startButton = Array.from(document.querySelectorAll('.dropdown-menu a'))
.filter(e => e.textContent.match(/Start presentation/))0; startButton.addEventListener('click', e => {
render(h(PresentationTimer, null, null), document.body);
});
})()
1回起動すると scprabox 内の遷移は SPA だからずっと出っぱなしになるけどまあいいや
プレゼンからちょっと出て別のページ見たりしたいし、消したきゃリロードしろ!!