@fullcalendar/core 6.1.15 → 7.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/index.cjs +48 -110
  2. package/index.d.ts +2 -1
  3. package/index.global.js +1558 -1794
  4. package/index.global.min.js +2 -2
  5. package/index.js +51 -113
  6. package/internal-common.cjs +1919 -2093
  7. package/internal-common.d.ts +176 -288
  8. package/internal-common.js +1905 -2073
  9. package/internal.cjs +16 -22
  10. package/internal.d.ts +2 -1
  11. package/internal.js +1 -1
  12. package/locales/af.global.js +1 -1
  13. package/locales/af.global.min.js +1 -1
  14. package/locales/ar-dz.global.js +1 -1
  15. package/locales/ar-dz.global.min.js +1 -1
  16. package/locales/ar-kw.global.js +1 -1
  17. package/locales/ar-kw.global.min.js +1 -1
  18. package/locales/ar-ly.global.js +1 -1
  19. package/locales/ar-ly.global.min.js +1 -1
  20. package/locales/ar-ma.global.js +1 -1
  21. package/locales/ar-ma.global.min.js +1 -1
  22. package/locales/ar-sa.global.js +1 -1
  23. package/locales/ar-sa.global.min.js +1 -1
  24. package/locales/ar-tn.global.js +1 -1
  25. package/locales/ar-tn.global.min.js +1 -1
  26. package/locales/ar.global.js +1 -1
  27. package/locales/ar.global.min.js +1 -1
  28. package/locales/az.global.js +1 -1
  29. package/locales/az.global.min.js +1 -1
  30. package/locales/bg.global.js +1 -1
  31. package/locales/bg.global.min.js +1 -1
  32. package/locales/bn.global.js +1 -1
  33. package/locales/bn.global.min.js +1 -1
  34. package/locales/bs.global.js +1 -1
  35. package/locales/bs.global.min.js +1 -1
  36. package/locales/ca.global.js +1 -1
  37. package/locales/ca.global.min.js +1 -1
  38. package/locales/cs.global.js +1 -1
  39. package/locales/cs.global.min.js +1 -1
  40. package/locales/cy.global.js +1 -1
  41. package/locales/cy.global.min.js +1 -1
  42. package/locales/da.global.js +1 -1
  43. package/locales/da.global.min.js +1 -1
  44. package/locales/de-at.global.js +1 -1
  45. package/locales/de-at.global.min.js +1 -1
  46. package/locales/de.global.js +1 -1
  47. package/locales/de.global.min.js +1 -1
  48. package/locales/el.global.js +1 -1
  49. package/locales/el.global.min.js +1 -1
  50. package/locales/en-au.global.js +1 -1
  51. package/locales/en-au.global.min.js +1 -1
  52. package/locales/en-gb.global.js +1 -1
  53. package/locales/en-gb.global.min.js +1 -1
  54. package/locales/en-nz.global.js +1 -1
  55. package/locales/en-nz.global.min.js +1 -1
  56. package/locales/eo.global.js +1 -1
  57. package/locales/eo.global.min.js +1 -1
  58. package/locales/es-us.global.js +1 -1
  59. package/locales/es-us.global.min.js +1 -1
  60. package/locales/es.global.js +1 -1
  61. package/locales/es.global.min.js +1 -1
  62. package/locales/et.global.js +1 -1
  63. package/locales/et.global.min.js +1 -1
  64. package/locales/eu.global.js +1 -1
  65. package/locales/eu.global.min.js +1 -1
  66. package/locales/fa.global.js +1 -1
  67. package/locales/fa.global.min.js +1 -1
  68. package/locales/fi.global.js +1 -1
  69. package/locales/fi.global.min.js +1 -1
  70. package/locales/fr-ca.global.js +1 -1
  71. package/locales/fr-ca.global.min.js +1 -1
  72. package/locales/fr-ch.global.js +1 -1
  73. package/locales/fr-ch.global.min.js +1 -1
  74. package/locales/fr.global.js +1 -1
  75. package/locales/fr.global.min.js +1 -1
  76. package/locales/gl.global.js +1 -1
  77. package/locales/gl.global.min.js +1 -1
  78. package/locales/he.global.js +1 -1
  79. package/locales/he.global.min.js +1 -1
  80. package/locales/hi.global.js +1 -1
  81. package/locales/hi.global.min.js +1 -1
  82. package/locales/hr.global.js +1 -1
  83. package/locales/hr.global.min.js +1 -1
  84. package/locales/hu.global.js +1 -1
  85. package/locales/hu.global.min.js +1 -1
  86. package/locales/hy-am.global.js +1 -1
  87. package/locales/hy-am.global.min.js +1 -1
  88. package/locales/id.global.js +1 -1
  89. package/locales/id.global.min.js +1 -1
  90. package/locales/is.global.js +1 -1
  91. package/locales/is.global.min.js +1 -1
  92. package/locales/it.global.js +1 -1
  93. package/locales/it.global.min.js +1 -1
  94. package/locales/ja.global.js +1 -1
  95. package/locales/ja.global.min.js +1 -1
  96. package/locales/ka.global.js +1 -1
  97. package/locales/ka.global.min.js +1 -1
  98. package/locales/kk.global.js +1 -1
  99. package/locales/kk.global.min.js +1 -1
  100. package/locales/km.global.js +1 -1
  101. package/locales/km.global.min.js +1 -1
  102. package/locales/ko.global.js +1 -1
  103. package/locales/ko.global.min.js +1 -1
  104. package/locales/ku.global.js +1 -1
  105. package/locales/ku.global.min.js +1 -1
  106. package/locales/lb.global.js +1 -1
  107. package/locales/lb.global.min.js +1 -1
  108. package/locales/lt.global.js +1 -1
  109. package/locales/lt.global.min.js +1 -1
  110. package/locales/lv.global.js +1 -1
  111. package/locales/lv.global.min.js +1 -1
  112. package/locales/mk.global.js +1 -1
  113. package/locales/mk.global.min.js +1 -1
  114. package/locales/ms.global.js +1 -1
  115. package/locales/ms.global.min.js +1 -1
  116. package/locales/nb.global.js +1 -1
  117. package/locales/nb.global.min.js +1 -1
  118. package/locales/ne.global.js +1 -1
  119. package/locales/ne.global.min.js +1 -1
  120. package/locales/nl.global.js +1 -1
  121. package/locales/nl.global.min.js +1 -1
  122. package/locales/nn.global.js +1 -1
  123. package/locales/nn.global.min.js +1 -1
  124. package/locales/pl.global.js +1 -1
  125. package/locales/pl.global.min.js +1 -1
  126. package/locales/pt-br.global.js +1 -1
  127. package/locales/pt-br.global.min.js +1 -1
  128. package/locales/pt.global.js +1 -1
  129. package/locales/pt.global.min.js +1 -1
  130. package/locales/ro.global.js +1 -1
  131. package/locales/ro.global.min.js +1 -1
  132. package/locales/ru.global.js +1 -1
  133. package/locales/ru.global.min.js +1 -1
  134. package/locales/si-lk.global.js +1 -1
  135. package/locales/si-lk.global.min.js +1 -1
  136. package/locales/sk.global.js +1 -1
  137. package/locales/sk.global.min.js +1 -1
  138. package/locales/sl.global.js +1 -1
  139. package/locales/sl.global.min.js +1 -1
  140. package/locales/sm.global.js +1 -1
  141. package/locales/sm.global.min.js +1 -1
  142. package/locales/sq.global.js +1 -1
  143. package/locales/sq.global.min.js +1 -1
  144. package/locales/sr-cyrl.global.js +1 -1
  145. package/locales/sr-cyrl.global.min.js +1 -1
  146. package/locales/sr.global.js +1 -1
  147. package/locales/sr.global.min.js +1 -1
  148. package/locales/sv.global.js +1 -1
  149. package/locales/sv.global.min.js +1 -1
  150. package/locales/ta-in.global.js +1 -1
  151. package/locales/ta-in.global.min.js +1 -1
  152. package/locales/th.global.js +1 -1
  153. package/locales/th.global.min.js +1 -1
  154. package/locales/tr.global.js +1 -1
  155. package/locales/tr.global.min.js +1 -1
  156. package/locales/ug.global.js +1 -1
  157. package/locales/ug.global.min.js +1 -1
  158. package/locales/uk.global.js +1 -1
  159. package/locales/uk.global.min.js +1 -1
  160. package/locales/uz-cy.global.js +1 -1
  161. package/locales/uz-cy.global.min.js +1 -1
  162. package/locales/uz.global.js +1 -1
  163. package/locales/uz.global.min.js +1 -1
  164. package/locales/vi.global.js +1 -1
  165. package/locales/vi.global.min.js +1 -1
  166. package/locales/zh-cn.global.js +1 -1
  167. package/locales/zh-cn.global.min.js +1 -1
  168. package/locales/zh-tw.global.js +1 -1
  169. package/locales/zh-tw.global.min.js +1 -1
  170. package/locales-all.global.js +1 -1
  171. package/locales-all.global.min.js +1 -1
  172. package/package.json +2 -2
  173. package/preact.d.ts +6 -0
  174. package/preact.js +1 -1
@@ -103,7 +103,7 @@ if (typeof document !== 'undefined') {
103
103
  registerStylesRoot(document);
104
104
  }
105
105
 
106
- var css_248z = ":root{--fc-small-font-size:.85em;--fc-page-bg-color:#fff;--fc-neutral-bg-color:hsla(0,0%,82%,.3);--fc-neutral-text-color:grey;--fc-border-color:#ddd;--fc-button-text-color:#fff;--fc-button-bg-color:#2c3e50;--fc-button-border-color:#2c3e50;--fc-button-hover-bg-color:#1e2b37;--fc-button-hover-border-color:#1a252f;--fc-button-active-bg-color:#1a252f;--fc-button-active-border-color:#151e27;--fc-event-bg-color:#3788d8;--fc-event-border-color:#3788d8;--fc-event-text-color:#fff;--fc-event-selected-overlay-color:rgba(0,0,0,.25);--fc-more-link-bg-color:#d0d0d0;--fc-more-link-text-color:inherit;--fc-event-resizer-thickness:8px;--fc-event-resizer-dot-total-width:8px;--fc-event-resizer-dot-border-width:1px;--fc-non-business-color:hsla(0,0%,84%,.3);--fc-bg-event-color:#8fdf82;--fc-bg-event-opacity:0.3;--fc-highlight-color:rgba(188,232,241,.3);--fc-today-bg-color:rgba(255,220,40,.15);--fc-now-indicator-color:red}.fc-not-allowed,.fc-not-allowed .fc-event{cursor:not-allowed}.fc{display:flex;flex-direction:column;font-size:1em}.fc,.fc *,.fc :after,.fc :before{box-sizing:border-box}.fc table{border-collapse:collapse;border-spacing:0;font-size:1em}.fc th{text-align:center}.fc td,.fc th{padding:0;vertical-align:top}.fc a[data-navlink]{cursor:pointer}.fc a[data-navlink]:hover{text-decoration:underline}.fc-direction-ltr{direction:ltr;text-align:left}.fc-direction-rtl{direction:rtl;text-align:right}.fc-theme-standard td,.fc-theme-standard th{border:1px solid var(--fc-border-color)}.fc-liquid-hack td,.fc-liquid-hack th{position:relative}@font-face{font-family:fcicons;font-style:normal;font-weight:400;src:url(\"data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMg8SBfAAAAC8AAAAYGNtYXAXVtKNAAABHAAAAFRnYXNwAAAAEAAAAXAAAAAIZ2x5ZgYydxIAAAF4AAAFNGhlYWQUJ7cIAAAGrAAAADZoaGVhB20DzAAABuQAAAAkaG10eCIABhQAAAcIAAAALGxvY2ED4AU6AAAHNAAAABhtYXhwAA8AjAAAB0wAAAAgbmFtZXsr690AAAdsAAABhnBvc3QAAwAAAAAI9AAAACAAAwPAAZAABQAAApkCzAAAAI8CmQLMAAAB6wAzAQkAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADpBgPA/8AAQAPAAEAAAAABAAAAAAAAAAAAAAAgAAAAAAADAAAAAwAAABwAAQADAAAAHAADAAEAAAAcAAQAOAAAAAoACAACAAIAAQAg6Qb//f//AAAAAAAg6QD//f//AAH/4xcEAAMAAQAAAAAAAAAAAAAAAQAB//8ADwABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAAAAAAAAAIAADc5AQAAAAABAWIAjQKeAskAEwAAJSc3NjQnJiIHAQYUFwEWMjc2NCcCnuLiDQ0MJAz/AA0NAQAMJAwNDcni4gwjDQwM/wANIwz/AA0NDCMNAAAAAQFiAI0CngLJABMAACUBNjQnASYiBwYUHwEHBhQXFjI3AZ4BAA0N/wAMJAwNDeLiDQ0MJAyNAQAMIw0BAAwMDSMM4uINIwwNDQAAAAIA4gC3Ax4CngATACcAACUnNzY0JyYiDwEGFB8BFjI3NjQnISc3NjQnJiIPAQYUHwEWMjc2NCcB87e3DQ0MIw3VDQ3VDSMMDQ0BK7e3DQ0MJAzVDQ3VDCQMDQ3zuLcMJAwNDdUNIwzWDAwNIwy4twwkDA0N1Q0jDNYMDA0jDAAAAgDiALcDHgKeABMAJwAAJTc2NC8BJiIHBhQfAQcGFBcWMjchNzY0LwEmIgcGFB8BBwYUFxYyNwJJ1Q0N1Q0jDA0Nt7cNDQwjDf7V1Q0N1QwkDA0Nt7cNDQwkDLfWDCMN1Q0NDCQMt7gMIw0MDNYMIw3VDQ0MJAy3uAwjDQwMAAADAFUAAAOrA1UAMwBoAHcAABMiBgcOAQcOAQcOARURFBYXHgEXHgEXHgEzITI2Nz4BNz4BNz4BNRE0JicuAScuAScuASMFITIWFx4BFx4BFx4BFREUBgcOAQcOAQcOASMhIiYnLgEnLgEnLgE1ETQ2Nz4BNz4BNz4BMxMhMjY1NCYjISIGFRQWM9UNGAwLFQkJDgUFBQUFBQ4JCRULDBgNAlYNGAwLFQkJDgUFBQUFBQ4JCRULDBgN/aoCVgQIBAQHAwMFAQIBAQIBBQMDBwQECAT9qgQIBAQHAwMFAQIBAQIBBQMDBwQECASAAVYRGRkR/qoRGRkRA1UFBAUOCQkVDAsZDf2rDRkLDBUJCA4FBQUFBQUOCQgVDAsZDQJVDRkLDBUJCQ4FBAVVAgECBQMCBwQECAX9qwQJAwQHAwMFAQICAgIBBQMDBwQDCQQCVQUIBAQHAgMFAgEC/oAZEhEZGRESGQAAAAADAFUAAAOrA1UAMwBoAIkAABMiBgcOAQcOAQcOARURFBYXHgEXHgEXHgEzITI2Nz4BNz4BNz4BNRE0JicuAScuAScuASMFITIWFx4BFx4BFx4BFREUBgcOAQcOAQcOASMhIiYnLgEnLgEnLgE1ETQ2Nz4BNz4BNz4BMxMzFRQWMzI2PQEzMjY1NCYrATU0JiMiBh0BIyIGFRQWM9UNGAwLFQkJDgUFBQUFBQ4JCRULDBgNAlYNGAwLFQkJDgUFBQUFBQ4JCRULDBgN/aoCVgQIBAQHAwMFAQIBAQIBBQMDBwQECAT9qgQIBAQHAwMFAQIBAQIBBQMDBwQECASAgBkSEhmAERkZEYAZEhIZgBEZGREDVQUEBQ4JCRUMCxkN/asNGQsMFQkIDgUFBQUFBQ4JCBUMCxkNAlUNGQsMFQkJDgUEBVUCAQIFAwIHBAQIBf2rBAkDBAcDAwUBAgICAgEFAwMHBAMJBAJVBQgEBAcCAwUCAQL+gIASGRkSgBkSERmAEhkZEoAZERIZAAABAOIAjQMeAskAIAAAExcHBhQXFjI/ARcWMjc2NC8BNzY0JyYiDwEnJiIHBhQX4uLiDQ0MJAzi4gwkDA0N4uINDQwkDOLiDCQMDQ0CjeLiDSMMDQ3h4Q0NDCMN4uIMIw0MDOLiDAwNIwwAAAABAAAAAQAAa5n0y18PPPUACwQAAAAAANivOVsAAAAA2K85WwAAAAADqwNVAAAACAACAAAAAAAAAAEAAAPA/8AAAAQAAAAAAAOrAAEAAAAAAAAAAAAAAAAAAAALBAAAAAAAAAAAAAAAAgAAAAQAAWIEAAFiBAAA4gQAAOIEAABVBAAAVQQAAOIAAAAAAAoAFAAeAEQAagCqAOoBngJkApoAAQAAAAsAigADAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAA4ArgABAAAAAAABAAcAAAABAAAAAAACAAcAYAABAAAAAAADAAcANgABAAAAAAAEAAcAdQABAAAAAAAFAAsAFQABAAAAAAAGAAcASwABAAAAAAAKABoAigADAAEECQABAA4ABwADAAEECQACAA4AZwADAAEECQADAA4APQADAAEECQAEAA4AfAADAAEECQAFABYAIAADAAEECQAGAA4AUgADAAEECQAKADQApGZjaWNvbnMAZgBjAGkAYwBvAG4Ac1ZlcnNpb24gMS4wAFYAZQByAHMAaQBvAG4AIAAxAC4AMGZjaWNvbnMAZgBjAGkAYwBvAG4Ac2ZjaWNvbnMAZgBjAGkAYwBvAG4Ac1JlZ3VsYXIAUgBlAGcAdQBsAGEAcmZjaWNvbnMAZgBjAGkAYwBvAG4Ac0ZvbnQgZ2VuZXJhdGVkIGJ5IEljb01vb24uAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\") format(\"truetype\")}.fc-icon{speak:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;display:inline-block;font-family:fcicons!important;font-style:normal;font-variant:normal;font-weight:400;height:1em;line-height:1;text-align:center;text-transform:none;-moz-user-select:none;user-select:none;width:1em}.fc-icon-chevron-left:before{content:\"\\e900\"}.fc-icon-chevron-right:before{content:\"\\e901\"}.fc-icon-chevrons-left:before{content:\"\\e902\"}.fc-icon-chevrons-right:before{content:\"\\e903\"}.fc-icon-minus-square:before{content:\"\\e904\"}.fc-icon-plus-square:before{content:\"\\e905\"}.fc-icon-x:before{content:\"\\e906\"}.fc .fc-button{border-radius:0;font-family:inherit;font-size:inherit;line-height:inherit;margin:0;overflow:visible;text-transform:none}.fc .fc-button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}.fc .fc-button{-webkit-appearance:button}.fc .fc-button:not(:disabled){cursor:pointer}.fc .fc-button{background-color:transparent;border:1px solid transparent;border-radius:.25em;display:inline-block;font-size:1em;font-weight:400;line-height:1.5;padding:.4em .65em;text-align:center;-moz-user-select:none;user-select:none;vertical-align:middle}.fc .fc-button:hover{text-decoration:none}.fc .fc-button:focus{box-shadow:0 0 0 .2rem rgba(44,62,80,.25);outline:0}.fc .fc-button:disabled{opacity:.65}.fc .fc-button-primary{background-color:var(--fc-button-bg-color);border-color:var(--fc-button-border-color);color:var(--fc-button-text-color)}.fc .fc-button-primary:hover{background-color:var(--fc-button-hover-bg-color);border-color:var(--fc-button-hover-border-color);color:var(--fc-button-text-color)}.fc .fc-button-primary:disabled{background-color:var(--fc-button-bg-color);border-color:var(--fc-button-border-color);color:var(--fc-button-text-color)}.fc .fc-button-primary:focus{box-shadow:0 0 0 .2rem rgba(76,91,106,.5)}.fc .fc-button-primary:not(:disabled).fc-button-active,.fc .fc-button-primary:not(:disabled):active{background-color:var(--fc-button-active-bg-color);border-color:var(--fc-button-active-border-color);color:var(--fc-button-text-color)}.fc .fc-button-primary:not(:disabled).fc-button-active:focus,.fc .fc-button-primary:not(:disabled):active:focus{box-shadow:0 0 0 .2rem rgba(76,91,106,.5)}.fc .fc-button .fc-icon{font-size:1.5em;vertical-align:middle}.fc .fc-button-group{display:inline-flex;position:relative;vertical-align:middle}.fc .fc-button-group>.fc-button{flex:1 1 auto;position:relative}.fc .fc-button-group>.fc-button.fc-button-active,.fc .fc-button-group>.fc-button:active,.fc .fc-button-group>.fc-button:focus,.fc .fc-button-group>.fc-button:hover{z-index:1}.fc-direction-ltr .fc-button-group>.fc-button:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0;margin-left:-1px}.fc-direction-ltr .fc-button-group>.fc-button:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0}.fc-direction-rtl .fc-button-group>.fc-button:not(:first-child){border-bottom-right-radius:0;border-top-right-radius:0;margin-right:-1px}.fc-direction-rtl .fc-button-group>.fc-button:not(:last-child){border-bottom-left-radius:0;border-top-left-radius:0}.fc .fc-toolbar{align-items:center;display:flex;justify-content:space-between}.fc .fc-toolbar.fc-header-toolbar{margin-bottom:1.5em}.fc .fc-toolbar.fc-footer-toolbar{margin-top:1.5em}.fc .fc-toolbar-title{font-size:1.75em;margin:0}.fc-direction-ltr .fc-toolbar>*>:not(:first-child){margin-left:.75em}.fc-direction-rtl .fc-toolbar>*>:not(:first-child){margin-right:.75em}.fc-direction-rtl .fc-toolbar-ltr{flex-direction:row-reverse}.fc .fc-scroller{-webkit-overflow-scrolling:touch;position:relative}.fc .fc-scroller-liquid{height:100%}.fc .fc-scroller-liquid-absolute{bottom:0;left:0;position:absolute;right:0;top:0}.fc .fc-scroller-harness{direction:ltr;overflow:hidden;position:relative}.fc .fc-scroller-harness-liquid{height:100%}.fc-direction-rtl .fc-scroller-harness>.fc-scroller{direction:rtl}.fc-theme-standard .fc-scrollgrid{border:1px solid var(--fc-border-color)}.fc .fc-scrollgrid,.fc .fc-scrollgrid table{table-layout:fixed;width:100%}.fc .fc-scrollgrid table{border-left-style:hidden;border-right-style:hidden;border-top-style:hidden}.fc .fc-scrollgrid{border-bottom-width:0;border-collapse:separate;border-right-width:0}.fc .fc-scrollgrid-liquid{height:100%}.fc .fc-scrollgrid-section,.fc .fc-scrollgrid-section table,.fc .fc-scrollgrid-section>td{height:1px}.fc .fc-scrollgrid-section-liquid>td{height:100%}.fc .fc-scrollgrid-section>*{border-left-width:0;border-top-width:0}.fc .fc-scrollgrid-section-footer>*,.fc .fc-scrollgrid-section-header>*{border-bottom-width:0}.fc .fc-scrollgrid-section-body table,.fc .fc-scrollgrid-section-footer table{border-bottom-style:hidden}.fc .fc-scrollgrid-section-sticky>*{background:var(--fc-page-bg-color);position:sticky;z-index:3}.fc .fc-scrollgrid-section-header.fc-scrollgrid-section-sticky>*{top:0}.fc .fc-scrollgrid-section-footer.fc-scrollgrid-section-sticky>*{bottom:0}.fc .fc-scrollgrid-sticky-shim{height:1px;margin-bottom:-1px}.fc-sticky{position:sticky}.fc .fc-view-harness{flex-grow:1;position:relative}.fc .fc-view-harness-active>.fc-view{bottom:0;left:0;position:absolute;right:0;top:0}.fc .fc-col-header-cell-cushion{display:inline-block;padding:2px 4px}.fc .fc-bg-event,.fc .fc-highlight,.fc .fc-non-business{bottom:0;left:0;position:absolute;right:0;top:0}.fc .fc-non-business{background:var(--fc-non-business-color)}.fc .fc-bg-event{background:var(--fc-bg-event-color);opacity:var(--fc-bg-event-opacity)}.fc .fc-bg-event .fc-event-title{font-size:var(--fc-small-font-size);font-style:italic;margin:.5em}.fc .fc-highlight{background:var(--fc-highlight-color)}.fc .fc-cell-shaded,.fc .fc-day-disabled{background:var(--fc-neutral-bg-color)}a.fc-event,a.fc-event:hover{text-decoration:none}.fc-event.fc-event-draggable,.fc-event[href]{cursor:pointer}.fc-event .fc-event-main{position:relative;z-index:2}.fc-event-dragging:not(.fc-event-selected){opacity:.75}.fc-event-dragging.fc-event-selected{box-shadow:0 2px 7px rgba(0,0,0,.3)}.fc-event .fc-event-resizer{display:none;position:absolute;z-index:4}.fc-event-selected .fc-event-resizer,.fc-event:hover .fc-event-resizer{display:block}.fc-event-selected .fc-event-resizer{background:var(--fc-page-bg-color);border-color:inherit;border-radius:calc(var(--fc-event-resizer-dot-total-width)/2);border-style:solid;border-width:var(--fc-event-resizer-dot-border-width);height:var(--fc-event-resizer-dot-total-width);width:var(--fc-event-resizer-dot-total-width)}.fc-event-selected .fc-event-resizer:before{bottom:-20px;content:\"\";left:-20px;position:absolute;right:-20px;top:-20px}.fc-event-selected,.fc-event:focus{box-shadow:0 2px 5px rgba(0,0,0,.2)}.fc-event-selected:before,.fc-event:focus:before{bottom:0;content:\"\";left:0;position:absolute;right:0;top:0;z-index:3}.fc-event-selected:after,.fc-event:focus:after{background:var(--fc-event-selected-overlay-color);bottom:-1px;content:\"\";left:-1px;position:absolute;right:-1px;top:-1px;z-index:1}.fc-h-event{background-color:var(--fc-event-bg-color);border:1px solid var(--fc-event-border-color);display:block}.fc-h-event .fc-event-main{color:var(--fc-event-text-color)}.fc-h-event .fc-event-main-frame{display:flex}.fc-h-event .fc-event-time{max-width:100%;overflow:hidden}.fc-h-event .fc-event-title-container{flex-grow:1;flex-shrink:1;min-width:0}.fc-h-event .fc-event-title{display:inline-block;left:0;max-width:100%;overflow:hidden;right:0;vertical-align:top}.fc-h-event.fc-event-selected:before{bottom:-10px;top:-10px}.fc-direction-ltr .fc-daygrid-block-event:not(.fc-event-start),.fc-direction-rtl .fc-daygrid-block-event:not(.fc-event-end){border-bottom-left-radius:0;border-left-width:0;border-top-left-radius:0}.fc-direction-ltr .fc-daygrid-block-event:not(.fc-event-end),.fc-direction-rtl .fc-daygrid-block-event:not(.fc-event-start){border-bottom-right-radius:0;border-right-width:0;border-top-right-radius:0}.fc-h-event:not(.fc-event-selected) .fc-event-resizer{bottom:0;top:0;width:var(--fc-event-resizer-thickness)}.fc-direction-ltr .fc-h-event:not(.fc-event-selected) .fc-event-resizer-start,.fc-direction-rtl .fc-h-event:not(.fc-event-selected) .fc-event-resizer-end{cursor:w-resize;left:calc(var(--fc-event-resizer-thickness)*-.5)}.fc-direction-ltr .fc-h-event:not(.fc-event-selected) .fc-event-resizer-end,.fc-direction-rtl .fc-h-event:not(.fc-event-selected) .fc-event-resizer-start{cursor:e-resize;right:calc(var(--fc-event-resizer-thickness)*-.5)}.fc-h-event.fc-event-selected .fc-event-resizer{margin-top:calc(var(--fc-event-resizer-dot-total-width)*-.5);top:50%}.fc-direction-ltr .fc-h-event.fc-event-selected .fc-event-resizer-start,.fc-direction-rtl .fc-h-event.fc-event-selected .fc-event-resizer-end{left:calc(var(--fc-event-resizer-dot-total-width)*-.5)}.fc-direction-ltr .fc-h-event.fc-event-selected .fc-event-resizer-end,.fc-direction-rtl .fc-h-event.fc-event-selected .fc-event-resizer-start{right:calc(var(--fc-event-resizer-dot-total-width)*-.5)}.fc .fc-popover{box-shadow:0 2px 6px rgba(0,0,0,.15);position:absolute;z-index:9999}.fc .fc-popover-header{align-items:center;display:flex;flex-direction:row;justify-content:space-between;padding:3px 4px}.fc .fc-popover-title{margin:0 2px}.fc .fc-popover-close{cursor:pointer;font-size:1.1em;opacity:.65}.fc-theme-standard .fc-popover{background:var(--fc-page-bg-color);border:1px solid var(--fc-border-color)}.fc-theme-standard .fc-popover-header{background:var(--fc-neutral-bg-color)}";
106
+ var css_248z = ":root{--fc-small-font-size:.85em;--fc-page-bg-color:#fff;--fc-neutral-bg-color:hsla(0,0%,82%,.3);--fc-neutral-text-color:grey;--fc-border-color:#ddd;--fc-button-text-color:#fff;--fc-button-bg-color:#2c3e50;--fc-button-border-color:#2c3e50;--fc-button-hover-bg-color:#1e2b37;--fc-button-hover-border-color:#1a252f;--fc-button-active-bg-color:#1a252f;--fc-button-active-border-color:#151e27;--fc-event-bg-color:#3788d8;--fc-event-border-color:#3788d8;--fc-event-text-color:#fff;--fc-event-selected-overlay-color:rgba(0,0,0,.25);--fc-more-link-bg-color:#d0d0d0;--fc-more-link-text-color:inherit;--fc-event-resizer-thickness:8px;--fc-event-resizer-dot-total-width:8px;--fc-event-resizer-dot-border-width:1px;--fc-non-business-color:hsla(0,0%,84%,.3);--fc-bg-event-color:#8fdf82;--fc-bg-event-opacity:0.3;--fc-highlight-color:rgba(188,232,241,.3);--fc-today-bg-color:rgba(255,220,40,.15);--fc-now-indicator-color:red}.fc{display:flex;flex-direction:column;gap:1.5em}.fc,.fc *,.fc :after,.fc :before{box-sizing:border-box}.fc-direction-ltr{direction:ltr;text-align:left}.fc-direction-rtl{direction:rtl;text-align:right}.fc-flex-row{display:flex;flex-direction:row}.fc-flex-column{display:flex;flex-direction:column}.fc-grow{flex-grow:1}.fc-basis0,.fc-liquid{flex-basis:0}.fc-liquid{flex-grow:1;min-height:0;min-width:0}.fc-row{display:flex;flex-direction:row}.fc-rowgroup{display:flex;flex-direction:column}.fc-row,.fc-rowdivider,.fc-rowgroup{border-color:var(--fc-border-color);border-style:solid;border-width:1px 0 0}.fc-row:first-child:not(.fc-not-first),.fc-rowdivider+.fc-row,.fc-rowdivider+.fc-rowgroup,.fc-rowdivider:first-child,.fc-rowgroup:first-child,.fc-sticky-header+.fc-row,.fc-sticky-header+.fc-rowgroup{border-top-width:0}.fc-cell,.fc-celldivider{border:0 solid var(--fc-border-color)}.fc-cell{margin:0!important;padding:0!important}.fc-cell-inner{overflow:hidden;white-space:nowrap}.fc-direction-ltr .fc-cell,.fc-direction-ltr .fc-celldivider{border-left-width:1px}.fc-direction-ltr .fc-cell:first-child:not(.fc-not-first),.fc-direction-ltr .fc-celldivider+.fc-cell{border-left-width:0}.fc-direction-ltr .fc-celldivider,.fc-direction-rtl .fc-cell,.fc-direction-rtl .fc-celldivider{border-right-width:1px}.fc-direction-rtl .fc-cell:first-child:not(.fc-not-first),.fc-direction-rtl .fc-celldivider+.fc-cell{border-right-width:0}.fc-direction-rtl .fc-celldivider{border-left-width:1px}.fc-cell:only-child{flex-basis:0;flex-grow:1;min-width:0}.fc-celldivider,.fc-rowdivider{background:var(--fc-neutral-bg-color)}.fc-celldivider{padding-left:2px}.fc-rowdivider{padding-bottom:2px}.fc-rowdivider,.fc-sticky-header{border-bottom-width:1px}.fc-rel{position:relative}.fc-abs{position:absolute}.fc-fill{bottom:0;top:0}.fc-fill,.fc-fill-x{left:0;position:absolute;right:0}.fc-fill-y{bottom:0;position:absolute;top:0}.fc-sticky-y{bottom:0;position:sticky;top:0}.fc-sticky-x{left:0;position:sticky;right:0}.fc-sticky-header{background:var(--fc-page-bg-color);position:sticky;top:0;z-index:9999}.fc-sticky-footer{bottom:0;position:sticky;z-index:9999}.fc-content-box{box-sizing:content-box}.fc-border{border:1px solid var(--fc-border-color)}.fc-offscreen{left:-10000px;position:absolute}.fc-shaded{background-color:var(--fc-neutral-bg-color)}.fc-padding-sm{padding:2px 4px}.fc-padding-md{padding:4px 5px}.fc-padding-lg{padding:8px}.fc-justify-center{justify-content:center}.fc-align-center{align-items:center}.fc-align-start{align-items:flex-start}.fc a[data-navlink]{cursor:pointer}.fc a[data-navlink]:hover{text-decoration:underline}.fc-view-harness{position:relative}.fc-view-harness-fixedheight,.fc-view-harness-liquid{display:flex;flex-direction:column}.fc-view-harness-fixedheight>.fc-view,.fc-view-harness-liquid,.fc-view-harness-liquid>.fc-view{flex-basis:0;flex-grow:1;min-height:0}.fc-view-harness-aspectratio>.fc-view{bottom:0;left:0;position:absolute;right:0;top:0}a.fc-event,a.fc-event:hover{text-decoration:none}.fc-event.fc-event-draggable,.fc-event[href]{cursor:pointer}.fc-event-dragging:not(.fc-event-selected){opacity:.75}.fc-event-dragging.fc-event-selected{box-shadow:0 2px 7px rgba(0,0,0,.3)}.fc-event-selected,.fc-event:focus{box-shadow:0 2px 5px rgba(0,0,0,.2)}.fc-event-selected:before,.fc-event:focus:before{bottom:0;content:\"\";left:0;position:absolute;right:0;top:0;z-index:3}.fc-event-selected:after,.fc-event:focus:after{background:var(--fc-event-selected-overlay-color);bottom:-1px;content:\"\";left:-1px;position:absolute;right:-1px;top:-1px;z-index:1}.fc-event-inner{position:relative;z-index:2}.fc-event-resizer{display:none;position:absolute;z-index:4}.fc-event-selected .fc-event-resizer,.fc-event:hover .fc-event-resizer{display:block}.fc-event-selected .fc-event-resizer{background:var(--fc-page-bg-color);border-color:inherit;border-radius:calc(var(--fc-event-resizer-dot-total-width)/2);border-style:solid;border-width:var(--fc-event-resizer-dot-border-width);height:var(--fc-event-resizer-dot-total-width);width:var(--fc-event-resizer-dot-total-width)}.fc-event-selected .fc-event-resizer:before{bottom:-20px;content:\"\";left:-20px;position:absolute;right:-20px;top:-20px}.fc-bg-event,.fc-highlight,.fc-non-business{bottom:0;left:0;position:absolute;right:0;top:0}.fc-non-business{background:var(--fc-non-business-color)}.fc-bg-event{background:var(--fc-bg-event-color);opacity:var(--fc-bg-event-opacity)}.fc-bg-event .fc-event-title{font-size:var(--fc-small-font-size);font-style:italic;margin:.5em}.fc-highlight{background:var(--fc-highlight-color)}.fc-day-disabled{background:var(--fc-neutral-bg-color)}.fc-h-event{background-color:var(--fc-event-bg-color);border:1px solid var(--fc-event-border-color);display:flex;flex-direction:column;position:relative}.fc-h-event.fc-event-selected:before{bottom:-10px;top:-10px}.fc-h-event .fc-event-inner{color:var(--fc-event-text-color);display:flex;flex-direction:row;flex-grow:1;min-width:0}.fc-h-event .fc-event-time,.fc-h-event .fc-event-title{overflow:hidden;white-space:nowrap}.fc-h-event .fc-event-title-outer{display:flex;flex-basis:0;flex-direction:row;flex-grow:1;min-width:0}.fc-h-event .fc-event-title{left:0;position:sticky;right:0}.fc-h-event:not(.fc-event-selected) .fc-event-resizer{bottom:0;top:0;width:var(--fc-event-resizer-thickness)}.fc-direction-ltr .fc-h-event:not(.fc-event-selected) .fc-event-resizer-start,.fc-direction-rtl .fc-h-event:not(.fc-event-selected) .fc-event-resizer-end{cursor:w-resize;left:calc(var(--fc-event-resizer-thickness)*-.5)}.fc-direction-ltr .fc-h-event:not(.fc-event-selected) .fc-event-resizer-end,.fc-direction-rtl .fc-h-event:not(.fc-event-selected) .fc-event-resizer-start{cursor:e-resize;right:calc(var(--fc-event-resizer-thickness)*-.5)}.fc-h-event.fc-event-selected .fc-event-resizer{margin-top:calc(var(--fc-event-resizer-dot-total-width)*-.5);top:50%}.fc-direction-ltr .fc-h-event.fc-event-selected .fc-event-resizer-start,.fc-direction-rtl .fc-h-event.fc-event-selected .fc-event-resizer-end{left:calc(var(--fc-event-resizer-dot-total-width)*-.5)}.fc-direction-ltr .fc-h-event.fc-event-selected .fc-event-resizer-end,.fc-direction-rtl .fc-h-event.fc-event-selected .fc-event-resizer-start{right:calc(var(--fc-event-resizer-dot-total-width)*-.5)}.fc-popover{box-shadow:0 2px 6px rgba(0,0,0,.15);position:absolute;z-index:9999}.fc-popover-header{align-items:center;display:flex;flex-direction:row;justify-content:space-between;padding:3px 4px}.fc-popover-title{margin:0 2px}.fc-popover-close{cursor:pointer;font-size:1.1em;opacity:.65}.fc-theme-standard .fc-popover{background:var(--fc-page-bg-color);border:1px solid var(--fc-border-color)}.fc-theme-standard .fc-popover-header{background:var(--fc-neutral-bg-color)}.fc-scroller-nobars{-ms-overflow-style:none;scrollbar-width:none}.fc-scroller-nobars::-webkit-scrollbar{display:none}.fc-not-allowed,.fc-not-allowed .fc-event{cursor:not-allowed}@font-face{font-family:fcicons;font-style:normal;font-weight:400;src:url(\"data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMg8SBfAAAAC8AAAAYGNtYXAXVtKNAAABHAAAAFRnYXNwAAAAEAAAAXAAAAAIZ2x5ZgYydxIAAAF4AAAFNGhlYWQUJ7cIAAAGrAAAADZoaGVhB20DzAAABuQAAAAkaG10eCIABhQAAAcIAAAALGxvY2ED4AU6AAAHNAAAABhtYXhwAA8AjAAAB0wAAAAgbmFtZXsr690AAAdsAAABhnBvc3QAAwAAAAAI9AAAACAAAwPAAZAABQAAApkCzAAAAI8CmQLMAAAB6wAzAQkAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADpBgPA/8AAQAPAAEAAAAABAAAAAAAAAAAAAAAgAAAAAAADAAAAAwAAABwAAQADAAAAHAADAAEAAAAcAAQAOAAAAAoACAACAAIAAQAg6Qb//f//AAAAAAAg6QD//f//AAH/4xcEAAMAAQAAAAAAAAAAAAAAAQAB//8ADwABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAAAAAAAAAIAADc5AQAAAAABAWIAjQKeAskAEwAAJSc3NjQnJiIHAQYUFwEWMjc2NCcCnuLiDQ0MJAz/AA0NAQAMJAwNDcni4gwjDQwM/wANIwz/AA0NDCMNAAAAAQFiAI0CngLJABMAACUBNjQnASYiBwYUHwEHBhQXFjI3AZ4BAA0N/wAMJAwNDeLiDQ0MJAyNAQAMIw0BAAwMDSMM4uINIwwNDQAAAAIA4gC3Ax4CngATACcAACUnNzY0JyYiDwEGFB8BFjI3NjQnISc3NjQnJiIPAQYUHwEWMjc2NCcB87e3DQ0MIw3VDQ3VDSMMDQ0BK7e3DQ0MJAzVDQ3VDCQMDQ3zuLcMJAwNDdUNIwzWDAwNIwy4twwkDA0N1Q0jDNYMDA0jDAAAAgDiALcDHgKeABMAJwAAJTc2NC8BJiIHBhQfAQcGFBcWMjchNzY0LwEmIgcGFB8BBwYUFxYyNwJJ1Q0N1Q0jDA0Nt7cNDQwjDf7V1Q0N1QwkDA0Nt7cNDQwkDLfWDCMN1Q0NDCQMt7gMIw0MDNYMIw3VDQ0MJAy3uAwjDQwMAAADAFUAAAOrA1UAMwBoAHcAABMiBgcOAQcOAQcOARURFBYXHgEXHgEXHgEzITI2Nz4BNz4BNz4BNRE0JicuAScuAScuASMFITIWFx4BFx4BFx4BFREUBgcOAQcOAQcOASMhIiYnLgEnLgEnLgE1ETQ2Nz4BNz4BNz4BMxMhMjY1NCYjISIGFRQWM9UNGAwLFQkJDgUFBQUFBQ4JCRULDBgNAlYNGAwLFQkJDgUFBQUFBQ4JCRULDBgN/aoCVgQIBAQHAwMFAQIBAQIBBQMDBwQECAT9qgQIBAQHAwMFAQIBAQIBBQMDBwQECASAAVYRGRkR/qoRGRkRA1UFBAUOCQkVDAsZDf2rDRkLDBUJCA4FBQUFBQUOCQgVDAsZDQJVDRkLDBUJCQ4FBAVVAgECBQMCBwQECAX9qwQJAwQHAwMFAQICAgIBBQMDBwQDCQQCVQUIBAQHAgMFAgEC/oAZEhEZGRESGQAAAAADAFUAAAOrA1UAMwBoAIkAABMiBgcOAQcOAQcOARURFBYXHgEXHgEXHgEzITI2Nz4BNz4BNz4BNRE0JicuAScuAScuASMFITIWFx4BFx4BFx4BFREUBgcOAQcOAQcOASMhIiYnLgEnLgEnLgE1ETQ2Nz4BNz4BNz4BMxMzFRQWMzI2PQEzMjY1NCYrATU0JiMiBh0BIyIGFRQWM9UNGAwLFQkJDgUFBQUFBQ4JCRULDBgNAlYNGAwLFQkJDgUFBQUFBQ4JCRULDBgN/aoCVgQIBAQHAwMFAQIBAQIBBQMDBwQECAT9qgQIBAQHAwMFAQIBAQIBBQMDBwQECASAgBkSEhmAERkZEYAZEhIZgBEZGREDVQUEBQ4JCRUMCxkN/asNGQsMFQkIDgUFBQUFBQ4JCBUMCxkNAlUNGQsMFQkJDgUEBVUCAQIFAwIHBAQIBf2rBAkDBAcDAwUBAgICAgEFAwMHBAMJBAJVBQgEBAcCAwUCAQL+gIASGRkSgBkSERmAEhkZEoAZERIZAAABAOIAjQMeAskAIAAAExcHBhQXFjI/ARcWMjc2NC8BNzY0JyYiDwEnJiIHBhQX4uLiDQ0MJAzi4gwkDA0N4uINDQwkDOLiDCQMDQ0CjeLiDSMMDQ3h4Q0NDCMN4uIMIw0MDOLiDAwNIwwAAAABAAAAAQAAa5n0y18PPPUACwQAAAAAANivOVsAAAAA2K85WwAAAAADqwNVAAAACAACAAAAAAAAAAEAAAPA/8AAAAQAAAAAAAOrAAEAAAAAAAAAAAAAAAAAAAALBAAAAAAAAAAAAAAAAgAAAAQAAWIEAAFiBAAA4gQAAOIEAABVBAAAVQQAAOIAAAAAAAoAFAAeAEQAagCqAOoBngJkApoAAQAAAAsAigADAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAA4ArgABAAAAAAABAAcAAAABAAAAAAACAAcAYAABAAAAAAADAAcANgABAAAAAAAEAAcAdQABAAAAAAAFAAsAFQABAAAAAAAGAAcASwABAAAAAAAKABoAigADAAEECQABAA4ABwADAAEECQACAA4AZwADAAEECQADAA4APQADAAEECQAEAA4AfAADAAEECQAFABYAIAADAAEECQAGAA4AUgADAAEECQAKADQApGZjaWNvbnMAZgBjAGkAYwBvAG4Ac1ZlcnNpb24gMS4wAFYAZQByAHMAaQBvAG4AIAAxAC4AMGZjaWNvbnMAZgBjAGkAYwBvAG4Ac2ZjaWNvbnMAZgBjAGkAYwBvAG4Ac1JlZ3VsYXIAUgBlAGcAdQBsAGEAcmZjaWNvbnMAZgBjAGkAYwBvAG4Ac0ZvbnQgZ2VuZXJhdGVkIGJ5IEljb01vb24uAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\") format(\"truetype\")}.fc-icon{speak:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;display:inline-block;font-family:fcicons!important;font-style:normal;font-variant:normal;font-weight:400;height:1em;line-height:1;text-align:center;text-transform:none;-moz-user-select:none;user-select:none;width:1em}.fc-icon-chevron-left:before{content:\"\\e900\"}.fc-icon-chevron-right:before{content:\"\\e901\"}.fc-icon-chevrons-left:before{content:\"\\e902\"}.fc-icon-chevrons-right:before{content:\"\\e903\"}.fc-icon-minus-square:before{content:\"\\e904\"}.fc-icon-plus-square:before{content:\"\\e905\"}.fc-icon-x:before{content:\"\\e906\"}.fc-button{border-radius:0;font-family:inherit;font-size:inherit;line-height:inherit;margin:0;overflow:visible;text-transform:none}.fc-button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}.fc-button{-webkit-appearance:button}.fc-button:not(:disabled){cursor:pointer}.fc-button{background-color:transparent;border:1px solid transparent;border-radius:.25em;display:inline-block;font-size:1em;font-weight:400;line-height:1.5;padding:.4em .65em;text-align:center;-moz-user-select:none;user-select:none;vertical-align:middle}.fc-button:hover{text-decoration:none}.fc-button:focus{box-shadow:0 0 0 .2rem rgba(44,62,80,.25);outline:0}.fc-button:disabled{opacity:.65}.fc-button-primary{background-color:var(--fc-button-bg-color);border-color:var(--fc-button-border-color);color:var(--fc-button-text-color)}.fc-button-primary:hover{background-color:var(--fc-button-hover-bg-color);border-color:var(--fc-button-hover-border-color);color:var(--fc-button-text-color)}.fc-button-primary:disabled{background-color:var(--fc-button-bg-color);border-color:var(--fc-button-border-color);color:var(--fc-button-text-color)}.fc-button-primary:focus{box-shadow:0 0 0 .2rem rgba(76,91,106,.5)}.fc-button-primary:not(:disabled).fc-button-active,.fc-button-primary:not(:disabled):active{background-color:var(--fc-button-active-bg-color);border-color:var(--fc-button-active-border-color);color:var(--fc-button-text-color)}.fc-button-primary:not(:disabled).fc-button-active:focus,.fc-button-primary:not(:disabled):active:focus{box-shadow:0 0 0 .2rem rgba(76,91,106,.5)}.fc-button .fc-icon{font-size:1.5em;vertical-align:middle}.fc-button-group{display:inline-flex;position:relative;vertical-align:middle}.fc-button-group>.fc-button{flex:1 1 auto;position:relative}.fc-button-group>.fc-button.fc-button-active,.fc-button-group>.fc-button:active,.fc-button-group>.fc-button:focus,.fc-button-group>.fc-button:hover{z-index:1}.fc-direction-ltr .fc-button-group>.fc-button:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0;margin-left:-1px}.fc-direction-ltr .fc-button-group>.fc-button:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0}.fc-direction-rtl .fc-button-group>.fc-button:not(:first-child){border-bottom-right-radius:0;border-top-right-radius:0;margin-right:-1px}.fc-direction-rtl .fc-button-group>.fc-button:not(:last-child){border-bottom-left-radius:0;border-top-left-radius:0}.fc-toolbar{align-items:center;display:flex;flex-direction:row;gap:.75em;justify-content:space-between}.fc-direction-rtl .fc-toolbar-ltr{flex-direction:row-reverse}.fc-toolbar-chunk{display:flex;flex-direction:row;flex-shrink:0;gap:.75em}.fc-toolbar-title{font-size:1.75em;margin:0;white-space:nowrap}";
107
107
  injectStyles(css_248z);
