Adrian Heine
2 years ago
3 changed files with 260 additions and 262 deletions
@ -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 |
||||
|
} |
||||
|
}) |
@ -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 |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue