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