108
108
 
109
109
  class DelayedRunner {
@@ -465,20 +465,6 @@ function compareNumbers(a, b) {
465
465
  function isInt(n) {
466
466
  return n % 1 === 0;
467
467
  }
468
- /* FC-specific DOM dimension stuff
469
- ----------------------------------------------------------------------------------------------------------------------*/
470
- function computeSmallestCellWidth(cellEl) {
471
- let allWidthEl = cellEl.querySelector('.fc-scrollgrid-shrink-frame');
472
- let contentWidthEl = cellEl.querySelector('.fc-scrollgrid-shrink-cushion');
473
- if (!allWidthEl) {
474
- throw new Error('needs fc-scrollgrid-shrink-frame className'); // TODO: use const
475
- }
476
- if (!contentWidthEl) {
477
- throw new Error('needs fc-scrollgrid-shrink-cushion className');
478
- }
479
- return cellEl.getBoundingClientRect().width - allWidthEl.getBoundingClientRect().width + // the cell padding+border
480
- contentWidthEl.getBoundingClientRect().width;
481
- }
482
468
 
483
469
  const INTERNAL_UNITS = ['years', 'months', 'days', 'milliseconds'];
484
470
  const PARSE_RE = /^(-?)(?:(\d+)\.)?(\d+):(\d\d)(?::(\d\d)(?:\.(\d\d\d))?)?/;
@@ -1391,8 +1377,6 @@ const BASE_OPTION_REFINERS = {
1391
1377
  dropAccept: identity,
1392
1378
  eventOrder: parseFieldSpecs,
1393
1379
  eventOrderStrict: Boolean,
1394
- handleWindowResize: Boolean,
1395
- windowResizeDelay: Number,
1396
1380
  longPressDelay: Number,
1397
1381
  eventDragMinDistance: Number,
1398
1382
  expandRows: Boolean,
@@ -1542,8 +1526,6 @@ const BASE_OPTION_DEFAULTS = {
1542
1526
  dropAccept: '*',
1543
1527
  eventOrder: 'start,-duration,allDay,title',
1544
1528
  dayPopoverFormat: { month: 'long', day: 'numeric', year: 'numeric' },
1545
- handleWindowResize: true,
1546
- windowResizeDelay: 100,
1547
1529
  longPressDelay: 1000,
1548
1530
  eventDragMinDistance: 5,
1549
1531
  expandRows: false,
@@ -1562,7 +1544,6 @@ const CALENDAR_LISTENER_REFINERS = {
1562
1544
  eventAdd: identity,
1563
1545
  eventChange: identity,
1564
1546
  eventRemove: identity,
1565
- windowResize: identity,
1566
1547
  eventClick: identity,
1567
1548
  eventMouseEnter: identity,
1568
1549
  eventMouseLeave: identity,
@@ -1575,8 +1556,7 @@ const CALENDAR_LISTENER_REFINERS = {
1575
1556
  _afterprint: identity,
1576
1557
  _noEventDrop: identity,
1577
1558
  _noEventResize: identity,
1578
- _resize: identity,
1579
- _scrollRequest: identity,
1559
+ _timeScrollRequest: identity,
1580
1560
  };
1581
1561
  // calendar-specific options
1582
1562
  // -------------------------
@@ -2246,6 +2226,9 @@ Theme.prototype.iconOverridePrefix = '';
2246
2226
  NOTE: this can be a public API, especially createElement for hooks.
2247
2227
  See examples/typescript-scheduler/src/index.ts
2248
2228
  */
2229
+ /*
2230
+ TODO: rethink this
2231
+ */
2249
2232
  function flushSync(runBeforeFlush) {
2250
2233
  runBeforeFlush();
2251
2234
  let oldDebounceRendering = preact__namespace.options.debounceRendering; // orig
@@ -2295,42 +2278,6 @@ function createContext(defaultValue) {
2295
2278
  return ContextType;
2296
2279
  }
2297
2280
 
2298
- class ScrollResponder {
2299
- constructor(execFunc, emitter, scrollTime, scrollTimeReset) {
2300
- this.execFunc = execFunc;
2301
- this.emitter = emitter;
2302
- this.scrollTime = scrollTime;
2303
- this.scrollTimeReset = scrollTimeReset;
2304
- this.handleScrollRequest = (request) => {
2305
- this.queuedRequest = Object.assign({}, this.queuedRequest || {}, request);
2306
- this.drain();
2307
- };
2308
- emitter.on('_scrollRequest', this.handleScrollRequest);
2309
- this.fireInitialScroll();
2310
- }
2311
- detach() {
2312
- this.emitter.off('_scrollRequest', this.handleScrollRequest);
2313
- }
2314
- update(isDatesNew) {
2315
- if (isDatesNew && this.scrollTimeReset) {
2316
- this.fireInitialScroll(); // will drain
2317
- }
2318
- else {
2319
- this.drain();
2320
- }
2321
- }
2322
- fireInitialScroll() {
2323
- this.handleScrollRequest({
2324
- time: this.scrollTime,
2325
- });
2326
- }
2327
- drain() {
2328
- if (this.queuedRequest && this.execFunc(this.queuedRequest)) {
2329
- this.queuedRequest = null;
2330
- }
2331
- }
2332
- }
2333
-
2334
2281
  const ViewContextType = createContext({}); // for Components
2335
2282
  function buildViewContext(viewSpec, viewApi, viewOptions, dateProfileGenerator, dateEnv, theme, pluginHooks, dispatch, getCurrentData, emitter, calendarApi, registerInteractiveComponent, unregisterInteractiveComponent) {
2336
2283
  return {
@@ -2346,15 +2293,6 @@ function buildViewContext(viewSpec, viewApi, viewOptions, dateProfileGenerator,
2346
2293
  dateProfileGenerator,
2347
2294
  theme,
2348
2295
  isRtl: viewOptions.direction === 'rtl',
2349
- addResizeHandler(handler) {
2350
- emitter.on('_resize', handler);
2351
- },
2352
- removeResizeHandler(handler) {
2353
- emitter.off('_resize', handler);
2354
- },
2355
- createScrollResponder(execFunc) {
2356
- return new ScrollResponder(execFunc, emitter, createDuration(viewOptions.scrollTime), viewOptions.scrollTimeReset);
2357
- },
2358
2296
  registerInteractiveComponent,
2359
2297
  unregisterInteractiveComponent,
2360
2298
  };
@@ -3417,6 +3355,9 @@ function parseClassNames(raw) {
3417
3355
  }
3418
3356
  return [];
3419
3357
  }
3358
+ function fracToCssDim(frac) {
3359
+ return frac * 100 + '%';
3360
+ }
3420
3361
 
3421
3362
  // TODO: better called "EventSettings" or "EventConfig"
3422
3363
  // TODO: move this file into structs
@@ -4356,12 +4297,12 @@ function sliceEventStore(eventStore, eventUiBases, framingRange, nextDayThreshol
4356
4297
  function hasBgRendering(def) {
4357
4298
  return def.ui.display === 'background' || def.ui.display === 'inverse-background';
4358
4299
  }
4359
- function setElSeg(el, seg) {
4360
- el.fcSeg = seg;
4300
+ function setElEventRange(el, eventRange) {
4301
+ el.fcEventRange = eventRange;
4361
4302
  }
4362
- function getElSeg(el) {
4363
- return el.fcSeg ||
4364
- el.parentNode.fcSeg || // for the harness
4303
+ function getElEventRange(el) {
4304
+ return el.fcEventRange ||
4305
+ el.parentNode.fcEventRange || // for the harness
4365
4306
  null;
4366
4307
  }
4367
4308
  // event ui computation
@@ -4381,7 +4322,7 @@ function compileEventUi(eventDef, eventUiBases) {
4381
4322
  }
4382
4323
  function sortEventSegs(segs, eventOrderSpecs) {
4383
4324
  let objs = segs.map(buildSegCompareObj);
4384
- objs.sort((obj0, obj1) => compareByFieldSpecs(obj0, obj1, eventOrderSpecs));
4325
+ objs.sort((obj0, obj1) => compareByFieldSpecs(obj0, obj1, eventOrderSpecs)); // !!!
4385
4326
  return objs.map((c) => c._seg);
4386
4327
  }
4387
4328
  // returns a object with all primitive props that can be compared
@@ -4394,29 +4335,23 @@ function buildSegCompareObj(seg) {
4394
4335
  return Object.assign(Object.assign(Object.assign({}, eventDef.extendedProps), eventDef), { id: eventDef.publicId, start,
4395
4336
  end, duration: end - start, allDay: Number(eventDef.allDay), _seg: seg });
4396
4337
  }
4397
- function computeSegDraggable(seg, context) {
4338
+ function computeEventRangeDraggable(eventRange, context) {
4398
4339
  let { pluginHooks } = context;
4399
4340
  let transformers = pluginHooks.isDraggableTransformers;
4400
- let { def, ui } = seg.eventRange;
4341
+ let { def, ui } = eventRange;
4401
4342
  let val = ui.startEditable;
4402
4343
  for (let transformer of transformers) {
4403
4344
  val = transformer(val, def, ui, context);
4404
4345
  }
4405
4346
  return val;
4406
4347
  }
4407
- function computeSegStartResizable(seg, context) {
4408
- return seg.isStart && seg.eventRange.ui.durationEditable && context.options.eventResizableFromStart;
4409
- }
4410
- function computeSegEndResizable(seg, context) {
4411
- return seg.isEnd && seg.eventRange.ui.durationEditable;
4412
- }
4413
- function buildSegTimeText(seg, timeFormat, context, defaultDisplayEventTime, // defaults to true
4348
+ function buildEventRangeTimeText(eventRange, timeFormat, context, defaultDisplayEventTime, // defaults to true
4414
4349
  defaultDisplayEventEnd, // defaults to true
4415
4350
  startOverride, endOverride) {
4416
4351
  let { dateEnv, options } = context;
4417
4352
  let { displayEventTime, displayEventEnd } = options;
4418
- let eventDef = seg.eventRange.def;
4419
- let eventInstance = seg.eventRange.instance;
4353
+ let eventDef = eventRange.def;
4354
+ let eventInstance = eventRange.instance;
4420
4355
  if (displayEventTime == null) {
4421
4356
  displayEventTime = defaultDisplayEventTime !== false;
4422
4357
  }
@@ -4425,8 +4360,8 @@ startOverride, endOverride) {
4425
4360
  }
4426
4361
  let wholeEventStart = eventInstance.range.start;
4427
4362
  let wholeEventEnd = eventInstance.range.end;
4428
- let segStart = startOverride || seg.start || seg.eventRange.range.start;
4429
- let segEnd = endOverride || seg.end || seg.eventRange.range.end;
4363
+ let segStart = startOverride || eventRange.range.start;
4364
+ let segEnd = endOverride || eventRange.range.end;
4430
4365
  let isStartDay = startOfDay(wholeEventStart).valueOf() === startOfDay(segStart).valueOf();
4431
4366
  let isEndDay = startOfDay(addMs(wholeEventEnd, -1)).valueOf() === startOfDay(addMs(segEnd, -1)).valueOf();
4432
4367
  if (displayEventTime && !eventDef.allDay && (isStartDay || isEndDay)) {
@@ -4444,8 +4379,8 @@ startOverride, endOverride) {
4444
4379
  }
4445
4380
  return '';
4446
4381
  }
4447
- function getSegMeta(seg, todayRange, nowDate) {
4448
- let segRange = seg.eventRange.range;
4382
+ function getEventRangeMeta(eventRange, todayRange, nowDate) {
4383
+ let segRange = eventRange.range;
4449
4384
  return {
4450
4385
  isPast: segRange.end <= (nowDate || todayRange.start),
4451
4386
  isFuture: segRange.start >= (nowDate || todayRange.end),
@@ -4495,8 +4430,8 @@ function buildEventRangeKey(eventRange) {
4495
4430
  : `${eventRange.def.defId}:${eventRange.range.start.toISOString()}`;
4496
4431
  // inverse-background events don't have specific instances. TODO: better solution
4497
4432
  }
4498
- function getSegAnchorAttrs(seg, context) {
4499
- let { def, instance } = seg.eventRange;
4433
+ function getEventRangeAnchorAttrs(eventRange, context) {
4434
+ let { def, instance } = eventRange;
4500
4435
  let { url } = def;
4501
4436
  if (url) {
4502
4437
  return { href: url };
@@ -4676,33 +4611,6 @@ function requestJson(method, url, params) {
4676
4611
  });
4677
4612
  }
4678
4613
 
4679
- let canVGrowWithinCell;
4680
- function getCanVGrowWithinCell() {
4681
- if (canVGrowWithinCell == null) {
4682
- canVGrowWithinCell = computeCanVGrowWithinCell();
4683
- }
4684
- return canVGrowWithinCell;
4685
- }
4686
- function computeCanVGrowWithinCell() {
4687
- // for SSR, because this function is call immediately at top-level
4688
- // TODO: just make this logic execute top-level, immediately, instead of doing lazily
4689
- if (typeof document === 'undefined') {
4690
- return true;
4691
- }
4692
- let el = document.createElement('div');
4693
- el.style.position = 'absolute';
4694
- el.style.top = '0px';
4695
- el.style.left = '0px';
4696
- el.innerHTML = '<table><tr><td><div></div></td></tr></table>';
4697
- el.querySelector('table').style.height = '100px';
4698
- el.querySelector('div').style.height = '100%';
4699
- document.body.appendChild(el);
4700
- let div = el.querySelector('div');
4701
- let possible = div.offsetHeight > 0;
4702
- document.body.removeChild(el);
4703
- return possible;
4704
- }
4705
-
4706
4614
  class CalendarRoot extends BaseComponent {
4707
4615
  constructor() {
4708
4616
  super(...arguments);
@@ -4721,21 +4629,16 @@ class CalendarRoot extends BaseComponent {
4721
4629
  };
4722
4630
  }
4723
4631
  render() {
4724
- let { props } = this;
4632
+ let { props, state } = this;
4725
4633
  let { options } = props;
4726
- let { forPrint } = this.state;
4727
- let isHeightAuto = forPrint || options.height === 'auto' || options.contentHeight === 'auto';
4728
- let height = (!isHeightAuto && options.height != null) ? options.height : '';
4634
+ let { forPrint } = state;
4729
4635
  let classNames = [
4730
4636
  'fc',
4731
4637
  forPrint ? 'fc-media-print' : 'fc-media-screen',
4732
4638
  `fc-direction-${options.direction}`,
4733
4639
  props.theme.getClass('root'),
4734
4640
  ];
4735
- if (!getCanVGrowWithinCell()) {
4736
- classNames.push('fc-liquid-hack');
4737
- }
4738
- return props.children(classNames, height, isHeightAuto, forPrint);
4641
+ return props.children(classNames, options.height, forPrint);
4739
4642
  }
4740
4643
  componentDidMount() {
4741
4644
  let { emitter } = this.props;
@@ -4773,978 +4676,695 @@ function interactionSettingsToStore(settings) {
4773
4676
  // global state
4774
4677
  const interactionSettingsStore = {};
4775
4678
 
4776
- class CalendarImpl {
4777
- getCurrentData() {
4778
- return this.currentDataManager.getCurrentData();
4779
- }
4780
- dispatch(action) {
4781
- this.currentDataManager.dispatch(action);
4782
- }
4783
- get view() { return this.getCurrentData().viewApi; }
4784
- batchRendering(callback) {
4785
- callback();
4786
- }
4787
- updateSize() {
4788
- this.trigger('_resize', true);
4789
- }
4790
- // Options
4791
- // -----------------------------------------------------------------------------------------------------------------
4792
- setOption(name, val) {
4793
- this.dispatch({
4794
- type: 'SET_OPTION',
4795
- optionName: name,
4796
- rawOptionValue: val,
4797
- });
4798
- }
4799
- getOption(name) {
4800
- return this.currentDataManager.currentCalendarOptionsInput[name];
4679
+ function pointInsideRect(point, rect) {
4680
+ return point.left >= rect.left &&
4681
+ point.left < rect.right &&
4682
+ point.top >= rect.top &&
4683
+ point.top < rect.bottom;
4684
+ }
4685
+ // Returns a new rectangle that is the intersection of the two rectangles. If they don't intersect, returns false
4686
+ function intersectRects(rect1, rect2) {
4687
+ let res = {
4688
+ left: Math.max(rect1.left, rect2.left),
4689
+ right: Math.min(rect1.right, rect2.right),
4690
+ top: Math.max(rect1.top, rect2.top),
4691
+ bottom: Math.min(rect1.bottom, rect2.bottom),
4692
+ };
4693
+ if (res.left < res.right && res.top < res.bottom) {
4694
+ return res;
4801
4695
  }
4802
- getAvailableLocaleCodes() {
4803
- return Object.keys(this.getCurrentData().availableRawLocales);
4696
+ return false;
4697
+ }
4698
+ function translateRect(rect, deltaX, deltaY) {
4699
+ return {
4700
+ left: rect.left + deltaX,
4701
+ right: rect.right + deltaX,
4702
+ top: rect.top + deltaY,
4703
+ bottom: rect.bottom + deltaY,
4704
+ };
4705
+ }
4706
+ // Returns a new point that will have been moved to reside within the given rectangle
4707
+ function constrainPoint(point, rect) {
4708
+ return {
4709
+ left: Math.min(Math.max(point.left, rect.left), rect.right),
4710
+ top: Math.min(Math.max(point.top, rect.top), rect.bottom),
4711
+ };
4712
+ }
4713
+ // Returns a point that is the center of the given rectangle
4714
+ function getRectCenter(rect) {
4715
+ return {
4716
+ left: (rect.left + rect.right) / 2,
4717
+ top: (rect.top + rect.bottom) / 2,
4718
+ };
4719
+ }
4720
+ // Subtracts point2's coordinates from point1's coordinates, returning a delta
4721
+ function diffPoints(point1, point2) {
4722
+ return {
4723
+ left: point1.left - point2.left,
4724
+ top: point1.top - point2.top,
4725
+ };
4726
+ }
4727
+
4728
+ const EMPTY_EVENT_STORE = createEmptyEventStore(); // for purecomponents. TODO: keep elsewhere
4729
+ class Splitter {
4730
+ constructor() {
4731
+ this.getKeysForEventDefs = memoize(this._getKeysForEventDefs);
4732
+ this.splitDateSelection = memoize(this._splitDateSpan);
4733
+ this.splitEventStore = memoize(this._splitEventStore);
4734
+ this.splitIndividualUi = memoize(this._splitIndividualUi);
4735
+ this.splitEventDrag = memoize(this._splitInteraction);
4736
+ this.splitEventResize = memoize(this._splitInteraction);
4737
+ this.eventUiBuilders = {}; // TODO: typescript protection
4804
4738
  }
4805
- // Trigger
4806
- // -----------------------------------------------------------------------------------------------------------------
4807
- on(handlerName, handler) {
4808
- let { currentDataManager } = this;
4809
- if (currentDataManager.currentCalendarOptionsRefiners[handlerName]) {
4810
- currentDataManager.emitter.on(handlerName, handler);
4811
- }
4812
- else {
4813
- console.warn(`Unknown listener name '${handlerName}'`);
4739
+ splitProps(props) {
4740
+ let keyInfos = this.getKeyInfo(props);
4741
+ let defKeys = this.getKeysForEventDefs(props.eventStore);
4742
+ let dateSelections = this.splitDateSelection(props.dateSelection);
4743
+ let individualUi = this.splitIndividualUi(props.eventUiBases, defKeys); // the individual *bases*
4744
+ let eventStores = this.splitEventStore(props.eventStore, defKeys);
4745
+ let eventDrags = this.splitEventDrag(props.eventDrag);
4746
+ let eventResizes = this.splitEventResize(props.eventResize);
4747
+ let splitProps = {};
4748
+ this.eventUiBuilders = mapHash(keyInfos, (info, key) => this.eventUiBuilders[key] || memoize(buildEventUiForKey));
4749
+ for (let key in keyInfos) {
4750
+ let keyInfo = keyInfos[key];
4751
+ let eventStore = eventStores[key] || EMPTY_EVENT_STORE;
4752
+ let buildEventUi = this.eventUiBuilders[key];
4753
+ splitProps[key] = {
4754
+ businessHours: keyInfo.businessHours || props.businessHours,
4755
+ dateSelection: dateSelections[key] || null,
4756
+ eventStore,
4757
+ eventUiBases: buildEventUi(props.eventUiBases[''], keyInfo.ui, individualUi[key]),
4758
+ eventDrag: eventDrags[key] || null,
4759
+ eventResize: eventResizes[key] || null,
4760
+ eventSelection: eventStore.instances[props.eventSelection] ? props.eventSelection : '',
4761
+ };
4814
4762
  }
4763
+ return splitProps;
4815
4764
  }
4816
- off(handlerName, handler) {
4817
- this.currentDataManager.emitter.off(handlerName, handler);
4765
+ _splitDateSpan(dateSpan) {
4766
+ let dateSpans = {};
4767
+ if (dateSpan) {
4768
+ let keys = this.getKeysForDateSpan(dateSpan);
4769
+ for (let key of keys) {
4770
+ dateSpans[key] = dateSpan;
4771
+ }
4772
+ }
4773
+ return dateSpans;
4818
4774
  }
4819
- // not meant for public use
4820
- trigger(handlerName, ...args) {
4821
- this.currentDataManager.emitter.trigger(handlerName, ...args);
4775
+ _getKeysForEventDefs(eventStore) {
4776
+ return mapHash(eventStore.defs, (eventDef) => this.getKeysForEventDef(eventDef));
4822
4777
  }
4823
- // View
4824
- // -----------------------------------------------------------------------------------------------------------------
4825
- changeView(viewType, dateOrRange) {
4826
- this.batchRendering(() => {
4827
- this.unselect();
4828
- if (dateOrRange) {
4829
- if (dateOrRange.start && dateOrRange.end) { // a range
4830
- this.dispatch({
4831
- type: 'CHANGE_VIEW_TYPE',
4832
- viewType,
4833
- });
4834
- this.dispatch({
4835
- type: 'SET_OPTION',
4836
- optionName: 'visibleRange',
4837
- rawOptionValue: dateOrRange,
4838
- });
4839
- }
4840
- else {
4841
- let { dateEnv } = this.getCurrentData();
4842
- this.dispatch({
4843
- type: 'CHANGE_VIEW_TYPE',
4844
- viewType,
4845
- dateMarker: dateEnv.createMarker(dateOrRange),
4846
- });
4778
+ _splitEventStore(eventStore, defKeys) {
4779
+ let { defs, instances } = eventStore;
4780
+ let splitStores = {};
4781
+ for (let defId in defs) {
4782
+ for (let key of defKeys[defId]) {
4783
+ if (!splitStores[key]) {
4784
+ splitStores[key] = createEmptyEventStore();
4847
4785
  }
4786
+ splitStores[key].defs[defId] = defs[defId];
4848
4787
  }
4849
- else {
4850
- this.dispatch({
4851
- type: 'CHANGE_VIEW_TYPE',
4852
- viewType,
4853
- });
4854
- }
4855
- });
4856
- }
4857
- // Forces navigation to a view for the given date.
4858
- // `viewType` can be a specific view name or a generic one like "week" or "day".
4859
- // needs to change
4860
- zoomTo(dateMarker, viewType) {
4861
- let state = this.getCurrentData();
4862
- let spec;
4863
- viewType = viewType || 'day'; // day is default zoom
4864
- spec = state.viewSpecs[viewType] || this.getUnitViewSpec(viewType);
4865
- this.unselect();
4866
- if (spec) {
4867
- this.dispatch({
4868
- type: 'CHANGE_VIEW_TYPE',
4869
- viewType: spec.type,
4870
- dateMarker,
4871
- });
4872
- }
4873
- else {
4874
- this.dispatch({
4875
- type: 'CHANGE_DATE',
4876
- dateMarker,
4877
- });
4878
- }
4879
- }
4880
- // Given a duration singular unit, like "week" or "day", finds a matching view spec.
4881
- // Preference is given to views that have corresponding buttons.
4882
- getUnitViewSpec(unit) {
4883
- let { viewSpecs, toolbarConfig } = this.getCurrentData();
4884
- let viewTypes = [].concat(toolbarConfig.header ? toolbarConfig.header.viewsWithButtons : [], toolbarConfig.footer ? toolbarConfig.footer.viewsWithButtons : []);
4885
- let i;
4886
- let spec;
4887
- for (let viewType in viewSpecs) {
4888
- viewTypes.push(viewType);
4889
4788
  }
4890
- for (i = 0; i < viewTypes.length; i += 1) {
4891
- spec = viewSpecs[viewTypes[i]];
4892
- if (spec) {
4893
- if (spec.singleUnit === unit) {
4894
- return spec;
4789
+ for (let instanceId in instances) {
4790
+ let instance = instances[instanceId];
4791
+ for (let key of defKeys[instance.defId]) {
4792
+ if (splitStores[key]) { // must have already been created
4793
+ splitStores[key].instances[instanceId] = instance;
4895
4794
  }
4896
4795
  }
4897
4796
  }
4898
- return null;
4797
+ return splitStores;
4899
4798
  }
4900
- // Current Date
4901
- // -----------------------------------------------------------------------------------------------------------------
4902
- prev() {
4903
- this.unselect();
4904
- this.dispatch({ type: 'PREV' });
4799
+ _splitIndividualUi(eventUiBases, defKeys) {
4800
+ let splitHashes = {};
4801
+ for (let defId in eventUiBases) {
4802
+ if (defId) { // not the '' key
4803
+ for (let key of defKeys[defId]) {
4804
+ if (!splitHashes[key]) {
4805
+ splitHashes[key] = {};
4806
+ }
4807
+ splitHashes[key][defId] = eventUiBases[defId];
4808
+ }
4809
+ }
4810
+ }
4811
+ return splitHashes;
4905
4812
  }
4906
- next() {
4907
- this.unselect();
4908
- this.dispatch({ type: 'NEXT' });
4813
+ _splitInteraction(interaction) {
4814
+ let splitStates = {};
4815
+ if (interaction) {
4816
+ let affectedStores = this._splitEventStore(interaction.affectedEvents, this._getKeysForEventDefs(interaction.affectedEvents));
4817
+ // can't rely on defKeys because event data is mutated
4818
+ let mutatedKeysByDefId = this._getKeysForEventDefs(interaction.mutatedEvents);
4819
+ let mutatedStores = this._splitEventStore(interaction.mutatedEvents, mutatedKeysByDefId);
4820
+ let populate = (key) => {
4821
+ if (!splitStates[key]) {
4822
+ splitStates[key] = {
4823
+ affectedEvents: affectedStores[key] || EMPTY_EVENT_STORE,
4824
+ mutatedEvents: mutatedStores[key] || EMPTY_EVENT_STORE,
4825
+ isEvent: interaction.isEvent,
4826
+ };
4827
+ }
4828
+ };
4829
+ for (let key in affectedStores) {
4830
+ populate(key);
4831
+ }
4832
+ for (let key in mutatedStores) {
4833
+ populate(key);
4834
+ }
4835
+ }
4836
+ return splitStates;
4909
4837
  }
4910
- prevYear() {
4911
- let state = this.getCurrentData();
4912
- this.unselect();
4913
- this.dispatch({
4914
- type: 'CHANGE_DATE',
4915
- dateMarker: state.dateEnv.addYears(state.currentDate, -1),
4916
- });
4838
+ }
4839
+ function buildEventUiForKey(allUi, eventUiForKey, individualUi) {
4840
+ let baseParts = [];
4841
+ if (allUi) {
4842
+ baseParts.push(allUi);
4917
4843
  }
4918
- nextYear() {
4919
- let state = this.getCurrentData();
4920
- this.unselect();
4921
- this.dispatch({
4922
- type: 'CHANGE_DATE',
4923
- dateMarker: state.dateEnv.addYears(state.currentDate, 1),
4924
- });
4844
+ if (eventUiForKey) {
4845
+ baseParts.push(eventUiForKey);
4925
4846
  }
4926
- today() {
4927
- let state = this.getCurrentData();
4928
- this.unselect();
4929
- this.dispatch({
4930
- type: 'CHANGE_DATE',
4931
- dateMarker: getNow(state.calendarOptions.now, state.dateEnv),
4932
- });
4847
+ let stuff = {
4848
+ '': combineEventUis(baseParts),
4849
+ };
4850
+ if (individualUi) {
4851
+ Object.assign(stuff, individualUi);
4933
4852
  }
4934
- gotoDate(zonedDateInput) {
4935
- let state = this.getCurrentData();
4936
- this.unselect();
4937
- this.dispatch({
4938
- type: 'CHANGE_DATE',
4939
- dateMarker: state.dateEnv.createMarker(zonedDateInput),
4940
- });
4853
+ return stuff;
4854
+ }
4855
+
4856
+ function getDateMeta(date, todayRange, nowDate, dateProfile) {
4857
+ return {
4858
+ dow: date.getUTCDay(),
4859
+ isDisabled: Boolean(dateProfile && !rangeContainsMarker(dateProfile.activeRange, date)),
4860
+ isOther: Boolean(dateProfile && !rangeContainsMarker(dateProfile.currentRange, date)),
4861
+ isToday: Boolean(todayRange && rangeContainsMarker(todayRange, date)),
4862
+ isPast: Boolean(nowDate ? (date < nowDate) : todayRange ? (date < todayRange.start) : false),
4863
+ isFuture: Boolean(nowDate ? (date > nowDate) : todayRange ? (date >= todayRange.end) : false),
4864
+ };
4865
+ }
4866
+ function getDayClassNames(meta, theme) {
4867
+ let classNames = [
4868
+ 'fc-day',
4869
+ `fc-day-${DAY_IDS[meta.dow]}`,
4870
+ ];
4871
+ if (meta.isDisabled) {
4872
+ classNames.push('fc-day-disabled');
4941
4873
  }
4942
- incrementDate(deltaInput) {
4943
- let state = this.getCurrentData();
4944
- let delta = createDuration(deltaInput);
4945
- if (delta) { // else, warn about invalid input?
4946
- this.unselect();
4947
- this.dispatch({
4948
- type: 'CHANGE_DATE',
4949
- dateMarker: state.dateEnv.add(state.currentDate, delta),
4950
- });
4874
+ else {
4875
+ if (meta.isToday) {
4876
+ classNames.push('fc-day-today');
4877
+ }
4878
+ if (meta.isPast) {
4879
+ classNames.push('fc-day-past');
4880
+ }
4881
+ if (meta.isFuture) {
4882
+ classNames.push('fc-day-future');
4883
+ }
4884
+ if (meta.isOther) {
4885
+ classNames.push('fc-day-other');
4951
4886
  }
4952
4887
  }
4953
- getDate() {
4954
- let state = this.getCurrentData();
4955
- return state.dateEnv.toDate(state.currentDate);
4956
- }
4957
- // Date Formatting Utils
4958
- // -----------------------------------------------------------------------------------------------------------------
4959
- formatDate(d, formatter) {
4960
- let { dateEnv } = this.getCurrentData();
4961
- return dateEnv.format(dateEnv.createMarker(d), createFormatter(formatter));
4888
+ return classNames;
4889
+ }
4890
+ function getSlotClassNames(meta, theme) {
4891
+ let classNames = [
4892
+ 'fc-slot',
4893
+ `fc-slot-${DAY_IDS[meta.dow]}`,
4894
+ ];
4895
+ if (meta.isDisabled) {
4896
+ classNames.push('fc-slot-disabled');
4962
4897
  }
4963
- // `settings` is for formatter AND isEndExclusive
4964
- formatRange(d0, d1, settings) {
4965
- let { dateEnv } = this.getCurrentData();
4966
- return dateEnv.formatRange(dateEnv.createMarker(d0), dateEnv.createMarker(d1), createFormatter(settings), settings);
4898
+ else {
4899
+ if (meta.isToday) {
4900
+ classNames.push('fc-slot-today');
4901
+ }
4902
+ if (meta.isPast) {
4903
+ classNames.push('fc-slot-past');
4904
+ }
4905
+ if (meta.isFuture) {
4906
+ classNames.push('fc-slot-future');
4907
+ }
4967
4908
  }
4968
- formatIso(d, omitTime) {
4969
- let { dateEnv } = this.getCurrentData();
4970
- return dateEnv.formatIso(dateEnv.createMarker(d), { omitTime });
4909
+ return classNames;
4910
+ }
4911
+
4912
+ // TODO: kill Component::safeSetState
4913
+ // TODO: kill
4914
+ function setStateDimMap(component, key, newMap) {
4915
+ const currentMap = component.state[key];
4916
+ if (!currentMap || !isDimMapsEqual(currentMap, newMap)) {
4917
+ component.setState({ [key]: newMap });
4918
+ }
4919
+ }
4920
+ // TODO: kill
4921
+ function isDimMapsEqual(oldMap, newMap) {
4922
+ for (const key in newMap) {
4923
+ if (!isDimsEqual(oldMap[key], newMap[key])) {
4924
+ return false;
4925
+ }
4971
4926
  }
4972
- // Date Selection / Event Selection / DayClick
4973
- // -----------------------------------------------------------------------------------------------------------------
4974
- select(dateOrObj, endDate) {
4975
- let selectionInput;
4976
- if (endDate == null) {
4977
- if (dateOrObj.start != null) {
4978
- selectionInput = dateOrObj;
4927
+ return true;
4928
+ }
4929
+ // TODO: kill
4930
+ function isDimsEqual(v0, v1) {
4931
+ return v0 != null && (v0 === v1 || Math.abs(v0 - v1) < 0.01);
4932
+ }
4933
+
4934
+ const callbackMap = new Map();
4935
+ let flushedCallbackSet = new Set();
4936
+ let isHandling = false;
4937
+ /*
4938
+ Performant from multiple perspectives:
4939
+ - less memory with one ResizeObserver
4940
+ - batches firing
4941
+ */
4942
+ const resizeObserver = new ResizeObserver((entries) => {
4943
+ isHandling = true;
4944
+ for (let entry of entries) {
4945
+ const callback = callbackMap.get(entry.target);
4946
+ if (entry.contentBoxSize) {
4947
+ // The standard makes contentBoxSize an array...
4948
+ if (entry.contentBoxSize[0]) {
4949
+ callback(entry.contentBoxSize[0].inlineSize, entry.contentBoxSize[0].blockSize);
4979
4950
  }
4980
4951
  else {
4981
- selectionInput = {
4982
- start: dateOrObj,
4983
- end: null,
4984
- };
4952
+ // ...but old versions of Firefox treat it as a single item
4953
+ callback(entry.contentBoxSize.inlineSize, entry.contentBoxSize.blockSize);
4985
4954
  }
4986
4955
  }
4987
4956
  else {
4988
- selectionInput = {
4989
- start: dateOrObj,
4990
- end: endDate,
4991
- };
4992
- }
4993
- let state = this.getCurrentData();
4994
- let selection = parseDateSpan(selectionInput, state.dateEnv, createDuration({ days: 1 }));
4995
- if (selection) { // throw parse error otherwise?
4996
- this.dispatch({ type: 'SELECT_DATES', selection });
4997
- triggerDateSelect(selection, null, state);
4957
+ callback(entry.contentRect.width, entry.contentRect.height);
4998
4958
  }
4999
4959
  }
5000
- unselect(pev) {
5001
- let state = this.getCurrentData();
5002
- if (state.dateSelection) {
5003
- this.dispatch({ type: 'UNSELECT_DATES' });
5004
- triggerDateUnselect(pev, state);
5005
- }
4960
+ for (const flushedCallback of flushedCallbackSet.values()) {
4961
+ flushedCallback();
4962
+ flushedCallbackSet.delete(flushedCallback);
5006
4963
  }
5007
- // Public Events API
5008
- // -----------------------------------------------------------------------------------------------------------------
5009
- addEvent(eventInput, sourceInput) {
5010
- if (eventInput instanceof EventImpl) {
5011
- let def = eventInput._def;
5012
- let instance = eventInput._instance;
5013
- let currentData = this.getCurrentData();
5014
- // not already present? don't want to add an old snapshot
5015
- if (!currentData.eventStore.defs[def.defId]) {
5016
- this.dispatch({
5017
- type: 'ADD_EVENTS',
5018
- eventStore: eventTupleToStore({ def, instance }), // TODO: better util for two args?
5019
- });
5020
- this.triggerEventAdd(eventInput);
5021
- }
5022
- return eventInput;
5023
- }
5024
- let state = this.getCurrentData();
5025
- let eventSource;
5026
- if (sourceInput instanceof EventSourceImpl) {
5027
- eventSource = sourceInput.internalEventSource;
5028
- }
5029
- else if (typeof sourceInput === 'boolean') {
5030
- if (sourceInput) { // true. part of the first event source
5031
- [eventSource] = hashValuesToArray(state.eventSources);
5032
- }
5033
- }
5034
- else if (sourceInput != null) { // an ID. accepts a number too
5035
- let sourceApi = this.getEventSourceById(sourceInput); // TODO: use an internal function
5036
- if (!sourceApi) {
5037
- console.warn(`Could not find an event source with ID "${sourceInput}"`); // TODO: test
5038
- return null;
5039
- }
5040
- eventSource = sourceApi.internalEventSource;
4964
+ isHandling = false;
4965
+ });
4966
+ /*
4967
+ PRECONDITIONS:
4968
+ - element can only have one listener attached ever
4969
+ - element cannot have border or padding
4970
+
4971
+ TODO:
4972
+ - always force border/padding on these elements to `0 !important` ???
4973
+ */
4974
+ function watchSize(el, callback) {
4975
+ callbackMap.set(el, callback);
4976
+ resizeObserver.observe(el);
4977
+ return () => {
4978
+ callbackMap.delete(el);
4979
+ resizeObserver.unobserve(el);
4980
+ };
4981
+ }
4982
+ function watchWidth(el, callback) {
4983
+ let currentWidth;
4984
+ return watchSize(el, (width) => {
4985
+ if (currentWidth == null || currentWidth !== width) {
4986
+ callback(currentWidth = width);
5041
4987
  }
5042
- let tuple = parseEvent(eventInput, eventSource, state, false);
5043
- if (tuple) {
5044
- let newEventApi = new EventImpl(state, tuple.def, tuple.def.recurringDef ? null : tuple.instance);
5045
- this.dispatch({
5046
- type: 'ADD_EVENTS',
5047
- eventStore: eventTupleToStore(tuple),
5048
- });
5049
- this.triggerEventAdd(newEventApi);
5050
- return newEventApi;
4988
+ });
4989
+ }
4990
+ function watchHeight(el, callback) {
4991
+ let currentHeight;
4992
+ return watchSize(el, (_width, height) => {
4993
+ if (currentHeight == null || currentHeight !== height) {
4994
+ callback(currentHeight = height);
5051
4995
  }
5052
- return null;
4996
+ });
4997
+ }
4998
+ function afterSize(callback) {
4999
+ if (isHandling) {
5000
+ flushedCallbackSet.add(callback);
5053
5001
  }
5054
- triggerEventAdd(eventApi) {
5055
- let { emitter } = this.getCurrentData();
5056
- emitter.trigger('eventAdd', {
5057
- event: eventApi,
5058
- relatedEvents: [],
5059
- revert: () => {
5060
- this.dispatch({
5061
- type: 'REMOVE_EVENTS',
5062
- eventStore: eventApiToStore(eventApi),
5063
- });
5064
- },
5065
- });
5066
- }
5067
- // TODO: optimize
5068
- getEventById(id) {
5069
- let state = this.getCurrentData();
5070
- let { defs, instances } = state.eventStore;
5071
- id = String(id);
5072
- for (let defId in defs) {
5073
- let def = defs[defId];
5074
- if (def.publicId === id) {
5075
- if (def.recurringDef) {
5076
- return new EventImpl(state, def, null);
5077
- }
5078
- for (let instanceId in instances) {
5079
- let instance = instances[instanceId];
5080
- if (instance.defId === def.defId) {
5081
- return new EventImpl(state, def, instance);
5082
- }
5083
- }
5084
- }
5085
- }
5086
- return null;
5087
- }
5088
- getEvents() {
5089
- let currentData = this.getCurrentData();
5090
- return buildEventApis(currentData.eventStore, currentData);
5091
- }
5092
- removeAllEvents() {
5093
- this.dispatch({ type: 'REMOVE_ALL_EVENTS' });
5094
- }
5095
- // Public Event Sources API
5096
- // -----------------------------------------------------------------------------------------------------------------
5097
- getEventSources() {
5098
- let state = this.getCurrentData();
5099
- let sourceHash = state.eventSources;
5100
- let sourceApis = [];
5101
- for (let internalId in sourceHash) {
5102
- sourceApis.push(new EventSourceImpl(state, sourceHash[internalId]));
5103
- }
5104
- return sourceApis;
5002
+ else {
5003
+ callback(); // TODO: should we queue microtask?
5105
5004
  }
5106
- getEventSourceById(id) {
5107
- let state = this.getCurrentData();
5108
- let sourceHash = state.eventSources;
5109
- id = String(id);
5110
- for (let sourceId in sourceHash) {
5111
- if (sourceHash[sourceId].publicId === id) {
5112
- return new EventSourceImpl(state, sourceHash[sourceId]);
5005
+ }
5006
+
5007
+ const DAY_FORMAT = createFormatter({ year: 'numeric', month: 'long', day: 'numeric' });
5008
+ const WEEK_FORMAT = createFormatter({ week: 'long' });
5009
+ function buildNavLinkAttrs(context, dateMarker, viewType = 'day', isTabbable = true) {
5010
+ const { dateEnv, options, calendarApi } = context;
5011
+ let dateStr = dateEnv.format(dateMarker, viewType === 'week' ? WEEK_FORMAT : DAY_FORMAT);
5012
+ if (options.navLinks) {
5013
+ let zonedDate = dateEnv.toDate(dateMarker);
5014
+ const handleInteraction = (ev) => {
5015
+ let customAction = viewType === 'day' ? options.navLinkDayClick :
5016
+ viewType === 'week' ? options.navLinkWeekClick : null;
5017
+ if (typeof customAction === 'function') {
5018
+ customAction.call(calendarApi, dateEnv.toDate(dateMarker), ev);
5113
5019
  }
5114
- }
5115
- return null;
5116
- }
5117
- addEventSource(sourceInput) {
5118
- let state = this.getCurrentData();
5119
- if (sourceInput instanceof EventSourceImpl) {
5120
- // not already present? don't want to add an old snapshot
5121
- if (!state.eventSources[sourceInput.internalEventSource.sourceId]) {
5122
- this.dispatch({
5123
- type: 'ADD_EVENT_SOURCES',
5124
- sources: [sourceInput.internalEventSource],
5125
- });
5020
+ else {
5021
+ if (typeof customAction === 'string') {
5022
+ viewType = customAction;
5023
+ }
5024
+ calendarApi.zoomTo(dateMarker, viewType);
5126
5025
  }
5127
- return sourceInput;
5128
- }
5129
- let eventSource = parseEventSource(sourceInput, state);
5130
- if (eventSource) { // TODO: error otherwise?
5131
- this.dispatch({ type: 'ADD_EVENT_SOURCES', sources: [eventSource] });
5132
- return new EventSourceImpl(state, eventSource);
5133
- }
5134
- return null;
5135
- }
5136
- removeAllEventSources() {
5137
- this.dispatch({ type: 'REMOVE_ALL_EVENT_SOURCES' });
5138
- }
5139
- refetchEvents() {
5140
- this.dispatch({ type: 'FETCH_EVENT_SOURCES', isRefetch: true });
5141
- }
5142
- // Scroll
5143
- // -----------------------------------------------------------------------------------------------------------------
5144
- scrollToTime(timeInput) {
5145
- let time = createDuration(timeInput);
5146
- if (time) {
5147
- this.trigger('_scrollRequest', { time });
5148
- }
5026
+ };
5027
+ return Object.assign({ title: formatWithOrdinals(options.navLinkHint, [dateStr, zonedDate], dateStr), 'data-navlink': '' }, (isTabbable
5028
+ ? createAriaClickAttrs(handleInteraction)
5029
+ : { onClick: handleInteraction }));
5149
5030
  }
5031
+ return { 'aria-label': dateStr };
5150
5032
  }
5151
5033
 
5152
- function pointInsideRect(point, rect) {
5153
- return point.left >= rect.left &&
5154
- point.left < rect.right &&
5155
- point.top >= rect.top &&
5156
- point.top < rect.bottom;
5034
+ let _isRtlScrollbarOnLeft = null;
5035
+ function getIsRtlScrollbarOnLeft() {
5036
+ if (_isRtlScrollbarOnLeft === null) {
5037
+ _isRtlScrollbarOnLeft = computeIsRtlScrollbarOnLeft();
5038
+ }
5039
+ return _isRtlScrollbarOnLeft;
5157
5040
  }
5158
- // Returns a new rectangle that is the intersection of the two rectangles. If they don't intersect, returns false
5159
- function intersectRects(rect1, rect2) {
5160
- let res = {
5161
- left: Math.max(rect1.left, rect2.left),
5162
- right: Math.min(rect1.right, rect2.right),
5163
- top: Math.max(rect1.top, rect2.top),
5164
- bottom: Math.min(rect1.bottom, rect2.bottom),
5165
- };
5166
- if (res.left < res.right && res.top < res.bottom) {
5167
- return res;
5041
+ function computeIsRtlScrollbarOnLeft() {
5042
+ let outerEl = document.createElement('div');
5043
+ applyStyle(outerEl, {
5044
+ position: 'absolute',
5045
+ top: -1000,
5046
+ left: 0,
5047
+ border: 0,
5048
+ padding: 0,
5049
+ overflow: 'scroll',
5050
+ direction: 'rtl',
5051
+ });
5052
+ outerEl.innerHTML = '<div></div>';
5053
+ document.body.appendChild(outerEl);
5054
+ let innerEl = outerEl.firstChild;
5055
+ let res = innerEl.getBoundingClientRect().left > outerEl.getBoundingClientRect().left;
5056
+ removeElement(outerEl);
5057
+ return res;
5058
+ }
5059
+
5060
+ let _scrollbarWidths;
5061
+ function getScrollbarWidths() {
5062
+ if (!_scrollbarWidths) {
5063
+ _scrollbarWidths = computeScrollbarWidths();
5168
5064
  }
5169
- return false;
5065
+ return _scrollbarWidths;
5170
5066
  }
5171
- function translateRect(rect, deltaX, deltaY) {
5067
+ function computeScrollbarWidths() {
5068
+ let el = document.createElement('div');
5069
+ el.style.overflow = 'scroll';
5070
+ el.style.position = 'absolute';
5071
+ el.style.top = '-9999px';
5072
+ el.style.left = '-9999px';
5073
+ document.body.appendChild(el);
5074
+ let res = computeScrollbarWidthsForEl(el);
5075
+ document.body.removeChild(el);
5076
+ return res;
5077
+ }
5078
+ // WARNING: will include border
5079
+ function computeScrollbarWidthsForEl(el) {
5172
5080
  return {
5173
- left: rect.left + deltaX,
5174
- right: rect.right + deltaX,
5175
- top: rect.top + deltaY,
5176
- bottom: rect.bottom + deltaY,
5081
+ x: el.offsetHeight - el.clientHeight,
5082
+ y: el.offsetWidth - el.clientWidth,
5177
5083
  };
5178
5084
  }
5179
- // Returns a new point that will have been moved to reside within the given rectangle
5180
- function constrainPoint(point, rect) {
5181
- return {
5182
- left: Math.min(Math.max(point.left, rect.left), rect.right),
5183
- top: Math.min(Math.max(point.top, rect.top), rect.bottom),
5085
+
5086
+ function computeEdges(el, getPadding = false) {
5087
+ let computedStyle = window.getComputedStyle(el);
5088
+ let borderLeft = parseInt(computedStyle.borderLeftWidth, 10) || 0;
5089
+ let borderRight = parseInt(computedStyle.borderRightWidth, 10) || 0;
5090
+ let borderTop = parseInt(computedStyle.borderTopWidth, 10) || 0;
5091
+ let borderBottom = parseInt(computedStyle.borderBottomWidth, 10) || 0;
5092
+ let badScrollbarWidths = computeScrollbarWidthsForEl(el); // includes border!
5093
+ let scrollbarLeftRight = badScrollbarWidths.y - borderLeft - borderRight;
5094
+ let scrollbarBottom = badScrollbarWidths.x - borderTop - borderBottom;
5095
+ let res = {
5096
+ borderLeft,
5097
+ borderRight,
5098
+ borderTop,
5099
+ borderBottom,
5100
+ scrollbarBottom,
5101
+ scrollbarLeft: 0,
5102
+ scrollbarRight: 0,
5184
5103
  };
5104
+ if (getIsRtlScrollbarOnLeft() && computedStyle.direction === 'rtl') { // is the scrollbar on the left side?
5105
+ res.scrollbarLeft = scrollbarLeftRight;
5106
+ }
5107
+ else {
5108
+ res.scrollbarRight = scrollbarLeftRight;
5109
+ }
5110
+ if (getPadding) {
5111
+ res.paddingLeft = parseInt(computedStyle.paddingLeft, 10) || 0;
5112
+ res.paddingRight = parseInt(computedStyle.paddingRight, 10) || 0;
5113
+ res.paddingTop = parseInt(computedStyle.paddingTop, 10) || 0;
5114
+ res.paddingBottom = parseInt(computedStyle.paddingBottom, 10) || 0;
5115
+ }
5116
+ return res;
5185
5117
  }
5186
- // Returns a point that is the center of the given rectangle
5187
- function getRectCenter(rect) {
5188
- return {
5189
- left: (rect.left + rect.right) / 2,
5190
- top: (rect.top + rect.bottom) / 2,
5118
+ function computeInnerRect(el, goWithinPadding = false, doFromWindowViewport) {
5119
+ let outerRect = doFromWindowViewport ? el.getBoundingClientRect() : computeRect(el);
5120
+ let edges = computeEdges(el, goWithinPadding);
5121
+ let res = {
5122
+ left: outerRect.left + edges.borderLeft + edges.scrollbarLeft,
5123
+ right: outerRect.right - edges.borderRight - edges.scrollbarRight,
5124
+ top: outerRect.top + edges.borderTop,
5125
+ bottom: outerRect.bottom - edges.borderBottom - edges.scrollbarBottom,
5191
5126
  };
5127
+ if (goWithinPadding) {
5128
+ res.left += edges.paddingLeft;
5129
+ res.right -= edges.paddingRight;
5130
+ res.top += edges.paddingTop;
5131
+ res.bottom -= edges.paddingBottom;
5132
+ }
5133
+ return res;
5192
5134
  }
5193
- // Subtracts point2's coordinates from point1's coordinates, returning a delta
5194
- function diffPoints(point1, point2) {
5135
+ function computeRect(el) {
5136
+ let rect = el.getBoundingClientRect();
5195
5137
  return {
5196
- left: point1.left - point2.left,
5197
- top: point1.top - point2.top,
5138
+ left: rect.left + window.scrollX,
5139
+ top: rect.top + window.scrollY,
5140
+ right: rect.right + window.scrollX,
5141
+ bottom: rect.bottom + window.scrollY,
5198
5142
  };
5199
5143
  }
5200
-
5201
- const EMPTY_EVENT_STORE = createEmptyEventStore(); // for purecomponents. TODO: keep elsewhere
5202
- class Splitter {
5203
- constructor() {
5204
- this.getKeysForEventDefs = memoize(this._getKeysForEventDefs);
5205
- this.splitDateSelection = memoize(this._splitDateSpan);
5206
- this.splitEventStore = memoize(this._splitEventStore);
5207
- this.splitIndividualUi = memoize(this._splitIndividualUi);
5208
- this.splitEventDrag = memoize(this._splitInteraction);
5209
- this.splitEventResize = memoize(this._splitInteraction);
5210
- this.eventUiBuilders = {}; // TODO: typescript protection
5211
- }
5212
- splitProps(props) {
5213
- let keyInfos = this.getKeyInfo(props);
5214
- let defKeys = this.getKeysForEventDefs(props.eventStore);
5215
- let dateSelections = this.splitDateSelection(props.dateSelection);
5216
- let individualUi = this.splitIndividualUi(props.eventUiBases, defKeys); // the individual *bases*
5217
- let eventStores = this.splitEventStore(props.eventStore, defKeys);
5218
- let eventDrags = this.splitEventDrag(props.eventDrag);
5219
- let eventResizes = this.splitEventResize(props.eventResize);
5220
- let splitProps = {};
5221
- this.eventUiBuilders = mapHash(keyInfos, (info, key) => this.eventUiBuilders[key] || memoize(buildEventUiForKey));
5222
- for (let key in keyInfos) {
5223
- let keyInfo = keyInfos[key];
5224
- let eventStore = eventStores[key] || EMPTY_EVENT_STORE;
5225
- let buildEventUi = this.eventUiBuilders[key];
5226
- splitProps[key] = {
5227
- businessHours: keyInfo.businessHours || props.businessHours,
5228
- dateSelection: dateSelections[key] || null,
5229
- eventStore,
5230
- eventUiBases: buildEventUi(props.eventUiBases[''], keyInfo.ui, individualUi[key]),
5231
- eventSelection: eventStore.instances[props.eventSelection] ? props.eventSelection : '',
5232
- eventDrag: eventDrags[key] || null,
5233
- eventResize: eventResizes[key] || null,
5234
- };
5144
+ function computeClippedClientRect(el) {
5145
+ let clippingParents = getClippingParents(el);
5146
+ let rect = el.getBoundingClientRect();
5147
+ for (let clippingParent of clippingParents) {
5148
+ let intersection = intersectRects(rect, clippingParent.getBoundingClientRect());
5149
+ if (intersection) {
5150
+ rect = intersection;
5151
+ }
5152
+ else {
5153
+ return null;
5235
5154
  }
5236
- return splitProps;
5237
5155
  }
5238
- _splitDateSpan(dateSpan) {
5239
- let dateSpans = {};
5240
- if (dateSpan) {
5241
- let keys = this.getKeysForDateSpan(dateSpan);
5242
- for (let key of keys) {
5243
- dateSpans[key] = dateSpan;
5244
- }
5156
+ return rect;
5157
+ }
5158
+ // does not return window
5159
+ function getClippingParents(el) {
5160
+ let parents = [];
5161
+ while (el instanceof HTMLElement) { // will stop when gets to document or null
5162
+ let computedStyle = window.getComputedStyle(el);
5163
+ if (computedStyle.position === 'fixed') {
5164
+ break;
5245
5165
  }
5246
- return dateSpans;
5166
+ if ((/(auto|scroll)/).test(computedStyle.overflow + computedStyle.overflowY + computedStyle.overflowX)) {
5167
+ parents.push(el);
5168
+ }
5169
+ el = el.parentNode;
5247
5170
  }
5248
- _getKeysForEventDefs(eventStore) {
5249
- return mapHash(eventStore.defs, (eventDef) => this.getKeysForEventDef(eventDef));
5171
+ return parents;
5172
+ }
5173
+
5174
+ /*
5175
+ Records offset information for a set of elements, relative to an origin element.
5176
+ Can record the left/right OR the top/bottom OR both.
5177
+ Provides methods for querying the cache by position.
5178
+ */
5179
+ class PositionCache {
5180
+ constructor(originEl, els, isHorizontal, isVertical) {
5181
+ this.els = els;
5182
+ let originClientRect = this.originClientRect = originEl.getBoundingClientRect(); // relative to viewport top-left
5183
+ if (isHorizontal) {
5184
+ this.buildElHorizontals(originClientRect.left);
5185
+ }
5186
+ if (isVertical) {
5187
+ this.buildElVerticals(originClientRect.top);
5188
+ }
5250
5189
  }
5251
- _splitEventStore(eventStore, defKeys) {
5252
- let { defs, instances } = eventStore;
5253
- let splitStores = {};
5254
- for (let defId in defs) {
5255
- for (let key of defKeys[defId]) {
5256
- if (!splitStores[key]) {
5257
- splitStores[key] = createEmptyEventStore();
5258
- }
5259
- splitStores[key].defs[defId] = defs[defId];
5260
- }
5190
+ // Populates the left/right internal coordinate arrays
5191
+ buildElHorizontals(originClientLeft) {
5192
+ let lefts = [];
5193
+ let rights = [];
5194
+ for (let el of this.els) {
5195
+ let rect = el.getBoundingClientRect();
5196
+ lefts.push(rect.left - originClientLeft);
5197
+ rights.push(rect.right - originClientLeft);
5261
5198
  }
5262
- for (let instanceId in instances) {
5263
- let instance = instances[instanceId];
5264
- for (let key of defKeys[instance.defId]) {
5265
- if (splitStores[key]) { // must have already been created
5266
- splitStores[key].instances[instanceId] = instance;
5267
- }
5268
- }
5199
+ this.lefts = lefts;
5200
+ this.rights = rights;
5201
+ }
5202
+ // Populates the top/bottom internal coordinate arrays
5203
+ buildElVerticals(originClientTop) {
5204
+ let tops = [];
5205
+ let bottoms = [];
5206
+ for (let el of this.els) {
5207
+ let rect = el.getBoundingClientRect();
5208
+ tops.push(rect.top - originClientTop);
5209
+ bottoms.push(rect.bottom - originClientTop);
5269
5210
  }
5270
- return splitStores;
5211
+ this.tops = tops;
5212
+ this.bottoms = bottoms;
5271
5213
  }
5272
- _splitIndividualUi(eventUiBases, defKeys) {
5273
- let splitHashes = {};
5274
- for (let defId in eventUiBases) {
5275
- if (defId) { // not the '' key
5276
- for (let key of defKeys[defId]) {
5277
- if (!splitHashes[key]) {
5278
- splitHashes[key] = {};
5279
- }
5280
- splitHashes[key][defId] = eventUiBases[defId];
5281
- }
5214
+ // Given a left offset (from document left), returns the index of the el that it horizontally intersects.
5215
+ // If no intersection is made, returns undefined.
5216
+ leftToIndex(leftPosition) {
5217
+ let { lefts, rights } = this;
5218
+ let len = lefts.length;
5219
+ let i;
5220
+ for (i = 0; i < len; i += 1) {
5221
+ if (leftPosition >= lefts[i] && leftPosition < rights[i]) {
5222
+ return i;
5282
5223
  }
5283
5224
  }
5284
- return splitHashes;
5225
+ return undefined; // TODO: better
5285
5226
  }
5286
- _splitInteraction(interaction) {
5287
- let splitStates = {};
5288
- if (interaction) {
5289
- let affectedStores = this._splitEventStore(interaction.affectedEvents, this._getKeysForEventDefs(interaction.affectedEvents));
5290
- // can't rely on defKeys because event data is mutated
5291
- let mutatedKeysByDefId = this._getKeysForEventDefs(interaction.mutatedEvents);
5292
- let mutatedStores = this._splitEventStore(interaction.mutatedEvents, mutatedKeysByDefId);
5293
- let populate = (key) => {
5294
- if (!splitStates[key]) {
5295
- splitStates[key] = {
5296
- affectedEvents: affectedStores[key] || EMPTY_EVENT_STORE,
5297
- mutatedEvents: mutatedStores[key] || EMPTY_EVENT_STORE,
5298
- isEvent: interaction.isEvent,
5299
- };
5300
- }
5301
- };
5302
- for (let key in affectedStores) {
5303
- populate(key);
5304
- }
5305
- for (let key in mutatedStores) {
5306
- populate(key);
5227
+ // Given a top offset (from document top), returns the index of the el that it vertically intersects.
5228
+ // If no intersection is made, returns undefined.
5229
+ topToIndex(topPosition) {
5230
+ let { tops, bottoms } = this;
5231
+ let len = tops.length;
5232
+ let i;
5233
+ for (i = 0; i < len; i += 1) {
5234
+ if (topPosition >= tops[i] && topPosition < bottoms[i]) {
5235
+ return i;
5307
5236
  }
5308
5237
  }
5309
- return splitStates;
5310
- }
5311
- }
5312
- function buildEventUiForKey(allUi, eventUiForKey, individualUi) {
5313
- let baseParts = [];
5314
- if (allUi) {
5315
- baseParts.push(allUi);
5316
- }
5317
- if (eventUiForKey) {
5318
- baseParts.push(eventUiForKey);
5238
+ return undefined; // TODO: better
5319
5239
  }
5320
- let stuff = {
5321
- '': combineEventUis(baseParts),
5322
- };
5323
- if (individualUi) {
5324
- Object.assign(stuff, individualUi);
5240
+ // Gets the width of the element at the given index
5241
+ getWidth(leftIndex) {
5242
+ return this.rights[leftIndex] - this.lefts[leftIndex];
5325
5243
  }
5326
- return stuff;
5327
- }
5328
-
5329
- function getDateMeta(date, todayRange, nowDate, dateProfile) {
5330
- return {
5331
- dow: date.getUTCDay(),
5332
- isDisabled: Boolean(dateProfile && !rangeContainsMarker(dateProfile.activeRange, date)),
5333
- isOther: Boolean(dateProfile && !rangeContainsMarker(dateProfile.currentRange, date)),
5334
- isToday: Boolean(todayRange && rangeContainsMarker(todayRange, date)),
5335
- isPast: Boolean(nowDate ? (date < nowDate) : todayRange ? (date < todayRange.start) : false),
5336
- isFuture: Boolean(nowDate ? (date > nowDate) : todayRange ? (date >= todayRange.end) : false),
5337
- };
5338
- }
5339
- function getDayClassNames(meta, theme) {
5340
- let classNames = [
5341
- 'fc-day',
5342
- `fc-day-${DAY_IDS[meta.dow]}`,
5343
- ];
5344
- if (meta.isDisabled) {
5345
- classNames.push('fc-day-disabled');
5244
+ // Gets the height of the element at the given index
5245
+ getHeight(topIndex) {
5246
+ return this.bottoms[topIndex] - this.tops[topIndex];
5346
5247
  }
5347
- else {
5348
- if (meta.isToday) {
5349
- classNames.push('fc-day-today');
5350
- classNames.push(theme.getClass('today'));
5351
- }
5352
- if (meta.isPast) {
5353
- classNames.push('fc-day-past');
5354
- }
5355
- if (meta.isFuture) {
5356
- classNames.push('fc-day-future');
5357
- }
5358
- if (meta.isOther) {
5359
- classNames.push('fc-day-other');
5360
- }
5248
+ similarTo(otherCache) {
5249
+ return similarNumArrays(this.tops || [], otherCache.tops || []) &&
5250
+ similarNumArrays(this.bottoms || [], otherCache.bottoms || []) &&
5251
+ similarNumArrays(this.lefts || [], otherCache.lefts || []) &&
5252
+ similarNumArrays(this.rights || [], otherCache.rights || []);
5361
5253
  }
5362
- return classNames;
5363
5254
  }
5364
- function getSlotClassNames(meta, theme) {
5365
- let classNames = [
5366
- 'fc-slot',
5367
- `fc-slot-${DAY_IDS[meta.dow]}`,
5368
- ];
5369
- if (meta.isDisabled) {
5370
- classNames.push('fc-slot-disabled');
5255
+ function similarNumArrays(a, b) {
5256
+ const len = a.length;
5257
+ if (len !== b.length) {
5258
+ return false;
5371
5259
  }
5372
- else {
5373
- if (meta.isToday) {
5374
- classNames.push('fc-slot-today');
5375
- classNames.push(theme.getClass('today'));
5376
- }
5377
- if (meta.isPast) {
5378
- classNames.push('fc-slot-past');
5379
- }
5380
- if (meta.isFuture) {
5381
- classNames.push('fc-slot-future');
5260
+ for (let i = 0; i < len; i++) {
5261
+ if (Math.round(a[i]) !== Math.round(b[i])) {
5262
+ return false;
5382
5263
  }
5383
5264
  }
5384
- return classNames;
5265
+ return true;
5385
5266
  }
5386
5267
 
5387
- const DAY_FORMAT = createFormatter({ year: 'numeric', month: 'long', day: 'numeric' });
5388
- const WEEK_FORMAT = createFormatter({ week: 'long' });
5389
- function buildNavLinkAttrs(context, dateMarker, viewType = 'day', isTabbable = true) {
5390
- const { dateEnv, options, calendarApi } = context;
5391
- let dateStr = dateEnv.format(dateMarker, viewType === 'week' ? WEEK_FORMAT : DAY_FORMAT);
5392
- if (options.navLinks) {
5393
- let zonedDate = dateEnv.toDate(dateMarker);
5394
- const handleInteraction = (ev) => {
5395
- let customAction = viewType === 'day' ? options.navLinkDayClick :
5396
- viewType === 'week' ? options.navLinkWeekClick : null;
5397
- if (typeof customAction === 'function') {
5398
- customAction.call(calendarApi, dateEnv.toDate(dateMarker), ev);
5399
- }
5400
- else {
5401
- if (typeof customAction === 'string') {
5402
- viewType = customAction;
5403
- }
5404
- calendarApi.zoomTo(dateMarker, viewType);
5405
- }
5406
- };
5407
- return Object.assign({ title: formatWithOrdinals(options.navLinkHint, [dateStr, zonedDate], dateStr), 'data-navlink': '' }, (isTabbable
5408
- ? createAriaClickAttrs(handleInteraction)
5409
- : { onClick: handleInteraction }));
5268
+ /* eslint max-classes-per-file: "off" */
5269
+ /*
5270
+ An object for getting/setting scroll-related information for an element.
5271
+ Internally, this is done very differently for window versus DOM element,
5272
+ so this object serves as a common interface.
5273
+ */
5274
+ class ScrollController {
5275
+ getMaxScrollTop() {
5276
+ return this.getScrollHeight() - this.getClientHeight();
5410
5277
  }
5411
- return { 'aria-label': dateStr };
5412
- }
5413
-
5414
- let _isRtlScrollbarOnLeft = null;
5415
- function getIsRtlScrollbarOnLeft() {
5416
- if (_isRtlScrollbarOnLeft === null) {
5417
- _isRtlScrollbarOnLeft = computeIsRtlScrollbarOnLeft();
5278
+ getMaxScrollLeft() {
5279
+ return this.getScrollWidth() - this.getClientWidth();
5418
5280
  }
5419
- return _isRtlScrollbarOnLeft;
5420
- }
5421
- function computeIsRtlScrollbarOnLeft() {
5422
- let outerEl = document.createElement('div');
5423
- applyStyle(outerEl, {
5424
- position: 'absolute',
5425
- top: -1000,
5426
- left: 0,
5427
- border: 0,
5428
- padding: 0,
5429
- overflow: 'scroll',
5430
- direction: 'rtl',
5431
- });
5432
- outerEl.innerHTML = '<div></div>';
5433
- document.body.appendChild(outerEl);
5434
- let innerEl = outerEl.firstChild;
5435
- let res = innerEl.getBoundingClientRect().left > outerEl.getBoundingClientRect().left;
5436
- removeElement(outerEl);
5437
- return res;
5438
- }
5439
-
5440
- let _scrollbarWidths;
5441
- function getScrollbarWidths() {
5442
- if (!_scrollbarWidths) {
5443
- _scrollbarWidths = computeScrollbarWidths();
5281
+ canScrollVertically() {
5282
+ return this.getMaxScrollTop() > 0;
5444
5283
  }
5445
- return _scrollbarWidths;
5446
- }
5447
- function computeScrollbarWidths() {
5448
- let el = document.createElement('div');
5449
- el.style.overflow = 'scroll';
5450
- el.style.position = 'absolute';
5451
- el.style.top = '-9999px';
5452
- el.style.left = '-9999px';
5453
- document.body.appendChild(el);
5454
- let res = computeScrollbarWidthsForEl(el);
5455
- document.body.removeChild(el);
5456
- return res;
5457
- }
5458
- // WARNING: will include border
5459
- function computeScrollbarWidthsForEl(el) {
5460
- return {
5461
- x: el.offsetHeight - el.clientHeight,
5462
- y: el.offsetWidth - el.clientWidth,
5463
- };
5464
- }
5465
-
5466
- function computeEdges(el, getPadding = false) {
5467
- let computedStyle = window.getComputedStyle(el);
5468
- let borderLeft = parseInt(computedStyle.borderLeftWidth, 10) || 0;
5469
- let borderRight = parseInt(computedStyle.borderRightWidth, 10) || 0;
5470
- let borderTop = parseInt(computedStyle.borderTopWidth, 10) || 0;
5471
- let borderBottom = parseInt(computedStyle.borderBottomWidth, 10) || 0;
5472
- let badScrollbarWidths = computeScrollbarWidthsForEl(el); // includes border!
5473
- let scrollbarLeftRight = badScrollbarWidths.y - borderLeft - borderRight;
5474
- let scrollbarBottom = badScrollbarWidths.x - borderTop - borderBottom;
5475
- let res = {
5476
- borderLeft,
5477
- borderRight,
5478
- borderTop,
5479
- borderBottom,
5480
- scrollbarBottom,
5481
- scrollbarLeft: 0,
5482
- scrollbarRight: 0,
5483
- };
5484
- if (getIsRtlScrollbarOnLeft() && computedStyle.direction === 'rtl') { // is the scrollbar on the left side?
5485
- res.scrollbarLeft = scrollbarLeftRight;
5284
+ canScrollHorizontally() {
5285
+ return this.getMaxScrollLeft() > 0;
5486
5286
  }
5487
- else {
5488
- res.scrollbarRight = scrollbarLeftRight;
5287
+ canScrollUp() {
5288
+ return this.getScrollTop() > 0;
5489
5289
  }
5490
- if (getPadding) {
5491
- res.paddingLeft = parseInt(computedStyle.paddingLeft, 10) || 0;
5492
- res.paddingRight = parseInt(computedStyle.paddingRight, 10) || 0;
5493
- res.paddingTop = parseInt(computedStyle.paddingTop, 10) || 0;
5494
- res.paddingBottom = parseInt(computedStyle.paddingBottom, 10) || 0;
5290
+ canScrollDown() {
5291
+ return this.getScrollTop() < this.getMaxScrollTop();
5495
5292
  }
5496
- return res;
5497
- }
5498
- function computeInnerRect(el, goWithinPadding = false, doFromWindowViewport) {
5499
- let outerRect = doFromWindowViewport ? el.getBoundingClientRect() : computeRect(el);
5500
- let edges = computeEdges(el, goWithinPadding);
5501
- let res = {
5502
- left: outerRect.left + edges.borderLeft + edges.scrollbarLeft,
5503
- right: outerRect.right - edges.borderRight - edges.scrollbarRight,
5504
- top: outerRect.top + edges.borderTop,
5505
- bottom: outerRect.bottom - edges.borderBottom - edges.scrollbarBottom,
5506
- };
5507
- if (goWithinPadding) {
5508
- res.left += edges.paddingLeft;
5509
- res.right -= edges.paddingRight;
5510
- res.top += edges.paddingTop;
5511
- res.bottom -= edges.paddingBottom;
5293
+ canScrollLeft() {
5294
+ return this.getScrollLeft() > 0;
5295
+ }
5296
+ canScrollRight() {
5297
+ return this.getScrollLeft() < this.getMaxScrollLeft();
5512
5298
  }
5513
- return res;
5514
- }
5515
- function computeRect(el) {
5516
- let rect = el.getBoundingClientRect();
5517
- return {
5518
- left: rect.left + window.scrollX,
5519
- top: rect.top + window.scrollY,
5520
- right: rect.right + window.scrollX,
5521
- bottom: rect.bottom + window.scrollY,
5522
- };
5523
5299
  }
5524
- function computeClippedClientRect(el) {
5525
- let clippingParents = getClippingParents(el);
5526
- let rect = el.getBoundingClientRect();
5527
- for (let clippingParent of clippingParents) {
5528
- let intersection = intersectRects(rect, clippingParent.getBoundingClientRect());
5529
- if (intersection) {
5530
- rect = intersection;
5531
- }
5532
- else {
5533
- return null;
5534
- }
5300
+ class ElementScrollController extends ScrollController {
5301
+ constructor(el) {
5302
+ super();
5303
+ this.el = el;
5304
+ }
5305
+ getScrollTop() {
5306
+ return this.el.scrollTop;
5307
+ }
5308
+ getScrollLeft() {
5309
+ return this.el.scrollLeft;
5310
+ }
5311
+ setScrollTop(top) {
5312
+ this.el.scrollTop = top;
5313
+ }
5314
+ setScrollLeft(left) {
5315
+ this.el.scrollLeft = left;
5316
+ }
5317
+ getScrollWidth() {
5318
+ return this.el.scrollWidth;
5319
+ }
5320
+ getScrollHeight() {
5321
+ return this.el.scrollHeight;
5322
+ }
5323
+ getClientHeight() {
5324
+ return this.el.clientHeight;
5325
+ }
5326
+ getClientWidth() {
5327
+ return this.el.clientWidth;
5535
5328
  }
5536
- return rect;
5537
5329
  }
5538
- // does not return window
5539
- function getClippingParents(el) {
5540
- let parents = [];
5541
- while (el instanceof HTMLElement) { // will stop when gets to document or null
5542
- let computedStyle = window.getComputedStyle(el);
5543
- if (computedStyle.position === 'fixed') {
5544
- break;
5545
- }
5546
- if ((/(auto|scroll)/).test(computedStyle.overflow + computedStyle.overflowY + computedStyle.overflowX)) {
5547
- parents.push(el);
5548
- }
5549
- el = el.parentNode;
5330
+ class WindowScrollController extends ScrollController {
5331
+ getScrollTop() {
5332
+ return window.scrollY;
5333
+ }
5334
+ getScrollLeft() {
5335
+ return window.scrollX;
5336
+ }
5337
+ setScrollTop(n) {
5338
+ window.scroll(window.scrollX, n);
5339
+ }
5340
+ setScrollLeft(n) {
5341
+ window.scroll(n, window.scrollY);
5342
+ }
5343
+ getScrollWidth() {
5344
+ return document.documentElement.scrollWidth;
5345
+ }
5346
+ getScrollHeight() {
5347
+ return document.documentElement.scrollHeight;
5348
+ }
5349
+ getClientHeight() {
5350
+ return document.documentElement.clientHeight;
5351
+ }
5352
+ getClientWidth() {
5353
+ return document.documentElement.clientWidth;
5550
5354
  }
5551
- return parents;
5552
5355
  }
5553
5356
 
5554
5357
  /*
5555
- Records offset information for a set of elements, relative to an origin element.
5556
- Can record the left/right OR the top/bottom OR both.
5557
- Provides methods for querying the cache by position.
5358
+ an INTERACTABLE date component
5359
+
5360
+ PURPOSES:
5361
+ - hook up to fg, fill, and mirror renderers
5362
+ - interface for dragging and hits
5558
5363
  */
5559
- class PositionCache {
5560
- constructor(originEl, els, isHorizontal, isVertical) {
5561
- this.els = els;
5562
- let originClientRect = this.originClientRect = originEl.getBoundingClientRect(); // relative to viewport top-left
5563
- if (isHorizontal) {
5564
- this.buildElHorizontals(originClientRect.left);
5565
- }
5566
- if (isVertical) {
5567
- this.buildElVerticals(originClientRect.top);
5568
- }
5569
- }
5570
- // Populates the left/right internal coordinate arrays
5571
- buildElHorizontals(originClientLeft) {
5572
- let lefts = [];
5573
- let rights = [];
5574
- for (let el of this.els) {
5575
- let rect = el.getBoundingClientRect();
5576
- lefts.push(rect.left - originClientLeft);
5577
- rights.push(rect.right - originClientLeft);
5578
- }
5579
- this.lefts = lefts;
5580
- this.rights = rights;
5581
- }
5582
- // Populates the top/bottom internal coordinate arrays
5583
- buildElVerticals(originClientTop) {
5584
- let tops = [];
5585
- let bottoms = [];
5586
- for (let el of this.els) {
5587
- let rect = el.getBoundingClientRect();
5588
- tops.push(rect.top - originClientTop);
5589
- bottoms.push(rect.bottom - originClientTop);
5590
- }
5591
- this.tops = tops;
5592
- this.bottoms = bottoms;
5593
- }
5594
- // Given a left offset (from document left), returns the index of the el that it horizontally intersects.
5595
- // If no intersection is made, returns undefined.
5596
- leftToIndex(leftPosition) {
5597
- let { lefts, rights } = this;
5598
- let len = lefts.length;
5599
- let i;
5600
- for (i = 0; i < len; i += 1) {
5601
- if (leftPosition >= lefts[i] && leftPosition < rights[i]) {
5602
- return i;
5603
- }
5604
- }
5605
- return undefined; // TODO: better
5606
- }
5607
- // Given a top offset (from document top), returns the index of the el that it vertically intersects.
5608
- // If no intersection is made, returns undefined.
5609
- topToIndex(topPosition) {
5610
- let { tops, bottoms } = this;
5611
- let len = tops.length;
5612
- let i;
5613
- for (i = 0; i < len; i += 1) {
5614
- if (topPosition >= tops[i] && topPosition < bottoms[i]) {
5615
- return i;
5616
- }
5617
- }
5618
- return undefined; // TODO: better
5619
- }
5620
- // Gets the width of the element at the given index
5621
- getWidth(leftIndex) {
5622
- return this.rights[leftIndex] - this.lefts[leftIndex];
5623
- }
5624
- // Gets the height of the element at the given index
5625
- getHeight(topIndex) {
5626
- return this.bottoms[topIndex] - this.tops[topIndex];
5627
- }
5628
- similarTo(otherCache) {
5629
- return similarNumArrays(this.tops || [], otherCache.tops || []) &&
5630
- similarNumArrays(this.bottoms || [], otherCache.bottoms || []) &&
5631
- similarNumArrays(this.lefts || [], otherCache.lefts || []) &&
5632
- similarNumArrays(this.rights || [], otherCache.rights || []);
5633
- }
5634
- }
5635
- function similarNumArrays(a, b) {
5636
- const len = a.length;
5637
- if (len !== b.length) {
5638
- return false;
5639
- }
5640
- for (let i = 0; i < len; i++) {
5641
- if (Math.round(a[i]) !== Math.round(b[i])) {
5642
- return false;
5643
- }
5644
- }
5645
- return true;
5646
- }
5647
-
5648
- /* eslint max-classes-per-file: "off" */
5649
- /*
5650
- An object for getting/setting scroll-related information for an element.
5651
- Internally, this is done very differently for window versus DOM element,
5652
- so this object serves as a common interface.
5653
- */
5654
- class ScrollController {
5655
- getMaxScrollTop() {
5656
- return this.getScrollHeight() - this.getClientHeight();
5657
- }
5658
- getMaxScrollLeft() {
5659
- return this.getScrollWidth() - this.getClientWidth();
5660
- }
5661
- canScrollVertically() {
5662
- return this.getMaxScrollTop() > 0;
5663
- }
5664
- canScrollHorizontally() {
5665
- return this.getMaxScrollLeft() > 0;
5666
- }
5667
- canScrollUp() {
5668
- return this.getScrollTop() > 0;
5669
- }
5670
- canScrollDown() {
5671
- return this.getScrollTop() < this.getMaxScrollTop();
5672
- }
5673
- canScrollLeft() {
5674
- return this.getScrollLeft() > 0;
5675
- }
5676
- canScrollRight() {
5677
- return this.getScrollLeft() < this.getMaxScrollLeft();
5678
- }
5679
- }
5680
- class ElementScrollController extends ScrollController {
5681
- constructor(el) {
5682
- super();
5683
- this.el = el;
5684
- }
5685
- getScrollTop() {
5686
- return this.el.scrollTop;
5687
- }
5688
- getScrollLeft() {
5689
- return this.el.scrollLeft;
5690
- }
5691
- setScrollTop(top) {
5692
- this.el.scrollTop = top;
5693
- }
5694
- setScrollLeft(left) {
5695
- this.el.scrollLeft = left;
5696
- }
5697
- getScrollWidth() {
5698
- return this.el.scrollWidth;
5699
- }
5700
- getScrollHeight() {
5701
- return this.el.scrollHeight;
5702
- }
5703
- getClientHeight() {
5704
- return this.el.clientHeight;
5705
- }
5706
- getClientWidth() {
5707
- return this.el.clientWidth;
5708
- }
5709
- }
5710
- class WindowScrollController extends ScrollController {
5711
- getScrollTop() {
5712
- return window.scrollY;
5713
- }
5714
- getScrollLeft() {
5715
- return window.scrollX;
5716
- }
5717
- setScrollTop(n) {
5718
- window.scroll(window.scrollX, n);
5719
- }
5720
- setScrollLeft(n) {
5721
- window.scroll(n, window.scrollY);
5722
- }
5723
- getScrollWidth() {
5724
- return document.documentElement.scrollWidth;
5725
- }
5726
- getScrollHeight() {
5727
- return document.documentElement.scrollHeight;
5728
- }
5729
- getClientHeight() {
5730
- return document.documentElement.clientHeight;
5731
- }
5732
- getClientWidth() {
5733
- return document.documentElement.clientWidth;
5734
- }
5735
- }
5736
-
5737
- /*
5738
- an INTERACTABLE date component
5739
-
5740
- PURPOSES:
5741
- - hook up to fg, fill, and mirror renderers
5742
- - interface for dragging and hits
5743
- */
5744
- class DateComponent extends BaseComponent {
5745
- constructor() {
5746
- super(...arguments);
5747
- this.uid = guid();
5364
+ class DateComponent extends BaseComponent {
5365
+ constructor() {
5366
+ super(...arguments);
5367
+ this.uid = guid();
5748
5368
  }
5749
5369
  // Hit System
5750
5370
  // -----------------------------------------------------------------------------------------------------------------
@@ -5774,440 +5394,603 @@ class NamedTimeZoneImpl {
5774
5394
  }
5775
5395
  }
5776
5396
 
5777
- class SegHierarchy {
5778
- constructor(getEntryThickness = (entry) => {
5779
- // if no thickness known, assume 1 (if 0, so small it always fits)
5780
- return entry.thickness || 1;
5781
- }) {
5782
- this.getEntryThickness = getEntryThickness;
5783
- // settings
5784
- this.strictOrder = false;
5785
- this.allowReslicing = false;
5786
- this.maxCoord = -1; // -1 means no max
5787
- this.maxStackCnt = -1; // -1 means no max
5788
- this.levelCoords = []; // ordered
5789
- this.entriesByLevel = []; // parallel with levelCoords
5790
- this.stackCnts = {}; // TODO: use better technique!?
5791
- }
5792
- addSegs(inputs) {
5793
- let hiddenEntries = [];
5794
- for (let input of inputs) {
5795
- this.insertEntry(input, hiddenEntries);
5796
- }
5797
- return hiddenEntries;
5798
- }
5799
- insertEntry(entry, hiddenEntries) {
5800
- let insertion = this.findInsertion(entry);
5801
- if (this.isInsertionValid(insertion, entry)) {
5802
- this.insertEntryAt(entry, insertion);
5803
- }
5804
- else {
5805
- this.handleInvalidInsertion(insertion, entry, hiddenEntries);
5806
- }
5807
- }
5808
- isInsertionValid(insertion, entry) {
5809
- return (this.maxCoord === -1 || insertion.levelCoord + this.getEntryThickness(entry) <= this.maxCoord) &&
5810
- (this.maxStackCnt === -1 || insertion.stackCnt < this.maxStackCnt);
5811
- }
5812
- handleInvalidInsertion(insertion, entry, hiddenEntries) {
5813
- if (this.allowReslicing && insertion.touchingEntry) {
5814
- const hiddenEntry = Object.assign(Object.assign({}, entry), { span: intersectSpans(entry.span, insertion.touchingEntry.span) });
5815
- hiddenEntries.push(hiddenEntry);
5816
- this.splitEntry(entry, insertion.touchingEntry, hiddenEntries);
5817
- }
5818
- else {
5819
- hiddenEntries.push(entry);
5820
- }
5397
+ const DAY_NUM_FORMAT = createFormatter({ day: 'numeric' });
5398
+ class DayCellContainer extends BaseComponent {
5399
+ constructor() {
5400
+ super(...arguments);
5401
+ this.refineRenderProps = memoizeObjArg(refineRenderProps);
5821
5402
  }
5822
- /*
5823
- Does NOT add what hit the `barrier` into hiddenEntries. Should already be done.
5824
- */
5825
- splitEntry(entry, barrier, hiddenEntries) {
5826
- let entrySpan = entry.span;
5827
- let barrierSpan = barrier.span;
5828
- if (entrySpan.start < barrierSpan.start) {
5829
- this.insertEntry({
5830
- index: entry.index,
5831
- thickness: entry.thickness,
5832
- span: { start: entrySpan.start, end: barrierSpan.start },
5833
- }, hiddenEntries);
5834
- }
5835
- if (entrySpan.end > barrierSpan.end) {
5836
- this.insertEntry({
5837
- index: entry.index,
5838
- thickness: entry.thickness,
5839
- span: { start: barrierSpan.end, end: entrySpan.end },
5840
- }, hiddenEntries);
5841
- }
5403
+ render() {
5404
+ let { props, context } = this;
5405
+ let { options } = context;
5406
+ let renderProps = this.refineRenderProps({
5407
+ date: props.date,
5408
+ dateProfile: props.dateProfile,
5409
+ todayRange: props.todayRange,
5410
+ isMonthStart: props.isMonthStart || false,
5411
+ showDayNumber: props.showDayNumber,
5412
+ extraRenderProps: props.extraRenderProps,
5413
+ viewApi: context.viewApi,
5414
+ dateEnv: context.dateEnv,
5415
+ monthStartFormat: options.monthStartFormat,
5416
+ });
5417
+ return (preact.createElement(ContentContainer, Object.assign({}, props /* includes children */, { elClasses: [
5418
+ ...getDayClassNames(renderProps, context.theme),
5419
+ ...(props.elClasses || []),
5420
+ ], elAttrs: Object.assign(Object.assign({}, props.elAttrs), (renderProps.isDisabled ? {} : { 'data-date': formatDayString(props.date) })), renderProps: renderProps, generatorName: "dayCellContent", customGenerator: options.dayCellContent, defaultGenerator: props.defaultGenerator, classNameGenerator:
5421
+ // don't use custom classNames if disabled
5422
+ renderProps.isDisabled ? undefined : options.dayCellClassNames, didMount: options.dayCellDidMount, willUnmount: options.dayCellWillUnmount })));
5842
5423
  }
5843
- insertEntryAt(entry, insertion) {
5844
- let { entriesByLevel, levelCoords } = this;
5845
- if (insertion.lateral === -1) {
5846
- // create a new level
5847
- insertAt(levelCoords, insertion.level, insertion.levelCoord);
5848
- insertAt(entriesByLevel, insertion.level, [entry]);
5849
- }
5850
- else {
5851
- // insert into existing level
5852
- insertAt(entriesByLevel[insertion.level], insertion.lateral, entry);
5853
- }
5854
- this.stackCnts[buildEntryKey(entry)] = insertion.stackCnt;
5855
- }
5856
- /*
5857
- does not care about limits
5858
- */
5859
- findInsertion(newEntry) {
5860
- let { levelCoords, entriesByLevel, strictOrder, stackCnts } = this;
5861
- let levelCnt = levelCoords.length;
5862
- let candidateCoord = 0;
5863
- let touchingLevel = -1;
5864
- let touchingLateral = -1;
5865
- let touchingEntry = null;
5866
- let stackCnt = 0;
5867
- for (let trackingLevel = 0; trackingLevel < levelCnt; trackingLevel += 1) {
5868
- const trackingCoord = levelCoords[trackingLevel];
5869
- // if the current level is past the placed entry, we have found a good empty space and can stop.
5870
- // if strictOrder, keep finding more lateral intersections.
5871
- if (!strictOrder && trackingCoord >= candidateCoord + this.getEntryThickness(newEntry)) {
5872
- break;
5424
+ }
5425
+ function hasCustomDayCellContent(options) {
5426
+ return Boolean(options.dayCellContent || hasCustomRenderingHandler('dayCellContent', options));
5427
+ }
5428
+ function refineRenderProps(raw) {
5429
+ let { date, dateEnv, dateProfile, isMonthStart } = raw;
5430
+ let dayMeta = getDateMeta(date, raw.todayRange, null, dateProfile);
5431
+ let dayNumberText = raw.showDayNumber ? (dateEnv.format(date, isMonthStart ? raw.monthStartFormat : DAY_NUM_FORMAT)) : '';
5432
+ return Object.assign(Object.assign(Object.assign({ date: dateEnv.toDate(date), view: raw.viewApi }, dayMeta), { isMonthStart,
5433
+ dayNumberText }), raw.extraRenderProps);
5434
+ }
5435
+
5436
+ const PADDING_FROM_VIEWPORT = 10;
5437
+ class Popover extends BaseComponent {
5438
+ constructor() {
5439
+ super(...arguments);
5440
+ this.state = {
5441
+ titleId: getUniqueDomId(),
5442
+ };
5443
+ this.handleRootEl = (el) => {
5444
+ this.rootEl = el;
5445
+ if (this.props.elRef) {
5446
+ setRef(this.props.elRef, el);
5873
5447
  }
5874
- let trackingEntries = entriesByLevel[trackingLevel];
5875
- let trackingEntry;
5876
- let searchRes = binarySearch(trackingEntries, newEntry.span.start, getEntrySpanEnd); // find first entry after newEntry's end
5877
- let lateralIndex = searchRes[0] + searchRes[1]; // if exact match (which doesn't collide), go to next one
5878
- while ( // loop through entries that horizontally intersect
5879
- (trackingEntry = trackingEntries[lateralIndex]) && // but not past the whole entry list
5880
- trackingEntry.span.start < newEntry.span.end // and not entirely past newEntry
5881
- ) {
5882
- let trackingEntryBottom = trackingCoord + this.getEntryThickness(trackingEntry);
5883
- // intersects into the top of the candidate?
5884
- if (trackingEntryBottom > candidateCoord) {
5885
- candidateCoord = trackingEntryBottom;
5886
- touchingEntry = trackingEntry;
5887
- touchingLevel = trackingLevel;
5888
- touchingLateral = lateralIndex;
5889
- }
5890
- // butts up against top of candidate? (will happen if just intersected as well)
5891
- if (trackingEntryBottom === candidateCoord) {
5892
- // accumulate the highest possible stackCnt of the trackingEntries that butt up
5893
- stackCnt = Math.max(stackCnt, stackCnts[buildEntryKey(trackingEntry)] + 1);
5894
- }
5895
- lateralIndex += 1;
5448
+ };
5449
+ // Triggered when the user clicks *anywhere* in the document, for the autoHide feature
5450
+ this.handleDocumentMouseDown = (ev) => {
5451
+ // only hide the popover if the click happened outside the popover
5452
+ const target = getEventTargetViaRoot(ev);
5453
+ if (!this.rootEl.contains(target)) {
5454
+ this.handleCloseClick();
5896
5455
  }
5897
- }
5898
- // the destination level will be after touchingEntry's level. find it
5899
- let destLevel = 0;
5900
- if (touchingEntry) {
5901
- destLevel = touchingLevel + 1;
5902
- while (destLevel < levelCnt && levelCoords[destLevel] < candidateCoord) {
5903
- destLevel += 1;
5456
+ };
5457
+ this.handleDocumentKeyDown = (ev) => {
5458
+ if (ev.key === 'Escape') {
5459
+ this.handleCloseClick();
5460
+ }
5461
+ };
5462
+ this.handleCloseClick = () => {
5463
+ let { onClose } = this.props;
5464
+ if (onClose) {
5465
+ onClose();
5904
5466
  }
5905
- }
5906
- // if adding to an existing level, find where to insert
5907
- let destLateral = -1;
5908
- if (destLevel < levelCnt && levelCoords[destLevel] === candidateCoord) {
5909
- destLateral = binarySearch(entriesByLevel[destLevel], newEntry.span.end, getEntrySpanEnd)[0];
5910
- }
5911
- return {
5912
- touchingLevel,
5913
- touchingLateral,
5914
- touchingEntry,
5915
- stackCnt,
5916
- levelCoord: candidateCoord,
5917
- level: destLevel,
5918
- lateral: destLateral,
5919
5467
  };
5920
5468
  }
5921
- // sorted by levelCoord (lowest to highest)
5922
- toRects() {
5923
- let { entriesByLevel, levelCoords } = this;
5924
- let levelCnt = entriesByLevel.length;
5925
- let rects = [];
5926
- for (let level = 0; level < levelCnt; level += 1) {
5927
- let entries = entriesByLevel[level];
5928
- let levelCoord = levelCoords[level];
5929
- for (let entry of entries) {
5930
- rects.push(Object.assign(Object.assign({}, entry), { thickness: this.getEntryThickness(entry), levelCoord }));
5469
+ render() {
5470
+ let { theme, options } = this.context;
5471
+ let { props, state } = this;
5472
+ let classNames = [
5473
+ 'fc-popover',
5474
+ theme.getClass('popover'),
5475
+ ].concat(props.extraClassNames || []);
5476
+ return compat.createPortal(preact.createElement("div", Object.assign({}, props.extraAttrs, { id: props.id, className: classNames.join(' '), "aria-labelledby": state.titleId, ref: this.handleRootEl }),
5477
+ preact.createElement("div", { className: 'fc-popover-header ' + theme.getClass('popoverHeader') },
5478
+ preact.createElement("span", { className: "fc-popover-title", id: state.titleId }, props.title),
5479
+ preact.createElement("span", { className: 'fc-popover-close ' + theme.getIconClass('close'), title: options.closeHint, onClick: this.handleCloseClick })),
5480
+ preact.createElement("div", { className: 'fc-popover-body ' + theme.getClass('popoverContent') }, props.children)), props.parentEl);
5481
+ }
5482
+ componentDidMount() {
5483
+ document.addEventListener('mousedown', this.handleDocumentMouseDown);
5484
+ document.addEventListener('keydown', this.handleDocumentKeyDown);
5485
+ this.updateSize();
5486
+ }
5487
+ componentWillUnmount() {
5488
+ document.removeEventListener('mousedown', this.handleDocumentMouseDown);
5489
+ document.removeEventListener('keydown', this.handleDocumentKeyDown);
5490
+ }
5491
+ updateSize() {
5492
+ let { isRtl } = this.context;
5493
+ let { alignmentEl, alignGridTop } = this.props;
5494
+ let { rootEl } = this;
5495
+ let alignmentRect = computeClippedClientRect(alignmentEl);
5496
+ if (alignmentRect) {
5497
+ let popoverDims = rootEl.getBoundingClientRect();
5498
+ if (alignGridTop) {
5499
+ throw new Error('alignGridTop not supported yet');
5931
5500
  }
5501
+ // position relative to viewport
5502
+ let popoverTop = alignGridTop
5503
+ ? elementClosest(alignmentEl, '.fc-scrollgrid').getBoundingClientRect().top // BAD!!!
5504
+ : alignmentRect.top;
5505
+ let popoverLeft = isRtl ? alignmentRect.right - popoverDims.width : alignmentRect.left;
5506
+ // constrain
5507
+ popoverTop = Math.max(popoverTop, PADDING_FROM_VIEWPORT);
5508
+ popoverLeft = Math.min(popoverLeft, document.documentElement.clientWidth - PADDING_FROM_VIEWPORT - popoverDims.width);
5509
+ popoverLeft = Math.max(popoverLeft, PADDING_FROM_VIEWPORT);
5510
+ let origin = rootEl.offsetParent.getBoundingClientRect();
5511
+ applyStyle(rootEl, {
5512
+ top: popoverTop - origin.top,
5513
+ left: popoverLeft - origin.left,
5514
+ });
5932
5515
  }
5933
- return rects;
5934
5516
  }
5935
5517
  }
5936
- function getEntrySpanEnd(entry) {
5937
- return entry.span.end;
5938
- }
5939
- function buildEntryKey(entry) {
5940
- return entry.index + ':' + entry.span.start;
5941
- }
5942
- // returns groups with entries sorted by input order
5943
- function groupIntersectingEntries(entries) {
5944
- let merges = [];
5945
- for (let entry of entries) {
5946
- let filteredMerges = [];
5947
- let hungryMerge = {
5948
- span: entry.span,
5949
- entries: [entry],
5950
- };
5951
- for (let merge of merges) {
5952
- if (intersectSpans(merge.span, hungryMerge.span)) {
5953
- hungryMerge = {
5954
- entries: merge.entries.concat(hungryMerge.entries),
5955
- span: joinSpans(merge.span, hungryMerge.span),
5956
- };
5518
+
5519
+ class MorePopover extends DateComponent {
5520
+ constructor() {
5521
+ super(...arguments);
5522
+ this.handleRootEl = (rootEl) => {
5523
+ this.rootEl = rootEl;
5524
+ if (rootEl) {
5525
+ this.context.registerInteractiveComponent(this, {
5526
+ el: rootEl,
5527
+ useEventCenter: false,
5528
+ });
5957
5529
  }
5958
5530
  else {
5959
- filteredMerges.push(merge);
5531
+ this.context.unregisterInteractiveComponent(this);
5960
5532
  }
5961
- }
5962
- filteredMerges.push(hungryMerge);
5963
- merges = filteredMerges;
5964
- }
5965
- return merges;
5966
- }
5967
- function joinSpans(span0, span1) {
5968
- return {
5969
- start: Math.min(span0.start, span1.start),
5970
- end: Math.max(span0.end, span1.end),
5971
- };
5972
- }
5973
- function intersectSpans(span0, span1) {
5974
- let start = Math.max(span0.start, span1.start);
5975
- let end = Math.min(span0.end, span1.end);
5976
- if (start < end) {
5977
- return { start, end };
5533
+ };
5978
5534
  }
5979
- return null;
5980
- }
5981
- // general util
5982
- // ---------------------------------------------------------------------------------------------------------------------
5983
- function insertAt(arr, index, item) {
5984
- arr.splice(index, 0, item);
5985
- }
5986
- function binarySearch(a, searchVal, getItemVal) {
5987
- let startIndex = 0;
5988
- let endIndex = a.length; // exclusive
5989
- if (!endIndex || searchVal < getItemVal(a[startIndex])) { // no items OR before first item
5990
- return [0, 0];
5535
+ render() {
5536
+ let { options, dateEnv } = this.context;
5537
+ let { props } = this;
5538
+ let { startDate, todayRange, dateProfile } = props;
5539
+ let title = dateEnv.format(startDate, options.dayPopoverFormat);
5540
+ return (preact.createElement(DayCellContainer, { elRef: this.handleRootEl, date: startDate, dateProfile: dateProfile, todayRange: todayRange }, (InnerContent, renderProps, elAttrs) => (preact.createElement(Popover, { elRef: elAttrs.ref, id: props.id, title: title, extraClassNames: ['fc-more-popover'].concat(elAttrs.className || []), extraAttrs: elAttrs /* TODO: make these time-based when not whole-day? */, parentEl: props.parentEl, alignmentEl: props.alignmentEl, alignGridTop: props.alignGridTop, onClose: props.onClose },
5541
+ hasCustomDayCellContent(options) && (preact.createElement(InnerContent, { elTag: "div", elClasses: ['fc-more-popover-misc'] })),
5542
+ props.children))));
5991
5543
  }
5992
- if (searchVal > getItemVal(a[endIndex - 1])) { // after last item
5993
- return [endIndex, 0];
5544
+ queryHit(positionLeft, positionTop, elWidth, elHeight) {
5545
+ let { rootEl, props } = this;
5546
+ if (positionLeft >= 0 && positionLeft < elWidth &&
5547
+ positionTop >= 0 && positionTop < elHeight) {
5548
+ return {
5549
+ dateProfile: props.dateProfile,
5550
+ dateSpan: Object.assign({ allDay: !props.forceTimed, range: {
5551
+ start: props.startDate,
5552
+ end: props.endDate,
5553
+ } }, props.extraDateSpan),
5554
+ dayEl: rootEl,
5555
+ rect: {
5556
+ left: 0,
5557
+ top: 0,
5558
+ right: elWidth,
5559
+ bottom: elHeight,
5560
+ },
5561
+ layer: 1, // important when comparing with hits from other components
5562
+ };
5563
+ }
5564
+ return null;
5994
5565
  }
5995
- while (startIndex < endIndex) {
5996
- let middleIndex = Math.floor(startIndex + (endIndex - startIndex) / 2);
5997
- let middleVal = getItemVal(a[middleIndex]);
5998
- if (searchVal < middleVal) {
5999
- endIndex = middleIndex;
6000
- }
6001
- else if (searchVal > middleVal) {
6002
- startIndex = middleIndex + 1;
6003
- }
6004
- else { // equal!
6005
- return [middleIndex, 1];
6006
- }
6007
- }
6008
- return [startIndex, 0];
6009
5566
  }
6010
5567
 
6011
- /*
6012
- An abstraction for a dragging interaction originating on an event.
6013
- Does higher-level things than PointerDragger, such as possibly:
6014
- - a "mirror" that moves with the pointer
6015
- - a minimum number of pixels or other criteria for a true drag to begin
6016
-
6017
- subclasses must emit:
6018
- - pointerdown
6019
- - dragstart
6020
- - dragmove
6021
- - pointerup
6022
- - dragend
6023
- */
6024
- class ElementDragging {
6025
- constructor(el, selector) {
6026
- this.emitter = new Emitter();
5568
+ class MoreLinkContainer extends BaseComponent {
5569
+ constructor() {
5570
+ super(...arguments);
5571
+ this.state = {
5572
+ isPopoverOpen: false,
5573
+ popoverId: getUniqueDomId(),
5574
+ };
5575
+ this.handleLinkEl = (linkEl) => {
5576
+ this.linkEl = linkEl;
5577
+ if (this.props.elRef) {
5578
+ setRef(this.props.elRef, linkEl);
5579
+ }
5580
+ };
5581
+ this.handleClick = (ev) => {
5582
+ let { props, context } = this;
5583
+ let { moreLinkClick } = context.options;
5584
+ let date = computeRange(props).start;
5585
+ function buildPublicSeg(seg) {
5586
+ let { def, instance, range } = seg.eventRange;
5587
+ return {
5588
+ event: new EventImpl(context, def, instance),
5589
+ start: context.dateEnv.toDate(range.start),
5590
+ end: context.dateEnv.toDate(range.end),
5591
+ isStart: seg.isStart,
5592
+ isEnd: seg.isEnd,
5593
+ };
5594
+ }
5595
+ if (typeof moreLinkClick === 'function') {
5596
+ moreLinkClick = moreLinkClick({
5597
+ date,
5598
+ allDay: Boolean(props.allDayDate),
5599
+ allSegs: props.segs.map(buildPublicSeg),
5600
+ hiddenSegs: props.hiddenSegs.map(buildPublicSeg),
5601
+ jsEvent: ev,
5602
+ view: context.viewApi,
5603
+ });
5604
+ }
5605
+ if (!moreLinkClick || moreLinkClick === 'popover') {
5606
+ this.setState({ isPopoverOpen: true });
5607
+ }
5608
+ else if (typeof moreLinkClick === 'string') { // a view name
5609
+ context.calendarApi.zoomTo(date, moreLinkClick);
5610
+ }
5611
+ };
5612
+ this.handlePopoverClose = () => {
5613
+ this.setState({ isPopoverOpen: false });
5614
+ };
6027
5615
  }
6028
- destroy() {
5616
+ render() {
5617
+ let { props, state } = this;
5618
+ return (preact.createElement(ViewContextType.Consumer, null, (context) => {
5619
+ let { viewApi, options, calendarApi } = context;
5620
+ let { moreLinkText } = options;
5621
+ let moreCnt = props.hiddenSegs.length;
5622
+ let range = computeRange(props);
5623
+ let text = typeof moreLinkText === 'function' // TODO: eventually use formatWithOrdinals
5624
+ ? moreLinkText.call(calendarApi, moreCnt)
5625
+ : `+${moreCnt} ${moreLinkText}`;
5626
+ let hint = formatWithOrdinals(options.moreLinkHint, [moreCnt], text);
5627
+ let renderProps = {
5628
+ num: moreCnt,
5629
+ shortText: `+${moreCnt}`,
5630
+ text,
5631
+ view: viewApi,
5632
+ };
5633
+ return (preact.createElement(preact.Fragment, null,
5634
+ Boolean(moreCnt) && (preact.createElement(ContentContainer, { elTag: props.elTag || 'a', elRef: this.handleLinkEl, elClasses: [
5635
+ ...(props.elClasses || []),
5636
+ 'fc-more-link',
5637
+ ], elStyle: props.elStyle, elAttrs: Object.assign(Object.assign(Object.assign({}, props.elAttrs), createAriaClickAttrs(this.handleClick)), { title: hint, 'aria-expanded': state.isPopoverOpen, 'aria-controls': state.isPopoverOpen ? state.popoverId : '' }), renderProps: renderProps, generatorName: "moreLinkContent", customGenerator: options.moreLinkContent, defaultGenerator: props.defaultGenerator || renderMoreLinkInner, classNameGenerator: options.moreLinkClassNames, didMount: options.moreLinkDidMount, willUnmount: options.moreLinkWillUnmount }, props.children)),
5638
+ state.isPopoverOpen && (preact.createElement(MorePopover, { id: state.popoverId, startDate: range.start, endDate: range.end, dateProfile: props.dateProfile, todayRange: props.todayRange, extraDateSpan: props.extraDateSpan, parentEl: this.parentEl, alignmentEl: props.alignmentElRef ?
5639
+ props.alignmentElRef.current :
5640
+ this.linkEl, alignGridTop: props.alignGridTop, forceTimed: props.forceTimed, onClose: this.handlePopoverClose }, props.popoverContent()))));
5641
+ }));
6029
5642
  }
6030
- setMirrorIsVisible(bool) {
6031
- // optional if subclass doesn't want to support a mirror
5643
+ componentDidMount() {
5644
+ this.updateParentEl();
6032
5645
  }
6033
- setMirrorNeedsRevert(bool) {
6034
- // optional if subclass doesn't want to support a mirror
5646
+ componentDidUpdate() {
5647
+ this.updateParentEl();
6035
5648
  }
6036
- setAutoScrollEnabled(bool) {
6037
- // optional
5649
+ updateParentEl() {
5650
+ if (this.linkEl) {
5651
+ this.parentEl = elementClosest(this.linkEl, '.fc-view-harness'); // HACK. reconsider
5652
+ }
6038
5653
  }
6039
5654
  }
6040
-
6041
- // TODO: get rid of this in favor of options system,
6042
- // tho it's really easy to access this globally rather than pass thru options.
6043
- const config = {};
6044
-
6045
- /*
6046
- Information about what will happen when an external element is dragged-and-dropped
6047
- onto a calendar. Contains information for creating an event.
6048
- */
6049
- const DRAG_META_REFINERS = {
6050
- startTime: createDuration,
6051
- duration: createDuration,
6052
- create: Boolean,
6053
- sourceId: String,
6054
- };
6055
- function parseDragMeta(raw) {
6056
- let { refined, extra } = refineProps(raw, DRAG_META_REFINERS);
5655
+ function renderMoreLinkInner(props) {
5656
+ return props.text;
5657
+ }
5658
+ function computeRange(props) {
5659
+ if (props.allDayDate) {
5660
+ return {
5661
+ start: props.allDayDate,
5662
+ end: addDays(props.allDayDate, 1),
5663
+ };
5664
+ }
6057
5665
  return {
6058
- startTime: refined.startTime || null,
6059
- duration: refined.duration || null,
6060
- create: refined.create != null ? refined.create : true,
6061
- sourceId: refined.sourceId,
6062
- leftoverProps: extra,
5666
+ start: computeEarliestSegStart(props.hiddenSegs),
5667
+ end: computeLatestSegEnd(props.hiddenSegs),
6063
5668
  };
6064
5669
  }
6065
-
6066
- // Computes a default column header formatting string if `colFormat` is not explicitly defined
6067
- function computeFallbackHeaderFormat(datesRepDistinctDays, dayCnt) {
6068
- // if more than one week row, or if there are a lot of columns with not much space,
6069
- // put just the day numbers will be in each cell
6070
- if (!datesRepDistinctDays || dayCnt > 10) {
6071
- return createFormatter({ weekday: 'short' }); // "Sat"
6072
- }
6073
- if (dayCnt > 1) {
6074
- return createFormatter({ weekday: 'short', month: 'numeric', day: 'numeric', omitCommas: true }); // "Sat 11/12"
6075
- }
6076
- return createFormatter({ weekday: 'long' }); // "Saturday"
5670
+ function computeEarliestSegStart(segs) {
5671
+ return segs.reduce(pickEarliestStart).eventRange.range.start;
6077
5672
  }
6078
-
6079
- const CLASS_NAME = 'fc-col-header-cell'; // do the cushion too? no
6080
- function renderInner$1(renderProps) {
6081
- return renderProps.text;
5673
+ function pickEarliestStart(seg0, seg1) {
5674
+ return seg0.eventRange.range.start < seg1.eventRange.range.start ? seg0 : seg1;
6082
5675
  }
6083
-
6084
- // BAD name for this class now. used in the Header
6085
- class TableDateCell extends BaseComponent {
6086
- render() {
6087
- let { dateEnv, options, theme, viewApi } = this.context;
6088
- let { props } = this;
6089
- let { date, dateProfile } = props;
6090
- let dayMeta = getDateMeta(date, props.todayRange, null, dateProfile);
6091
- let classNames = [CLASS_NAME].concat(getDayClassNames(dayMeta, theme));
6092
- let text = dateEnv.format(date, props.dayHeaderFormat);
6093
- // if colCnt is 1, we are already in a day-view and don't need a navlink
6094
- let navLinkAttrs = (!dayMeta.isDisabled && props.colCnt > 1)
6095
- ? buildNavLinkAttrs(this.context, date)
6096
- : {};
6097
- let renderProps = Object.assign(Object.assign(Object.assign({ date: dateEnv.toDate(date), view: viewApi }, props.extraRenderProps), { text }), dayMeta);
6098
- return (preact.createElement(ContentContainer, { elTag: "th", elClasses: classNames, elAttrs: Object.assign({ role: 'columnheader', colSpan: props.colSpan, 'data-date': !dayMeta.isDisabled ? formatDayString(date) : undefined }, props.extraDataAttrs), renderProps: renderProps, generatorName: "dayHeaderContent", customGenerator: options.dayHeaderContent, defaultGenerator: renderInner$1, classNameGenerator: options.dayHeaderClassNames, didMount: options.dayHeaderDidMount, willUnmount: options.dayHeaderWillUnmount }, (InnerContainer) => (preact.createElement("div", { className: "fc-scrollgrid-sync-inner" }, !dayMeta.isDisabled && (preact.createElement(InnerContainer, { elTag: "a", elAttrs: navLinkAttrs, elClasses: [
6099
- 'fc-col-header-cell-cushion',
6100
- props.isSticky && 'fc-sticky',
6101
- ] }))))));
6102
- }
5676
+ function computeLatestSegEnd(segs) {
5677
+ return segs.reduce(pickLatestEnd).eventRange.range.end;
6103
5678
  }
6104
-
6105
- const WEEKDAY_FORMAT = createFormatter({ weekday: 'long' });
6106
- class TableDowCell extends BaseComponent {
6107
- render() {
6108
- let { props } = this;
6109
- let { dateEnv, theme, viewApi, options } = this.context;
6110
- let date = addDays(new Date(259200000), props.dow); // start with Sun, 04 Jan 1970 00:00:00 GMT
6111
- let dateMeta = {
6112
- dow: props.dow,
6113
- isDisabled: false,
6114
- isFuture: false,
6115
- isPast: false,
6116
- isToday: false,
6117
- isOther: false,
6118
- };
6119
- let text = dateEnv.format(date, props.dayHeaderFormat);
6120
- let renderProps = Object.assign(Object.assign(Object.assign(Object.assign({ // TODO: make this public?
6121
- date }, dateMeta), { view: viewApi }), props.extraRenderProps), { text });
6122
- return (preact.createElement(ContentContainer, { elTag: "th", elClasses: [
6123
- CLASS_NAME,
6124
- ...getDayClassNames(dateMeta, theme),
6125
- ...(props.extraClassNames || []),
6126
- ], elAttrs: Object.assign({ role: 'columnheader', colSpan: props.colSpan }, props.extraDataAttrs), renderProps: renderProps, generatorName: "dayHeaderContent", customGenerator: options.dayHeaderContent, defaultGenerator: renderInner$1, classNameGenerator: options.dayHeaderClassNames, didMount: options.dayHeaderDidMount, willUnmount: options.dayHeaderWillUnmount }, (InnerContent) => (preact.createElement("div", { className: "fc-scrollgrid-sync-inner" },
6127
- preact.createElement(InnerContent, { elTag: "a", elClasses: [
6128
- 'fc-col-header-cell-cushion',
6129
- props.isSticky && 'fc-sticky',
6130
- ], elAttrs: {
6131
- 'aria-label': dateEnv.format(date, WEEKDAY_FORMAT),
6132
- } })))));
6133
- }
5679
+ function pickLatestEnd(seg0, seg1) {
5680
+ return seg0.eventRange.range.end > seg1.eventRange.range.end ? seg0 : seg1;
6134
5681
  }
6135
5682
 
6136
- class NowTimer extends preact.Component {
6137
- constructor(props, context) {
6138
- super(props, context);
6139
- this.initialNowDate = getNow(context.options.now, context.dateEnv);
6140
- this.initialNowQueriedMs = new Date().valueOf();
6141
- this.state = this.computeTiming().currentState;
6142
- }
6143
- render() {
6144
- let { props, state } = this;
6145
- return props.children(state.nowDate, state.todayRange);
6146
- }
6147
- componentDidMount() {
6148
- this.setTimeout();
5683
+ class SegHierarchy {
5684
+ constructor(getEntryThickness = (entry) => {
5685
+ // if no thickness known, assume 1 (if 0, so small it always fits)
5686
+ return entry.thickness;
5687
+ }) {
5688
+ this.getEntryThickness = getEntryThickness;
5689
+ // settings
5690
+ this.strictOrder = false;
5691
+ this.allowReslicing = false;
5692
+ this.maxCoord = -1; // -1 means no max
5693
+ this.maxStackCnt = -1; // -1 means no max
5694
+ this.levelCoords = []; // ordered
5695
+ this.entriesByLevel = []; // parallel with levelCoords
5696
+ this.stackCnts = {}; // TODO: use better technique!?
6149
5697
  }
6150
- componentDidUpdate(prevProps) {
6151
- if (prevProps.unit !== this.props.unit) {
6152
- this.clearTimeout();
6153
- this.setTimeout();
5698
+ addSegs(inputs) {
5699
+ let hiddenEntries = [];
5700
+ for (let input of inputs) {
5701
+ this.insertEntry(input, hiddenEntries);
6154
5702
  }
5703
+ return hiddenEntries;
6155
5704
  }
6156
- componentWillUnmount() {
6157
- this.clearTimeout();
6158
- }
6159
- computeTiming() {
6160
- let { props, context } = this;
6161
- let unroundedNow = addMs(this.initialNowDate, new Date().valueOf() - this.initialNowQueriedMs);
6162
- let currentUnitStart = context.dateEnv.startOf(unroundedNow, props.unit);
6163
- let nextUnitStart = context.dateEnv.add(currentUnitStart, createDuration(1, props.unit));
6164
- let waitMs = nextUnitStart.valueOf() - unroundedNow.valueOf();
6165
- // there is a max setTimeout ms value (https://stackoverflow.com/a/3468650/96342)
6166
- // ensure no longer than a day
6167
- waitMs = Math.min(1000 * 60 * 60 * 24, waitMs);
6168
- return {
6169
- currentState: { nowDate: currentUnitStart, todayRange: buildDayRange(currentUnitStart) },
6170
- nextState: { nowDate: nextUnitStart, todayRange: buildDayRange(nextUnitStart) },
6171
- waitMs,
6172
- };
5705
+ insertEntry(entry, hiddenEntries) {
5706
+ let entryThickness = this.getEntryThickness(entry);
5707
+ if (entryThickness == null) {
5708
+ hiddenEntries.push(entry);
5709
+ }
5710
+ else {
5711
+ let insertion = this.findInsertion(entry, entryThickness);
5712
+ if (this.isInsertionValid(insertion, entry, entryThickness)) {
5713
+ this.insertEntryAt(entry, insertion);
5714
+ }
5715
+ else {
5716
+ this.handleInvalidInsertion(insertion, entry, hiddenEntries);
5717
+ }
5718
+ }
6173
5719
  }
6174
- setTimeout() {
6175
- let { nextState, waitMs } = this.computeTiming();
6176
- this.timeoutId = setTimeout(() => {
6177
- this.setState(nextState, () => {
6178
- this.setTimeout();
6179
- });
6180
- }, waitMs);
5720
+ isInsertionValid(insertion, entry, entryThickness) {
5721
+ return (this.maxCoord === -1 || insertion.levelCoord + entryThickness <= this.maxCoord) &&
5722
+ (this.maxStackCnt === -1 || insertion.stackCnt < this.maxStackCnt);
6181
5723
  }
6182
- clearTimeout() {
6183
- if (this.timeoutId) {
6184
- clearTimeout(this.timeoutId);
5724
+ handleInvalidInsertion(insertion, entry, hiddenEntries) {
5725
+ if (this.allowReslicing && insertion.touchingEntry) {
5726
+ const hiddenEntry = Object.assign(Object.assign({}, entry), { span: intersectSpans(entry.span, insertion.touchingEntry.span) });
5727
+ hiddenEntries.push(hiddenEntry);
5728
+ this.splitEntry(entry, insertion.touchingEntry, hiddenEntries);
5729
+ }
5730
+ else {
5731
+ hiddenEntries.push(entry);
6185
5732
  }
6186
5733
  }
6187
- }
6188
- NowTimer.contextType = ViewContextType;
6189
- function buildDayRange(date) {
6190
- let start = startOfDay(date);
6191
- let end = addDays(start, 1);
6192
- return { start, end };
6193
- }
6194
-
6195
- class DayHeader extends BaseComponent {
6196
- constructor() {
6197
- super(...arguments);
6198
- this.createDayHeaderFormatter = memoize(createDayHeaderFormatter);
6199
- }
6200
- render() {
6201
- let { context } = this;
6202
- let { dates, dateProfile, datesRepDistinctDays, renderIntro } = this.props;
6203
- let dayHeaderFormat = this.createDayHeaderFormatter(context.options.dayHeaderFormat, datesRepDistinctDays, dates.length);
6204
- return (preact.createElement(NowTimer, { unit: "day" }, (nowDate, todayRange) => (preact.createElement("tr", { role: "row" },
6205
- renderIntro && renderIntro('day'),
6206
- dates.map((date) => (datesRepDistinctDays ? (preact.createElement(TableDateCell, { key: date.toISOString(), date: date, dateProfile: dateProfile, todayRange: todayRange, colCnt: dates.length, dayHeaderFormat: dayHeaderFormat })) : (preact.createElement(TableDowCell, { key: date.getUTCDay(), dow: date.getUTCDay(), dayHeaderFormat: dayHeaderFormat }))))))));
5734
+ /*
5735
+ Does NOT add what hit the `barrier` into hiddenEntries. Should already be done.
5736
+ */
5737
+ splitEntry(entry, barrier, hiddenEntries) {
5738
+ let entrySpan = entry.span;
5739
+ let barrierSpan = barrier.span;
5740
+ if (entrySpan.start < barrierSpan.start) {
5741
+ this.insertEntry({
5742
+ index: entry.index,
5743
+ seg: entry.seg,
5744
+ thickness: entry.thickness,
5745
+ span: { start: entrySpan.start, end: barrierSpan.start },
5746
+ }, hiddenEntries);
5747
+ }
5748
+ if (entrySpan.end > barrierSpan.end) {
5749
+ this.insertEntry({
5750
+ index: entry.index,
5751
+ seg: entry.seg,
5752
+ thickness: entry.thickness,
5753
+ span: { start: barrierSpan.end, end: entrySpan.end },
5754
+ }, hiddenEntries);
5755
+ }
5756
+ }
5757
+ insertEntryAt(entry, insertion) {
5758
+ let { entriesByLevel, levelCoords } = this;
5759
+ if (insertion.lateral === -1) {
5760
+ // create a new level
5761
+ insertAt(levelCoords, insertion.level, insertion.levelCoord);
5762
+ insertAt(entriesByLevel, insertion.level, [entry]);
5763
+ }
5764
+ else {
5765
+ // insert into existing level
5766
+ insertAt(entriesByLevel[insertion.level], insertion.lateral, entry);
5767
+ }
5768
+ this.stackCnts[buildEntryKey(entry)] = insertion.stackCnt;
5769
+ }
5770
+ /*
5771
+ does not care about limits
5772
+ */
5773
+ findInsertion(newEntry, newEntryThickness) {
5774
+ let { levelCoords, entriesByLevel, strictOrder, stackCnts } = this;
5775
+ let levelCnt = levelCoords.length;
5776
+ let candidateCoord = 0;
5777
+ let touchingLevel = -1;
5778
+ let touchingLateral = -1;
5779
+ let touchingEntry = null;
5780
+ let stackCnt = 0;
5781
+ for (let trackingLevel = 0; trackingLevel < levelCnt; trackingLevel += 1) {
5782
+ const trackingCoord = levelCoords[trackingLevel];
5783
+ // if the current level is past the placed entry, we have found a good empty space and can stop.
5784
+ // if strictOrder, keep finding more lateral intersections.
5785
+ if (!strictOrder && trackingCoord >= candidateCoord + newEntryThickness) {
5786
+ break;
5787
+ }
5788
+ let trackingEntries = entriesByLevel[trackingLevel];
5789
+ let trackingEntry;
5790
+ let searchRes = binarySearch(trackingEntries, newEntry.span.start, getEntrySpanEnd); // find first entry after newEntry's end
5791
+ let lateralIndex = searchRes[0] + searchRes[1]; // if exact match (which doesn't collide), go to next one
5792
+ while ( // loop through entries that horizontally intersect
5793
+ (trackingEntry = trackingEntries[lateralIndex]) && // but not past the whole entry list
5794
+ trackingEntry.span.start < newEntry.span.end // and not entirely past newEntry
5795
+ ) {
5796
+ let trackingEntryBottom = trackingCoord + this.getEntryThickness(trackingEntry);
5797
+ // intersects into the top of the candidate?
5798
+ if (trackingEntryBottom > candidateCoord) {
5799
+ candidateCoord = trackingEntryBottom;
5800
+ touchingEntry = trackingEntry;
5801
+ touchingLevel = trackingLevel;
5802
+ touchingLateral = lateralIndex;
5803
+ }
5804
+ // butts up against top of candidate? (will happen if just intersected as well)
5805
+ if (trackingEntryBottom === candidateCoord) {
5806
+ // accumulate the highest possible stackCnt of the trackingEntries that butt up
5807
+ stackCnt = Math.max(stackCnt, stackCnts[buildEntryKey(trackingEntry)] + 1);
5808
+ }
5809
+ lateralIndex += 1;
5810
+ }
5811
+ }
5812
+ // the destination level will be after touchingEntry's level. find it
5813
+ let destLevel = 0;
5814
+ if (touchingEntry) {
5815
+ destLevel = touchingLevel + 1;
5816
+ while (destLevel < levelCnt && levelCoords[destLevel] < candidateCoord) {
5817
+ destLevel += 1;
5818
+ }
5819
+ }
5820
+ // if adding to an existing level, find where to insert
5821
+ let destLateral = -1;
5822
+ if (destLevel < levelCnt && levelCoords[destLevel] === candidateCoord) {
5823
+ destLateral = binarySearch(entriesByLevel[destLevel], newEntry.span.end, getEntrySpanEnd)[0];
5824
+ }
5825
+ return {
5826
+ touchingLevel,
5827
+ touchingLateral,
5828
+ touchingEntry,
5829
+ stackCnt,
5830
+ levelCoord: candidateCoord,
5831
+ level: destLevel,
5832
+ lateral: destLateral,
5833
+ };
5834
+ }
5835
+ // sorted by levelCoord (lowest to highest)
5836
+ toRects() {
5837
+ let { entriesByLevel, levelCoords } = this;
5838
+ let levelCnt = entriesByLevel.length;
5839
+ let rects = [];
5840
+ for (let level = 0; level < levelCnt; level += 1) {
5841
+ let entries = entriesByLevel[level];
5842
+ let levelCoord = levelCoords[level];
5843
+ for (let entry of entries) {
5844
+ rects.push(Object.assign(Object.assign({}, entry), { thickness: this.getEntryThickness(entry), levelCoord }));
5845
+ }
5846
+ }
5847
+ return rects;
5848
+ }
5849
+ }
5850
+ function getEntrySpanEnd(entry) {
5851
+ return entry.span.end;
5852
+ }
5853
+ /*
5854
+ Generates a unique ID whose lifespan is a single run of SegHierarchy, so can be really specific
5855
+ without fear of accidentally busting the cache on subsequent rerenders
5856
+ */
5857
+ function buildEntryKey(entry) {
5858
+ return entry.index + ':' + entry.span.start;
5859
+ }
5860
+ /*
5861
+ returns groups with entries sorted by input order
5862
+ */
5863
+ function groupIntersectingEntries(entries) {
5864
+ let merges = [];
5865
+ for (let entry of entries) {
5866
+ let filteredMerges = [];
5867
+ let hungryMerge = {
5868
+ span: entry.span,
5869
+ entries: [entry],
5870
+ };
5871
+ for (let merge of merges) {
5872
+ if (intersectSpans(merge.span, hungryMerge.span)) {
5873
+ hungryMerge = {
5874
+ span: joinSpans(merge.span, hungryMerge.span),
5875
+ entries: merge.entries.concat(hungryMerge.entries), // keep preexisting merge's items first. maintains order
5876
+ };
5877
+ }
5878
+ else {
5879
+ filteredMerges.push(merge);
5880
+ }
5881
+ }
5882
+ filteredMerges.push(hungryMerge);
5883
+ merges = filteredMerges;
5884
+ }
5885
+ return merges.map((merge) => {
5886
+ const segs = merge.entries.map(extractEntrySeg);
5887
+ return {
5888
+ key: buildIsoString(computeEarliestSegStart(segs)),
5889
+ span: merge.span,
5890
+ segs,
5891
+ };
5892
+ });
5893
+ }
5894
+ function extractEntrySeg(entry) {
5895
+ return entry.seg;
5896
+ }
5897
+ function joinSpans(span0, span1) {
5898
+ return {
5899
+ start: Math.min(span0.start, span1.start),
5900
+ end: Math.max(span0.end, span1.end),
5901
+ };
5902
+ }
5903
+ function intersectSpans(span0, span1) {
5904
+ let start = Math.max(span0.start, span1.start);
5905
+ let end = Math.min(span0.end, span1.end);
5906
+ if (start < end) {
5907
+ return { start, end };
5908
+ }
5909
+ return null;
5910
+ }
5911
+ // general util
5912
+ // ---------------------------------------------------------------------------------------------------------------------
5913
+ function insertAt(arr, index, item) {
5914
+ arr.splice(index, 0, item);
5915
+ }
5916
+ function binarySearch(a, searchVal, getItemVal) {
5917
+ let startIndex = 0;
5918
+ let endIndex = a.length; // exclusive
5919
+ if (!endIndex || searchVal < getItemVal(a[startIndex])) { // no items OR before first item
5920
+ return [0, 0];
5921
+ }
5922
+ if (searchVal > getItemVal(a[endIndex - 1])) { // after last item
5923
+ return [endIndex, 0];
5924
+ }
5925
+ while (startIndex < endIndex) {
5926
+ let middleIndex = Math.floor(startIndex + (endIndex - startIndex) / 2);
5927
+ let middleVal = getItemVal(a[middleIndex]);
5928
+ if (searchVal < middleVal) {
5929
+ endIndex = middleIndex;
5930
+ }
5931
+ else if (searchVal > middleVal) {
5932
+ startIndex = middleIndex + 1;
5933
+ }
5934
+ else { // equal!
5935
+ return [middleIndex, 1];
5936
+ }
5937
+ }
5938
+ return [startIndex, 0];
5939
+ }
5940
+
5941
+ /*
5942
+ An abstraction for a dragging interaction originating on an event.
5943
+ Does higher-level things than PointerDragger, such as possibly:
5944
+ - a "mirror" that moves with the pointer
5945
+ - a minimum number of pixels or other criteria for a true drag to begin
5946
+
5947
+ subclasses must emit:
5948
+ - pointerdown
5949
+ - dragstart
5950
+ - dragmove
5951
+ - pointerup
5952
+ - dragend
5953
+ */
5954
+ class ElementDragging {
5955
+ constructor(el, selector) {
5956
+ this.emitter = new Emitter();
5957
+ }
5958
+ destroy() {
5959
+ }
5960
+ setMirrorIsVisible(bool) {
5961
+ // optional if subclass doesn't want to support a mirror
5962
+ }
5963
+ setMirrorNeedsRevert(bool) {
5964
+ // optional if subclass doesn't want to support a mirror
5965
+ }
5966
+ setAutoScrollEnabled(bool) {
5967
+ // optional
6207
5968
  }
6208
5969
  }
6209
- function createDayHeaderFormatter(explicitFormat, datesRepDistinctDays, dateCnt) {
6210
- return explicitFormat || computeFallbackHeaderFormat(datesRepDistinctDays, dateCnt);
5970
+
5971
+ // TODO: get rid of this in favor of options system,
5972
+ // tho it's really easy to access this globally rather than pass thru options.
5973
+ const config = {};
5974
+
5975
+ /*
5976
+ Information about what will happen when an external element is dragged-and-dropped
5977
+ onto a calendar. Contains information for creating an event.
5978
+ */
5979
+ const DRAG_META_REFINERS = {
5980
+ startTime: createDuration,
5981
+ duration: createDuration,
5982
+ create: Boolean,
5983
+ sourceId: String,
5984
+ };
5985
+ function parseDragMeta(raw) {
5986
+ let { refined, extra } = refineProps(raw, DRAG_META_REFINERS);
5987
+ return {
5988
+ startTime: refined.startTime || null,
5989
+ duration: refined.duration || null,
5990
+ create: refined.create != null ? refined.create : true,
5991
+ sourceId: refined.sourceId,
5992
+ leftoverProps: extra,
5993
+ };
6211
5994
  }
6212
5995
 
6213
5996
  class DaySeriesModel {
@@ -6291,7 +6074,7 @@ class DayTableModel {
6291
6074
  this.rowCnt = rowCnt;
6292
6075
  this.colCnt = daysPerRow;
6293
6076
  this.daySeries = daySeries;
6294
- this.cells = this.buildCells();
6077
+ this.cellRows = this.buildCells();
6295
6078
  this.headerDates = this.buildHeaderDates();
6296
6079
  }
6297
6080
  buildCells() {
@@ -6315,7 +6098,7 @@ class DayTableModel {
6315
6098
  buildHeaderDates() {
6316
6099
  let dates = [];
6317
6100
  for (let col = 0; col < this.colCnt; col += 1) {
6318
- dates.push(this.cells[0][col].date);
6101
+ dates.push(this.cellRows[0][col].date);
6319
6102
  }
6320
6103
  return dates;
6321
6104
  }
@@ -6343,18 +6126,272 @@ class DayTableModel {
6343
6126
  }
6344
6127
  }
6345
6128
 
6346
- class Slicer {
6347
- constructor() {
6348
- this.sliceBusinessHours = memoize(this._sliceBusinessHours);
6349
- this.sliceDateSelection = memoize(this._sliceDateSpan);
6350
- this.sliceEventStore = memoize(this._sliceEventStore);
6351
- this.sliceEventDrag = memoize(this._sliceInteraction);
6352
- this.sliceEventResize = memoize(this._sliceInteraction);
6353
- this.forceDayIfListItem = false; // hack
6354
- }
6355
- sliceProps(props, dateProfile, nextDayThreshold, context, ...extraArgs) {
6356
- let { eventUiBases } = props;
6357
- let eventSegs = this.sliceEventStore(props.eventStore, eventUiBases, dateProfile, nextDayThreshold, ...extraArgs);
6129
+ const WHEEL_EVENT_NAMES = 'wheel mousewheel DomMouseScroll MozMousePixelScroll'.split(' ');
6130
+ /*
6131
+ Fires:
6132
+ - scrollStart (always user)
6133
+ - scroll
6134
+ - scrollEnd (always user)
6135
+
6136
+ NOTE: detection is complicated (w/ touch and wheel) because ScrollerSyncer needs to know about it,
6137
+ but are we sure we can't just ignore programmatic scrollTo() calls with a flag? and determine the
6138
+ the scroll-master simply by who was the newest scroller? Does passive:true do things asynchronously?
6139
+ */
6140
+ class ScrollListener {
6141
+ constructor(el) {
6142
+ this.el = el;
6143
+ this.emitter = new Emitter();
6144
+ this.isScrolling = false;
6145
+ this.isTouching = false; // user currently has finger down?
6146
+ this.isRecentlyWheeled = false;
6147
+ this.isRecentlyScrolled = false;
6148
+ this.wheelWaiter = new DelayedRunner(this._handleWheelWaited.bind(this));
6149
+ this.scrollWaiter = new DelayedRunner(this._handleScrollWaited.bind(this));
6150
+ // Handlers
6151
+ // ----------------------------------------------------------------------------------------------
6152
+ this.handleScroll = () => {
6153
+ this.startScroll();
6154
+ this.emitter.trigger('scroll', this.isRecentlyWheeled, this.isTouching);
6155
+ this.isRecentlyScrolled = true;
6156
+ this.scrollWaiter.request(500);
6157
+ };
6158
+ // will fire *before* the scroll event is fired (might not cause a scroll)
6159
+ this.handleWheel = () => {
6160
+ this.isRecentlyWheeled = true;
6161
+ this.wheelWaiter.request(500);
6162
+ };
6163
+ // will fire *before* the scroll event is fired (might not cause a scroll)
6164
+ this.handleTouchStart = () => {
6165
+ this.isTouching = true;
6166
+ };
6167
+ this.handleTouchEnd = () => {
6168
+ this.isTouching = false;
6169
+ // if the user ended their touch, and the scroll area wasn't moving,
6170
+ // we consider this to be the end of the scroll.
6171
+ if (!this.isRecentlyScrolled) {
6172
+ this.endScroll(); // won't fire if already ended
6173
+ }
6174
+ };
6175
+ el.addEventListener('scroll', this.handleScroll);
6176
+ el.addEventListener('touchstart', this.handleTouchStart, { passive: true });
6177
+ el.addEventListener('touchend', this.handleTouchEnd);
6178
+ for (let eventName of WHEEL_EVENT_NAMES) {
6179
+ el.addEventListener(eventName, this.handleWheel, { passive: true });
6180
+ }
6181
+ }
6182
+ destroy() {
6183
+ let { el } = this;
6184
+ el.removeEventListener('scroll', this.handleScroll);
6185
+ el.removeEventListener('touchstart', this.handleTouchStart, { passive: true });
6186
+ el.removeEventListener('touchend', this.handleTouchEnd);
6187
+ for (let eventName of WHEEL_EVENT_NAMES) {
6188
+ el.removeEventListener(eventName, this.handleWheel, { passive: true });
6189
+ }
6190
+ }
6191
+ // Start / Stop
6192
+ // ----------------------------------------------------------------------------------------------
6193
+ startScroll() {
6194
+ if (!this.isScrolling) {
6195
+ this.isScrolling = true;
6196
+ this.emitter.trigger('scrollStart', this.isRecentlyWheeled, this.isTouching);
6197
+ }
6198
+ }
6199
+ endScroll() {
6200
+ if (this.isScrolling) {
6201
+ this.emitter.trigger('scrollEnd');
6202
+ this.isScrolling = false;
6203
+ this.isRecentlyScrolled = true;
6204
+ this.isRecentlyWheeled = false;
6205
+ this.scrollWaiter.clear();
6206
+ this.wheelWaiter.clear();
6207
+ }
6208
+ }
6209
+ _handleScrollWaited() {
6210
+ this.isRecentlyScrolled = false;
6211
+ // only end the scroll if not currently touching.
6212
+ // if touching, the scrolling will end later, on touchend.
6213
+ if (!this.isTouching) {
6214
+ this.endScroll(); // won't fire if already ended
6215
+ }
6216
+ }
6217
+ _handleWheelWaited() {
6218
+ this.isRecentlyWheeled = false;
6219
+ }
6220
+ }
6221
+
6222
+ class Scroller extends DateComponent {
6223
+ constructor() {
6224
+ super(...arguments);
6225
+ // ref
6226
+ this.elRef = preact.createRef();
6227
+ }
6228
+ render() {
6229
+ const { props } = this;
6230
+ // if there's only one axis that needs scrolling, the other axis will unintentionally have
6231
+ // scrollbars too, so we must force to 'hidden'
6232
+ const fallbackOverflow = (props.horizontal || props.vertical) ? 'hidden' : '';
6233
+ return (preact.createElement("div", { ref: this.elRef, className: [
6234
+ 'fc-scroller',
6235
+ props.hideScrollbars ? 'fc-scroller-nobars' : '',
6236
+ ...(props.elClassNames || []),
6237
+ ].join(' '), style: Object.assign(Object.assign({}, props.elStyle), { overflowX: props.horizontal ? 'auto' : fallbackOverflow, overflowY: props.vertical ? 'auto' : fallbackOverflow }) }, props.children));
6238
+ }
6239
+ componentDidMount() {
6240
+ const el = this.elRef.current; // TODO: make dynamic with useEffect
6241
+ this.listener = new ScrollListener(el);
6242
+ this.disconnectSize = watchSize(el, (contentWidth, contentHeight) => {
6243
+ const { props, context } = this;
6244
+ const bottomScrollbarWidth = el.offsetHeight - el.clientHeight;
6245
+ const horizontalScrollbarWidth = el.offsetWidth - el.clientWidth;
6246
+ let rightScrollbarWidth = 0;
6247
+ let leftScrollbarWidth = 0;
6248
+ if (context.isRtl && getRtlScrollerConfig().leftScrollbars) {
6249
+ leftScrollbarWidth = horizontalScrollbarWidth;
6250
+ }
6251
+ else {
6252
+ rightScrollbarWidth = horizontalScrollbarWidth;
6253
+ }
6254
+ if (!isDimsEqual(this.currentWidth, contentWidth)) {
6255
+ setRef(props.widthRef, this.currentWidth = contentWidth);
6256
+ }
6257
+ if (!isDimsEqual(this.currentHeight, contentHeight)) {
6258
+ setRef(props.heightRef, this.currentHeight = contentHeight);
6259
+ }
6260
+ if (!isDimsEqual(this.currentBottomScrollbarWidth, bottomScrollbarWidth)) {
6261
+ setRef(props.bottomScrollbarWidthRef, this.currentBottomScrollbarWidth = bottomScrollbarWidth);
6262
+ }
6263
+ if (!isDimsEqual(this.currentRightScrollbarWidth, rightScrollbarWidth)) {
6264
+ setRef(props.rightScrollbarWidthRef, this.currentRightScrollbarWidth = rightScrollbarWidth);
6265
+ }
6266
+ if (!isDimsEqual(this.currentLeftScrollbarWidth, leftScrollbarWidth)) {
6267
+ setRef(props.leftScrollbarWidthRef, this.currentLeftScrollbarWidth = leftScrollbarWidth);
6268
+ }
6269
+ });
6270
+ }
6271
+ componentWillUnmount() {
6272
+ const { props } = this;
6273
+ this.disconnectSize();
6274
+ this.listener.destroy();
6275
+ setRef(props.widthRef, null);
6276
+ setRef(props.heightRef, null);
6277
+ setRef(props.bottomScrollbarWidthRef, null);
6278
+ setRef(props.rightScrollbarWidthRef, null);
6279
+ setRef(props.leftScrollbarWidthRef, null);
6280
+ }
6281
+ endScroll() {
6282
+ this.listener.endScroll();
6283
+ }
6284
+ // Public API
6285
+ // -----------------------------------------------------------------------------------------------
6286
+ get x() {
6287
+ const { isRtl } = this.context;
6288
+ const el = this.elRef.current;
6289
+ return getNormalizedScrollX(el, isRtl);
6290
+ }
6291
+ get y() {
6292
+ const el = this.elRef.current;
6293
+ return el.scrollTop;
6294
+ }
6295
+ scrollTo({ x, y }) {
6296
+ const { isRtl } = this.context;
6297
+ const el = this.elRef.current;
6298
+ if (y != null) {
6299
+ el.scrollTop = y;
6300
+ }
6301
+ if (x != null) {
6302
+ setNormalizedScrollX(el, isRtl, x);
6303
+ }
6304
+ }
6305
+ addScrollEndListener(handler) {
6306
+ this.listener.emitter.on('scrollEnd', handler);
6307
+ }
6308
+ removeScrollEndListener(handler) {
6309
+ this.listener.emitter.off('scrollEnd', handler);
6310
+ }
6311
+ }
6312
+ // Public API
6313
+ // -------------------------------------------------------------------------------------------------
6314
+ // TODO: consolidate with scroll-left-norm.ts
6315
+ function getNormalizedScrollX(el, isRtl) {
6316
+ const { scrollLeft } = el;
6317
+ return isRtl ? getNormalizedRtlScrollX(scrollLeft, el) : scrollLeft;
6318
+ }
6319
+ function setNormalizedScrollX(el, isRtl, x) {
6320
+ el.scrollLeft = isRtl ? getNormalizedRtlScrollLeft(x, el) : x;
6321
+ }
6322
+ /*
6323
+ Returns a value in the 'reverse' system
6324
+ */
6325
+ function getNormalizedRtlScrollX(scrollLeft, el) {
6326
+ switch (getRtlScrollerConfig().system) {
6327
+ case 'positive':
6328
+ return el.scrollWidth - el.clientWidth - scrollLeft;
6329
+ case 'negative':
6330
+ return -scrollLeft;
6331
+ }
6332
+ return scrollLeft;
6333
+ }
6334
+ /*
6335
+ Receives a value in the 'reverse' system
6336
+ TODO: is this really the same equations as getNormalizedRtlScrollX??? I think so
6337
+ If so, consolidate. With isRtl check too
6338
+ */
6339
+ function getNormalizedRtlScrollLeft(x, el) {
6340
+ switch (getRtlScrollerConfig().system) {
6341
+ case 'positive':
6342
+ return el.scrollWidth - el.clientWidth - x;
6343
+ case 'negative':
6344
+ return -x;
6345
+ }
6346
+ return x;
6347
+ }
6348
+ let _rtlScrollerConfig;
6349
+ function getRtlScrollerConfig() {
6350
+ return _rtlScrollerConfig || (_rtlScrollerConfig = detectRtlScrollerConfig());
6351
+ }
6352
+ function detectRtlScrollerConfig() {
6353
+ let el = document.createElement('div');
6354
+ el.style.position = 'absolute';
6355
+ el.style.top = '-1000px';
6356
+ el.style.width = '100px'; // must be at least the side of scrollbars or you get inaccurate values (#7335)
6357
+ el.style.height = '100px'; // "
6358
+ el.style.overflow = 'scroll';
6359
+ el.style.direction = 'rtl';
6360
+ let innerEl = document.createElement('div');
6361
+ innerEl.style.width = '200px';
6362
+ innerEl.style.height = '200px';
6363
+ el.appendChild(innerEl);
6364
+ document.body.appendChild(el);
6365
+ let system;
6366
+ if (el.scrollLeft > 0) {
6367
+ system = 'positive'; // scroll is a positive number from the left edge
6368
+ }
6369
+ else {
6370
+ el.scrollLeft = 50;
6371
+ if (el.scrollLeft > 0) {
6372
+ system = 'reverse'; // scroll is a positive number from the right edge
6373
+ }
6374
+ else {
6375
+ system = 'negative'; // scroll is a negative number from the right edge
6376
+ }
6377
+ }
6378
+ let rightScrollbars = innerEl.getBoundingClientRect().right < el.getBoundingClientRect().right;
6379
+ removeElement(el);
6380
+ return { system, leftScrollbars: !rightScrollbars };
6381
+ }
6382
+
6383
+ class Slicer {
6384
+ constructor() {
6385
+ this.sliceBusinessHours = memoize(this._sliceBusinessHours);
6386
+ this.sliceDateSelection = memoize(this._sliceDateSpan);
6387
+ this.sliceEventStore = memoize(this._sliceEventStore);
6388
+ this.sliceEventDrag = memoize(this._sliceInteraction);
6389
+ this.sliceEventResize = memoize(this._sliceInteraction);
6390
+ this.forceDayIfListItem = false; // hack
6391
+ }
6392
+ sliceProps(props, dateProfile, nextDayThreshold, context, ...extraArgs) {
6393
+ let { eventUiBases } = props;
6394
+ let eventSegs = this.sliceEventStore(props.eventStore, eventUiBases, dateProfile, nextDayThreshold, ...extraArgs);
6358
6395
  return {
6359
6396
  dateSelectionSegs: this.sliceDateSelection(props.dateSelection, dateProfile, nextDayThreshold, eventUiBases, context, ...extraArgs),
6360
6397
  businessHourSegs: this.sliceBusinessHours(props.businessHours, dateProfile, nextDayThreshold, context, ...extraArgs),
@@ -6645,423 +6682,124 @@ function anyRangesContainRange(outerRanges, innerRange) {
6645
6682
  return false;
6646
6683
  }
6647
6684
 
6648
- const VISIBLE_HIDDEN_RE = /^(visible|hidden)$/;
6649
- class Scroller extends BaseComponent {
6650
- constructor() {
6651
- super(...arguments);
6652
- this.handleEl = (el) => {
6653
- this.el = el;
6654
- setRef(this.props.elRef, el);
6655
- };
6656
- }
6657
- render() {
6658
- let { props } = this;
6659
- let { liquid, liquidIsAbsolute } = props;
6660
- let isAbsolute = liquid && liquidIsAbsolute;
6661
- let className = ['fc-scroller'];
6662
- if (liquid) {
6663
- if (liquidIsAbsolute) {
6664
- className.push('fc-scroller-liquid-absolute');
6665
- }
6666
- else {
6667
- className.push('fc-scroller-liquid');
6668
- }
6669
- }
6670
- return (preact.createElement("div", { ref: this.handleEl, className: className.join(' '), style: {
6671
- overflowX: props.overflowX,
6672
- overflowY: props.overflowY,
6673
- left: (isAbsolute && -(props.overcomeLeft || 0)) || '',
6674
- right: (isAbsolute && -(props.overcomeRight || 0)) || '',
6675
- bottom: (isAbsolute && -(props.overcomeBottom || 0)) || '',
6676
- marginLeft: (!isAbsolute && -(props.overcomeLeft || 0)) || '',
6677
- marginRight: (!isAbsolute && -(props.overcomeRight || 0)) || '',
6678
- marginBottom: (!isAbsolute && -(props.overcomeBottom || 0)) || '',
6679
- maxHeight: props.maxHeight || '',
6680
- } }, props.children));
6681
- }
6682
- needsXScrolling() {
6683
- if (VISIBLE_HIDDEN_RE.test(this.props.overflowX)) {
6684
- return false;
6685
- }
6686
- // testing scrollWidth>clientWidth is unreliable cross-browser when pixel heights aren't integers.
6687
- // much more reliable to see if children are taller than the scroller, even tho doesn't account for
6688
- // inner-child margins and absolute positioning
6689
- let { el } = this;
6690
- let realClientWidth = this.el.getBoundingClientRect().width - this.getYScrollbarWidth();
6691
- let { children } = el;
6692
- for (let i = 0; i < children.length; i += 1) {
6693
- let childEl = children[i];
6694
- if (childEl.getBoundingClientRect().width > realClientWidth) {
6695
- return true;
6696
- }
6697
- }
6698
- return false;
6699
- }
6700
- needsYScrolling() {
6701
- if (VISIBLE_HIDDEN_RE.test(this.props.overflowY)) {
6702
- return false;
6703
- }
6704
- // testing scrollHeight>clientHeight is unreliable cross-browser when pixel heights aren't integers.
6705
- // much more reliable to see if children are taller than the scroller, even tho doesn't account for
6706
- // inner-child margins and absolute positioning
6707
- let { el } = this;
6708
- let realClientHeight = this.el.getBoundingClientRect().height - this.getXScrollbarWidth();
6709
- let { children } = el;
6710
- for (let i = 0; i < children.length; i += 1) {
6711
- let childEl = children[i];
6712
- if (childEl.getBoundingClientRect().height > realClientHeight) {
6713
- return true;
6714
- }
6715
- }
6716
- return false;
6685
+ function getIsHeightAuto(options) {
6686
+ return options.height === 'auto' || options.viewHeight === 'auto';
6687
+ }
6688
+ function getStickyHeaderDates(options) {
6689
+ let { stickyHeaderDates } = options;
6690
+ if (stickyHeaderDates == null || stickyHeaderDates === 'auto') {
6691
+ stickyHeaderDates = getIsHeightAuto(options);
6717
6692
  }
6718
- getXScrollbarWidth() {
6719
- if (VISIBLE_HIDDEN_RE.test(this.props.overflowX)) {
6720
- return 0;
6721
- }
6722
- return this.el.offsetHeight - this.el.clientHeight; // only works because we guarantee no borders. TODO: add to CSS with important?
6693
+ return stickyHeaderDates;
6694
+ }
6695
+ function getStickyFooterScrollbar(options) {
6696
+ let { stickyFooterScrollbar } = options;
6697
+ if (stickyFooterScrollbar == null || stickyFooterScrollbar === 'auto') {
6698
+ stickyFooterScrollbar = getIsHeightAuto(options);
6723
6699
  }
6724
- getYScrollbarWidth() {
6725
- if (VISIBLE_HIDDEN_RE.test(this.props.overflowY)) {
6726
- return 0;
6727
- }
6728
- return this.el.offsetWidth - this.el.clientWidth; // only works because we guarantee no borders. TODO: add to CSS with important?
6700
+ return stickyFooterScrollbar;
6701
+ }
6702
+ function getScrollerSyncerClass(pluginHooks) {
6703
+ const ScrollerSyncer = pluginHooks.scrollerSyncerClass;
6704
+ if (!ScrollerSyncer) {
6705
+ throw new RangeError('Must import @fullcalendar/scrollgrid');
6729
6706
  }
6707
+ return ScrollerSyncer;
6730
6708
  }
6731
6709
 
6732
6710
  /*
6733
- TODO: somehow infer OtherArgs from masterCallback?
6734
- TODO: infer RefType from masterCallback if provided
6711
+ TODO: make API where createRefMap() called
6735
6712
  */
6736
6713
  class RefMap {
6737
6714
  constructor(masterCallback) {
6738
6715
  this.masterCallback = masterCallback;
6739
- this.currentMap = {};
6740
- this.depths = {};
6741
- this.callbackMap = {};
6716
+ this.rev = '';
6717
+ this.current = new Map();
6718
+ this.callbacks = new Map;
6742
6719
  this.handleValue = (val, key) => {
6743
- let { depths, currentMap } = this;
6744
- let removed = false;
6745
- let added = false;
6746
- if (val !== null) {
6747
- // for bug... ACTUALLY: can probably do away with this now that callers don't share numeric indices anymore
6748
- removed = (key in currentMap);
6749
- currentMap[key] = val;
6750
- depths[key] = (depths[key] || 0) + 1;
6751
- added = true;
6720
+ let { current, callbacks } = this;
6721
+ if (val === null) {
6722
+ current.delete(key);
6723
+ callbacks.delete(key);
6752
6724
  }
6753
6725
  else {
6754
- depths[key] -= 1;
6755
- if (!depths[key]) {
6756
- delete currentMap[key];
6757
- delete this.callbackMap[key];
6758
- removed = true;
6759
- }
6726
+ current.set(key, val);
6760
6727
  }
6728
+ this.rev = guid();
6761
6729
  if (this.masterCallback) {
6762
- if (removed) {
6763
- this.masterCallback(null, String(key));
6764
- }
6765
- if (added) {
6766
- this.masterCallback(val, String(key));
6767
- }
6730
+ this.masterCallback(val, key);
6768
6731
  }
6769
6732
  };
6770
6733
  }
6771
6734
  createRef(key) {
6772
- let refCallback = this.callbackMap[key];
6735
+ let refCallback = this.callbacks.get(key);
6773
6736
  if (!refCallback) {
6774
- refCallback = this.callbackMap[key] = (val) => {
6775
- this.handleValue(val, String(key));
6737
+ refCallback = (val) => {
6738
+ this.handleValue(val, key);
6776
6739
  };
6740
+ this.callbacks.set(key, refCallback);
6777
6741
  }
6778
6742
  return refCallback;
6779
6743
  }
6780
- // TODO: check callers that don't care about order. should use getAll instead
6781
- // NOTE: this method has become less valuable now that we are encouraged to map order by some other index
6782
- // TODO: provide ONE array-export function, buildArray, which fails on non-numeric indexes. caller can manipulate and "collect"
6783
- collect(startIndex, endIndex, step) {
6784
- return collectFromHash(this.currentMap, startIndex, endIndex, step);
6785
- }
6786
- getAll() {
6787
- return hashValuesToArray(this.currentMap);
6788
- }
6789
6744
  }
6790
6745
 
6791
- function computeShrinkWidth(chunkEls) {
6792
- let shrinkCells = findElements(chunkEls, '.fc-scrollgrid-shrink');
6793
- let largestWidth = 0;
6794
- for (let shrinkCell of shrinkCells) {
6795
- largestWidth = Math.max(largestWidth, computeSmallestCellWidth(shrinkCell));
6796
- }
6797
- return Math.ceil(largestWidth); // <table> elements work best with integers. round up to ensure contents fits
6798
- }
6799
- function getSectionHasLiquidHeight(props, sectionConfig) {
6800
- return props.liquid && sectionConfig.liquid; // does the section do liquid-height? (need to have whole scrollgrid liquid-height as well)
6801
- }
6802
- function getAllowYScrolling(props, sectionConfig) {
6803
- return sectionConfig.maxHeight != null || // if its possible for the height to max out, we might need scrollbars
6804
- getSectionHasLiquidHeight(props, sectionConfig); // if the section is liquid height, it might condense enough to require scrollbars
6805
- }
6806
- // TODO: ONLY use `arg`. force out internal function to use same API
6807
- function renderChunkContent(sectionConfig, chunkConfig, arg, isHeader) {
6808
- let { expandRows } = arg;
6809
- let content = typeof chunkConfig.content === 'function' ?
6810
- chunkConfig.content(arg) :
6811
- preact.createElement('table', {
6812
- role: 'presentation',
6813
- className: [
6814
- chunkConfig.tableClassName,
6815
- sectionConfig.syncRowHeights ? 'fc-scrollgrid-sync-table' : '',
6816
- ].join(' '),
6817
- style: {
6818
- minWidth: arg.tableMinWidth,
6819
- width: arg.clientWidth,
6820
- height: expandRows ? arg.clientHeight : '', // css `height` on a <table> serves as a min-height
6821
- },
6822
- }, arg.tableColGroupNode, preact.createElement(isHeader ? 'thead' : 'tbody', {
6823
- role: 'presentation',
6824
- }, typeof chunkConfig.rowContent === 'function'
6825
- ? chunkConfig.rowContent(arg)
6826
- : chunkConfig.rowContent));
6827
- return content;
6828
- }
6829
- function isColPropsEqual(cols0, cols1) {
6830
- return isArraysEqual(cols0, cols1, isPropsEqual);
6831
- }
6832
- function renderMicroColGroup(cols, shrinkWidth) {
6833
- let colNodes = [];
6834
- /*
6835
- for ColProps with spans, it would have been great to make a single <col span="">
6836
- HOWEVER, Chrome was getting messing up distributing the width to <td>/<th> elements with colspans.
6837
- SOLUTION: making individual <col> elements makes Chrome behave.
6838
- */
6839
- for (let colProps of cols) {
6840
- let span = colProps.span || 1;
6841
- for (let i = 0; i < span; i += 1) {
6842
- colNodes.push(preact.createElement("col", { style: {
6843
- width: colProps.width === 'shrink' ? sanitizeShrinkWidth(shrinkWidth) : (colProps.width || ''),
6844
- minWidth: colProps.minWidth || '',
6845
- } }));
6846
- }
6847
- }
6848
- return preact.createElement('colgroup', {}, ...colNodes);
6849
- }
6850
- function sanitizeShrinkWidth(shrinkWidth) {
6851
- /* why 4? if we do 0, it will kill any border, which are needed for computeSmallestCellWidth
6852
- 4 accounts for 2 2-pixel borders. TODO: better solution? */
6853
- return shrinkWidth == null ? 4 : shrinkWidth;
6854
- }
6855
- function hasShrinkWidth(cols) {
6856
- for (let col of cols) {
6857
- if (col.width === 'shrink') {
6858
- return true;
6859
- }
6860
- }
6861
- return false;
6862
- }
6863
- function getScrollGridClassNames(liquid, context) {
6864
- let classNames = [
6865
- 'fc-scrollgrid',
6866
- context.theme.getClass('table'),
6867
- ];
6868
- if (liquid) {
6869
- classNames.push('fc-scrollgrid-liquid');
6746
+ class NowTimer extends preact.Component {
6747
+ constructor(props, context) {
6748
+ super(props, context);
6749
+ this.initialNowDate = getNow(context.options.now, context.dateEnv);
6750
+ this.initialNowQueriedMs = new Date().valueOf();
6751
+ this.state = this.computeTiming().currentState;
6870
6752
  }
6871
- return classNames;
6872
- }
6873
- function getSectionClassNames(sectionConfig, wholeTableVGrow) {
6874
- let classNames = [
6875
- 'fc-scrollgrid-section',
6876
- `fc-scrollgrid-section-${sectionConfig.type}`,
6877
- sectionConfig.className, // used?
6878
- ];
6879
- if (wholeTableVGrow && sectionConfig.liquid && sectionConfig.maxHeight == null) {
6880
- classNames.push('fc-scrollgrid-section-liquid');
6753
+ render() {
6754
+ let { props, state } = this;
6755
+ return props.children(state.nowDate, state.todayRange);
6881
6756
  }
6882
- if (sectionConfig.isSticky) {
6883
- classNames.push('fc-scrollgrid-section-sticky');
6757
+ componentDidMount() {
6758
+ this.setTimeout();
6884
6759
  }
6885
- return classNames;
6886
- }
6887
- function renderScrollShim(arg) {
6888
- return (preact.createElement("div", { className: "fc-scrollgrid-sticky-shim", style: {
6889
- width: arg.clientWidth,
6890
- minWidth: arg.tableMinWidth,
6891
- } }));
6892
- }
6893
- function getStickyHeaderDates(options) {
6894
- let { stickyHeaderDates } = options;
6895
- if (stickyHeaderDates == null || stickyHeaderDates === 'auto') {
6896
- stickyHeaderDates = options.height === 'auto' || options.viewHeight === 'auto';
6760
+ componentDidUpdate(prevProps) {
6761
+ if (prevProps.unit !== this.props.unit) {
6762
+ this.clearTimeout();
6763
+ this.setTimeout();
6764
+ }
6897
6765
  }
6898
- return stickyHeaderDates;
6899
- }
6900
- function getStickyFooterScrollbar(options) {
6901
- let { stickyFooterScrollbar } = options;
6902
- if (stickyFooterScrollbar == null || stickyFooterScrollbar === 'auto') {
6903
- stickyFooterScrollbar = options.height === 'auto' || options.viewHeight === 'auto';
6766
+ componentWillUnmount() {
6767
+ this.clearTimeout();
6904
6768
  }
6905
- return stickyFooterScrollbar;
6906
- }
6907
-
6908
- class SimpleScrollGrid extends BaseComponent {
6909
- constructor() {
6910
- super(...arguments);
6911
- this.processCols = memoize((a) => a, isColPropsEqual); // so we get same `cols` props every time
6912
- // yucky to memoize VNodes, but much more efficient for consumers
6913
- this.renderMicroColGroup = memoize(renderMicroColGroup);
6914
- this.scrollerRefs = new RefMap();
6915
- this.scrollerElRefs = new RefMap(this._handleScrollerEl.bind(this));
6916
- this.state = {
6917
- shrinkWidth: null,
6918
- forceYScrollbars: false,
6919
- scrollerClientWidths: {},
6920
- scrollerClientHeights: {},
6921
- };
6922
- // TODO: can do a really simple print-view. dont need to join rows
6923
- this.handleSizing = () => {
6924
- this.safeSetState(Object.assign({ shrinkWidth: this.computeShrinkWidth() }, this.computeScrollerDims()));
6769
+ computeTiming() {
6770
+ let { props, context } = this;
6771
+ let unroundedNow = addMs(this.initialNowDate, new Date().valueOf() - this.initialNowQueriedMs);
6772
+ let currentUnitStart = context.dateEnv.startOf(unroundedNow, props.unit);
6773
+ let nextUnitStart = context.dateEnv.add(currentUnitStart, createDuration(1, props.unit));
6774
+ let waitMs = nextUnitStart.valueOf() - unroundedNow.valueOf();
6775
+ // there is a max setTimeout ms value (https://stackoverflow.com/a/3468650/96342)
6776
+ // ensure no longer than a day
6777
+ waitMs = Math.min(1000 * 60 * 60 * 24, waitMs);
6778
+ return {
6779
+ currentState: { nowDate: currentUnitStart, todayRange: buildDayRange(currentUnitStart) },
6780
+ nextState: { nowDate: nextUnitStart, todayRange: buildDayRange(nextUnitStart) },
6781
+ waitMs,
6925
6782
  };
6926
6783
  }
6927
- render() {
6928
- let { props, state, context } = this;
6929
- let sectionConfigs = props.sections || [];
6930
- let cols = this.processCols(props.cols);
6931
- let microColGroupNode = this.renderMicroColGroup(cols, state.shrinkWidth);
6932
- let classNames = getScrollGridClassNames(props.liquid, context);
6933
- if (props.collapsibleWidth) {
6934
- classNames.push('fc-scrollgrid-collapsible');
6935
- }
6936
- // TODO: make DRY
6937
- let configCnt = sectionConfigs.length;
6938
- let configI = 0;
6939
- let currentConfig;
6940
- let headSectionNodes = [];
6941
- let bodySectionNodes = [];
6942
- let footSectionNodes = [];
6943
- while (configI < configCnt && (currentConfig = sectionConfigs[configI]).type === 'header') {
6944
- headSectionNodes.push(this.renderSection(currentConfig, microColGroupNode, true));
6945
- configI += 1;
6946
- }
6947
- while (configI < configCnt && (currentConfig = sectionConfigs[configI]).type === 'body') {
6948
- bodySectionNodes.push(this.renderSection(currentConfig, microColGroupNode, false));
6949
- configI += 1;
6950
- }
6951
- while (configI < configCnt && (currentConfig = sectionConfigs[configI]).type === 'footer') {
6952
- footSectionNodes.push(this.renderSection(currentConfig, microColGroupNode, true));
6953
- configI += 1;
6954
- }
6955
- // firefox bug: when setting height on table and there is a thead or tfoot,
6956
- // the necessary height:100% on the liquid-height body section forces the *whole* table to be taller. (bug #5524)
6957
- // use getCanVGrowWithinCell as a way to detect table-stupid firefox.
6958
- // if so, use a simpler dom structure, jam everything into a lone tbody.
6959
- let isBuggy = !getCanVGrowWithinCell();
6960
- const roleAttrs = { role: 'rowgroup' };
6961
- return preact.createElement('table', {
6962
- role: 'grid',
6963
- className: classNames.join(' '),
6964
- style: { height: props.height },
6965
- }, Boolean(!isBuggy && headSectionNodes.length) && preact.createElement('thead', roleAttrs, ...headSectionNodes), Boolean(!isBuggy && bodySectionNodes.length) && preact.createElement('tbody', roleAttrs, ...bodySectionNodes), Boolean(!isBuggy && footSectionNodes.length) && preact.createElement('tfoot', roleAttrs, ...footSectionNodes), isBuggy && preact.createElement('tbody', roleAttrs, ...headSectionNodes, ...bodySectionNodes, ...footSectionNodes));
6966
- }
6967
- renderSection(sectionConfig, microColGroupNode, isHeader) {
6968
- if ('outerContent' in sectionConfig) {
6969
- return (preact.createElement(preact.Fragment, { key: sectionConfig.key }, sectionConfig.outerContent));
6970
- }
6971
- return (preact.createElement("tr", { key: sectionConfig.key, role: "presentation", className: getSectionClassNames(sectionConfig, this.props.liquid).join(' ') }, this.renderChunkTd(sectionConfig, microColGroupNode, sectionConfig.chunk, isHeader)));
6972
- }
6973
- renderChunkTd(sectionConfig, microColGroupNode, chunkConfig, isHeader) {
6974
- if ('outerContent' in chunkConfig) {
6975
- return chunkConfig.outerContent;
6976
- }
6977
- let { props } = this;
6978
- let { forceYScrollbars, scrollerClientWidths, scrollerClientHeights } = this.state;
6979
- let needsYScrolling = getAllowYScrolling(props, sectionConfig); // TODO: do lazily. do in section config?
6980
- let isLiquid = getSectionHasLiquidHeight(props, sectionConfig);
6981
- // for `!props.liquid` - is WHOLE scrollgrid natural height?
6982
- // TODO: do same thing in advanced scrollgrid? prolly not b/c always has horizontal scrollbars
6983
- let overflowY = !props.liquid ? 'visible' :
6984
- forceYScrollbars ? 'scroll' :
6985
- !needsYScrolling ? 'hidden' :
6986
- 'auto';
6987
- let sectionKey = sectionConfig.key;
6988
- let content = renderChunkContent(sectionConfig, chunkConfig, {
6989
- tableColGroupNode: microColGroupNode,
6990
- tableMinWidth: '',
6991
- clientWidth: (!props.collapsibleWidth && scrollerClientWidths[sectionKey] !== undefined) ? scrollerClientWidths[sectionKey] : null,
6992
- clientHeight: scrollerClientHeights[sectionKey] !== undefined ? scrollerClientHeights[sectionKey] : null,
6993
- expandRows: sectionConfig.expandRows,
6994
- syncRowHeights: false,
6995
- rowSyncHeights: [],
6996
- reportRowHeightChange: () => { },
6997
- }, isHeader);
6998
- return preact.createElement(isHeader ? 'th' : 'td', {
6999
- ref: chunkConfig.elRef,
7000
- role: 'presentation',
7001
- }, preact.createElement("div", { className: `fc-scroller-harness${isLiquid ? ' fc-scroller-harness-liquid' : ''}` },
7002
- preact.createElement(Scroller, { ref: this.scrollerRefs.createRef(sectionKey), elRef: this.scrollerElRefs.createRef(sectionKey), overflowY: overflowY, overflowX: !props.liquid ? 'visible' : 'hidden' /* natural height? */, maxHeight: sectionConfig.maxHeight, liquid: isLiquid, liquidIsAbsolute // because its within a harness
7003
- : true }, content)));
7004
- }
7005
- _handleScrollerEl(scrollerEl, key) {
7006
- let section = getSectionByKey(this.props.sections, key);
7007
- if (section) {
7008
- setRef(section.chunk.scrollerElRef, scrollerEl);
7009
- }
7010
- }
7011
- componentDidMount() {
7012
- this.handleSizing();
7013
- this.context.addResizeHandler(this.handleSizing);
7014
- }
7015
- componentDidUpdate() {
7016
- // TODO: need better solution when state contains non-sizing things
7017
- this.handleSizing();
6784
+ setTimeout() {
6785
+ let { nextState, waitMs } = this.computeTiming();
6786
+ this.timeoutId = setTimeout(() => {
6787
+ this.setState(nextState, () => {
6788
+ this.setTimeout();
6789
+ });
6790
+ }, waitMs);
7018
6791
  }
7019
- componentWillUnmount() {
7020
- this.context.removeResizeHandler(this.handleSizing);
7021
- }
7022
- computeShrinkWidth() {
7023
- return hasShrinkWidth(this.props.cols)
7024
- ? computeShrinkWidth(this.scrollerElRefs.getAll())
7025
- : 0;
7026
- }
7027
- computeScrollerDims() {
7028
- let scrollbarWidth = getScrollbarWidths();
7029
- let { scrollerRefs, scrollerElRefs } = this;
7030
- let forceYScrollbars = false;
7031
- let scrollerClientWidths = {};
7032
- let scrollerClientHeights = {};
7033
- for (let sectionKey in scrollerRefs.currentMap) {
7034
- let scroller = scrollerRefs.currentMap[sectionKey];
7035
- if (scroller && scroller.needsYScrolling()) {
7036
- forceYScrollbars = true;
7037
- break;
7038
- }
7039
- }
7040
- for (let section of this.props.sections) {
7041
- let sectionKey = section.key;
7042
- let scrollerEl = scrollerElRefs.currentMap[sectionKey];
7043
- if (scrollerEl) {
7044
- let harnessEl = scrollerEl.parentNode; // TODO: weird way to get this. need harness b/c doesn't include table borders
7045
- scrollerClientWidths[sectionKey] = Math.floor(harnessEl.getBoundingClientRect().width - (forceYScrollbars
7046
- ? scrollbarWidth.y // use global because scroller might not have scrollbars yet but will need them in future
7047
- : 0));
7048
- scrollerClientHeights[sectionKey] = Math.floor(harnessEl.getBoundingClientRect().height);
7049
- }
6792
+ clearTimeout() {
6793
+ if (this.timeoutId) {
6794
+ clearTimeout(this.timeoutId);
7050
6795
  }
7051
- return { forceYScrollbars, scrollerClientWidths, scrollerClientHeights };
7052
6796
  }
7053
6797
  }
7054
- SimpleScrollGrid.addStateEquality({
7055
- scrollerClientWidths: isPropsEqual,
7056
- scrollerClientHeights: isPropsEqual,
7057
- });
7058
- function getSectionByKey(sections, key) {
7059
- for (let section of sections) {
7060
- if (section.key === key) {
7061
- return section;
7062
- }
7063
- }
7064
- return null;
6798
+ NowTimer.contextType = ViewContextType;
6799
+ function buildDayRange(date) {
6800
+ let start = startOfDay(date);
6801
+ let end = addDays(start, 1);
6802
+ return { start, end };
7065
6803
  }
7066
6804
 
7067
6805
  class EventContainer extends BaseComponent {
@@ -7070,15 +6808,14 @@ class EventContainer extends BaseComponent {
7070
6808
  this.handleEl = (el) => {
7071
6809
  this.el = el;
7072
6810
  if (el) {
7073
- setElSeg(el, this.props.seg);
6811
+ setElEventRange(el, this.props.eventRange);
7074
6812
  }
7075
6813
  };
7076
6814
  }
7077
6815
  render() {
7078
6816
  const { props, context } = this;
7079
6817
  const { options } = context;
7080
- const { seg } = props;
7081
- const { eventRange } = seg;
6818
+ const { eventRange } = props;
7082
6819
  const { ui } = eventRange;
7083
6820
  const renderProps = {
7084
6821
  event: new EventImpl(context, eventRange.def, eventRange.instance),
@@ -7087,12 +6824,12 @@ class EventContainer extends BaseComponent {
7087
6824
  textColor: ui.textColor,
7088
6825
  backgroundColor: ui.backgroundColor,
7089
6826
  borderColor: ui.borderColor,
7090
- isDraggable: !props.disableDragging && computeSegDraggable(seg, context),
7091
- isStartResizable: !props.disableResizing && computeSegStartResizable(seg, context),
7092
- isEndResizable: !props.disableResizing && computeSegEndResizable(seg),
6827
+ isDraggable: !props.disableDragging && computeEventRangeDraggable(eventRange, context),
6828
+ isStartResizable: !props.disableResizing && props.isStart && eventRange.ui.durationEditable && options.eventResizableFromStart,
6829
+ isEndResizable: !props.disableResizing && props.isEnd && eventRange.ui.durationEditable,
7093
6830
  isMirror: Boolean(props.isDragging || props.isResizing || props.isDateSelecting),
7094
- isStart: Boolean(seg.isStart),
7095
- isEnd: Boolean(seg.isEnd),
6831
+ isStart: Boolean(props.isStart),
6832
+ isEnd: Boolean(props.isEnd),
7096
6833
  isPast: Boolean(props.isPast),
7097
6834
  isFuture: Boolean(props.isFuture),
7098
6835
  isToday: Boolean(props.isToday),
@@ -7102,13 +6839,13 @@ class EventContainer extends BaseComponent {
7102
6839
  };
7103
6840
  return (preact.createElement(ContentContainer, Object.assign({}, props /* contains children */, { elRef: this.handleEl, elClasses: [
7104
6841
  ...getEventClassNames(renderProps),
7105
- ...seg.eventRange.ui.classNames,
6842
+ ...eventRange.ui.classNames,
7106
6843
  ...(props.elClasses || []),
7107
6844
  ], renderProps: renderProps, generatorName: "eventContent", customGenerator: options.eventContent, defaultGenerator: props.defaultGenerator, classNameGenerator: options.eventClassNames, didMount: options.eventDidMount, willUnmount: options.eventWillUnmount })));
7108
6845
  }
7109
6846
  componentDidUpdate(prevProps) {
7110
- if (this.el && this.props.seg !== prevProps.seg) {
7111
- setElSeg(this.el, this.props.seg);
6847
+ if (this.el && this.props.eventRange !== prevProps.eventRange) {
6848
+ setElEventRange(this.el, this.props.eventRange);
7112
6849
  }
7113
6850
  }
7114
6851
  }
@@ -7118,24 +6855,24 @@ class StandardEvent extends BaseComponent {
7118
6855
  render() {
7119
6856
  let { props, context } = this;
7120
6857
  let { options } = context;
7121
- let { seg } = props;
7122
- let { ui } = seg.eventRange;
6858
+ let { eventRange } = props;
6859
+ let { ui } = eventRange;
7123
6860
  let timeFormat = options.eventTimeFormat || props.defaultTimeFormat;
7124
- let timeText = buildSegTimeText(seg, timeFormat, context, props.defaultDisplayEventTime, props.defaultDisplayEventEnd);
6861
+ let timeText = buildEventRangeTimeText(eventRange, timeFormat, context, props.defaultDisplayEventTime, props.defaultDisplayEventEnd, props.startOverride, props.endOverride);
7125
6862
  return (preact.createElement(EventContainer, Object.assign({}, props /* includes elRef */, { elTag: "a", elStyle: {
7126
6863
  borderColor: ui.borderColor,
7127
6864
  backgroundColor: ui.backgroundColor,
7128
- }, elAttrs: getSegAnchorAttrs(seg, context), defaultGenerator: renderInnerContent$1, timeText: timeText }), (InnerContent, eventContentArg) => (preact.createElement(preact.Fragment, null,
7129
- preact.createElement(InnerContent, { elTag: "div", elClasses: ['fc-event-main'], elStyle: { color: eventContentArg.textColor } }),
6865
+ }, elAttrs: getEventRangeAnchorAttrs(eventRange, context), defaultGenerator: renderInnerContent$1, timeText: timeText }), (InnerContent, eventContentArg) => (preact.createElement(preact.Fragment, null,
6866
+ preact.createElement(InnerContent, { elTag: "div", elClasses: ['fc-event-inner'], elStyle: { color: eventContentArg.textColor } }),
7130
6867
  Boolean(eventContentArg.isStartResizable) && (preact.createElement("div", { className: "fc-event-resizer fc-event-resizer-start" })),
7131
6868
  Boolean(eventContentArg.isEndResizable) && (preact.createElement("div", { className: "fc-event-resizer fc-event-resizer-end" }))))));
7132
6869
  }
7133
6870
  }
7134
6871
  function renderInnerContent$1(innerProps) {
7135
- return (preact.createElement("div", { className: "fc-event-main-frame" },
6872
+ return (preact.createElement(preact.Fragment, null,
7136
6873
  innerProps.timeText && (preact.createElement("div", { className: "fc-event-time" }, innerProps.timeText)),
7137
- preact.createElement("div", { className: "fc-event-title-container" },
7138
- preact.createElement("div", { className: "fc-event-title fc-sticky" }, innerProps.event.title || preact.createElement(preact.Fragment, null, "\u00A0")))));
6874
+ preact.createElement("div", { className: "fc-event-title-outer" },
6875
+ preact.createElement("div", { className: "fc-event-title" }, innerProps.event.title || preact.createElement(preact.Fragment, null, "\u00A0")))));
7139
6876
  }
7140
6877
 
7141
6878
  const NowIndicatorContainer = (props) => (preact.createElement(ViewContextType.Consumer, null, (context) => {
@@ -7148,50 +6885,11 @@ const NowIndicatorContainer = (props) => (preact.createElement(ViewContextType.C
7148
6885
  return (preact.createElement(ContentContainer, Object.assign({}, props /* includes children */, { elTag: props.elTag || 'div', renderProps: renderProps, generatorName: "nowIndicatorContent", customGenerator: options.nowIndicatorContent, classNameGenerator: options.nowIndicatorClassNames, didMount: options.nowIndicatorDidMount, willUnmount: options.nowIndicatorWillUnmount })));
7149
6886
  }));
7150
6887
 
7151
- const DAY_NUM_FORMAT = createFormatter({ day: 'numeric' });
7152
- class DayCellContainer extends BaseComponent {
7153
- constructor() {
7154
- super(...arguments);
7155
- this.refineRenderProps = memoizeObjArg(refineRenderProps);
7156
- }
7157
- render() {
7158
- let { props, context } = this;
7159
- let { options } = context;
7160
- let renderProps = this.refineRenderProps({
7161
- date: props.date,
7162
- dateProfile: props.dateProfile,
7163
- todayRange: props.todayRange,
7164
- isMonthStart: props.isMonthStart || false,
7165
- showDayNumber: props.showDayNumber,
7166
- extraRenderProps: props.extraRenderProps,
7167
- viewApi: context.viewApi,
7168
- dateEnv: context.dateEnv,
7169
- monthStartFormat: options.monthStartFormat,
7170
- });
7171
- return (preact.createElement(ContentContainer, Object.assign({}, props /* includes children */, { elClasses: [
7172
- ...getDayClassNames(renderProps, context.theme),
7173
- ...(props.elClasses || []),
7174
- ], elAttrs: Object.assign(Object.assign({}, props.elAttrs), (renderProps.isDisabled ? {} : { 'data-date': formatDayString(props.date) })), renderProps: renderProps, generatorName: "dayCellContent", customGenerator: options.dayCellContent, defaultGenerator: props.defaultGenerator, classNameGenerator:
7175
- // don't use custom classNames if disabled
7176
- renderProps.isDisabled ? undefined : options.dayCellClassNames, didMount: options.dayCellDidMount, willUnmount: options.dayCellWillUnmount })));
7177
- }
7178
- }
7179
- function hasCustomDayCellContent(options) {
7180
- return Boolean(options.dayCellContent || hasCustomRenderingHandler('dayCellContent', options));
7181
- }
7182
- function refineRenderProps(raw) {
7183
- let { date, dateEnv, dateProfile, isMonthStart } = raw;
7184
- let dayMeta = getDateMeta(date, raw.todayRange, null, dateProfile);
7185
- let dayNumberText = raw.showDayNumber ? (dateEnv.format(date, isMonthStart ? raw.monthStartFormat : DAY_NUM_FORMAT)) : '';
7186
- return Object.assign(Object.assign(Object.assign({ date: dateEnv.toDate(date), view: raw.viewApi }, dayMeta), { isMonthStart,
7187
- dayNumberText }), raw.extraRenderProps);
7188
- }
7189
-
7190
- class BgEvent extends BaseComponent {
6888
+ class BgEvent extends BaseComponent {
7191
6889
  render() {
7192
6890
  let { props } = this;
7193
- let { seg } = props;
7194
- return (preact.createElement(EventContainer, { elTag: "div", elClasses: ['fc-bg-event'], elStyle: { backgroundColor: seg.eventRange.ui.backgroundColor }, defaultGenerator: renderInnerContent, seg: seg, timeText: "", isDragging: false, isResizing: false, isDateSelecting: false, isSelected: false, isPast: props.isPast, isFuture: props.isFuture, isToday: props.isToday, disableDragging: true, disableResizing: true }));
6891
+ let { eventRange } = props;
6892
+ return (preact.createElement(EventContainer, { elTag: "div", elClasses: ['fc-bg-event'], elStyle: { backgroundColor: eventRange.ui.backgroundColor }, defaultGenerator: renderInnerContent, eventRange: eventRange, isStart: props.isStart, isEnd: props.isEnd, timeText: "", isDragging: false, isResizing: false, isDateSelecting: false, isSelected: false, isPast: props.isPast, isFuture: props.isFuture, isToday: props.isToday, disableDragging: true, disableResizing: true }));
7195
6893
  }
7196
6894
  }
7197
6895
  function renderInnerContent(props) {
@@ -7216,250 +6914,384 @@ function renderInner(innerProps) {
7216
6914
  return innerProps.text;
7217
6915
  }
7218
6916
 
7219
- const PADDING_FROM_VIEWPORT = 10;
7220
- class Popover extends BaseComponent {
7221
- constructor() {
7222
- super(...arguments);
7223
- this.state = {
7224
- titleId: getUniqueDomId(),
7225
- };
7226
- this.handleRootEl = (el) => {
7227
- this.rootEl = el;
7228
- if (this.props.elRef) {
7229
- setRef(this.props.elRef, el);
7230
- }
7231
- };
7232
- // Triggered when the user clicks *anywhere* in the document, for the autoHide feature
7233
- this.handleDocumentMouseDown = (ev) => {
7234
- // only hide the popover if the click happened outside the popover
7235
- const target = getEventTargetViaRoot(ev);
7236
- if (!this.rootEl.contains(target)) {
7237
- this.handleCloseClick();
6917
+ /*
6918
+ Calendar instance for ALL frameworks
6919
+ */
6920
+ class CalendarImpl {
6921
+ getCurrentData() {
6922
+ return this.currentDataManager.getCurrentData();
6923
+ }
6924
+ dispatch(action) {
6925
+ this.currentDataManager.dispatch(action);
6926
+ }
6927
+ get view() { return this.getCurrentData().viewApi; }
6928
+ batchRendering(callback) {
6929
+ callback();
6930
+ }
6931
+ updateSize() {
6932
+ console.warn('Doesnt do anything!');
6933
+ }
6934
+ // Options
6935
+ // -----------------------------------------------------------------------------------------------------------------
6936
+ setOption(name, val) {
6937
+ this.dispatch({
6938
+ type: 'SET_OPTION',
6939
+ optionName: name,
6940
+ rawOptionValue: val,
6941
+ });
6942
+ }
6943
+ getOption(name) {
6944
+ return this.currentDataManager.currentCalendarOptionsInput[name];
6945
+ }
6946
+ getAvailableLocaleCodes() {
6947
+ return Object.keys(this.getCurrentData().availableRawLocales);
6948
+ }
6949
+ // Trigger
6950
+ // -----------------------------------------------------------------------------------------------------------------
6951
+ on(handlerName, handler) {
6952
+ let { currentDataManager } = this;
6953
+ if (currentDataManager.currentCalendarOptionsRefiners[handlerName]) {
6954
+ currentDataManager.emitter.on(handlerName, handler);
6955
+ }
6956
+ else {
6957
+ console.warn(`Unknown listener name '${handlerName}'`);
6958
+ }
6959
+ }
6960
+ off(handlerName, handler) {
6961
+ this.currentDataManager.emitter.off(handlerName, handler);
6962
+ }
6963
+ // not meant for public use
6964
+ trigger(handlerName, ...args) {
6965
+ this.currentDataManager.emitter.trigger(handlerName, ...args);
6966
+ }
6967
+ // View
6968
+ // -----------------------------------------------------------------------------------------------------------------
6969
+ changeView(viewType, dateOrRange) {
6970
+ this.batchRendering(() => {
6971
+ this.unselect();
6972
+ if (dateOrRange) {
6973
+ if (dateOrRange.start && dateOrRange.end) { // a range
6974
+ this.dispatch({
6975
+ type: 'CHANGE_VIEW_TYPE',
6976
+ viewType,
6977
+ });
6978
+ this.dispatch({
6979
+ type: 'SET_OPTION',
6980
+ optionName: 'visibleRange',
6981
+ rawOptionValue: dateOrRange,
6982
+ });
6983
+ }
6984
+ else {
6985
+ let { dateEnv } = this.getCurrentData();
6986
+ this.dispatch({
6987
+ type: 'CHANGE_VIEW_TYPE',
6988
+ viewType,
6989
+ dateMarker: dateEnv.createMarker(dateOrRange),
6990
+ });
6991
+ }
7238
6992
  }
7239
- };
7240
- this.handleDocumentKeyDown = (ev) => {
7241
- if (ev.key === 'Escape') {
7242
- this.handleCloseClick();
6993
+ else {
6994
+ this.dispatch({
6995
+ type: 'CHANGE_VIEW_TYPE',
6996
+ viewType,
6997
+ });
7243
6998
  }
7244
- };
7245
- this.handleCloseClick = () => {
7246
- let { onClose } = this.props;
7247
- if (onClose) {
7248
- onClose();
6999
+ });
7000
+ }
7001
+ // Forces navigation to a view for the given date.
7002
+ // `viewType` can be a specific view name or a generic one like "week" or "day".
7003
+ // needs to change
7004
+ zoomTo(dateMarker, viewType) {
7005
+ let state = this.getCurrentData();
7006
+ let spec;
7007
+ viewType = viewType || 'day'; // day is default zoom
7008
+ spec = state.viewSpecs[viewType] || this.getUnitViewSpec(viewType);
7009
+ this.unselect();
7010
+ if (spec) {
7011
+ this.dispatch({
7012
+ type: 'CHANGE_VIEW_TYPE',
7013
+ viewType: spec.type,
7014
+ dateMarker,
7015
+ });
7016
+ }
7017
+ else {
7018
+ this.dispatch({
7019
+ type: 'CHANGE_DATE',
7020
+ dateMarker,
7021
+ });
7022
+ }
7023
+ }
7024
+ // Given a duration singular unit, like "week" or "day", finds a matching view spec.
7025
+ // Preference is given to views that have corresponding buttons.
7026
+ getUnitViewSpec(unit) {
7027
+ let { viewSpecs, toolbarConfig } = this.getCurrentData();
7028
+ let viewTypes = [].concat(toolbarConfig.header ? toolbarConfig.header.viewsWithButtons : [], toolbarConfig.footer ? toolbarConfig.footer.viewsWithButtons : []);
7029
+ let i;
7030
+ let spec;
7031
+ for (let viewType in viewSpecs) {
7032
+ viewTypes.push(viewType);
7033
+ }
7034
+ for (i = 0; i < viewTypes.length; i += 1) {
7035
+ spec = viewSpecs[viewTypes[i]];
7036
+ if (spec) {
7037
+ if (spec.singleUnit === unit) {
7038
+ return spec;
7039
+ }
7249
7040
  }
7250
- };
7041
+ }
7042
+ return null;
7251
7043
  }
7252
- render() {
7253
- let { theme, options } = this.context;
7254
- let { props, state } = this;
7255
- let classNames = [
7256
- 'fc-popover',
7257
- theme.getClass('popover'),
7258
- ].concat(props.extraClassNames || []);
7259
- return compat.createPortal(preact.createElement("div", Object.assign({}, props.extraAttrs, { id: props.id, className: classNames.join(' '), "aria-labelledby": state.titleId, ref: this.handleRootEl }),
7260
- preact.createElement("div", { className: 'fc-popover-header ' + theme.getClass('popoverHeader') },
7261
- preact.createElement("span", { className: "fc-popover-title", id: state.titleId }, props.title),
7262
- preact.createElement("span", { className: 'fc-popover-close ' + theme.getIconClass('close'), title: options.closeHint, onClick: this.handleCloseClick })),
7263
- preact.createElement("div", { className: 'fc-popover-body ' + theme.getClass('popoverContent') }, props.children)), props.parentEl);
7044
+ // Current Date
7045
+ // -----------------------------------------------------------------------------------------------------------------
7046
+ prev() {
7047
+ this.unselect();
7048
+ this.dispatch({ type: 'PREV' });
7264
7049
  }
7265
- componentDidMount() {
7266
- document.addEventListener('mousedown', this.handleDocumentMouseDown);
7267
- document.addEventListener('keydown', this.handleDocumentKeyDown);
7268
- this.updateSize();
7050
+ next() {
7051
+ this.unselect();
7052
+ this.dispatch({ type: 'NEXT' });
7269
7053
  }
7270
- componentWillUnmount() {
7271
- document.removeEventListener('mousedown', this.handleDocumentMouseDown);
7272
- document.removeEventListener('keydown', this.handleDocumentKeyDown);
7054
+ prevYear() {
7055
+ let state = this.getCurrentData();
7056
+ this.unselect();
7057
+ this.dispatch({
7058
+ type: 'CHANGE_DATE',
7059
+ dateMarker: state.dateEnv.addYears(state.currentDate, -1),
7060
+ });
7273
7061
  }
7274
- updateSize() {
7275
- let { isRtl } = this.context;
7276
- let { alignmentEl, alignGridTop } = this.props;
7277
- let { rootEl } = this;
7278
- let alignmentRect = computeClippedClientRect(alignmentEl);
7279
- if (alignmentRect) {
7280
- let popoverDims = rootEl.getBoundingClientRect();
7281
- // position relative to viewport
7282
- let popoverTop = alignGridTop
7283
- ? elementClosest(alignmentEl, '.fc-scrollgrid').getBoundingClientRect().top
7284
- : alignmentRect.top;
7285
- let popoverLeft = isRtl ? alignmentRect.right - popoverDims.width : alignmentRect.left;
7286
- // constrain
7287
- popoverTop = Math.max(popoverTop, PADDING_FROM_VIEWPORT);
7288
- popoverLeft = Math.min(popoverLeft, document.documentElement.clientWidth - PADDING_FROM_VIEWPORT - popoverDims.width);
7289
- popoverLeft = Math.max(popoverLeft, PADDING_FROM_VIEWPORT);
7290
- let origin = rootEl.offsetParent.getBoundingClientRect();
7291
- applyStyle(rootEl, {
7292
- top: popoverTop - origin.top,
7293
- left: popoverLeft - origin.left,
7062
+ nextYear() {
7063
+ let state = this.getCurrentData();
7064
+ this.unselect();
7065
+ this.dispatch({
7066
+ type: 'CHANGE_DATE',
7067
+ dateMarker: state.dateEnv.addYears(state.currentDate, 1),
7068
+ });
7069
+ }
7070
+ today() {
7071
+ let state = this.getCurrentData();
7072
+ this.unselect();
7073
+ this.dispatch({
7074
+ type: 'CHANGE_DATE',
7075
+ dateMarker: getNow(state.calendarOptions.now, state.dateEnv),
7076
+ });
7077
+ }
7078
+ gotoDate(zonedDateInput) {
7079
+ let state = this.getCurrentData();
7080
+ this.unselect();
7081
+ this.dispatch({
7082
+ type: 'CHANGE_DATE',
7083
+ dateMarker: state.dateEnv.createMarker(zonedDateInput),
7084
+ });
7085
+ }
7086
+ incrementDate(deltaInput) {
7087
+ let state = this.getCurrentData();
7088
+ let delta = createDuration(deltaInput);
7089
+ if (delta) { // else, warn about invalid input?
7090
+ this.unselect();
7091
+ this.dispatch({
7092
+ type: 'CHANGE_DATE',
7093
+ dateMarker: state.dateEnv.add(state.currentDate, delta),
7094
+ });
7095
+ }
7096
+ }
7097
+ getDate() {
7098
+ let state = this.getCurrentData();
7099
+ return state.dateEnv.toDate(state.currentDate);
7100
+ }
7101
+ // Date Formatting Utils
7102
+ // -----------------------------------------------------------------------------------------------------------------
7103
+ formatDate(d, formatter) {
7104
+ let { dateEnv } = this.getCurrentData();
7105
+ return dateEnv.format(dateEnv.createMarker(d), createFormatter(formatter));
7106
+ }
7107
+ // `settings` is for formatter AND isEndExclusive
7108
+ formatRange(d0, d1, settings) {
7109
+ let { dateEnv } = this.getCurrentData();
7110
+ return dateEnv.formatRange(dateEnv.createMarker(d0), dateEnv.createMarker(d1), createFormatter(settings), settings);
7111
+ }
7112
+ formatIso(d, omitTime) {
7113
+ let { dateEnv } = this.getCurrentData();
7114
+ return dateEnv.formatIso(dateEnv.createMarker(d), { omitTime });
7115
+ }
7116
+ // Date Selection / Event Selection / DayClick
7117
+ // -----------------------------------------------------------------------------------------------------------------
7118
+ select(dateOrObj, endDate) {
7119
+ let selectionInput;
7120
+ if (endDate == null) {
7121
+ if (dateOrObj.start != null) {
7122
+ selectionInput = dateOrObj;
7123
+ }
7124
+ else {
7125
+ selectionInput = {
7126
+ start: dateOrObj,
7127
+ end: null,
7128
+ };
7129
+ }
7130
+ }
7131
+ else {
7132
+ selectionInput = {
7133
+ start: dateOrObj,
7134
+ end: endDate,
7135
+ };
7136
+ }
7137
+ let state = this.getCurrentData();
7138
+ let selection = parseDateSpan(selectionInput, state.dateEnv, createDuration({ days: 1 }));
7139
+ if (selection) { // throw parse error otherwise?
7140
+ this.dispatch({ type: 'SELECT_DATES', selection });
7141
+ triggerDateSelect(selection, null, state);
7142
+ }
7143
+ }
7144
+ unselect(pev) {
7145
+ let state = this.getCurrentData();
7146
+ if (state.dateSelection) {
7147
+ this.dispatch({ type: 'UNSELECT_DATES' });
7148
+ triggerDateUnselect(pev, state);
7149
+ }
7150
+ }
7151
+ // Public Events API
7152
+ // -----------------------------------------------------------------------------------------------------------------
7153
+ addEvent(eventInput, sourceInput) {
7154
+ if (eventInput instanceof EventImpl) {
7155
+ let def = eventInput._def;
7156
+ let instance = eventInput._instance;
7157
+ let currentData = this.getCurrentData();
7158
+ // not already present? don't want to add an old snapshot
7159
+ if (!currentData.eventStore.defs[def.defId]) {
7160
+ this.dispatch({
7161
+ type: 'ADD_EVENTS',
7162
+ eventStore: eventTupleToStore({ def, instance }), // TODO: better util for two args?
7163
+ });
7164
+ this.triggerEventAdd(eventInput);
7165
+ }
7166
+ return eventInput;
7167
+ }
7168
+ let state = this.getCurrentData();
7169
+ let eventSource;
7170
+ if (sourceInput instanceof EventSourceImpl) {
7171
+ eventSource = sourceInput.internalEventSource;
7172
+ }
7173
+ else if (typeof sourceInput === 'boolean') {
7174
+ if (sourceInput) { // true. part of the first event source
7175
+ [eventSource] = hashValuesToArray(state.eventSources);
7176
+ }
7177
+ }
7178
+ else if (sourceInput != null) { // an ID. accepts a number too
7179
+ let sourceApi = this.getEventSourceById(sourceInput); // TODO: use an internal function
7180
+ if (!sourceApi) {
7181
+ console.warn(`Could not find an event source with ID "${sourceInput}"`); // TODO: test
7182
+ return null;
7183
+ }
7184
+ eventSource = sourceApi.internalEventSource;
7185
+ }
7186
+ let tuple = parseEvent(eventInput, eventSource, state, false);
7187
+ if (tuple) {
7188
+ let newEventApi = new EventImpl(state, tuple.def, tuple.def.recurringDef ? null : tuple.instance);
7189
+ this.dispatch({
7190
+ type: 'ADD_EVENTS',
7191
+ eventStore: eventTupleToStore(tuple),
7294
7192
  });
7193
+ this.triggerEventAdd(newEventApi);
7194
+ return newEventApi;
7295
7195
  }
7196
+ return null;
7296
7197
  }
7297
- }
7298
-
7299
- class MorePopover extends DateComponent {
7300
- constructor() {
7301
- super(...arguments);
7302
- this.handleRootEl = (rootEl) => {
7303
- this.rootEl = rootEl;
7304
- if (rootEl) {
7305
- this.context.registerInteractiveComponent(this, {
7306
- el: rootEl,
7307
- useEventCenter: false,
7198
+ triggerEventAdd(eventApi) {
7199
+ let { emitter } = this.getCurrentData();
7200
+ emitter.trigger('eventAdd', {
7201
+ event: eventApi,
7202
+ relatedEvents: [],
7203
+ revert: () => {
7204
+ this.dispatch({
7205
+ type: 'REMOVE_EVENTS',
7206
+ eventStore: eventApiToStore(eventApi),
7308
7207
  });
7208
+ },
7209
+ });
7210
+ }
7211
+ // TODO: optimize
7212
+ getEventById(id) {
7213
+ let state = this.getCurrentData();
7214
+ let { defs, instances } = state.eventStore;
7215
+ id = String(id);
7216
+ for (let defId in defs) {
7217
+ let def = defs[defId];
7218
+ if (def.publicId === id) {
7219
+ if (def.recurringDef) {
7220
+ return new EventImpl(state, def, null);
7221
+ }
7222
+ for (let instanceId in instances) {
7223
+ let instance = instances[instanceId];
7224
+ if (instance.defId === def.defId) {
7225
+ return new EventImpl(state, def, instance);
7226
+ }
7227
+ }
7309
7228
  }
7310
- else {
7311
- this.context.unregisterInteractiveComponent(this);
7312
- }
7313
- };
7229
+ }
7230
+ return null;
7314
7231
  }
7315
- render() {
7316
- let { options, dateEnv } = this.context;
7317
- let { props } = this;
7318
- let { startDate, todayRange, dateProfile } = props;
7319
- let title = dateEnv.format(startDate, options.dayPopoverFormat);
7320
- return (preact.createElement(DayCellContainer, { elRef: this.handleRootEl, date: startDate, dateProfile: dateProfile, todayRange: todayRange }, (InnerContent, renderProps, elAttrs) => (preact.createElement(Popover, { elRef: elAttrs.ref, id: props.id, title: title, extraClassNames: ['fc-more-popover'].concat(elAttrs.className || []), extraAttrs: elAttrs /* TODO: make these time-based when not whole-day? */, parentEl: props.parentEl, alignmentEl: props.alignmentEl, alignGridTop: props.alignGridTop, onClose: props.onClose },
7321
- hasCustomDayCellContent(options) && (preact.createElement(InnerContent, { elTag: "div", elClasses: ['fc-more-popover-misc'] })),
7322
- props.children))));
7232
+ getEvents() {
7233
+ let currentData = this.getCurrentData();
7234
+ return buildEventApis(currentData.eventStore, currentData);
7323
7235
  }
7324
- queryHit(positionLeft, positionTop, elWidth, elHeight) {
7325
- let { rootEl, props } = this;
7326
- if (positionLeft >= 0 && positionLeft < elWidth &&
7327
- positionTop >= 0 && positionTop < elHeight) {
7328
- return {
7329
- dateProfile: props.dateProfile,
7330
- dateSpan: Object.assign({ allDay: !props.forceTimed, range: {
7331
- start: props.startDate,
7332
- end: props.endDate,
7333
- } }, props.extraDateSpan),
7334
- dayEl: rootEl,
7335
- rect: {
7336
- left: 0,
7337
- top: 0,
7338
- right: elWidth,
7339
- bottom: elHeight,
7340
- },
7341
- layer: 1, // important when comparing with hits from other components
7342
- };
7236
+ removeAllEvents() {
7237
+ this.dispatch({ type: 'REMOVE_ALL_EVENTS' });
7238
+ }
7239
+ // Public Event Sources API
7240
+ // -----------------------------------------------------------------------------------------------------------------
7241
+ getEventSources() {
7242
+ let state = this.getCurrentData();
7243
+ let sourceHash = state.eventSources;
7244
+ let sourceApis = [];
7245
+ for (let internalId in sourceHash) {
7246
+ sourceApis.push(new EventSourceImpl(state, sourceHash[internalId]));
7343
7247
  }
7344
- return null;
7248
+ return sourceApis;
7345
7249
  }
7346
- }
7347
-
7348
- class MoreLinkContainer extends BaseComponent {
7349
- constructor() {
7350
- super(...arguments);
7351
- this.state = {
7352
- isPopoverOpen: false,
7353
- popoverId: getUniqueDomId(),
7354
- };
7355
- this.handleLinkEl = (linkEl) => {
7356
- this.linkEl = linkEl;
7357
- if (this.props.elRef) {
7358
- setRef(this.props.elRef, linkEl);
7359
- }
7360
- };
7361
- this.handleClick = (ev) => {
7362
- let { props, context } = this;
7363
- let { moreLinkClick } = context.options;
7364
- let date = computeRange(props).start;
7365
- function buildPublicSeg(seg) {
7366
- let { def, instance, range } = seg.eventRange;
7367
- return {
7368
- event: new EventImpl(context, def, instance),
7369
- start: context.dateEnv.toDate(range.start),
7370
- end: context.dateEnv.toDate(range.end),
7371
- isStart: seg.isStart,
7372
- isEnd: seg.isEnd,
7373
- };
7250
+ getEventSourceById(id) {
7251
+ let state = this.getCurrentData();
7252
+ let sourceHash = state.eventSources;
7253
+ id = String(id);
7254
+ for (let sourceId in sourceHash) {
7255
+ if (sourceHash[sourceId].publicId === id) {
7256
+ return new EventSourceImpl(state, sourceHash[sourceId]);
7374
7257
  }
7375
- if (typeof moreLinkClick === 'function') {
7376
- moreLinkClick = moreLinkClick({
7377
- date,
7378
- allDay: Boolean(props.allDayDate),
7379
- allSegs: props.allSegs.map(buildPublicSeg),
7380
- hiddenSegs: props.hiddenSegs.map(buildPublicSeg),
7381
- jsEvent: ev,
7382
- view: context.viewApi,
7258
+ }
7259
+ return null;
7260
+ }
7261
+ addEventSource(sourceInput) {
7262
+ let state = this.getCurrentData();
7263
+ if (sourceInput instanceof EventSourceImpl) {
7264
+ // not already present? don't want to add an old snapshot
7265
+ if (!state.eventSources[sourceInput.internalEventSource.sourceId]) {
7266
+ this.dispatch({
7267
+ type: 'ADD_EVENT_SOURCES',
7268
+ sources: [sourceInput.internalEventSource],
7383
7269
  });
7384
7270
  }
7385
- if (!moreLinkClick || moreLinkClick === 'popover') {
7386
- this.setState({ isPopoverOpen: true });
7387
- }
7388
- else if (typeof moreLinkClick === 'string') { // a view name
7389
- context.calendarApi.zoomTo(date, moreLinkClick);
7390
- }
7391
- };
7392
- this.handlePopoverClose = () => {
7393
- this.setState({ isPopoverOpen: false });
7394
- };
7395
- }
7396
- render() {
7397
- let { props, state } = this;
7398
- return (preact.createElement(ViewContextType.Consumer, null, (context) => {
7399
- let { viewApi, options, calendarApi } = context;
7400
- let { moreLinkText } = options;
7401
- let { moreCnt } = props;
7402
- let range = computeRange(props);
7403
- let text = typeof moreLinkText === 'function' // TODO: eventually use formatWithOrdinals
7404
- ? moreLinkText.call(calendarApi, moreCnt)
7405
- : `+${moreCnt} ${moreLinkText}`;
7406
- let hint = formatWithOrdinals(options.moreLinkHint, [moreCnt], text);
7407
- let renderProps = {
7408
- num: moreCnt,
7409
- shortText: `+${moreCnt}`,
7410
- text,
7411
- view: viewApi,
7412
- };
7413
- return (preact.createElement(preact.Fragment, null,
7414
- Boolean(props.moreCnt) && (preact.createElement(ContentContainer, { elTag: props.elTag || 'a', elRef: this.handleLinkEl, elClasses: [
7415
- ...(props.elClasses || []),
7416
- 'fc-more-link',
7417
- ], elStyle: props.elStyle, elAttrs: Object.assign(Object.assign(Object.assign({}, props.elAttrs), createAriaClickAttrs(this.handleClick)), { title: hint, 'aria-expanded': state.isPopoverOpen, 'aria-controls': state.isPopoverOpen ? state.popoverId : '' }), renderProps: renderProps, generatorName: "moreLinkContent", customGenerator: options.moreLinkContent, defaultGenerator: props.defaultGenerator || renderMoreLinkInner, classNameGenerator: options.moreLinkClassNames, didMount: options.moreLinkDidMount, willUnmount: options.moreLinkWillUnmount }, props.children)),
7418
- state.isPopoverOpen && (preact.createElement(MorePopover, { id: state.popoverId, startDate: range.start, endDate: range.end, dateProfile: props.dateProfile, todayRange: props.todayRange, extraDateSpan: props.extraDateSpan, parentEl: this.parentEl, alignmentEl: props.alignmentElRef ?
7419
- props.alignmentElRef.current :
7420
- this.linkEl, alignGridTop: props.alignGridTop, forceTimed: props.forceTimed, onClose: this.handlePopoverClose }, props.popoverContent()))));
7421
- }));
7271
+ return sourceInput;
7272
+ }
7273
+ let eventSource = parseEventSource(sourceInput, state);
7274
+ if (eventSource) { // TODO: error otherwise?
7275
+ this.dispatch({ type: 'ADD_EVENT_SOURCES', sources: [eventSource] });
7276
+ return new EventSourceImpl(state, eventSource);
7277
+ }
7278
+ return null;
7422
7279
  }
7423
- componentDidMount() {
7424
- this.updateParentEl();
7280
+ removeAllEventSources() {
7281
+ this.dispatch({ type: 'REMOVE_ALL_EVENT_SOURCES' });
7425
7282
  }
7426
- componentDidUpdate() {
7427
- this.updateParentEl();
7283
+ refetchEvents() {
7284
+ this.dispatch({ type: 'FETCH_EVENT_SOURCES', isRefetch: true });
7428
7285
  }
7429
- updateParentEl() {
7430
- if (this.linkEl) {
7431
- this.parentEl = elementClosest(this.linkEl, '.fc-view-harness');
7286
+ // Scroll
7287
+ // -----------------------------------------------------------------------------------------------------------------
7288
+ scrollToTime(timeInput) {
7289
+ let time = createDuration(timeInput);
7290
+ if (time) {
7291
+ this.trigger('_timeScrollRequest', time);
7432
7292
  }
7433
7293
  }
7434
7294
  }
7435
- function renderMoreLinkInner(props) {
7436
- return props.text;
7437
- }
7438
- function computeRange(props) {
7439
- if (props.allDayDate) {
7440
- return {
7441
- start: props.allDayDate,
7442
- end: addDays(props.allDayDate, 1),
7443
- };
7444
- }
7445
- let { hiddenSegs } = props;
7446
- return {
7447
- start: computeEarliestSegStart(hiddenSegs),
7448
- end: computeLatestSegEnd(hiddenSegs),
7449
- };
7450
- }
7451
- function computeEarliestSegStart(segs) {
7452
- return segs.reduce(pickEarliestStart).eventRange.range.start;
7453
- }
7454
- function pickEarliestStart(seg0, seg1) {
7455
- return seg0.eventRange.range.start < seg1.eventRange.range.start ? seg0 : seg1;
7456
- }
7457
- function computeLatestSegEnd(segs) {
7458
- return segs.reduce(pickLatestEnd).eventRange.range.end;
7459
- }
7460
- function pickLatestEnd(seg0, seg1) {
7461
- return seg0.eventRange.range.end > seg1.eventRange.range.end ? seg0 : seg1;
7462
- }
7463
7295
 
7464
7296
  class Store {
7465
7297
  constructor() {
@@ -7520,7 +7352,6 @@ exports.DateComponent = DateComponent;
7520
7352
  exports.DateEnv = DateEnv;
7521
7353
  exports.DateProfileGenerator = DateProfileGenerator;
7522
7354
  exports.DayCellContainer = DayCellContainer;
7523
- exports.DayHeader = DayHeader;
7524
7355
  exports.DaySeriesModel = DaySeriesModel;
7525
7356
  exports.DayTableModel = DayTableModel;
7526
7357
  exports.DelayedRunner = DelayedRunner;
@@ -7540,15 +7371,11 @@ exports.PureComponent = PureComponent;
7540
7371
  exports.RefMap = RefMap;
7541
7372
  exports.RenderId = RenderId;
7542
7373
  exports.ScrollController = ScrollController;
7543
- exports.ScrollResponder = ScrollResponder;
7544
7374
  exports.Scroller = Scroller;
7545
7375
  exports.SegHierarchy = SegHierarchy;
7546
- exports.SimpleScrollGrid = SimpleScrollGrid;
7547
7376
  exports.Slicer = Slicer;
7548
7377
  exports.Splitter = Splitter;
7549
7378
  exports.StandardEvent = StandardEvent;
7550
- exports.TableDateCell = TableDateCell;
7551
- exports.TableDowCell = TableDowCell;
7552
7379
  exports.Theme = Theme;
7553
7380
  exports.VIEW_OPTION_REFINERS = VIEW_OPTION_REFINERS;
7554
7381
  exports.ViewContainer = ViewContainer;
@@ -7559,6 +7386,7 @@ exports.addDays = addDays;
7559
7386
  exports.addDurations = addDurations;
7560
7387
  exports.addMs = addMs;
7561
7388
  exports.addWeeks = addWeeks;
7389
+ exports.afterSize = afterSize;
7562
7390
  exports.allowContextMenu = allowContextMenu;
7563
7391
  exports.allowSelection = allowSelection;
7564
7392
  exports.applyMutationToEventStore = applyMutationToEventStore;
@@ -7574,11 +7402,11 @@ exports.buildElAttrs = buildElAttrs;
7574
7402
  exports.buildEntryKey = buildEntryKey;
7575
7403
  exports.buildEventApis = buildEventApis;
7576
7404
  exports.buildEventRangeKey = buildEventRangeKey;
7405
+ exports.buildEventRangeTimeText = buildEventRangeTimeText;
7577
7406
  exports.buildEventSourceRefiners = buildEventSourceRefiners;
7578
7407
  exports.buildIsoString = buildIsoString;
7579
7408
  exports.buildNavLinkAttrs = buildNavLinkAttrs;
7580
7409
  exports.buildRangeApiWithTimeZone = buildRangeApiWithTimeZone;
7581
- exports.buildSegTimeText = buildSegTimeText;
7582
7410
  exports.buildViewClassNames = buildViewClassNames;
7583
7411
  exports.buildViewContext = buildViewContext;
7584
7412
  exports.collectFromHash = collectFromHash;
@@ -7588,10 +7416,8 @@ exports.compareNumbers = compareNumbers;
7588
7416
  exports.compareObjs = compareObjs;
7589
7417
  exports.computeEarliestSegStart = computeEarliestSegStart;
7590
7418
  exports.computeEdges = computeEdges;
7591
- exports.computeFallbackHeaderFormat = computeFallbackHeaderFormat;
7592
7419
  exports.computeInnerRect = computeInnerRect;
7593
7420
  exports.computeRect = computeRect;
7594
- exports.computeShrinkWidth = computeShrinkWidth;
7595
7421
  exports.computeVisibleDayRange = computeVisibleDayRange;
7596
7422
  exports.config = config;
7597
7423
  exports.constrainPoint = constrainPoint;
@@ -7623,26 +7449,25 @@ exports.formatDayString = formatDayString;
7623
7449
  exports.formatIsoMonthStr = formatIsoMonthStr;
7624
7450
  exports.formatIsoTimeString = formatIsoTimeString;
7625
7451
  exports.formatWithOrdinals = formatWithOrdinals;
7626
- exports.getAllowYScrolling = getAllowYScrolling;
7627
- exports.getCanVGrowWithinCell = getCanVGrowWithinCell;
7452
+ exports.fracToCssDim = fracToCssDim;
7628
7453
  exports.getClippingParents = getClippingParents;
7629
7454
  exports.getDateMeta = getDateMeta;
7630
7455
  exports.getDayClassNames = getDayClassNames;
7631
7456
  exports.getDefaultEventEnd = getDefaultEventEnd;
7632
- exports.getElSeg = getElSeg;
7457
+ exports.getElEventRange = getElEventRange;
7633
7458
  exports.getEntrySpanEnd = getEntrySpanEnd;
7459
+ exports.getEventRangeAnchorAttrs = getEventRangeAnchorAttrs;
7460
+ exports.getEventRangeMeta = getEventRangeMeta;
7634
7461
  exports.getEventTargetViaRoot = getEventTargetViaRoot;
7635
7462
  exports.getInitialDate = getInitialDate;
7463
+ exports.getIsHeightAuto = getIsHeightAuto;
7636
7464
  exports.getIsRtlScrollbarOnLeft = getIsRtlScrollbarOnLeft;
7465
+ exports.getNormalizedScrollX = getNormalizedScrollX;
7637
7466
  exports.getNow = getNow;
7638
7467
  exports.getRectCenter = getRectCenter;
7639
7468
  exports.getRelevantEvents = getRelevantEvents;
7640
- exports.getScrollGridClassNames = getScrollGridClassNames;
7641
7469
  exports.getScrollbarWidths = getScrollbarWidths;
7642
- exports.getSectionClassNames = getSectionClassNames;
7643
- exports.getSectionHasLiquidHeight = getSectionHasLiquidHeight;
7644
- exports.getSegAnchorAttrs = getSegAnchorAttrs;
7645
- exports.getSegMeta = getSegMeta;
7470
+ exports.getScrollerSyncerClass = getScrollerSyncerClass;
7646
7471
  exports.getSlotClassNames = getSlotClassNames;
7647
7472
  exports.getStickyFooterScrollbar = getStickyFooterScrollbar;
7648
7473
  exports.getStickyHeaderDates = getStickyHeaderDates;
@@ -7652,7 +7477,6 @@ exports.groupIntersectingEntries = groupIntersectingEntries;
7652
7477
  exports.guid = guid;
7653
7478
  exports.hasBgRendering = hasBgRendering;
7654
7479
  exports.hasCustomDayCellContent = hasCustomDayCellContent;
7655
- exports.hasShrinkWidth = hasShrinkWidth;
7656
7480
  exports.hashValuesToArray = hashValuesToArray;
7657
7481
  exports.identity = identity;
7658
7482
  exports.injectStyles = injectStyles;
@@ -7662,9 +7486,10 @@ exports.intersectRanges = intersectRanges;
7662
7486
  exports.intersectRects = intersectRects;
7663
7487
  exports.intersectSpans = intersectSpans;
7664
7488
  exports.isArraysEqual = isArraysEqual;
7665
- exports.isColPropsEqual = isColPropsEqual;
7666
7489
  exports.isDateSelectionValid = isDateSelectionValid;
7667
7490
  exports.isDateSpansEqual = isDateSpansEqual;
7491
+ exports.isDimMapsEqual = isDimMapsEqual;
7492
+ exports.isDimsEqual = isDimsEqual;
7668
7493
  exports.isInt = isInt;
7669
7494
  exports.isInteractionValid = isInteractionValid;
7670
7495
  exports.isMultiDayRange = isMultiDayRange;
@@ -7705,14 +7530,12 @@ exports.refineEventDef = refineEventDef;
7705
7530
  exports.refineProps = refineProps;
7706
7531
  exports.removeElement = removeElement;
7707
7532
  exports.removeExact = removeExact;
7708
- exports.renderChunkContent = renderChunkContent;
7709
7533
  exports.renderFill = renderFill;
7710
- exports.renderMicroColGroup = renderMicroColGroup;
7711
- exports.renderScrollShim = renderScrollShim;
7712
7534
  exports.requestJson = requestJson;
7713
7535
  exports.rezoneEventStoreDates = rezoneEventStoreDates;
7714
- exports.sanitizeShrinkWidth = sanitizeShrinkWidth;
7536
+ exports.setNormalizedScrollX = setNormalizedScrollX;
7715
7537
  exports.setRef = setRef;
7538
+ exports.setStateDimMap = setStateDimMap;
7716
7539
  exports.sliceEventStore = sliceEventStore;
7717
7540
  exports.sortEventSegs = sortEventSegs;
7718
7541
  exports.startOfDay = startOfDay;
@@ -7720,5 +7543,8 @@ exports.subtractDurations = subtractDurations;
7720
7543
  exports.translateRect = translateRect;
7721
7544
  exports.triggerDateSelect = triggerDateSelect;
7722
7545
  exports.unpromisify = unpromisify;
7546
+ exports.watchHeight = watchHeight;
7547
+ exports.watchSize = watchSize;
7548
+ exports.watchWidth = watchWidth;
7723
7549
  exports.whenTransitionDone = whenTransitionDone;
7724
7550
  exports.wholeDivideDurations = wholeDivideDurations;