Skip to content

Instantly share code, notes, and snippets.

@grayrest
Created April 12, 2016 18:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save grayrest/f0575e6d2ddc36457a061dd5e526d9ad to your computer and use it in GitHub Desktop.
Save grayrest/f0575e6d2ddc36457a061dd5e526d9ad to your computer and use it in GitHub Desktop.
Deku Uptime Boxes
<!doctype html>
<html>
<head>
<title>Uptime Boxes</title>
<style type="text/css">
html, body {
margin: 0;
padding: 0;
font-family: sans-serif;
}
#fps {
position: fixed;
top: 0px;
right: 0px;
padding: 32px;
font-size: 32px;
text-align: right;
}
* {
box-sizing: border-box;
}
.server-uptime {
display: block;
overflow: hidden;
margin: 0 auto;
width: 50%;
}
.server-uptime + .server-uptime {
margin: 20px auto 0 auto;
border-top: 1px solid #999;
}
.days {
display: flex;
flex-direction: row;
flex-flow: wrap;
}
.uptime-day {
display: flex;
}
span.uptime-day-status {
width: 10px;
height: 10px;
margin: 1px;
}
.hover {
display: none;
}
.uptime-day-status:hover + .hover {
display: flex;
position: absolute;
margin-top: -35px;
margin-left: -30px;
border-radius: 4px;
color: #eee;
background-color: #333;
padding: 10px;
font-size: 11px;
}
</style>
</head>
<body>
<button id="playpause" onclick='togglePlay()'>Play</button>
<div id="output"></div>
<script src="../build/bundle.js"></script>
</body>
</html>
/** @jsx element */
import {element, createApp} from 'deku';
import {createStore} from 'redux';
function exponentialMovingAverage(alpha) {
return (val, next) => val ? val + alpha * (next - val) : next;
}
function generateServer(name) {
let days = [];
for (let i=0; i<=364; i++) {
let up = Math.random() > 0.2;
days.push({ number: i, up });
}
return { name, days };
}
function updateServer(server) {
for (let i=0; i < 365; i++) {
server.days[i].up = Math.random() > 0.2;
}
return server;
}
function generateServers() {
return [
generateServer("Stefan's Server"),
generateServer("Godfrey's Server"),
generateServer("Yehuda's Server")
]
}
let reducer = (state, act) => {
if (!state) {
state = {
fps: 0,
servers: generateServers()
};
}
if (act.type === 'render') {
state.fps = act.fps;
state.servers.forEach(updateServer);
}
return state;
}
let UptimeDay = {
render({props: {day}}, children, dispatch) {
let color = `background-color: ${day.up ? '#8cc665' : '#ccc'}`;
let memo = day.up ? 'Servers operational!' : 'Red alert!';
return <div class="uptime-day">
<span class="uptime-day-status" style={color}/>
<span class="hover">{day.number}: {memo}</span>
</div>
}
}
let ServerUptime = {
render({props: {name, days}, children, dispatch}) {
let upDays = days.reduce((acc, day) => acc + (day.up ? 1 : 0), 0);
let {maxStreak} = days.reduce((acc, day) => {
if (day.up) {
acc.streak += 1;
acc.maxStreak = Math.max(acc.streak, acc.maxStreak);
} else {
acc.streak = 0;
}
return acc;
}, {maxStreak: 0, streak: 0});
return <div class="server-uptime">
<h1>{name}</h1>
<h2>{upDays} Days Up</h2>
<h2>Biggest Streak: {maxStreak}</h2>
<div class="days">
{days.map(day => <UptimeDay day={day}/>)}
</div>
</div>
}
}
let Shell = {
render({props: {fps, servers}}, context, children, dispatch) {
return <div id="uptime-boxes">
<div id="fps">{fps} FPS</div>
{servers.map(server => <ServerUptime name={server.name} days={server.days}/>)}
</div>
}
}
let store = createStore(reducer);
let render = createApp(document.getElementById('output'), store.dispatch);
// begin hacky benchmarking stuff
let clear = null;
let lastFrame = 0;
let started = false;
let fps = 0;
let ema = exponentialMovingAverage(2/121);
function rafCallback() {
let thisFrame = performance.now();
let state = store.getState();
if (lastFrame) {
fps = ema(fps, (1000 / (thisFrame - lastFrame)))
}
store.dispatch({type: 'render', fps: Math.round(fps)});
lastFrame = thisFrame;
clear = requestAnimationFrame(rafCallback);
}
window.togglePlay = function () {
if (started) {
window['playpause'].innerHTML = "Play";
cancelAnimationFrame(clear);
clear = null;
fps = 0;
lastFrame = 0;
started = false;
} else {
window['playpause'].innerHTML = "Pause";
started = true;
lastFrame = null;
fps = 0;
clear = requestAnimationFrame(rafCallback);
}
}
store.subscribe(() => {
let state = store.getState();
render(<Shell fps={state.fps} servers={state.servers}/>, {ema});
});
store.dispatch({type:'render', fps: 0});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment