Last active
September 19, 2018 01:52
-
-
Save nil2013/d7efe95733b063932e09eeb745b03eed to your computer and use it in GitHub Desktop.
予定表を作成するSPA
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<title>スケジュール表</title> | |
<style> | |
div.row { | |
position: relative; | |
outline: 1px solid #aaa; | |
} | |
div.row:nth-child(even) { | |
background: #eee; | |
} | |
div.row:nth-child(odd) { | |
background: #fff; | |
} | |
div.row>div.col { | |
position: absolute; | |
border: 1px solid #aaa; | |
border-radius: 5px; | |
box-sizing: border-box; | |
width: 90%; | |
margin-left:5%; | |
} | |
div.row>div.col.h { | |
position: absolute; | |
border: none; | |
border-top: 1px dashed #000; | |
border-radius: 0; | |
width: 100%; | |
height: 7.5%; | |
text-align: center; | |
} | |
div.row>div.col.h:last-child { | |
border-bottom: 1px dashed #000; | |
} | |
div.row>div.col:nth-child(odd) { | |
background: #eee; | |
} | |
div.row>div.col:nth-child(even) { | |
background: #fff; | |
} | |
div.time > span.from:after { | |
content: "~"; | |
} | |
div.row_header { | |
position: relative; | |
opacity: 0.5; | |
color: #444; | |
top: 0; | |
text-align: center; | |
} | |
@media print { | |
a#copy_json, | |
button, | |
form#schedule_form { | |
display: none !important; | |
} | |
div.row>div.col { | |
border: 1px solid #888; | |
padding: 3px; | |
} | |
div.row > div.col .title { | |
text-decoration: underline; | |
font-size:0.9em; | |
} | |
div.row_header { | |
opacity: 1; | |
} | |
} | |
</style> | |
<script type="application/javascript"> | |
const defaultSchedules = []; | |
Element.prototype.removeChildren = function() { | |
while (this.firstChild) this.removeChild(this.firstChild); | |
return this; | |
}; | |
</script> | |
</head> | |
<body> | |
<div id="schedule_table" class="pure-g" style="height:100%"> | |
<div class="pure-u-1-8 row"> | |
<!-- 08:00-21:00 --> | |
<div class="col h" style="top: 1.0%;">08:00</div> | |
<div class="col h" style="top: 8.5%;">09:00</div> | |
<div class="col h" style="top: 16.0%;">10:00</div> | |
<div class="col h" style="top: 23.5%;">11:00</div> | |
<div class="col h" style="top: 31.0%;">12:00</div> | |
<div class="col h" style="top: 38.5%;">13:00</div> | |
<div class="col h" style="top: 46.0%;">14:00</div> | |
<div class="col h" style="top: 53.5%;">15:00</div> | |
<div class="col h" style="top: 61.0%;">16:00</div> | |
<div class="col h" style="top: 68.5%;">17:00</div> | |
<div class="col h" style="top: 76.0%;">18:00</div> | |
<div class="col h" style="top: 83.5%;">19:00</div> | |
<div class="col h" style="top: 91.0%;">20:00</div> | |
</div> | |
<div id="月曜" class="pure-u-1-8 row"></div> | |
<div id="火曜" class="pure-u-1-8 row"></div> | |
<div id="水曜" class="pure-u-1-8 row"></div> | |
<div id="木曜" class="pure-u-1-8 row"></div> | |
<div id="金曜" class="pure-u-1-8 row"></div> | |
<div id="土曜" class="pure-u-1-8 row"></div> | |
<div id="日曜" class="pure-u-1-8 row"></div> | |
</div> | |
<div style="padding: 10px;" id="forms"> | |
<h2>Schedule List <a href="#" id="copy_json"><small style="margin-left: 20px">copy to clipboard</small></a></h2> | |
<div class="pure-g" style="text-align:center;"> | |
<div class="pure-u-1-6">Date</div> | |
<div class="pure-u-1-3">Title</div> | |
<div class="pure-u-1-6">From</div> | |
<div class="pure-u-1-6">To</div> | |
</div> | |
<div id="schedule_queue"> | |
</div> | |
<form class="pure-form pure-form-stacked" id="schedule_form"> | |
<fieldset> | |
<div class="pure-g"> | |
<div class="pure-u-1-6"> | |
<!-- Row --> | |
<select class="pure-u-22-24" name="row"></select> | |
</div> | |
<div class="pure-u-1-3"> | |
<!-- Title --> | |
<input class="pure-u-23-24" type="text" name="title" placeholder="Title"> | |
</div> | |
<div class="pure-u-1-6"> | |
<!-- From --> | |
<div class="pure-u-3-8"><input class="pure-input-1" type="number" name="from.h" /></div> | |
<div class="pure-u-1-8" style="text-align:center;"> : </div> | |
<div class="pure-u-3-8"><input class="pure-input-1" type="number" name="from.m" /></div> | |
</div> | |
<div class="pure-u-1-6"> | |
<!-- To --> | |
<div class="pure-u-3-8"><input class="pure-input-1" type="number" name="to.h" /></div> | |
<div class="pure-u-1-8" style="text-align:center;"> : </div> | |
<div class="pure-u-3-8"><input class="pure-input-1" type="number" name="to.m" /></div> | |
</div> | |
<div class="pure-u-1-6"> | |
<!-- Submit --> | |
<button type="submit" class="pure-button pure-button-primary">Add</button> | |
</div> | |
</div> | |
</fieldset> | |
</form> | |
</div> | |
</body> | |
<script> | |
const zeroPadding = function(num, length) { | |
if(num < 10**length) { | |
var padding = ""; | |
for(var i = 0; i < length; i ++) { padding += "0"; } | |
return (padding + num).slice(-length); | |
} else { | |
return num; | |
} | |
} | |
const min = 99.0 / 780; | |
const queue = document.getElementById("schedule_queue"); | |
// console.log(`${min}%/min`); | |
const addScheduleDom = function(s) { | |
console.log(s); | |
const title = document.createElement("div"); | |
title.innerText = s.title; | |
title.classList.add("title"); | |
const from = document.createElement("span"); | |
from.innerText = `${s.from.h}:${zeroPadding(s.from.m, 2)}`; | |
const to = document.createElement("span"); | |
to.innerText = `${s.to.h}:${zeroPadding(s.to.m, 2)}`; | |
const time = document.createElement("div"); | |
time.appendChild(from).classList.add("from"); | |
time.appendChild(to); | |
time.classList.add("time"); | |
const column = document.createElement('div'); | |
column.classList.add("col"); | |
const start = (s.from.h - 8) * 60 + s.from.m; | |
const span = (s.to.m - s.from.m) + ((s.to.h - s.from.h) * 60); | |
console.log(`${s.from.h}:${s.from.m} ~ ${s.to.h}:${s.to.m}, start: ${start}, span: ${span}`); | |
column.style.top = `${1 + start * min}%`; | |
column.style.height = `${span * min}%`; | |
// column.innerText = s.title; | |
column.appendChild(title); | |
column.appendChild(time); | |
console.log(column); | |
document.querySelector(`#${s.row}`).appendChild(column); | |
} | |
document.querySelectorAll("div#schedule_table > div").forEach(div => { | |
if (div.id != undefined && div.id != "") { | |
const header = document.createElement('div'); | |
header.classList.add("row_header"); | |
header.innerText = div.id; | |
div.appendChild(header); | |
} | |
}); | |
document.querySelectorAll("select[name='row']").forEach(sel => { | |
Array.prototype.slice.call(document.querySelectorAll("div.row")).forEach(r => { | |
if (r.id && r.id != "") { | |
const opt = document.createElement('option'); | |
opt.innerText = r.id; | |
opt.value = r.id; | |
sel.appendChild(opt); | |
} | |
}) | |
}); | |
const parse = function(form) { | |
const map = {}; | |
Array.prototype.slice.call(form.elements).forEach(elem => { | |
if (elem && elem.name && !elem.name.isEmpty) { | |
map[elem.name] = elem.value; | |
} | |
}); | |
return map; | |
}; | |
const createNewForm = function(from) { | |
const newForm = document.importNode(from, true); | |
newForm.id = ""; | |
const btn = newForm.querySelector("button"); | |
btn.innerText = "Update"; | |
btn.onclick = function() { | |
updateSchedule(); | |
return false | |
}; | |
return newForm; | |
} | |
const pressAddSchedule = function(ev) { | |
const form = this; | |
const map = parse(form); | |
// console.log(map); | |
const newForm = createNewForm(form); | |
newForm.querySelector(`option[value='${map.row}']`).selected = "selected"; | |
queue.appendChild(newForm); | |
document.body.scrollTop = document.body.scrollHeight; | |
Array.prototype.slice.call(form.elements).forEach(elem => elem.value = ""); | |
form.querySelector("select").focus(); | |
updateSchedule(); | |
return false; | |
}; | |
const parseJson = (json => addScheduleAll(JSON.parse(json))); | |
const addScheduleAll = function(schedules) { | |
queue.removeChildren(); | |
schedules.forEach(entry => { | |
const newForm = createNewForm(document.getElementById("schedule_form")); | |
newForm.querySelector(`option[value='${entry.row}']`).selected = "selected"; | |
newForm.querySelector(`input[name='title']`).value = entry.title; | |
newForm.querySelector(`input[name='from.h']`).value = entry.from.h; | |
newForm.querySelector(`input[name='from.m']`).value = entry.from.m; | |
newForm.querySelector(`input[name='to.h']`).value = entry.to.h; | |
newForm.querySelector(`input[name='to.m']`).value = entry.to.m; | |
queue.appendChild(newForm); | |
}); | |
updateSchedule(); | |
} | |
const getSchedules = function() { | |
return Array.prototype.slice.call(queue.children).filter(elem => elem.tagName.toLowerCase() == "form").map(parse).map(data => { | |
return { | |
row: data.row, | |
from: { | |
h: parseInt(data["from.h"]), | |
m: parseInt(data["from.m"]) | |
}, | |
to: { | |
h: parseInt(data["to.h"]), | |
m: parseInt(data["to.m"]) | |
}, | |
title: data.title | |
}; | |
}); | |
} | |
const updateSchedule = function() { | |
document.querySelectorAll("schedule_table > div").forEach(row => { | |
if (row && row.id && !row.id.isEmpty) { | |
row.removeChildren(); | |
} | |
}); | |
console.log("json: " + JSON.stringify(getSchedules())); | |
getSchedules().forEach(addScheduleDom); | |
} | |
const throwToClipboard = function(text) { | |
var input = document.createElement('input'); | |
input.setAttribute('id', 'copyinput'); | |
document.body.appendChild(input); | |
input.value = text; | |
input.select(); | |
document.execCommand('copy'); | |
document.body.removeChild(input); | |
}; | |
const copyJson = function() { | |
const json = JSON.stringify(getSchedules()); | |
// コピー | |
throwToClipboard(json); | |
return false; | |
} | |
document.querySelector("a#copy_json").onclick = copyJson; | |
document.querySelectorAll("form").forEach(form => form.onsubmit = pressAddSchedule); | |
addScheduleAll(defaultSchedules); | |
</script> | |
<link rel="stylesheet" href="https://unpkg.com/[email protected]/build/pure-min.css" integrity="sha384-nn4HPE8lTHyVtfCBi5yW9d20FjT8BJwUXyWZT9InLYax14RDjBj46LmSztkmNP9w" crossorigin="anonymous"> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment