diff --git a/index.php b/index.php index ca9a112..bd21837 100644 --- a/index.php +++ b/index.php @@ -75,180 +75,7 @@ function print_month($start) { Soli-Bus-Fahrten nach Lützerath - +

Soli-Bus-Fahrten von Berlin nach Lützerath

@@ -277,94 +104,7 @@ print_month($month_end); ?> - +

Tag X

Kontaktmöglichkeiten

diff --git a/script.js b/script.js new file mode 100644 index 0000000..637366a --- /dev/null +++ b/script.js @@ -0,0 +1,84 @@ +"use strict" + +const months = document.getElementById('months') +let selecting = null // { origin, className, range } +let is_select = false + +months.addEventListener("pointerdown", event => { + selecting = null + is_select = false + for (const elem of document.querySelectorAll(".selecting, .unselecting")) { + elem.className = "" + } + + if (event.buttons !== 1) return + let target = event.target + if (target.nodeType == 3) target = target.parentNode + if (target.tagName !== "LABEL") return + + event.preventDefault() + + const className = target.className = document.getElementById(target.htmlFor).checked ? "unselecting" : "selecting" + selecting = { + origin: target, + className, + range: [ target ] + } +}) + +months.addEventListener('pointermove', event => { + if (selecting === null) return + event.preventDefault() + is_select = true + + const range = document.createRange() + range.selectNode(selecting.origin) + if (range.comparePoint(event.target, 0) > 0) range.setEndAfter(event.target) + else range.setStartBefore(event.target) + + const treeWalker = document.createTreeWalker( + range.commonAncestorContainer, + NodeFilter.SHOW_ELEMENT, + { acceptNode: (node) => node.nodeName.toLowerCase() === 'label' ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP } + ) + + const new_range = [] + let currentNode + while (currentNode = treeWalker.nextNode()) { + if (range.intersectsNode(currentNode)) { + new_range.push(currentNode) + const idx = selecting.range.indexOf(currentNode) + if (idx !== -1) selecting.range.splice(idx, 1) + else currentNode.className = selecting.className + } + } + for (const elem of selecting.range) { + elem.className = "" + } + selecting.range = new_range +}) + +document.addEventListener('pointercancel', event => { + if (selecting === null) return + for (const elem of selecting.range) { + elem.className = "" + } + selecting = null + is_select = false +}) + +document.addEventListener('pointerup', event => { + if (selecting === null) return + event.preventDefault() + for (const elem of selecting.range) { + if (is_select) document.getElementById(elem.htmlFor).checked = selecting.className == "selecting" + elem.className = "" + } + selecting = null +}) +months.addEventListener('click', event => { + if (is_select) { + event.preventDefault() + is_select = false + } +}) diff --git a/style.css b/style.css new file mode 100644 index 0000000..74b4887 --- /dev/null +++ b/style.css @@ -0,0 +1,174 @@ +/* LAYOUT */ +body { + margin: 0 +} +footer { + border-top: 0.3em dashed #722CA2; + padding: 1em 1em 0 1em; + background: #f1cc37; + color: #5A4B0B; + padding: 1em 0 +} +main { + background: #FFE98E; + padding: 1em 0 +} +header { + border-bottom: 0.3em dashed #722CA2; + background: #f1cc37; + color: #5A4B0B; + padding: 1em 0 +} +h1 { + margin: 0 +} +footer > *, +header > *, +main > * { + margin: 0 auto; + max-width: 80rem; + padding: 0 1rem +} + +/* CALENDAR */ +.month { + display: inline-block; + vertical-align: top; + margin-right: 2em; + border-spacing: 0 +} +.month tbody { + border-radius: 0.3em; + color: #5A4B0B; + box-shadow: .4em .5em 0 0 RGBA(114, 44, 162,0.5) +} +.month th:first-of-type { + border-top-left-radius: 0.3em +} +.month th:last-of-type { + border-top-right-radius: 0.3em +} +.month tr > :last-of-type { + border-right-width: 1px +} +.month tr:last-of-type td:first-of-type { + border-bottom-left-radius: 0.3em +} +.month tr:last-of-type td:last-of-type { + border-bottom-right-radius: 0.3em +} +.month tr:last-of-type td { + border-bottom-width: 1px +} + +/* date in calendar */ +.month th, .month td { + width: 3.5em; + height: 3.5em; + border: 1px solid #CBC19A; + border-width: 1px 0 0 1px; + text-align: center; + padding: 0; + background: white; + box-sizing: border-box +} +.month input[type=checkbox] { + appearance: none; + position: absolute +} +.month label { + display: block; + line-height: 3.5em +} +.month :focus + label { + border: 1px solid #722CA2; + margin: -1px +} +:checked + label:not(.unselecting), +.selecting { + background: #f1cc37 +} +.invalid { + color: #CBC19A +} + +/* calendar on smaller screens */ +@media only screen and (max-width: 55em) { + .month { + margin-right: 1.5vw + } + .month th, .month td { + width: 6vw + } +} +@media only screen and (max-width: 40em) { + .month { + margin: 0 auto; + display: table + } + .month th, .month td { + width: 3.5em + } +} + +/* events in calendar */ +.event { + position: relative; + top: -3.5em; + right: -0.9em; + line-height: initial; + display: block +} +.event::before { + content: "★"; + position: absolute; + padding: 0.5em; + top: -0.5em; + right: 0.5em +} +.event-content { + visibility: hidden; + position: absolute; + background: white; + top: 0.4em; + left: 1.8em; + padding: 0.4em; + border: 1px solid #CBC19A; + border-radius: 0.2em; + box-shadow: .2em .25em 0 0 RGBA(114, 44, 162,0.5); + z-index: 1 +} +.month :focus + label .event-content, +.month td:hover .event-content, +.month td:active .event-content { + visibility: initial +} + +/* OTHER FORM ELEMENTS */ +.row { + display: block; + margin: 1em 0 +} +.row > input, +.row > select { + padding: 0.5rem; + margin-right: 1rem; + width: 12rem; + box-sizing: border-box; + border: 1px solid #5A4B0B; + border-radius: .3em; + box-shadow: .4em .5em 0 0 RGBA(114, 44, 162,0.5) +} +.row > input[type="number"] { + margin-left: 8.5rem; + width: 3.5rem +} +.row > input[type="submit"] { + background: #f1cc37 +} +.row > input[type="submit"]:active { + background: white +} +.row :focus-visible { + outline: 0.2em #722CA2 solid +}