You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

84 lines
2.3 KiB

  1. "use strict"
  2. const months = document.getElementById('months')
  3. let selecting = null // { origin, className, range }
  4. let is_select = false
  5. months.addEventListener("pointerdown", event => {
  6. selecting = null
  7. is_select = false
  8. for (const elem of document.querySelectorAll(".selecting, .unselecting")) {
  9. elem.className = ""
  10. }
  11. if (event.buttons !== 1) return
  12. let target = event.target
  13. if (target.nodeType == 3) target = target.parentNode
  14. if (target.tagName !== "LABEL") return
  15. event.preventDefault()
  16. const className = target.className = document.getElementById(target.htmlFor).checked ? "unselecting" : "selecting"
  17. selecting = {
  18. origin: target,
  19. className,
  20. range: [ target ]
  21. }
  22. })
  23. months.addEventListener('pointermove', event => {
  24. if (selecting === null) return
  25. event.preventDefault()
  26. is_select = true
  27. const range = document.createRange()
  28. range.selectNode(selecting.origin)
  29. if (range.comparePoint(event.target, 0) > 0) range.setEndAfter(event.target)
  30. else range.setStartBefore(event.target)
  31. const treeWalker = document.createTreeWalker(
  32. range.commonAncestorContainer,
  33. NodeFilter.SHOW_ELEMENT,
  34. { acceptNode: (node) => node.nodeName.toLowerCase() === 'label' ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP }
  35. )
  36. const new_range = []
  37. let currentNode
  38. while (currentNode = treeWalker.nextNode()) {
  39. if (range.intersectsNode(currentNode)) {
  40. new_range.push(currentNode)
  41. const idx = selecting.range.indexOf(currentNode)
  42. if (idx !== -1) selecting.range.splice(idx, 1)
  43. else currentNode.className = selecting.className
  44. }
  45. }
  46. for (const elem of selecting.range) {
  47. elem.className = ""
  48. }
  49. selecting.range = new_range
  50. })
  51. document.addEventListener('pointercancel', event => {
  52. if (selecting === null) return
  53. for (const elem of selecting.range) {
  54. elem.className = ""
  55. }
  56. selecting = null
  57. is_select = false
  58. })
  59. document.addEventListener('pointerup', event => {
  60. if (selecting === null) return
  61. event.preventDefault()
  62. for (const elem of selecting.range) {
  63. if (is_select) document.getElementById(elem.htmlFor).checked = selecting.className == "selecting"
  64. elem.className = ""
  65. }
  66. selecting = null
  67. })
  68. months.addEventListener('click', event => {
  69. if (is_select) {
  70. event.preventDefault()
  71. is_select = false
  72. }
  73. })