A stopwatch app constructed using d3-component.
The following components are defined and used to construct the app:
app
buttonPanel
resetButton
reset
action when clicked. startStopButton
button
resetButton
and startStopButton
. timeDisplay
Built with blockbuilder.org
forked from curran's block: Posts with d3-component
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="https://unpkg.com/d3@4"></script>
<script src="https://unpkg.com/d3-component@1"></script>
<style>
body {
text-align: center;
margin-top: 75px;
}
.time-display {
color: #3d3d3d;
font-size: 10em;
font-family: mono;
cursor: default;
}
button {
background-color: #7c7c7c;
border: solid 3px #7c7c7c;
border-radius: 11px;
color: white;
padding: 20px 60px;
margin: 20px;
font-size: 58px;
cursor: pointer;
}
button:hover {
border: solid 3px black;
}
button:focus {
outline: none;
}
</style>
</head>
<body>
<script>
// This function formats the stopwatch time.
var stopwatchFormat = (function (){
var twoDigits = d3.format("02.0f");
return function (milliseconds){
var centiseconds = Math.floor(milliseconds / 10),
centisecond = centiseconds % 100,
seconds = Math.floor(centiseconds /100),
second = seconds % 60,
minutes = Math.floor(seconds / 60),
minute = minutes % 60,
hours = Math.floor(minutes / 60);
return [
hours >= 1 ? hours + ":" : "",
minutes >= 1 ? (
(hours >= 1 ? twoDigits(minute) : minute) + ":"
) : "",
(minutes >= 1 ? twoDigits(second) : second),
hours < 1 ? ":" + twoDigits(centisecond) : "",
].join("").replace(/0/g, "O"); // Don't show the dot in the zeros.
}
}());
// A component that renders the formatted stopwatch time.
var timeDisplay = d3.component("div", "time-display")
.update(function (d){
d3.select(this).text(stopwatchFormat(d.totalTime));
});
// A generic Button component.
var button = d3.component("button")
.update(function (d){
d3.select(this)
.text(d.text)
.on("click", d.onClick);
});
// The button that either starts or stops (pauses) the stopwatch,
//depending on whether the stopwatch is running or not.
var startStopButton = d3.component("span")
.update(function (d){
var stopped = d.mode === "stopped";
d3.select(this).call(button, {
text: stopped ? "Start" : "Stop",
onClick: stopped ? d.actions.start : d.actions.stop
});
});
// The reset button that stops and resets the stopwatch.
var resetButton = d3.component("span")
.update(function (d){
d3.select(this).call(button, {
text: "Reset",
onClick: d.actions.reset
});
});
// The panel that contains the two buttons.
var buttonPanel = d3.component("div")
.update(function (d){
d3.select(this)
.call(startStopButton, d)
.call(resetButton, d);
});
// The top-level app component.
var app = d3.component("div")
.update(function (d){
d3.select(this)
.call(timeDisplay, d)
.call(buttonPanel, d);
});
// DIY Redux - would not recommend.
// TODO update this example to use Redux proper.
function main(){
var state = {};
function setState(newState){
if(typeof newState === "function"){
newState = newState(state);
}
Object.assign(state, newState);
d3.select("body").call(app, state);
}
var actions = {
start: function (){
setState(function (state){
return {
mode: "started",
timer: d3.timer(function (elapsed){
setState({
totalTime: state.stopTime + elapsed
});
})
};
});
},
stop: function (){
setState(function (state){
state.timer.stop();
return {
mode: "stopped",
stopTime: state.totalTime
};
});
},
reset: function (){
setState(function (state){
state.timer && state.timer.stop();
return {
mode: "stopped",
totalTime: 0,
stopTime: 0
// Test cases for formatter:
//stopTime: 58000 // 58 seconds
//stopTime: 60000 * 59 + 58000 // 59 minutes 58 seconds
};
});
}
};
setState({
actions: actions
});
actions.reset();
}
main();
</script>
</body>
https://unpkg.com/d3@4
https://unpkg.com/d3-component@1