@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
@@ -1,5 +1,5 @@
1
1
  import * as preact from 'preact';
2
- import { Component, createElement, isValidElement, Fragment } from 'preact';
2
+ import { Component, createElement, isValidElement, Fragment, createRef } from 'preact';
3
3
  import { createPortal } from 'preact/compat';
4
4
 
5
5
  const styleTexts = [];
@@ -82,7 +82,7 @@ if (typeof document !== 'undefined') {
82
82
  registerStylesRoot(document);
83
83
  }
84
84
 
85
- 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)}";
85
+ 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}";
86
86
  injectStyles(css_248z);
87
87
 
88
88
  class DelayedRunner {
@@ -444,20 +444,6 @@ function compareNumbers(a, b) {
444
444
  function isInt(n) {
445
445
  return n % 1 === 0;
446
446
  }
447
- /* FC-specific DOM dimension stuff
448
- ----------------------------------------------------------------------------------------------------------------------*/
449
- function computeSmallestCellWidth(cellEl) {
450
- let allWidthEl = cellEl.querySelector('.fc-scrollgrid-shrink-frame');
451
- let contentWidthEl = cellEl.querySelector('.fc-scrollgrid-shrink-cushion');
452
- if (!allWidthEl) {
453
- throw new Error('needs fc-scrollgrid-shrink-frame className'); // TODO: use const
454
- }
455
- if (!contentWidthEl) {
456
- throw new Error('needs fc-scrollgrid-shrink-cushion className');
457
- }
458
- return cellEl.getBoundingClientRect().width - allWidthEl.getBoundingClientRect().width + // the cell padding+border
459
- contentWidthEl.getBoundingClientRect().width;
460
- }
461
447
 
462
448
  const INTERNAL_UNITS = ['years', 'months', 'days', 'milliseconds'];
463
449
  const PARSE_RE = /^(-?)(?:(\d+)\.)?(\d+):(\d\d)(?::(\d\d)(?:\.(\d\d\d))?)?/;
@@ -1370,8 +1356,6 @@ const BASE_OPTION_REFINERS = {
1370
1356
  dropAccept: identity,
1371
1357
  eventOrder: parseFieldSpecs,
1372
1358
  eventOrderStrict: Boolean,
1373
- handleWindowResize: Boolean,
1374
- windowResizeDelay: Number,
1375
1359
  longPressDelay: Number,
1376
1360
  eventDragMinDistance: Number,
1377
1361
  expandRows: Boolean,
@@ -1521,8 +1505,6 @@ const BASE_OPTION_DEFAULTS = {
1521
1505
  dropAccept: '*',
1522
1506
  eventOrder: 'start,-duration,allDay,title',
1523
1507
  dayPopoverFormat: { month: 'long', day: 'numeric', year: 'numeric' },
1524
- handleWindowResize: true,
1525
- windowResizeDelay: 100,
1526
1508
  longPressDelay: 1000,
1527
1509
  eventDragMinDistance: 5,
1528
1510
  expandRows: false,
@@ -1541,7 +1523,6 @@ const CALENDAR_LISTENER_REFINERS = {
1541
1523
  eventAdd: identity,
1542
1524
  eventChange: identity,
1543
1525
  eventRemove: identity,
1544
- windowResize: identity,
1545
1526
  eventClick: identity,
1546
1527
  eventMouseEnter: identity,
1547
1528
  eventMouseLeave: identity,
@@ -1554,8 +1535,7 @@ const CALENDAR_LISTENER_REFINERS = {
1554
1535
  _afterprint: identity,
1555
1536
  _noEventDrop: identity,
1556
1537
  _noEventResize: identity,
1557
- _resize: identity,
1558
- _scrollRequest: identity,
1538
+ _timeScrollRequest: identity,
1559
1539
  };
1560
1540
  // calendar-specific options
1561
1541
  // -------------------------
@@ -2225,6 +2205,9 @@ Theme.prototype.iconOverridePrefix = '';
2225
2205
  NOTE: this can be a public API, especially createElement for hooks.
2226
2206
  See examples/typescript-scheduler/src/index.ts
2227
2207
  */
2208
+ /*
2209
+ TODO: rethink this
2210
+ */
2228
2211
  function flushSync(runBeforeFlush) {
2229
2212
  runBeforeFlush();
2230
2213
  let oldDebounceRendering = preact.options.debounceRendering; // orig
@@ -2274,42 +2257,6 @@ function createContext(defaultValue) {
2274
2257
  return ContextType;
2275
2258
  }
2276
2259
 
2277
- class ScrollResponder {
2278
- constructor(execFunc, emitter, scrollTime, scrollTimeReset) {
2279
- this.execFunc = execFunc;
2280
- this.emitter = emitter;
2281
- this.scrollTime = scrollTime;
2282
- this.scrollTimeReset = scrollTimeReset;
2283
- this.handleScrollRequest = (request) => {
2284
- this.queuedRequest = Object.assign({}, this.queuedRequest || {}, request);
2285
- this.drain();
2286
- };
2287
- emitter.on('_scrollRequest', this.handleScrollRequest);
2288
- this.fireInitialScroll();
2289
- }
2290
- detach() {
2291
- this.emitter.off('_scrollRequest', this.handleScrollRequest);
2292
- }
2293
- update(isDatesNew) {
2294
- if (isDatesNew && this.scrollTimeReset) {
2295
- this.fireInitialScroll(); // will drain
2296
- }
2297
- else {
2298
- this.drain();
2299
- }
2300
- }
2301
- fireInitialScroll() {
2302
- this.handleScrollRequest({
2303
- time: this.scrollTime,
2304
- });
2305
- }
2306
- drain() {
2307
- if (this.queuedRequest && this.execFunc(this.queuedRequest)) {
2308
- this.queuedRequest = null;
2309
- }
2310
- }
2311
- }
2312
-
2313
2260
  const ViewContextType = createContext({}); // for Components
2314
2261
  function buildViewContext(viewSpec, viewApi, viewOptions, dateProfileGenerator, dateEnv, theme, pluginHooks, dispatch, getCurrentData, emitter, calendarApi, registerInteractiveComponent, unregisterInteractiveComponent) {
2315
2262
  return {
@@ -2325,15 +2272,6 @@ function buildViewContext(viewSpec, viewApi, viewOptions, dateProfileGenerator,
2325
2272
  dateProfileGenerator,
2326
2273
  theme,
2327
2274
  isRtl: viewOptions.direction === 'rtl',
2328
- addResizeHandler(handler) {
2329
- emitter.on('_resize', handler);
2330
- },
2331
- removeResizeHandler(handler) {
2332
- emitter.off('_resize', handler);
2333
- },
2334
- createScrollResponder(execFunc) {
2335
- return new ScrollResponder(execFunc, emitter, createDuration(viewOptions.scrollTime), viewOptions.scrollTimeReset);
2336
- },
2337
2275
  registerInteractiveComponent,
2338
2276
  unregisterInteractiveComponent,
2339
2277
  };
@@ -3396,6 +3334,9 @@ function parseClassNames(raw) {
3396
3334
  }
3397
3335
  return [];
3398
3336
  }
3337
+ function fracToCssDim(frac) {
3338
+ return frac * 100 + '%';
3339
+ }
3399
3340
 
3400
3341
  // TODO: better called "EventSettings" or "EventConfig"
3401
3342
  // TODO: move this file into structs
@@ -4335,12 +4276,12 @@ function sliceEventStore(eventStore, eventUiBases, framingRange, nextDayThreshol
4335
4276
  function hasBgRendering(def) {
4336
4277
  return def.ui.display === 'background' || def.ui.display === 'inverse-background';
4337
4278
  }
4338
- function setElSeg(el, seg) {
4339
- el.fcSeg = seg;
4279
+ function setElEventRange(el, eventRange) {
4280
+ el.fcEventRange = eventRange;
4340
4281
  }
4341
- function getElSeg(el) {
4342
- return el.fcSeg ||
4343
- el.parentNode.fcSeg || // for the harness
4282
+ function getElEventRange(el) {
4283
+ return el.fcEventRange ||
4284
+ el.parentNode.fcEventRange || // for the harness
4344
4285
  null;
4345
4286
  }
4346
4287
  // event ui computation
@@ -4360,7 +4301,7 @@ function compileEventUi(eventDef, eventUiBases) {
4360
4301
  }
4361
4302
  function sortEventSegs(segs, eventOrderSpecs) {
4362
4303
  let objs = segs.map(buildSegCompareObj);
4363
- objs.sort((obj0, obj1) => compareByFieldSpecs(obj0, obj1, eventOrderSpecs));
4304
+ objs.sort((obj0, obj1) => compareByFieldSpecs(obj0, obj1, eventOrderSpecs)); // !!!
4364
4305
  return objs.map((c) => c._seg);
4365
4306
  }
4366
4307
  // returns a object with all primitive props that can be compared
@@ -4373,29 +4314,23 @@ function buildSegCompareObj(seg) {
4373
4314
  return Object.assign(Object.assign(Object.assign({}, eventDef.extendedProps), eventDef), { id: eventDef.publicId, start,
4374
4315
  end, duration: end - start, allDay: Number(eventDef.allDay), _seg: seg });
4375
4316
  }
4376
- function computeSegDraggable(seg, context) {
4317
+ function computeEventRangeDraggable(eventRange, context) {
4377
4318
  let { pluginHooks } = context;
4378
4319
  let transformers = pluginHooks.isDraggableTransformers;
4379
- let { def, ui } = seg.eventRange;
4320
+ let { def, ui } = eventRange;
4380
4321
  let val = ui.startEditable;
4381
4322
  for (let transformer of transformers) {
4382
4323
  val = transformer(val, def, ui, context);
4383
4324
  }
4384
4325
  return val;
4385
4326
  }
4386
- function computeSegStartResizable(seg, context) {
4387
- return seg.isStart && seg.eventRange.ui.durationEditable && context.options.eventResizableFromStart;
4388
- }
4389
- function computeSegEndResizable(seg, context) {
4390
- return seg.isEnd && seg.eventRange.ui.durationEditable;
4391
- }
4392
- function buildSegTimeText(seg, timeFormat, context, defaultDisplayEventTime, // defaults to true
4327
+ function buildEventRangeTimeText(eventRange, timeFormat, context, defaultDisplayEventTime, // defaults to true
4393
4328
  defaultDisplayEventEnd, // defaults to true
4394
4329
  startOverride, endOverride) {
4395
4330
  let { dateEnv, options } = context;
4396
4331
  let { displayEventTime, displayEventEnd } = options;
4397
- let eventDef = seg.eventRange.def;
4398
- let eventInstance = seg.eventRange.instance;
4332
+ let eventDef = eventRange.def;
4333
+ let eventInstance = eventRange.instance;
4399
4334
  if (displayEventTime == null) {
4400
4335
  displayEventTime = defaultDisplayEventTime !== false;
4401
4336
  }
@@ -4404,8 +4339,8 @@ startOverride, endOverride) {
4404
4339
  }
4405
4340
  let wholeEventStart = eventInstance.range.start;
4406
4341
  let wholeEventEnd = eventInstance.range.end;
4407
- let segStart = startOverride || seg.start || seg.eventRange.range.start;
4408
- let segEnd = endOverride || seg.end || seg.eventRange.range.end;
4342
+ let segStart = startOverride || eventRange.range.start;
4343
+ let segEnd = endOverride || eventRange.range.end;
4409
4344
  let isStartDay = startOfDay(wholeEventStart).valueOf() === startOfDay(segStart).valueOf();
4410
4345
  let isEndDay = startOfDay(addMs(wholeEventEnd, -1)).valueOf() === startOfDay(addMs(segEnd, -1)).valueOf();
4411
4346
  if (displayEventTime && !eventDef.allDay && (isStartDay || isEndDay)) {
@@ -4423,8 +4358,8 @@ startOverride, endOverride) {
4423
4358
  }
4424
4359
  return '';
4425
4360
  }
4426
- function getSegMeta(seg, todayRange, nowDate) {
4427
- let segRange = seg.eventRange.range;
4361
+ function getEventRangeMeta(eventRange, todayRange, nowDate) {
4362
+ let segRange = eventRange.range;
4428
4363
  return {
4429
4364
  isPast: segRange.end <= (nowDate || todayRange.start),
4430
4365
  isFuture: segRange.start >= (nowDate || todayRange.end),
@@ -4474,8 +4409,8 @@ function buildEventRangeKey(eventRange) {
4474
4409
  : `${eventRange.def.defId}:${eventRange.range.start.toISOString()}`;
4475
4410
  // inverse-background events don't have specific instances. TODO: better solution
4476
4411
  }
4477
- function getSegAnchorAttrs(seg, context) {
4478
- let { def, instance } = seg.eventRange;
4412
+ function getEventRangeAnchorAttrs(eventRange, context) {
4413
+ let { def, instance } = eventRange;
4479
4414
  let { url } = def;
4480
4415
  if (url) {
4481
4416
  return { href: url };
@@ -4655,33 +4590,6 @@ function requestJson(method, url, params) {
4655
4590
  });
4656
4591
  }
4657
4592
 
4658
- let canVGrowWithinCell;
4659
- function getCanVGrowWithinCell() {
4660
- if (canVGrowWithinCell == null) {
4661
- canVGrowWithinCell = computeCanVGrowWithinCell();
4662
- }
4663
- return canVGrowWithinCell;
4664
- }
4665
- function computeCanVGrowWithinCell() {
4666
- // for SSR, because this function is call immediately at top-level
4667
- // TODO: just make this logic execute top-level, immediately, instead of doing lazily
4668
- if (typeof document === 'undefined') {
4669
- return true;
4670
- }
4671
- let el = document.createElement('div');
4672
- el.style.position = 'absolute';
4673
- el.style.top = '0px';
4674
- el.style.left = '0px';
4675
- el.innerHTML = '<table><tr><td><div></div></td></tr></table>';
4676
- el.querySelector('table').style.height = '100px';
4677
- el.querySelector('div').style.height = '100%';
4678
- document.body.appendChild(el);
4679
- let div = el.querySelector('div');
4680
- let possible = div.offsetHeight > 0;
4681
- document.body.removeChild(el);
4682
- return possible;
4683
- }
4684
-
4685
4593
  class CalendarRoot extends BaseComponent {
4686
4594
  constructor() {
4687
4595
  super(...arguments);
@@ -4700,21 +4608,16 @@ class CalendarRoot extends BaseComponent {
4700
4608
  };
4701
4609
  }
4702
4610
  render() {
4703
- let { props } = this;
4611
+ let { props, state } = this;
4704
4612
  let { options } = props;
4705
- let { forPrint } = this.state;
4706
- let isHeightAuto = forPrint || options.height === 'auto' || options.contentHeight === 'auto';
4707
- let height = (!isHeightAuto && options.height != null) ? options.height : '';
4613
+ let { forPrint } = state;
4708
4614
  let classNames = [
4709
4615
  'fc',
4710
4616
  forPrint ? 'fc-media-print' : 'fc-media-screen',
4711
4617
  `fc-direction-${options.direction}`,
4712
4618
  props.theme.getClass('root'),
4713
4619
  ];
4714
- if (!getCanVGrowWithinCell()) {
4715
- classNames.push('fc-liquid-hack');
4716
- }
4717
- return props.children(classNames, height, isHeightAuto, forPrint);
4620
+ return props.children(classNames, options.height, forPrint);
4718
4621
  }
4719
4622
  componentDidMount() {
4720
4623
  let { emitter } = this.props;
@@ -4752,978 +4655,695 @@ function interactionSettingsToStore(settings) {
4752
4655
  // global state
4753
4656
  const interactionSettingsStore = {};
4754
4657
 
4755
- class CalendarImpl {
4756
- getCurrentData() {
4757
- return this.currentDataManager.getCurrentData();
4758
- }
4759
- dispatch(action) {
4760
- this.currentDataManager.dispatch(action);
4761
- }
4762
- get view() { return this.getCurrentData().viewApi; }
4763
- batchRendering(callback) {
4764
- callback();
4765
- }
4766
- updateSize() {
4767
- this.trigger('_resize', true);
4768
- }
4769
- // Options
4770
- // -----------------------------------------------------------------------------------------------------------------
4771
- setOption(name, val) {
4772
- this.dispatch({
4773
- type: 'SET_OPTION',
4774
- optionName: name,
4775
- rawOptionValue: val,
4776
- });
4777
- }
4778
- getOption(name) {
4779
- return this.currentDataManager.currentCalendarOptionsInput[name];
4658
+ function pointInsideRect(point, rect) {
4659
+ return point.left >= rect.left &&
4660
+ point.left < rect.right &&
4661
+ point.top >= rect.top &&
4662
+ point.top < rect.bottom;
4663
+ }
4664
+ // Returns a new rectangle that is the intersection of the two rectangles. If they don't intersect, returns false
4665
+ function intersectRects(rect1, rect2) {
4666
+ let res = {
4667
+ left: Math.max(rect1.left, rect2.left),
4668
+ right: Math.min(rect1.right, rect2.right),
4669
+ top: Math.max(rect1.top, rect2.top),
4670
+ bottom: Math.min(rect1.bottom, rect2.bottom),
4671
+ };
4672
+ if (res.left < res.right && res.top < res.bottom) {
4673
+ return res;
4780
4674
  }
4781
- getAvailableLocaleCodes() {
4782
- return Object.keys(this.getCurrentData().availableRawLocales);
4675
+ return false;
4676
+ }
4677
+ function translateRect(rect, deltaX, deltaY) {
4678
+ return {
4679
+ left: rect.left + deltaX,
4680
+ right: rect.right + deltaX,
4681
+ top: rect.top + deltaY,
4682
+ bottom: rect.bottom + deltaY,
4683
+ };
4684
+ }
4685
+ // Returns a new point that will have been moved to reside within the given rectangle
4686
+ function constrainPoint(point, rect) {
4687
+ return {
4688
+ left: Math.min(Math.max(point.left, rect.left), rect.right),
4689
+ top: Math.min(Math.max(point.top, rect.top), rect.bottom),
4690
+ };
4691
+ }
4692
+ // Returns a point that is the center of the given rectangle
4693
+ function getRectCenter(rect) {
4694
+ return {
4695
+ left: (rect.left + rect.right) / 2,
4696
+ top: (rect.top + rect.bottom) / 2,
4697
+ };
4698
+ }
4699
+ // Subtracts point2's coordinates from point1's coordinates, returning a delta
4700
+ function diffPoints(point1, point2) {
4701
+ return {
4702
+ left: point1.left - point2.left,
4703
+ top: point1.top - point2.top,
4704
+ };
4705
+ }
4706
+
4707
+ const EMPTY_EVENT_STORE = createEmptyEventStore(); // for purecomponents. TODO: keep elsewhere
4708
+ class Splitter {
4709
+ constructor() {
4710
+ this.getKeysForEventDefs = memoize(this._getKeysForEventDefs);
4711
+ this.splitDateSelection = memoize(this._splitDateSpan);
4712
+ this.splitEventStore = memoize(this._splitEventStore);
4713
+ this.splitIndividualUi = memoize(this._splitIndividualUi);
4714
+ this.splitEventDrag = memoize(this._splitInteraction);
4715
+ this.splitEventResize = memoize(this._splitInteraction);
4716
+ this.eventUiBuilders = {}; // TODO: typescript protection
4783
4717
  }
4784
- // Trigger
4785
- // -----------------------------------------------------------------------------------------------------------------
4786
- on(handlerName, handler) {
4787
- let { currentDataManager } = this;
4788
- if (currentDataManager.currentCalendarOptionsRefiners[handlerName]) {
4789
- currentDataManager.emitter.on(handlerName, handler);
4790
- }
4791
- else {
4792
- console.warn(`Unknown listener name '${handlerName}'`);
4718
+ splitProps(props) {
4719
+ let keyInfos = this.getKeyInfo(props);
4720
+ let defKeys = this.getKeysForEventDefs(props.eventStore);
4721
+ let dateSelections = this.splitDateSelection(props.dateSelection);
4722
+ let individualUi = this.splitIndividualUi(props.eventUiBases, defKeys); // the individual *bases*
4723
+ let eventStores = this.splitEventStore(props.eventStore, defKeys);
4724
+ let eventDrags = this.splitEventDrag(props.eventDrag);
4725
+ let eventResizes = this.splitEventResize(props.eventResize);
4726
+ let splitProps = {};
4727
+ this.eventUiBuilders = mapHash(keyInfos, (info, key) => this.eventUiBuilders[key] || memoize(buildEventUiForKey));
4728
+ for (let key in keyInfos) {
4729
+ let keyInfo = keyInfos[key];
4730
+ let eventStore = eventStores[key] || EMPTY_EVENT_STORE;
4731
+ let buildEventUi = this.eventUiBuilders[key];
4732
+ splitProps[key] = {
4733
+ businessHours: keyInfo.businessHours || props.businessHours,
4734
+ dateSelection: dateSelections[key] || null,
4735
+ eventStore,
4736
+ eventUiBases: buildEventUi(props.eventUiBases[''], keyInfo.ui, individualUi[key]),
4737
+ eventDrag: eventDrags[key] || null,
4738
+ eventResize: eventResizes[key] || null,
4739
+ eventSelection: eventStore.instances[props.eventSelection] ? props.eventSelection : '',
4740
+ };
4793
4741
  }
4742
+ return splitProps;
4794
4743
  }
4795
- off(handlerName, handler) {
4796
- this.currentDataManager.emitter.off(handlerName, handler);
4744
+ _splitDateSpan(dateSpan) {
4745
+ let dateSpans = {};
4746
+ if (dateSpan) {
4747
+ let keys = this.getKeysForDateSpan(dateSpan);
4748
+ for (let key of keys) {
4749
+ dateSpans[key] = dateSpan;
4750
+ }
4751
+ }
4752
+ return dateSpans;
4797
4753
  }
4798
- // not meant for public use
4799
- trigger(handlerName, ...args) {
4800
- this.currentDataManager.emitter.trigger(handlerName, ...args);
4754
+ _getKeysForEventDefs(eventStore) {
4755
+ return mapHash(eventStore.defs, (eventDef) => this.getKeysForEventDef(eventDef));
4801
4756
  }
4802
- // View
4803
- // -----------------------------------------------------------------------------------------------------------------
4804
- changeView(viewType, dateOrRange) {
4805
- this.batchRendering(() => {
4806
- this.unselect();
4807
- if (dateOrRange) {
4808
- if (dateOrRange.start && dateOrRange.end) { // a range
4809
- this.dispatch({
4810
- type: 'CHANGE_VIEW_TYPE',
4811
- viewType,
4812
- });
4813
- this.dispatch({
4814
- type: 'SET_OPTION',
4815
- optionName: 'visibleRange',
4816
- rawOptionValue: dateOrRange,
4817
- });
4818
- }
4819
- else {
4820
- let { dateEnv } = this.getCurrentData();
4821
- this.dispatch({
4822
- type: 'CHANGE_VIEW_TYPE',
4823
- viewType,
4824
- dateMarker: dateEnv.createMarker(dateOrRange),
4825
- });
4757
+ _splitEventStore(eventStore, defKeys) {
4758
+ let { defs, instances } = eventStore;
4759
+ let splitStores = {};
4760
+ for (let defId in defs) {
4761
+ for (let key of defKeys[defId]) {
4762
+ if (!splitStores[key]) {
4763
+ splitStores[key] = createEmptyEventStore();
4826
4764
  }
4765
+ splitStores[key].defs[defId] = defs[defId];
4827
4766
  }
4828
- else {
4829
- this.dispatch({
4830
- type: 'CHANGE_VIEW_TYPE',
4831
- viewType,
4832
- });
4833
- }
4834
- });
4835
- }
4836
- // Forces navigation to a view for the given date.
4837
- // `viewType` can be a specific view name or a generic one like "week" or "day".
4838
- // needs to change
4839
- zoomTo(dateMarker, viewType) {
4840
- let state = this.getCurrentData();
4841
- let spec;
4842
- viewType = viewType || 'day'; // day is default zoom
4843
- spec = state.viewSpecs[viewType] || this.getUnitViewSpec(viewType);
4844
- this.unselect();
4845
- if (spec) {
4846
- this.dispatch({
4847
- type: 'CHANGE_VIEW_TYPE',
4848
- viewType: spec.type,
4849
- dateMarker,
4850
- });
4851
- }
4852
- else {
4853
- this.dispatch({
4854
- type: 'CHANGE_DATE',
4855
- dateMarker,
4856
- });
4857
- }
4858
- }
4859
- // Given a duration singular unit, like "week" or "day", finds a matching view spec.
4860
- // Preference is given to views that have corresponding buttons.
4861
- getUnitViewSpec(unit) {
4862
- let { viewSpecs, toolbarConfig } = this.getCurrentData();
4863
- let viewTypes = [].concat(toolbarConfig.header ? toolbarConfig.header.viewsWithButtons : [], toolbarConfig.footer ? toolbarConfig.footer.viewsWithButtons : []);
4864
- let i;
4865
- let spec;
4866
- for (let viewType in viewSpecs) {
4867
- viewTypes.push(viewType);
4868
4767
  }
4869
- for (i = 0; i < viewTypes.length; i += 1) {
4870
- spec = viewSpecs[viewTypes[i]];
4871
- if (spec) {
4872
- if (spec.singleUnit === unit) {
4873
- return spec;
4768
+ for (let instanceId in instances) {
4769
+ let instance = instances[instanceId];
4770
+ for (let key of defKeys[instance.defId]) {
4771
+ if (splitStores[key]) { // must have already been created
4772
+ splitStores[key].instances[instanceId] = instance;
4874
4773
  }
4875
4774
  }
4876
4775
  }
4877
- return null;
4776
+ return splitStores;
4878
4777
  }
4879
- // Current Date
4880
- // -----------------------------------------------------------------------------------------------------------------
4881
- prev() {
4882
- this.unselect();
4883
- this.dispatch({ type: 'PREV' });
4778
+ _splitIndividualUi(eventUiBases, defKeys) {
4779
+ let splitHashes = {};
4780
+ for (let defId in eventUiBases) {
4781
+ if (defId) { // not the '' key
4782
+ for (let key of defKeys[defId]) {
4783
+ if (!splitHashes[key]) {
4784
+ splitHashes[key] = {};
4785
+ }
4786
+ splitHashes[key][defId] = eventUiBases[defId];
4787
+ }
4788
+ }
4789
+ }
4790
+ return splitHashes;
4884
4791
  }
4885
- next() {
4886
- this.unselect();
4887
- this.dispatch({ type: 'NEXT' });
4792
+ _splitInteraction(interaction) {
4793
+ let splitStates = {};
4794
+ if (interaction) {
4795
+ let affectedStores = this._splitEventStore(interaction.affectedEvents, this._getKeysForEventDefs(interaction.affectedEvents));
4796
+ // can't rely on defKeys because event data is mutated
4797
+ let mutatedKeysByDefId = this._getKeysForEventDefs(interaction.mutatedEvents);
4798
+ let mutatedStores = this._splitEventStore(interaction.mutatedEvents, mutatedKeysByDefId);
4799
+ let populate = (key) => {
4800
+ if (!splitStates[key]) {
4801
+ splitStates[key] = {
4802
+ affectedEvents: affectedStores[key] || EMPTY_EVENT_STORE,
4803
+ mutatedEvents: mutatedStores[key] || EMPTY_EVENT_STORE,
4804
+ isEvent: interaction.isEvent,
4805
+ };
4806
+ }
4807
+ };
4808
+ for (let key in affectedStores) {
4809
+ populate(key);
4810
+ }
4811
+ for (let key in mutatedStores) {
4812
+ populate(key);
4813
+ }
4814
+ }
4815
+ return splitStates;
4888
4816
  }
4889
- prevYear() {
4890
- let state = this.getCurrentData();
4891
- this.unselect();
4892
- this.dispatch({
4893
- type: 'CHANGE_DATE',
4894
- dateMarker: state.dateEnv.addYears(state.currentDate, -1),
4895
- });
4817
+ }
4818
+ function buildEventUiForKey(allUi, eventUiForKey, individualUi) {
4819
+ let baseParts = [];
4820
+ if (allUi) {
4821
+ baseParts.push(allUi);
4896
4822
  }
4897
- nextYear() {
4898
- let state = this.getCurrentData();
4899
- this.unselect();
4900
- this.dispatch({
4901
- type: 'CHANGE_DATE',
4902
- dateMarker: state.dateEnv.addYears(state.currentDate, 1),
4903
- });
4823
+ if (eventUiForKey) {
4824
+ baseParts.push(eventUiForKey);
4904
4825
  }
4905
- today() {
4906
- let state = this.getCurrentData();
4907
- this.unselect();
4908
- this.dispatch({
4909
- type: 'CHANGE_DATE',
4910
- dateMarker: getNow(state.calendarOptions.now, state.dateEnv),
4911
- });
4826
+ let stuff = {
4827
+ '': combineEventUis(baseParts),
4828
+ };
4829
+ if (individualUi) {
4830
+ Object.assign(stuff, individualUi);
4912
4831
  }
4913
- gotoDate(zonedDateInput) {
4914
- let state = this.getCurrentData();
4915
- this.unselect();
4916
- this.dispatch({
4917
- type: 'CHANGE_DATE',
4918
- dateMarker: state.dateEnv.createMarker(zonedDateInput),
4919
- });
4832
+ return stuff;
4833
+ }
4834
+
4835
+ function getDateMeta(date, todayRange, nowDate, dateProfile) {
4836
+ return {
4837
+ dow: date.getUTCDay(),
4838
+ isDisabled: Boolean(dateProfile && !rangeContainsMarker(dateProfile.activeRange, date)),
4839
+ isOther: Boolean(dateProfile && !rangeContainsMarker(dateProfile.currentRange, date)),
4840
+ isToday: Boolean(todayRange && rangeContainsMarker(todayRange, date)),
4841
+ isPast: Boolean(nowDate ? (date < nowDate) : todayRange ? (date < todayRange.start) : false),
4842
+ isFuture: Boolean(nowDate ? (date > nowDate) : todayRange ? (date >= todayRange.end) : false),
4843
+ };
4844
+ }
4845
+ function getDayClassNames(meta, theme) {
4846
+ let classNames = [
4847
+ 'fc-day',
4848
+ `fc-day-${DAY_IDS[meta.dow]}`,
4849
+ ];
4850
+ if (meta.isDisabled) {
4851
+ classNames.push('fc-day-disabled');
4920
4852
  }
4921
- incrementDate(deltaInput) {
4922
- let state = this.getCurrentData();
4923
- let delta = createDuration(deltaInput);
4924
- if (delta) { // else, warn about invalid input?
4925
- this.unselect();
4926
- this.dispatch({
4927
- type: 'CHANGE_DATE',
4928
- dateMarker: state.dateEnv.add(state.currentDate, delta),
4929
- });
4853
+ else {
4854
+ if (meta.isToday) {
4855
+ classNames.push('fc-day-today');
4856
+ }
4857
+ if (meta.isPast) {
4858
+ classNames.push('fc-day-past');
4859
+ }
4860
+ if (meta.isFuture) {
4861
+ classNames.push('fc-day-future');
4862
+ }
4863
+ if (meta.isOther) {
4864
+ classNames.push('fc-day-other');
4930
4865
  }
4931
4866
  }
4932
- getDate() {
4933
- let state = this.getCurrentData();
4934
- return state.dateEnv.toDate(state.currentDate);
4935
- }
4936
- // Date Formatting Utils
4937
- // -----------------------------------------------------------------------------------------------------------------
4938
- formatDate(d, formatter) {
4939
- let { dateEnv } = this.getCurrentData();
4940
- return dateEnv.format(dateEnv.createMarker(d), createFormatter(formatter));
4867
+ return classNames;
4868
+ }
4869
+ function getSlotClassNames(meta, theme) {
4870
+ let classNames = [
4871
+ 'fc-slot',
4872
+ `fc-slot-${DAY_IDS[meta.dow]}`,
4873
+ ];
4874
+ if (meta.isDisabled) {
4875
+ classNames.push('fc-slot-disabled');
4941
4876
  }
4942
- // `settings` is for formatter AND isEndExclusive
4943
- formatRange(d0, d1, settings) {
4944
- let { dateEnv } = this.getCurrentData();
4945
- return dateEnv.formatRange(dateEnv.createMarker(d0), dateEnv.createMarker(d1), createFormatter(settings), settings);
4877
+ else {
4878
+ if (meta.isToday) {
4879
+ classNames.push('fc-slot-today');
4880
+ }
4881
+ if (meta.isPast) {
4882
+ classNames.push('fc-slot-past');
4883
+ }
4884
+ if (meta.isFuture) {
4885
+ classNames.push('fc-slot-future');
4886
+ }
4946
4887
  }
4947
- formatIso(d, omitTime) {
4948
- let { dateEnv } = this.getCurrentData();
4949
- return dateEnv.formatIso(dateEnv.createMarker(d), { omitTime });
4888
+ return classNames;
4889
+ }
4890
+
4891
+ // TODO: kill Component::safeSetState
4892
+ // TODO: kill
4893
+ function setStateDimMap(component, key, newMap) {
4894
+ const currentMap = component.state[key];
4895
+ if (!currentMap || !isDimMapsEqual(currentMap, newMap)) {
4896
+ component.setState({ [key]: newMap });
4897
+ }
4898
+ }
4899
+ // TODO: kill
4900
+ function isDimMapsEqual(oldMap, newMap) {
4901
+ for (const key in newMap) {
4902
+ if (!isDimsEqual(oldMap[key], newMap[key])) {
4903
+ return false;
4904
+ }
4950
4905
  }
4951
- // Date Selection / Event Selection / DayClick
4952
- // -----------------------------------------------------------------------------------------------------------------
4953
- select(dateOrObj, endDate) {
4954
- let selectionInput;
4955
- if (endDate == null) {
4956
- if (dateOrObj.start != null) {
4957
- selectionInput = dateOrObj;
4906
+ return true;
4907
+ }
4908
+ // TODO: kill
4909
+ function isDimsEqual(v0, v1) {
4910
+ return v0 != null && (v0 === v1 || Math.abs(v0 - v1) < 0.01);
4911
+ }
4912
+
4913
+ const callbackMap = new Map();
4914
+ let flushedCallbackSet = new Set();
4915
+ let isHandling = false;
4916
+ /*
4917
+ Performant from multiple perspectives:
4918
+ - less memory with one ResizeObserver
4919
+ - batches firing
4920
+ */
4921
+ const resizeObserver = new ResizeObserver((entries) => {
4922
+ isHandling = true;
4923
+ for (let entry of entries) {
4924
+ const callback = callbackMap.get(entry.target);
4925
+ if (entry.contentBoxSize) {
4926
+ // The standard makes contentBoxSize an array...
4927
+ if (entry.contentBoxSize[0]) {
4928
+ callback(entry.contentBoxSize[0].inlineSize, entry.contentBoxSize[0].blockSize);
4958
4929
  }
4959
4930
  else {
4960
- selectionInput = {
4961
- start: dateOrObj,
4962
- end: null,
4963
- };
4931
+ // ...but old versions of Firefox treat it as a single item
4932
+ callback(entry.contentBoxSize.inlineSize, entry.contentBoxSize.blockSize);
4964
4933
  }
4965
4934
  }
4966
4935
  else {
4967
- selectionInput = {
4968
- start: dateOrObj,
4969
- end: endDate,
4970
- };
4971
- }
4972
- let state = this.getCurrentData();
4973
- let selection = parseDateSpan(selectionInput, state.dateEnv, createDuration({ days: 1 }));
4974
- if (selection) { // throw parse error otherwise?
4975
- this.dispatch({ type: 'SELECT_DATES', selection });
4976
- triggerDateSelect(selection, null, state);
4936
+ callback(entry.contentRect.width, entry.contentRect.height);
4977
4937
  }
4978
4938
  }
4979
- unselect(pev) {
4980
- let state = this.getCurrentData();
4981
- if (state.dateSelection) {
4982
- this.dispatch({ type: 'UNSELECT_DATES' });
4983
- triggerDateUnselect(pev, state);
4984
- }
4939
+ for (const flushedCallback of flushedCallbackSet.values()) {
4940
+ flushedCallback();
4941
+ flushedCallbackSet.delete(flushedCallback);
4985
4942
  }
4986
- // Public Events API
4987
- // -----------------------------------------------------------------------------------------------------------------
4988
- addEvent(eventInput, sourceInput) {
4989
- if (eventInput instanceof EventImpl) {
4990
- let def = eventInput._def;
4991
- let instance = eventInput._instance;
4992
- let currentData = this.getCurrentData();
4993
- // not already present? don't want to add an old snapshot
4994
- if (!currentData.eventStore.defs[def.defId]) {
4995
- this.dispatch({
4996
- type: 'ADD_EVENTS',
4997
- eventStore: eventTupleToStore({ def, instance }), // TODO: better util for two args?
4998
- });
4999
- this.triggerEventAdd(eventInput);
5000
- }
5001
- return eventInput;
5002
- }
5003
- let state = this.getCurrentData();
5004
- let eventSource;
5005
- if (sourceInput instanceof EventSourceImpl) {
5006
- eventSource = sourceInput.internalEventSource;
5007
- }
5008
- else if (typeof sourceInput === 'boolean') {
5009
- if (sourceInput) { // true. part of the first event source
5010
- [eventSource] = hashValuesToArray(state.eventSources);
5011
- }
5012
- }
5013
- else if (sourceInput != null) { // an ID. accepts a number too
5014
- let sourceApi = this.getEventSourceById(sourceInput); // TODO: use an internal function
5015
- if (!sourceApi) {
5016
- console.warn(`Could not find an event source with ID "${sourceInput}"`); // TODO: test
5017
- return null;
5018
- }
5019
- eventSource = sourceApi.internalEventSource;
4943
+ isHandling = false;
4944
+ });
4945
+ /*
4946
+ PRECONDITIONS:
4947
+ - element can only have one listener attached ever
4948
+ - element cannot have border or padding
4949
+
4950
+ TODO:
4951
+ - always force border/padding on these elements to `0 !important` ???
4952
+ */
4953
+ function watchSize(el, callback) {
4954
+ callbackMap.set(el, callback);
4955
+ resizeObserver.observe(el);
4956
+ return () => {
4957
+ callbackMap.delete(el);
4958
+ resizeObserver.unobserve(el);
4959
+ };
4960
+ }
4961
+ function watchWidth(el, callback) {
4962
+ let currentWidth;
4963
+ return watchSize(el, (width) => {
4964
+ if (currentWidth == null || currentWidth !== width) {
4965
+ callback(currentWidth = width);
5020
4966
  }
5021
- let tuple = parseEvent(eventInput, eventSource, state, false);
5022
- if (tuple) {
5023
- let newEventApi = new EventImpl(state, tuple.def, tuple.def.recurringDef ? null : tuple.instance);
5024
- this.dispatch({
5025
- type: 'ADD_EVENTS',
5026
- eventStore: eventTupleToStore(tuple),
5027
- });
5028
- this.triggerEventAdd(newEventApi);
5029
- return newEventApi;
4967
+ });
4968
+ }
4969
+ function watchHeight(el, callback) {
4970
+ let currentHeight;
4971
+ return watchSize(el, (_width, height) => {
4972
+ if (currentHeight == null || currentHeight !== height) {
4973
+ callback(currentHeight = height);
5030
4974
  }
5031
- return null;
4975
+ });
4976
+ }
4977
+ function afterSize(callback) {
4978
+ if (isHandling) {
4979
+ flushedCallbackSet.add(callback);
5032
4980
  }
5033
- triggerEventAdd(eventApi) {
5034
- let { emitter } = this.getCurrentData();
5035
- emitter.trigger('eventAdd', {
5036
- event: eventApi,
5037
- relatedEvents: [],
5038
- revert: () => {
5039
- this.dispatch({
5040
- type: 'REMOVE_EVENTS',
5041
- eventStore: eventApiToStore(eventApi),
5042
- });
5043
- },
5044
- });
5045
- }
5046
- // TODO: optimize
5047
- getEventById(id) {
5048
- let state = this.getCurrentData();
5049
- let { defs, instances } = state.eventStore;
5050
- id = String(id);
5051
- for (let defId in defs) {
5052
- let def = defs[defId];
5053
- if (def.publicId === id) {
5054
- if (def.recurringDef) {
5055
- return new EventImpl(state, def, null);
5056
- }
5057
- for (let instanceId in instances) {
5058
- let instance = instances[instanceId];
5059
- if (instance.defId === def.defId) {
5060
- return new EventImpl(state, def, instance);
5061
- }
5062
- }
5063
- }
5064
- }
5065
- return null;
5066
- }
5067
- getEvents() {
5068
- let currentData = this.getCurrentData();
5069
- return buildEventApis(currentData.eventStore, currentData);
5070
- }
5071
- removeAllEvents() {
5072
- this.dispatch({ type: 'REMOVE_ALL_EVENTS' });
5073
- }
5074
- // Public Event Sources API
5075
- // -----------------------------------------------------------------------------------------------------------------
5076
- getEventSources() {
5077
- let state = this.getCurrentData();
5078
- let sourceHash = state.eventSources;
5079
- let sourceApis = [];
5080
- for (let internalId in sourceHash) {
5081
- sourceApis.push(new EventSourceImpl(state, sourceHash[internalId]));
5082
- }
5083
- return sourceApis;
4981
+ else {
4982
+ callback(); // TODO: should we queue microtask?
5084
4983
  }
5085
- getEventSourceById(id) {
5086
- let state = this.getCurrentData();
5087
- let sourceHash = state.eventSources;
5088
- id = String(id);
5089
- for (let sourceId in sourceHash) {
5090
- if (sourceHash[sourceId].publicId === id) {
5091
- return new EventSourceImpl(state, sourceHash[sourceId]);
4984
+ }
4985
+
4986
+ const DAY_FORMAT = createFormatter({ year: 'numeric', month: 'long', day: 'numeric' });
4987
+ const WEEK_FORMAT = createFormatter({ week: 'long' });
4988
+ function buildNavLinkAttrs(context, dateMarker, viewType = 'day', isTabbable = true) {
4989
+ const { dateEnv, options, calendarApi } = context;
4990
+ let dateStr = dateEnv.format(dateMarker, viewType === 'week' ? WEEK_FORMAT : DAY_FORMAT);
4991
+ if (options.navLinks) {
4992
+ let zonedDate = dateEnv.toDate(dateMarker);
4993
+ const handleInteraction = (ev) => {
4994
+ let customAction = viewType === 'day' ? options.navLinkDayClick :
4995
+ viewType === 'week' ? options.navLinkWeekClick : null;
4996
+ if (typeof customAction === 'function') {
4997
+ customAction.call(calendarApi, dateEnv.toDate(dateMarker), ev);
5092
4998
  }
5093
- }
5094
- return null;
5095
- }
5096
- addEventSource(sourceInput) {
5097
- let state = this.getCurrentData();
5098
- if (sourceInput instanceof EventSourceImpl) {
5099
- // not already present? don't want to add an old snapshot
5100
- if (!state.eventSources[sourceInput.internalEventSource.sourceId]) {
5101
- this.dispatch({
5102
- type: 'ADD_EVENT_SOURCES',
5103
- sources: [sourceInput.internalEventSource],
5104
- });
4999
+ else {
5000
+ if (typeof customAction === 'string') {
5001
+ viewType = customAction;
5002
+ }
5003
+ calendarApi.zoomTo(dateMarker, viewType);
5105
5004
  }
5106
- return sourceInput;
5107
- }
5108
- let eventSource = parseEventSource(sourceInput, state);
5109
- if (eventSource) { // TODO: error otherwise?
5110
- this.dispatch({ type: 'ADD_EVENT_SOURCES', sources: [eventSource] });
5111
- return new EventSourceImpl(state, eventSource);
5112
- }
5113
- return null;
5114
- }
5115
- removeAllEventSources() {
5116
- this.dispatch({ type: 'REMOVE_ALL_EVENT_SOURCES' });
5117
- }
5118
- refetchEvents() {
5119
- this.dispatch({ type: 'FETCH_EVENT_SOURCES', isRefetch: true });
5120
- }
5121
- // Scroll
5122
- // -----------------------------------------------------------------------------------------------------------------
5123
- scrollToTime(timeInput) {
5124
- let time = createDuration(timeInput);
5125
- if (time) {
5126
- this.trigger('_scrollRequest', { time });
5127
- }
5005
+ };
5006
+ return Object.assign({ title: formatWithOrdinals(options.navLinkHint, [dateStr, zonedDate], dateStr), 'data-navlink': '' }, (isTabbable
5007
+ ? createAriaClickAttrs(handleInteraction)
5008
+ : { onClick: handleInteraction }));
5128
5009
  }
5010
+ return { 'aria-label': dateStr };
5129
5011
  }
5130
5012
 
5131
- function pointInsideRect(point, rect) {
5132
- return point.left >= rect.left &&
5133
- point.left < rect.right &&
5134
- point.top >= rect.top &&
5135
- point.top < rect.bottom;
5013
+ let _isRtlScrollbarOnLeft = null;
5014
+ function getIsRtlScrollbarOnLeft() {
5015
+ if (_isRtlScrollbarOnLeft === null) {
5016
+ _isRtlScrollbarOnLeft = computeIsRtlScrollbarOnLeft();
5017
+ }
5018
+ return _isRtlScrollbarOnLeft;
5136
5019
  }
5137
- // Returns a new rectangle that is the intersection of the two rectangles. If they don't intersect, returns false
5138
- function intersectRects(rect1, rect2) {
5139
- let res = {
5140
- left: Math.max(rect1.left, rect2.left),
5141
- right: Math.min(rect1.right, rect2.right),
5142
- top: Math.max(rect1.top, rect2.top),
5143
- bottom: Math.min(rect1.bottom, rect2.bottom),
5144
- };
5145
- if (res.left < res.right && res.top < res.bottom) {
5146
- return res;
5020
+ function computeIsRtlScrollbarOnLeft() {
5021
+ let outerEl = document.createElement('div');
5022
+ applyStyle(outerEl, {
5023
+ position: 'absolute',
5024
+ top: -1000,
5025
+ left: 0,
5026
+ border: 0,
5027
+ padding: 0,
5028
+ overflow: 'scroll',
5029
+ direction: 'rtl',
5030
+ });
5031
+ outerEl.innerHTML = '<div></div>';
5032
+ document.body.appendChild(outerEl);
5033
+ let innerEl = outerEl.firstChild;
5034
+ let res = innerEl.getBoundingClientRect().left > outerEl.getBoundingClientRect().left;
5035
+ removeElement(outerEl);
5036
+ return res;
5037
+ }
5038
+
5039
+ let _scrollbarWidths;
5040
+ function getScrollbarWidths() {
5041
+ if (!_scrollbarWidths) {
5042
+ _scrollbarWidths = computeScrollbarWidths();
5147
5043
  }
5148
- return false;
5044
+ return _scrollbarWidths;
5149
5045
  }
5150
- function translateRect(rect, deltaX, deltaY) {
5046
+ function computeScrollbarWidths() {
5047
+ let el = document.createElement('div');
5048
+ el.style.overflow = 'scroll';
5049
+ el.style.position = 'absolute';
5050
+ el.style.top = '-9999px';
5051
+ el.style.left = '-9999px';
5052
+ document.body.appendChild(el);
5053
+ let res = computeScrollbarWidthsForEl(el);
5054
+ document.body.removeChild(el);
5055
+ return res;
5056
+ }
5057
+ // WARNING: will include border
5058
+ function computeScrollbarWidthsForEl(el) {
5151
5059
  return {
5152
- left: rect.left + deltaX,
5153
- right: rect.right + deltaX,
5154
- top: rect.top + deltaY,
5155
- bottom: rect.bottom + deltaY,
5060
+ x: el.offsetHeight - el.clientHeight,
5061
+ y: el.offsetWidth - el.clientWidth,
5156
5062
  };
5157
5063
  }
5158
- // Returns a new point that will have been moved to reside within the given rectangle
5159
- function constrainPoint(point, rect) {
5160
- return {
5161
- left: Math.min(Math.max(point.left, rect.left), rect.right),
5162
- top: Math.min(Math.max(point.top, rect.top), rect.bottom),
5064
+
5065
+ function computeEdges(el, getPadding = false) {
5066
+ let computedStyle = window.getComputedStyle(el);
5067
+ let borderLeft = parseInt(computedStyle.borderLeftWidth, 10) || 0;
5068
+ let borderRight = parseInt(computedStyle.borderRightWidth, 10) || 0;
5069
+ let borderTop = parseInt(computedStyle.borderTopWidth, 10) || 0;
5070
+ let borderBottom = parseInt(computedStyle.borderBottomWidth, 10) || 0;
5071
+ let badScrollbarWidths = computeScrollbarWidthsForEl(el); // includes border!
5072
+ let scrollbarLeftRight = badScrollbarWidths.y - borderLeft - borderRight;
5073
+ let scrollbarBottom = badScrollbarWidths.x - borderTop - borderBottom;
5074
+ let res = {
5075
+ borderLeft,
5076
+ borderRight,
5077
+ borderTop,
5078
+ borderBottom,
5079
+ scrollbarBottom,
5080
+ scrollbarLeft: 0,
5081
+ scrollbarRight: 0,
5163
5082
  };
5083
+ if (getIsRtlScrollbarOnLeft() && computedStyle.direction === 'rtl') { // is the scrollbar on the left side?
5084
+ res.scrollbarLeft = scrollbarLeftRight;
5085
+ }
5086
+ else {
5087
+ res.scrollbarRight = scrollbarLeftRight;
5088
+ }
5089
+ if (getPadding) {
5090
+ res.paddingLeft = parseInt(computedStyle.paddingLeft, 10) || 0;
5091
+ res.paddingRight = parseInt(computedStyle.paddingRight, 10) || 0;
5092
+ res.paddingTop = parseInt(computedStyle.paddingTop, 10) || 0;
5093
+ res.paddingBottom = parseInt(computedStyle.paddingBottom, 10) || 0;
5094
+ }
5095
+ return res;
5164
5096
  }
5165
- // Returns a point that is the center of the given rectangle
5166
- function getRectCenter(rect) {
5167
- return {
5168
- left: (rect.left + rect.right) / 2,
5169
- top: (rect.top + rect.bottom) / 2,
5097
+ function computeInnerRect(el, goWithinPadding = false, doFromWindowViewport) {
5098
+ let outerRect = doFromWindowViewport ? el.getBoundingClientRect() : computeRect(el);
5099
+ let edges = computeEdges(el, goWithinPadding);
5100
+ let res = {
5101
+ left: outerRect.left + edges.borderLeft + edges.scrollbarLeft,
5102
+ right: outerRect.right - edges.borderRight - edges.scrollbarRight,
5103
+ top: outerRect.top + edges.borderTop,
5104
+ bottom: outerRect.bottom - edges.borderBottom - edges.scrollbarBottom,
5170
5105
  };
5106
+ if (goWithinPadding) {
5107
+ res.left += edges.paddingLeft;
5108
+ res.right -= edges.paddingRight;
5109
+ res.top += edges.paddingTop;
5110
+ res.bottom -= edges.paddingBottom;
5111
+ }
5112
+ return res;
5171
5113
  }
5172
- // Subtracts point2's coordinates from point1's coordinates, returning a delta
5173
- function diffPoints(point1, point2) {
5114
+ function computeRect(el) {
5115
+ let rect = el.getBoundingClientRect();
5174
5116
  return {
5175
- left: point1.left - point2.left,
5176
- top: point1.top - point2.top,
5117
+ left: rect.left + window.scrollX,
5118
+ top: rect.top + window.scrollY,
5119
+ right: rect.right + window.scrollX,
5120
+ bottom: rect.bottom + window.scrollY,
5177
5121
  };
5178
5122
  }
5179
-
5180
- const EMPTY_EVENT_STORE = createEmptyEventStore(); // for purecomponents. TODO: keep elsewhere
5181
- class Splitter {
5182
- constructor() {
5183
- this.getKeysForEventDefs = memoize(this._getKeysForEventDefs);
5184
- this.splitDateSelection = memoize(this._splitDateSpan);
5185
- this.splitEventStore = memoize(this._splitEventStore);
5186
- this.splitIndividualUi = memoize(this._splitIndividualUi);
5187
- this.splitEventDrag = memoize(this._splitInteraction);
5188
- this.splitEventResize = memoize(this._splitInteraction);
5189
- this.eventUiBuilders = {}; // TODO: typescript protection
5190
- }
5191
- splitProps(props) {
5192
- let keyInfos = this.getKeyInfo(props);
5193
- let defKeys = this.getKeysForEventDefs(props.eventStore);
5194
- let dateSelections = this.splitDateSelection(props.dateSelection);
5195
- let individualUi = this.splitIndividualUi(props.eventUiBases, defKeys); // the individual *bases*
5196
- let eventStores = this.splitEventStore(props.eventStore, defKeys);
5197
- let eventDrags = this.splitEventDrag(props.eventDrag);
5198
- let eventResizes = this.splitEventResize(props.eventResize);
5199
- let splitProps = {};
5200
- this.eventUiBuilders = mapHash(keyInfos, (info, key) => this.eventUiBuilders[key] || memoize(buildEventUiForKey));
5201
- for (let key in keyInfos) {
5202
- let keyInfo = keyInfos[key];
5203
- let eventStore = eventStores[key] || EMPTY_EVENT_STORE;
5204
- let buildEventUi = this.eventUiBuilders[key];
5205
- splitProps[key] = {
5206
- businessHours: keyInfo.businessHours || props.businessHours,
5207
- dateSelection: dateSelections[key] || null,
5208
- eventStore,
5209
- eventUiBases: buildEventUi(props.eventUiBases[''], keyInfo.ui, individualUi[key]),
5210
- eventSelection: eventStore.instances[props.eventSelection] ? props.eventSelection : '',
5211
- eventDrag: eventDrags[key] || null,
5212
- eventResize: eventResizes[key] || null,
5213
- };
5123
+ function computeClippedClientRect(el) {
5124
+ let clippingParents = getClippingParents(el);
5125
+ let rect = el.getBoundingClientRect();
5126
+ for (let clippingParent of clippingParents) {
5127
+ let intersection = intersectRects(rect, clippingParent.getBoundingClientRect());
5128
+ if (intersection) {
5129
+ rect = intersection;
5130
+ }
5131
+ else {
5132
+ return null;
5214
5133
  }
5215
- return splitProps;
5216
5134
  }
5217
- _splitDateSpan(dateSpan) {
5218
- let dateSpans = {};
5219
- if (dateSpan) {
5220
- let keys = this.getKeysForDateSpan(dateSpan);
5221
- for (let key of keys) {
5222
- dateSpans[key] = dateSpan;
5223
- }
5135
+ return rect;
5136
+ }
5137
+ // does not return window
5138
+ function getClippingParents(el) {
5139
+ let parents = [];
5140
+ while (el instanceof HTMLElement) { // will stop when gets to document or null
5141
+ let computedStyle = window.getComputedStyle(el);
5142
+ if (computedStyle.position === 'fixed') {
5143
+ break;
5224
5144
  }
5225
- return dateSpans;
5145
+ if ((/(auto|scroll)/).test(computedStyle.overflow + computedStyle.overflowY + computedStyle.overflowX)) {
5146
+ parents.push(el);
5147
+ }
5148
+ el = el.parentNode;
5226
5149
  }
5227
- _getKeysForEventDefs(eventStore) {
5228
- return mapHash(eventStore.defs, (eventDef) => this.getKeysForEventDef(eventDef));
5150
+ return parents;
5151
+ }
5152
+
5153
+ /*
5154
+ Records offset information for a set of elements, relative to an origin element.
5155
+ Can record the left/right OR the top/bottom OR both.
5156
+ Provides methods for querying the cache by position.
5157
+ */
5158
+ class PositionCache {
5159
+ constructor(originEl, els, isHorizontal, isVertical) {
5160
+ this.els = els;
5161
+ let originClientRect = this.originClientRect = originEl.getBoundingClientRect(); // relative to viewport top-left
5162
+ if (isHorizontal) {
5163
+ this.buildElHorizontals(originClientRect.left);
5164
+ }
5165
+ if (isVertical) {
5166
+ this.buildElVerticals(originClientRect.top);
5167
+ }
5229
5168
  }
5230
- _splitEventStore(eventStore, defKeys) {
5231
- let { defs, instances } = eventStore;
5232
- let splitStores = {};
5233
- for (let defId in defs) {
5234
- for (let key of defKeys[defId]) {
5235
- if (!splitStores[key]) {
5236
- splitStores[key] = createEmptyEventStore();
5237
- }
5238
- splitStores[key].defs[defId] = defs[defId];
5239
- }
5169
+ // Populates the left/right internal coordinate arrays
5170
+ buildElHorizontals(originClientLeft) {
5171
+ let lefts = [];
5172
+ let rights = [];
5173
+ for (let el of this.els) {
5174
+ let rect = el.getBoundingClientRect();
5175
+ lefts.push(rect.left - originClientLeft);
5176
+ rights.push(rect.right - originClientLeft);
5240
5177
  }
5241
- for (let instanceId in instances) {
5242
- let instance = instances[instanceId];
5243
- for (let key of defKeys[instance.defId]) {
5244
- if (splitStores[key]) { // must have already been created
5245
- splitStores[key].instances[instanceId] = instance;
5246
- }
5247
- }
5178
+ this.lefts = lefts;
5179
+ this.rights = rights;
5180
+ }
5181
+ // Populates the top/bottom internal coordinate arrays
5182
+ buildElVerticals(originClientTop) {
5183
+ let tops = [];
5184
+ let bottoms = [];
5185
+ for (let el of this.els) {
5186
+ let rect = el.getBoundingClientRect();
5187
+ tops.push(rect.top - originClientTop);
5188
+ bottoms.push(rect.bottom - originClientTop);
5248
5189
  }
5249
- return splitStores;
5190
+ this.tops = tops;
5191
+ this.bottoms = bottoms;
5250
5192
  }
5251
- _splitIndividualUi(eventUiBases, defKeys) {
5252
- let splitHashes = {};
5253
- for (let defId in eventUiBases) {
5254
- if (defId) { // not the '' key
5255
- for (let key of defKeys[defId]) {
5256
- if (!splitHashes[key]) {
5257
- splitHashes[key] = {};
5258
- }
5259
- splitHashes[key][defId] = eventUiBases[defId];
5260
- }
5193
+ // Given a left offset (from document left), returns the index of the el that it horizontally intersects.
5194
+ // If no intersection is made, returns undefined.
5195
+ leftToIndex(leftPosition) {
5196
+ let { lefts, rights } = this;
5197
+ let len = lefts.length;
5198
+ let i;
5199
+ for (i = 0; i < len; i += 1) {
5200
+ if (leftPosition >= lefts[i] && leftPosition < rights[i]) {
5201
+ return i;
5261
5202
  }
5262
5203
  }
5263
- return splitHashes;
5204
+ return undefined; // TODO: better
5264
5205
  }
5265
- _splitInteraction(interaction) {
5266
- let splitStates = {};
5267
- if (interaction) {
5268
- let affectedStores = this._splitEventStore(interaction.affectedEvents, this._getKeysForEventDefs(interaction.affectedEvents));
5269
- // can't rely on defKeys because event data is mutated
5270
- let mutatedKeysByDefId = this._getKeysForEventDefs(interaction.mutatedEvents);
5271
- let mutatedStores = this._splitEventStore(interaction.mutatedEvents, mutatedKeysByDefId);
5272
- let populate = (key) => {
5273
- if (!splitStates[key]) {
5274
- splitStates[key] = {
5275
- affectedEvents: affectedStores[key] || EMPTY_EVENT_STORE,
5276
- mutatedEvents: mutatedStores[key] || EMPTY_EVENT_STORE,
5277
- isEvent: interaction.isEvent,
5278
- };
5279
- }
5280
- };
5281
- for (let key in affectedStores) {
5282
- populate(key);
5283
- }
5284
- for (let key in mutatedStores) {
5285
- populate(key);
5206
+ // Given a top offset (from document top), returns the index of the el that it vertically intersects.
5207
+ // If no intersection is made, returns undefined.
5208
+ topToIndex(topPosition) {
5209
+ let { tops, bottoms } = this;
5210
+ let len = tops.length;
5211
+ let i;
5212
+ for (i = 0; i < len; i += 1) {
5213
+ if (topPosition >= tops[i] && topPosition < bottoms[i]) {
5214
+ return i;
5286
5215
  }
5287
5216
  }
5288
- return splitStates;
5289
- }
5290
- }
5291
- function buildEventUiForKey(allUi, eventUiForKey, individualUi) {
5292
- let baseParts = [];
5293
- if (allUi) {
5294
- baseParts.push(allUi);
5295
- }
5296
- if (eventUiForKey) {
5297
- baseParts.push(eventUiForKey);
5217
+ return undefined; // TODO: better
5298
5218
  }
5299
- let stuff = {
5300
- '': combineEventUis(baseParts),
5301
- };
5302
- if (individualUi) {
5303
- Object.assign(stuff, individualUi);
5219
+ // Gets the width of the element at the given index
5220
+ getWidth(leftIndex) {
5221
+ return this.rights[leftIndex] - this.lefts[leftIndex];
5304
5222
  }
5305
- return stuff;
5306
- }
5307
-
5308
- function getDateMeta(date, todayRange, nowDate, dateProfile) {
5309
- return {
5310
- dow: date.getUTCDay(),
5311
- isDisabled: Boolean(dateProfile && !rangeContainsMarker(dateProfile.activeRange, date)),
5312
- isOther: Boolean(dateProfile && !rangeContainsMarker(dateProfile.currentRange, date)),
5313
- isToday: Boolean(todayRange && rangeContainsMarker(todayRange, date)),
5314
- isPast: Boolean(nowDate ? (date < nowDate) : todayRange ? (date < todayRange.start) : false),
5315
- isFuture: Boolean(nowDate ? (date > nowDate) : todayRange ? (date >= todayRange.end) : false),
5316
- };
5317
- }
5318
- function getDayClassNames(meta, theme) {
5319
- let classNames = [
5320
- 'fc-day',
5321
- `fc-day-${DAY_IDS[meta.dow]}`,
5322
- ];
5323
- if (meta.isDisabled) {
5324
- classNames.push('fc-day-disabled');
5223
+ // Gets the height of the element at the given index
5224
+ getHeight(topIndex) {
5225
+ return this.bottoms[topIndex] - this.tops[topIndex];
5325
5226
  }
5326
- else {
5327
- if (meta.isToday) {
5328
- classNames.push('fc-day-today');
5329
- classNames.push(theme.getClass('today'));
5330
- }
5331
- if (meta.isPast) {
5332
- classNames.push('fc-day-past');
5333
- }
5334
- if (meta.isFuture) {
5335
- classNames.push('fc-day-future');
5336
- }
5337
- if (meta.isOther) {
5338
- classNames.push('fc-day-other');
5339
- }
5227
+ similarTo(otherCache) {
5228
+ return similarNumArrays(this.tops || [], otherCache.tops || []) &&
5229
+ similarNumArrays(this.bottoms || [], otherCache.bottoms || []) &&
5230
+ similarNumArrays(this.lefts || [], otherCache.lefts || []) &&
5231
+ similarNumArrays(this.rights || [], otherCache.rights || []);
5340
5232
  }
5341
- return classNames;
5342
5233
  }
5343
- function getSlotClassNames(meta, theme) {
5344
- let classNames = [
5345
- 'fc-slot',
5346
- `fc-slot-${DAY_IDS[meta.dow]}`,
5347
- ];
5348
- if (meta.isDisabled) {
5349
- classNames.push('fc-slot-disabled');
5234
+ function similarNumArrays(a, b) {
5235
+ const len = a.length;
5236
+ if (len !== b.length) {
5237
+ return false;
5350
5238
  }
5351
- else {
5352
- if (meta.isToday) {
5353
- classNames.push('fc-slot-today');
5354
- classNames.push(theme.getClass('today'));
5355
- }
5356
- if (meta.isPast) {
5357
- classNames.push('fc-slot-past');
5358
- }
5359
- if (meta.isFuture) {
5360
- classNames.push('fc-slot-future');
5239
+ for (let i = 0; i < len; i++) {
5240
+ if (Math.round(a[i]) !== Math.round(b[i])) {
5241
+ return false;
5361
5242
  }
5362
5243
  }
5363
- return classNames;
5244
+ return true;
5364
5245
  }
5365
5246
 
5366
- const DAY_FORMAT = createFormatter({ year: 'numeric', month: 'long', day: 'numeric' });
5367
- const WEEK_FORMAT = createFormatter({ week: 'long' });
5368
- function buildNavLinkAttrs(context, dateMarker, viewType = 'day', isTabbable = true) {
5369
- const { dateEnv, options, calendarApi } = context;
5370
- let dateStr = dateEnv.format(dateMarker, viewType === 'week' ? WEEK_FORMAT : DAY_FORMAT);
5371
- if (options.navLinks) {
5372
- let zonedDate = dateEnv.toDate(dateMarker);
5373
- const handleInteraction = (ev) => {
5374
- let customAction = viewType === 'day' ? options.navLinkDayClick :
5375
- viewType === 'week' ? options.navLinkWeekClick : null;
5376
- if (typeof customAction === 'function') {
5377
- customAction.call(calendarApi, dateEnv.toDate(dateMarker), ev);
5378
- }
5379
- else {
5380
- if (typeof customAction === 'string') {
5381
- viewType = customAction;
5382
- }
5383
- calendarApi.zoomTo(dateMarker, viewType);
5384
- }
5385
- };
5386
- return Object.assign({ title: formatWithOrdinals(options.navLinkHint, [dateStr, zonedDate], dateStr), 'data-navlink': '' }, (isTabbable
5387
- ? createAriaClickAttrs(handleInteraction)
5388
- : { onClick: handleInteraction }));
5247
+ /* eslint max-classes-per-file: "off" */
5248
+ /*
5249
+ An object for getting/setting scroll-related information for an element.
5250
+ Internally, this is done very differently for window versus DOM element,
5251
+ so this object serves as a common interface.
5252
+ */
5253
+ class ScrollController {
5254
+ getMaxScrollTop() {
5255
+ return this.getScrollHeight() - this.getClientHeight();
5389
5256
  }
5390
- return { 'aria-label': dateStr };
5391
- }
5392
-
5393
- let _isRtlScrollbarOnLeft = null;
5394
- function getIsRtlScrollbarOnLeft() {
5395
- if (_isRtlScrollbarOnLeft === null) {
5396
- _isRtlScrollbarOnLeft = computeIsRtlScrollbarOnLeft();
5257
+ getMaxScrollLeft() {
5258
+ return this.getScrollWidth() - this.getClientWidth();
5397
5259
  }
5398
- return _isRtlScrollbarOnLeft;
5399
- }
5400
- function computeIsRtlScrollbarOnLeft() {
5401
- let outerEl = document.createElement('div');
5402
- applyStyle(outerEl, {
5403
- position: 'absolute',
5404
- top: -1000,
5405
- left: 0,
5406
- border: 0,
5407
- padding: 0,
5408
- overflow: 'scroll',
5409
- direction: 'rtl',
5410
- });
5411
- outerEl.innerHTML = '<div></div>';
5412
- document.body.appendChild(outerEl);
5413
- let innerEl = outerEl.firstChild;
5414
- let res = innerEl.getBoundingClientRect().left > outerEl.getBoundingClientRect().left;
5415
- removeElement(outerEl);
5416
- return res;
5417
- }
5418
-
5419
- let _scrollbarWidths;
5420
- function getScrollbarWidths() {
5421
- if (!_scrollbarWidths) {
5422
- _scrollbarWidths = computeScrollbarWidths();
5260
+ canScrollVertically() {
5261
+ return this.getMaxScrollTop() > 0;
5423
5262
  }
5424
- return _scrollbarWidths;
5425
- }
5426
- function computeScrollbarWidths() {
5427
- let el = document.createElement('div');
5428
- el.style.overflow = 'scroll';
5429
- el.style.position = 'absolute';
5430
- el.style.top = '-9999px';
5431
- el.style.left = '-9999px';
5432
- document.body.appendChild(el);
5433
- let res = computeScrollbarWidthsForEl(el);
5434
- document.body.removeChild(el);
5435
- return res;
5436
- }
5437
- // WARNING: will include border
5438
- function computeScrollbarWidthsForEl(el) {
5439
- return {
5440
- x: el.offsetHeight - el.clientHeight,
5441
- y: el.offsetWidth - el.clientWidth,
5442
- };
5443
- }
5444
-
5445
- function computeEdges(el, getPadding = false) {
5446
- let computedStyle = window.getComputedStyle(el);
5447
- let borderLeft = parseInt(computedStyle.borderLeftWidth, 10) || 0;
5448
- let borderRight = parseInt(computedStyle.borderRightWidth, 10) || 0;
5449
- let borderTop = parseInt(computedStyle.borderTopWidth, 10) || 0;
5450
- let borderBottom = parseInt(computedStyle.borderBottomWidth, 10) || 0;
5451
- let badScrollbarWidths = computeScrollbarWidthsForEl(el); // includes border!
5452
- let scrollbarLeftRight = badScrollbarWidths.y - borderLeft - borderRight;
5453
- let scrollbarBottom = badScrollbarWidths.x - borderTop - borderBottom;
5454
- let res = {
5455
- borderLeft,
5456
- borderRight,
5457
- borderTop,
5458
- borderBottom,
5459
- scrollbarBottom,
5460
- scrollbarLeft: 0,
5461
- scrollbarRight: 0,
5462
- };
5463
- if (getIsRtlScrollbarOnLeft() && computedStyle.direction === 'rtl') { // is the scrollbar on the left side?
5464
- res.scrollbarLeft = scrollbarLeftRight;
5263
+ canScrollHorizontally() {
5264
+ return this.getMaxScrollLeft() > 0;
5465
5265
  }
5466
- else {
5467
- res.scrollbarRight = scrollbarLeftRight;
5266
+ canScrollUp() {
5267
+ return this.getScrollTop() > 0;
5468
5268
  }
5469
- if (getPadding) {
5470
- res.paddingLeft = parseInt(computedStyle.paddingLeft, 10) || 0;
5471
- res.paddingRight = parseInt(computedStyle.paddingRight, 10) || 0;
5472
- res.paddingTop = parseInt(computedStyle.paddingTop, 10) || 0;
5473
- res.paddingBottom = parseInt(computedStyle.paddingBottom, 10) || 0;
5269
+ canScrollDown() {
5270
+ return this.getScrollTop() < this.getMaxScrollTop();
5474
5271
  }
5475
- return res;
5476
- }
5477
- function computeInnerRect(el, goWithinPadding = false, doFromWindowViewport) {
5478
- let outerRect = doFromWindowViewport ? el.getBoundingClientRect() : computeRect(el);
5479
- let edges = computeEdges(el, goWithinPadding);
5480
- let res = {
5481
- left: outerRect.left + edges.borderLeft + edges.scrollbarLeft,
5482
- right: outerRect.right - edges.borderRight - edges.scrollbarRight,
5483
- top: outerRect.top + edges.borderTop,
5484
- bottom: outerRect.bottom - edges.borderBottom - edges.scrollbarBottom,
5485
- };
5486
- if (goWithinPadding) {
5487
- res.left += edges.paddingLeft;
5488
- res.right -= edges.paddingRight;
5489
- res.top += edges.paddingTop;
5490
- res.bottom -= edges.paddingBottom;
5272
+ canScrollLeft() {
5273
+ return this.getScrollLeft() > 0;
5274
+ }
5275
+ canScrollRight() {
5276
+ return this.getScrollLeft() < this.getMaxScrollLeft();
5491
5277
  }
5492
- return res;
5493
- }
5494
- function computeRect(el) {
5495
- let rect = el.getBoundingClientRect();
5496
- return {
5497
- left: rect.left + window.scrollX,
5498
- top: rect.top + window.scrollY,
5499
- right: rect.right + window.scrollX,
5500
- bottom: rect.bottom + window.scrollY,
5501
- };
5502
5278
  }
5503
- function computeClippedClientRect(el) {
5504
- let clippingParents = getClippingParents(el);
5505
- let rect = el.getBoundingClientRect();
5506
- for (let clippingParent of clippingParents) {
5507
- let intersection = intersectRects(rect, clippingParent.getBoundingClientRect());
5508
- if (intersection) {
5509
- rect = intersection;
5510
- }
5511
- else {
5512
- return null;
5513
- }
5279
+ class ElementScrollController extends ScrollController {
5280
+ constructor(el) {
5281
+ super();
5282
+ this.el = el;
5283
+ }
5284
+ getScrollTop() {
5285
+ return this.el.scrollTop;
5286
+ }
5287
+ getScrollLeft() {
5288
+ return this.el.scrollLeft;
5289
+ }
5290
+ setScrollTop(top) {
5291
+ this.el.scrollTop = top;
5292
+ }
5293
+ setScrollLeft(left) {
5294
+ this.el.scrollLeft = left;
5295
+ }
5296
+ getScrollWidth() {
5297
+ return this.el.scrollWidth;
5298
+ }
5299
+ getScrollHeight() {
5300
+ return this.el.scrollHeight;
5301
+ }
5302
+ getClientHeight() {
5303
+ return this.el.clientHeight;
5304
+ }
5305
+ getClientWidth() {
5306
+ return this.el.clientWidth;
5514
5307
  }
5515
- return rect;
5516
5308
  }
5517
- // does not return window
5518
- function getClippingParents(el) {
5519
- let parents = [];
5520
- while (el instanceof HTMLElement) { // will stop when gets to document or null
5521
- let computedStyle = window.getComputedStyle(el);
5522
- if (computedStyle.position === 'fixed') {
5523
- break;
5524
- }
5525
- if ((/(auto|scroll)/).test(computedStyle.overflow + computedStyle.overflowY + computedStyle.overflowX)) {
5526
- parents.push(el);
5527
- }
5528
- el = el.parentNode;
5309
+ class WindowScrollController extends ScrollController {
5310
+ getScrollTop() {
5311
+ return window.scrollY;
5312
+ }
5313
+ getScrollLeft() {
5314
+ return window.scrollX;
5315
+ }
5316
+ setScrollTop(n) {
5317
+ window.scroll(window.scrollX, n);
5318
+ }
5319
+ setScrollLeft(n) {
5320
+ window.scroll(n, window.scrollY);
5321
+ }
5322
+ getScrollWidth() {
5323
+ return document.documentElement.scrollWidth;
5324
+ }
5325
+ getScrollHeight() {
5326
+ return document.documentElement.scrollHeight;
5327
+ }
5328
+ getClientHeight() {
5329
+ return document.documentElement.clientHeight;
5330
+ }
5331
+ getClientWidth() {
5332
+ return document.documentElement.clientWidth;
5529
5333
  }
5530
- return parents;
5531
5334
  }
5532
5335
 
5533
5336
  /*
5534
- Records offset information for a set of elements, relative to an origin element.
5535
- Can record the left/right OR the top/bottom OR both.
5536
- Provides methods for querying the cache by position.
5337
+ an INTERACTABLE date component
5338
+
5339
+ PURPOSES:
5340
+ - hook up to fg, fill, and mirror renderers
5341
+ - interface for dragging and hits
5537
5342
  */
5538
- class PositionCache {
5539
- constructor(originEl, els, isHorizontal, isVertical) {
5540
- this.els = els;
5541
- let originClientRect = this.originClientRect = originEl.getBoundingClientRect(); // relative to viewport top-left
5542
- if (isHorizontal) {
5543
- this.buildElHorizontals(originClientRect.left);
5544
- }
5545
- if (isVertical) {
5546
- this.buildElVerticals(originClientRect.top);
5547
- }
5548
- }
5549
- // Populates the left/right internal coordinate arrays
5550
- buildElHorizontals(originClientLeft) {
5551
- let lefts = [];
5552
- let rights = [];
5553
- for (let el of this.els) {
5554
- let rect = el.getBoundingClientRect();
5555
- lefts.push(rect.left - originClientLeft);
5556
- rights.push(rect.right - originClientLeft);
5557
- }
5558
- this.lefts = lefts;
5559
- this.rights = rights;
5560
- }
5561
- // Populates the top/bottom internal coordinate arrays
5562
- buildElVerticals(originClientTop) {
5563
- let tops = [];
5564
- let bottoms = [];
5565
- for (let el of this.els) {
5566
- let rect = el.getBoundingClientRect();
5567
- tops.push(rect.top - originClientTop);
5568
- bottoms.push(rect.bottom - originClientTop);
5569
- }
5570
- this.tops = tops;
5571
- this.bottoms = bottoms;
5572
- }
5573
- // Given a left offset (from document left), returns the index of the el that it horizontally intersects.
5574
- // If no intersection is made, returns undefined.
5575
- leftToIndex(leftPosition) {
5576
- let { lefts, rights } = this;
5577
- let len = lefts.length;
5578
- let i;
5579
- for (i = 0; i < len; i += 1) {
5580
- if (leftPosition >= lefts[i] && leftPosition < rights[i]) {
5581
- return i;
5582
- }
5583
- }
5584
- return undefined; // TODO: better
5585
- }
5586
- // Given a top offset (from document top), returns the index of the el that it vertically intersects.
5587
- // If no intersection is made, returns undefined.
5588
- topToIndex(topPosition) {
5589
- let { tops, bottoms } = this;
5590
- let len = tops.length;
5591
- let i;
5592
- for (i = 0; i < len; i += 1) {
5593
- if (topPosition >= tops[i] && topPosition < bottoms[i]) {
5594
- return i;
5595
- }
5596
- }
5597
- return undefined; // TODO: better
5598
- }
5599
- // Gets the width of the element at the given index
5600
- getWidth(leftIndex) {
5601
- return this.rights[leftIndex] - this.lefts[leftIndex];
5602
- }
5603
- // Gets the height of the element at the given index
5604
- getHeight(topIndex) {
5605
- return this.bottoms[topIndex] - this.tops[topIndex];
5606
- }
5607
- similarTo(otherCache) {
5608
- return similarNumArrays(this.tops || [], otherCache.tops || []) &&
5609
- similarNumArrays(this.bottoms || [], otherCache.bottoms || []) &&
5610
- similarNumArrays(this.lefts || [], otherCache.lefts || []) &&
5611
- similarNumArrays(this.rights || [], otherCache.rights || []);
5612
- }
5613
- }
5614
- function similarNumArrays(a, b) {
5615
- const len = a.length;
5616
- if (len !== b.length) {
5617
- return false;
5618
- }
5619
- for (let i = 0; i < len; i++) {
5620
- if (Math.round(a[i]) !== Math.round(b[i])) {
5621
- return false;
5622
- }
5623
- }
5624
- return true;
5625
- }
5626
-
5627
- /* eslint max-classes-per-file: "off" */
5628
- /*
5629
- An object for getting/setting scroll-related information for an element.
5630
- Internally, this is done very differently for window versus DOM element,
5631
- so this object serves as a common interface.
5632
- */
5633
- class ScrollController {
5634
- getMaxScrollTop() {
5635
- return this.getScrollHeight() - this.getClientHeight();
5636
- }
5637
- getMaxScrollLeft() {
5638
- return this.getScrollWidth() - this.getClientWidth();
5639
- }
5640
- canScrollVertically() {
5641
- return this.getMaxScrollTop() > 0;
5642
- }
5643
- canScrollHorizontally() {
5644
- return this.getMaxScrollLeft() > 0;
5645
- }
5646
- canScrollUp() {
5647
- return this.getScrollTop() > 0;
5648
- }
5649
- canScrollDown() {
5650
- return this.getScrollTop() < this.getMaxScrollTop();
5651
- }
5652
- canScrollLeft() {
5653
- return this.getScrollLeft() > 0;
5654
- }
5655
- canScrollRight() {
5656
- return this.getScrollLeft() < this.getMaxScrollLeft();
5657
- }
5658
- }
5659
- class ElementScrollController extends ScrollController {
5660
- constructor(el) {
5661
- super();
5662
- this.el = el;
5663
- }
5664
- getScrollTop() {
5665
- return this.el.scrollTop;
5666
- }
5667
- getScrollLeft() {
5668
- return this.el.scrollLeft;
5669
- }
5670
- setScrollTop(top) {
5671
- this.el.scrollTop = top;
5672
- }
5673
- setScrollLeft(left) {
5674
- this.el.scrollLeft = left;
5675
- }
5676
- getScrollWidth() {
5677
- return this.el.scrollWidth;
5678
- }
5679
- getScrollHeight() {
5680
- return this.el.scrollHeight;
5681
- }
5682
- getClientHeight() {
5683
- return this.el.clientHeight;
5684
- }
5685
- getClientWidth() {
5686
- return this.el.clientWidth;
5687
- }
5688
- }
5689
- class WindowScrollController extends ScrollController {
5690
- getScrollTop() {
5691
- return window.scrollY;
5692
- }
5693
- getScrollLeft() {
5694
- return window.scrollX;
5695
- }
5696
- setScrollTop(n) {
5697
- window.scroll(window.scrollX, n);
5698
- }
5699
- setScrollLeft(n) {
5700
- window.scroll(n, window.scrollY);
5701
- }
5702
- getScrollWidth() {
5703
- return document.documentElement.scrollWidth;
5704
- }
5705
- getScrollHeight() {
5706
- return document.documentElement.scrollHeight;
5707
- }
5708
- getClientHeight() {
5709
- return document.documentElement.clientHeight;
5710
- }
5711
- getClientWidth() {
5712
- return document.documentElement.clientWidth;
5713
- }
5714
- }
5715
-
5716
- /*
5717
- an INTERACTABLE date component
5718
-
5719
- PURPOSES:
5720
- - hook up to fg, fill, and mirror renderers
5721
- - interface for dragging and hits
5722
- */
5723
- class DateComponent extends BaseComponent {
5724
- constructor() {
5725
- super(...arguments);
5726
- this.uid = guid();
5343
+ class DateComponent extends BaseComponent {
5344
+ constructor() {
5345
+ super(...arguments);
5346
+ this.uid = guid();
5727
5347
  }
5728
5348
  // Hit System
5729
5349
  // -----------------------------------------------------------------------------------------------------------------
@@ -5753,440 +5373,603 @@ class NamedTimeZoneImpl {
5753
5373
  }
5754
5374
  }
5755
5375
 
5756
- class SegHierarchy {
5757
- constructor(getEntryThickness = (entry) => {
5758
- // if no thickness known, assume 1 (if 0, so small it always fits)
5759
- return entry.thickness || 1;
5760
- }) {
5761
- this.getEntryThickness = getEntryThickness;
5762
- // settings
5763
- this.strictOrder = false;
5764
- this.allowReslicing = false;
5765
- this.maxCoord = -1; // -1 means no max
5766
- this.maxStackCnt = -1; // -1 means no max
5767
- this.levelCoords = []; // ordered
5768
- this.entriesByLevel = []; // parallel with levelCoords
5769
- this.stackCnts = {}; // TODO: use better technique!?
5770
- }
5771
- addSegs(inputs) {
5772
- let hiddenEntries = [];
5773
- for (let input of inputs) {
5774
- this.insertEntry(input, hiddenEntries);
5775
- }
5776
- return hiddenEntries;
5777
- }
5778
- insertEntry(entry, hiddenEntries) {
5779
- let insertion = this.findInsertion(entry);
5780
- if (this.isInsertionValid(insertion, entry)) {
5781
- this.insertEntryAt(entry, insertion);
5782
- }
5783
- else {
5784
- this.handleInvalidInsertion(insertion, entry, hiddenEntries);
5785
- }
5786
- }
5787
- isInsertionValid(insertion, entry) {
5788
- return (this.maxCoord === -1 || insertion.levelCoord + this.getEntryThickness(entry) <= this.maxCoord) &&
5789
- (this.maxStackCnt === -1 || insertion.stackCnt < this.maxStackCnt);
5790
- }
5791
- handleInvalidInsertion(insertion, entry, hiddenEntries) {
5792
- if (this.allowReslicing && insertion.touchingEntry) {
5793
- const hiddenEntry = Object.assign(Object.assign({}, entry), { span: intersectSpans(entry.span, insertion.touchingEntry.span) });
5794
- hiddenEntries.push(hiddenEntry);
5795
- this.splitEntry(entry, insertion.touchingEntry, hiddenEntries);
5796
- }
5797
- else {
5798
- hiddenEntries.push(entry);
5799
- }
5376
+ const DAY_NUM_FORMAT = createFormatter({ day: 'numeric' });
5377
+ class DayCellContainer extends BaseComponent {
5378
+ constructor() {
5379
+ super(...arguments);
5380
+ this.refineRenderProps = memoizeObjArg(refineRenderProps);
5800
5381
  }
5801
- /*
5802
- Does NOT add what hit the `barrier` into hiddenEntries. Should already be done.
5803
- */
5804
- splitEntry(entry, barrier, hiddenEntries) {
5805
- let entrySpan = entry.span;
5806
- let barrierSpan = barrier.span;
5807
- if (entrySpan.start < barrierSpan.start) {
5808
- this.insertEntry({
5809
- index: entry.index,
5810
- thickness: entry.thickness,
5811
- span: { start: entrySpan.start, end: barrierSpan.start },
5812
- }, hiddenEntries);
5813
- }
5814
- if (entrySpan.end > barrierSpan.end) {
5815
- this.insertEntry({
5816
- index: entry.index,
5817
- thickness: entry.thickness,
5818
- span: { start: barrierSpan.end, end: entrySpan.end },
5819
- }, hiddenEntries);
5820
- }
5382
+ render() {
5383
+ let { props, context } = this;
5384
+ let { options } = context;
5385
+ let renderProps = this.refineRenderProps({
5386
+ date: props.date,
5387
+ dateProfile: props.dateProfile,
5388
+ todayRange: props.todayRange,
5389
+ isMonthStart: props.isMonthStart || false,
5390
+ showDayNumber: props.showDayNumber,
5391
+ extraRenderProps: props.extraRenderProps,
5392
+ viewApi: context.viewApi,
5393
+ dateEnv: context.dateEnv,
5394
+ monthStartFormat: options.monthStartFormat,
5395
+ });
5396
+ return (createElement(ContentContainer, Object.assign({}, props /* includes children */, { elClasses: [
5397
+ ...getDayClassNames(renderProps, context.theme),
5398
+ ...(props.elClasses || []),
5399
+ ], 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:
5400
+ // don't use custom classNames if disabled
5401
+ renderProps.isDisabled ? undefined : options.dayCellClassNames, didMount: options.dayCellDidMount, willUnmount: options.dayCellWillUnmount })));
5821
5402
  }
5822
- insertEntryAt(entry, insertion) {
5823
- let { entriesByLevel, levelCoords } = this;
5824
- if (insertion.lateral === -1) {
5825
- // create a new level
5826
- insertAt(levelCoords, insertion.level, insertion.levelCoord);
5827
- insertAt(entriesByLevel, insertion.level, [entry]);
5828
- }
5829
- else {
5830
- // insert into existing level
5831
- insertAt(entriesByLevel[insertion.level], insertion.lateral, entry);
5832
- }
5833
- this.stackCnts[buildEntryKey(entry)] = insertion.stackCnt;
5834
- }
5835
- /*
5836
- does not care about limits
5837
- */
5838
- findInsertion(newEntry) {
5839
- let { levelCoords, entriesByLevel, strictOrder, stackCnts } = this;
5840
- let levelCnt = levelCoords.length;
5841
- let candidateCoord = 0;
5842
- let touchingLevel = -1;
5843
- let touchingLateral = -1;
5844
- let touchingEntry = null;
5845
- let stackCnt = 0;
5846
- for (let trackingLevel = 0; trackingLevel < levelCnt; trackingLevel += 1) {
5847
- const trackingCoord = levelCoords[trackingLevel];
5848
- // if the current level is past the placed entry, we have found a good empty space and can stop.
5849
- // if strictOrder, keep finding more lateral intersections.
5850
- if (!strictOrder && trackingCoord >= candidateCoord + this.getEntryThickness(newEntry)) {
5851
- break;
5403
+ }
5404
+ function hasCustomDayCellContent(options) {
5405
+ return Boolean(options.dayCellContent || hasCustomRenderingHandler('dayCellContent', options));
5406
+ }
5407
+ function refineRenderProps(raw) {
5408
+ let { date, dateEnv, dateProfile, isMonthStart } = raw;
5409
+ let dayMeta = getDateMeta(date, raw.todayRange, null, dateProfile);
5410
+ let dayNumberText = raw.showDayNumber ? (dateEnv.format(date, isMonthStart ? raw.monthStartFormat : DAY_NUM_FORMAT)) : '';
5411
+ return Object.assign(Object.assign(Object.assign({ date: dateEnv.toDate(date), view: raw.viewApi }, dayMeta), { isMonthStart,
5412
+ dayNumberText }), raw.extraRenderProps);
5413
+ }
5414
+
5415
+ const PADDING_FROM_VIEWPORT = 10;
5416
+ class Popover extends BaseComponent {
5417
+ constructor() {
5418
+ super(...arguments);
5419
+ this.state = {
5420
+ titleId: getUniqueDomId(),
5421
+ };
5422
+ this.handleRootEl = (el) => {
5423
+ this.rootEl = el;
5424
+ if (this.props.elRef) {
5425
+ setRef(this.props.elRef, el);
5852
5426
  }
5853
- let trackingEntries = entriesByLevel[trackingLevel];
5854
- let trackingEntry;
5855
- let searchRes = binarySearch(trackingEntries, newEntry.span.start, getEntrySpanEnd); // find first entry after newEntry's end
5856
- let lateralIndex = searchRes[0] + searchRes[1]; // if exact match (which doesn't collide), go to next one
5857
- while ( // loop through entries that horizontally intersect
5858
- (trackingEntry = trackingEntries[lateralIndex]) && // but not past the whole entry list
5859
- trackingEntry.span.start < newEntry.span.end // and not entirely past newEntry
5860
- ) {
5861
- let trackingEntryBottom = trackingCoord + this.getEntryThickness(trackingEntry);
5862
- // intersects into the top of the candidate?
5863
- if (trackingEntryBottom > candidateCoord) {
5864
- candidateCoord = trackingEntryBottom;
5865
- touchingEntry = trackingEntry;
5866
- touchingLevel = trackingLevel;
5867
- touchingLateral = lateralIndex;
5868
- }
5869
- // butts up against top of candidate? (will happen if just intersected as well)
5870
- if (trackingEntryBottom === candidateCoord) {
5871
- // accumulate the highest possible stackCnt of the trackingEntries that butt up
5872
- stackCnt = Math.max(stackCnt, stackCnts[buildEntryKey(trackingEntry)] + 1);
5873
- }
5874
- lateralIndex += 1;
5427
+ };
5428
+ // Triggered when the user clicks *anywhere* in the document, for the autoHide feature
5429
+ this.handleDocumentMouseDown = (ev) => {
5430
+ // only hide the popover if the click happened outside the popover
5431
+ const target = getEventTargetViaRoot(ev);
5432
+ if (!this.rootEl.contains(target)) {
5433
+ this.handleCloseClick();
5875
5434
  }
5876
- }
5877
- // the destination level will be after touchingEntry's level. find it
5878
- let destLevel = 0;
5879
- if (touchingEntry) {
5880
- destLevel = touchingLevel + 1;
5881
- while (destLevel < levelCnt && levelCoords[destLevel] < candidateCoord) {
5882
- destLevel += 1;
5435
+ };
5436
+ this.handleDocumentKeyDown = (ev) => {
5437
+ if (ev.key === 'Escape') {
5438
+ this.handleCloseClick();
5439
+ }
5440
+ };
5441
+ this.handleCloseClick = () => {
5442
+ let { onClose } = this.props;
5443
+ if (onClose) {
5444
+ onClose();
5883
5445
  }
5884
- }
5885
- // if adding to an existing level, find where to insert
5886
- let destLateral = -1;
5887
- if (destLevel < levelCnt && levelCoords[destLevel] === candidateCoord) {
5888
- destLateral = binarySearch(entriesByLevel[destLevel], newEntry.span.end, getEntrySpanEnd)[0];
5889
- }
5890
- return {
5891
- touchingLevel,
5892
- touchingLateral,
5893
- touchingEntry,
5894
- stackCnt,
5895
- levelCoord: candidateCoord,
5896
- level: destLevel,
5897
- lateral: destLateral,
5898
5446
  };
5899
5447
  }
5900
- // sorted by levelCoord (lowest to highest)
5901
- toRects() {
5902
- let { entriesByLevel, levelCoords } = this;
5903
- let levelCnt = entriesByLevel.length;
5904
- let rects = [];
5905
- for (let level = 0; level < levelCnt; level += 1) {
5906
- let entries = entriesByLevel[level];
5907
- let levelCoord = levelCoords[level];
5908
- for (let entry of entries) {
5909
- rects.push(Object.assign(Object.assign({}, entry), { thickness: this.getEntryThickness(entry), levelCoord }));
5448
+ render() {
5449
+ let { theme, options } = this.context;
5450
+ let { props, state } = this;
5451
+ let classNames = [
5452
+ 'fc-popover',
5453
+ theme.getClass('popover'),
5454
+ ].concat(props.extraClassNames || []);
5455
+ return createPortal(createElement("div", Object.assign({}, props.extraAttrs, { id: props.id, className: classNames.join(' '), "aria-labelledby": state.titleId, ref: this.handleRootEl }),
5456
+ createElement("div", { className: 'fc-popover-header ' + theme.getClass('popoverHeader') },
5457
+ createElement("span", { className: "fc-popover-title", id: state.titleId }, props.title),
5458
+ createElement("span", { className: 'fc-popover-close ' + theme.getIconClass('close'), title: options.closeHint, onClick: this.handleCloseClick })),
5459
+ createElement("div", { className: 'fc-popover-body ' + theme.getClass('popoverContent') }, props.children)), props.parentEl);
5460
+ }
5461
+ componentDidMount() {
5462
+ document.addEventListener('mousedown', this.handleDocumentMouseDown);
5463
+ document.addEventListener('keydown', this.handleDocumentKeyDown);
5464
+ this.updateSize();
5465
+ }
5466
+ componentWillUnmount() {
5467
+ document.removeEventListener('mousedown', this.handleDocumentMouseDown);
5468
+ document.removeEventListener('keydown', this.handleDocumentKeyDown);
5469
+ }
5470
+ updateSize() {
5471
+ let { isRtl } = this.context;
5472
+ let { alignmentEl, alignGridTop } = this.props;
5473
+ let { rootEl } = this;
5474
+ let alignmentRect = computeClippedClientRect(alignmentEl);
5475
+ if (alignmentRect) {
5476
+ let popoverDims = rootEl.getBoundingClientRect();
5477
+ if (alignGridTop) {
5478
+ throw new Error('alignGridTop not supported yet');
5910
5479
  }
5480
+ // position relative to viewport
5481
+ let popoverTop = alignGridTop
5482
+ ? elementClosest(alignmentEl, '.fc-scrollgrid').getBoundingClientRect().top // BAD!!!
5483
+ : alignmentRect.top;
5484
+ let popoverLeft = isRtl ? alignmentRect.right - popoverDims.width : alignmentRect.left;
5485
+ // constrain
5486
+ popoverTop = Math.max(popoverTop, PADDING_FROM_VIEWPORT);
5487
+ popoverLeft = Math.min(popoverLeft, document.documentElement.clientWidth - PADDING_FROM_VIEWPORT - popoverDims.width);
5488
+ popoverLeft = Math.max(popoverLeft, PADDING_FROM_VIEWPORT);
5489
+ let origin = rootEl.offsetParent.getBoundingClientRect();
5490
+ applyStyle(rootEl, {
5491
+ top: popoverTop - origin.top,
5492
+ left: popoverLeft - origin.left,
5493
+ });
5911
5494
  }
5912
- return rects;
5913
5495
  }
5914
5496
  }
5915
- function getEntrySpanEnd(entry) {
5916
- return entry.span.end;
5917
- }
5918
- function buildEntryKey(entry) {
5919
- return entry.index + ':' + entry.span.start;
5920
- }
5921
- // returns groups with entries sorted by input order
5922
- function groupIntersectingEntries(entries) {
5923
- let merges = [];
5924
- for (let entry of entries) {
5925
- let filteredMerges = [];
5926
- let hungryMerge = {
5927
- span: entry.span,
5928
- entries: [entry],
5929
- };
5930
- for (let merge of merges) {
5931
- if (intersectSpans(merge.span, hungryMerge.span)) {
5932
- hungryMerge = {
5933
- entries: merge.entries.concat(hungryMerge.entries),
5934
- span: joinSpans(merge.span, hungryMerge.span),
5935
- };
5497
+
5498
+ class MorePopover extends DateComponent {
5499
+ constructor() {
5500
+ super(...arguments);
5501
+ this.handleRootEl = (rootEl) => {
5502
+ this.rootEl = rootEl;
5503
+ if (rootEl) {
5504
+ this.context.registerInteractiveComponent(this, {
5505
+ el: rootEl,
5506
+ useEventCenter: false,
5507
+ });
5936
5508
  }
5937
5509
  else {
5938
- filteredMerges.push(merge);
5510
+ this.context.unregisterInteractiveComponent(this);
5939
5511
  }
5940
- }
5941
- filteredMerges.push(hungryMerge);
5942
- merges = filteredMerges;
5943
- }
5944
- return merges;
5945
- }
5946
- function joinSpans(span0, span1) {
5947
- return {
5948
- start: Math.min(span0.start, span1.start),
5949
- end: Math.max(span0.end, span1.end),
5950
- };
5951
- }
5952
- function intersectSpans(span0, span1) {
5953
- let start = Math.max(span0.start, span1.start);
5954
- let end = Math.min(span0.end, span1.end);
5955
- if (start < end) {
5956
- return { start, end };
5512
+ };
5957
5513
  }
5958
- return null;
5959
- }
5960
- // general util
5961
- // ---------------------------------------------------------------------------------------------------------------------
5962
- function insertAt(arr, index, item) {
5963
- arr.splice(index, 0, item);
5964
- }
5965
- function binarySearch(a, searchVal, getItemVal) {
5966
- let startIndex = 0;
5967
- let endIndex = a.length; // exclusive
5968
- if (!endIndex || searchVal < getItemVal(a[startIndex])) { // no items OR before first item
5969
- return [0, 0];
5514
+ render() {
5515
+ let { options, dateEnv } = this.context;
5516
+ let { props } = this;
5517
+ let { startDate, todayRange, dateProfile } = props;
5518
+ let title = dateEnv.format(startDate, options.dayPopoverFormat);
5519
+ return (createElement(DayCellContainer, { elRef: this.handleRootEl, date: startDate, dateProfile: dateProfile, todayRange: todayRange }, (InnerContent, renderProps, elAttrs) => (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 },
5520
+ hasCustomDayCellContent(options) && (createElement(InnerContent, { elTag: "div", elClasses: ['fc-more-popover-misc'] })),
5521
+ props.children))));
5970
5522
  }
5971
- if (searchVal > getItemVal(a[endIndex - 1])) { // after last item
5972
- return [endIndex, 0];
5523
+ queryHit(positionLeft, positionTop, elWidth, elHeight) {
5524
+ let { rootEl, props } = this;
5525
+ if (positionLeft >= 0 && positionLeft < elWidth &&
5526
+ positionTop >= 0 && positionTop < elHeight) {
5527
+ return {
5528
+ dateProfile: props.dateProfile,
5529
+ dateSpan: Object.assign({ allDay: !props.forceTimed, range: {
5530
+ start: props.startDate,
5531
+ end: props.endDate,
5532
+ } }, props.extraDateSpan),
5533
+ dayEl: rootEl,
5534
+ rect: {
5535
+ left: 0,
5536
+ top: 0,
5537
+ right: elWidth,
5538
+ bottom: elHeight,
5539
+ },
5540
+ layer: 1, // important when comparing with hits from other components
5541
+ };
5542
+ }
5543
+ return null;
5973
5544
  }
5974
- while (startIndex < endIndex) {
5975
- let middleIndex = Math.floor(startIndex + (endIndex - startIndex) / 2);
5976
- let middleVal = getItemVal(a[middleIndex]);
5977
- if (searchVal < middleVal) {
5978
- endIndex = middleIndex;
5979
- }
5980
- else if (searchVal > middleVal) {
5981
- startIndex = middleIndex + 1;
5982
- }
5983
- else { // equal!
5984
- return [middleIndex, 1];
5985
- }
5986
- }
5987
- return [startIndex, 0];
5988
5545
  }
5989
5546
 
5990
- /*
5991
- An abstraction for a dragging interaction originating on an event.
5992
- Does higher-level things than PointerDragger, such as possibly:
5993
- - a "mirror" that moves with the pointer
5994
- - a minimum number of pixels or other criteria for a true drag to begin
5995
-
5996
- subclasses must emit:
5997
- - pointerdown
5998
- - dragstart
5999
- - dragmove
6000
- - pointerup
6001
- - dragend
6002
- */
6003
- class ElementDragging {
6004
- constructor(el, selector) {
6005
- this.emitter = new Emitter();
5547
+ class MoreLinkContainer extends BaseComponent {
5548
+ constructor() {
5549
+ super(...arguments);
5550
+ this.state = {
5551
+ isPopoverOpen: false,
5552
+ popoverId: getUniqueDomId(),
5553
+ };
5554
+ this.handleLinkEl = (linkEl) => {
5555
+ this.linkEl = linkEl;
5556
+ if (this.props.elRef) {
5557
+ setRef(this.props.elRef, linkEl);
5558
+ }
5559
+ };
5560
+ this.handleClick = (ev) => {
5561
+ let { props, context } = this;
5562
+ let { moreLinkClick } = context.options;
5563
+ let date = computeRange(props).start;
5564
+ function buildPublicSeg(seg) {
5565
+ let { def, instance, range } = seg.eventRange;
5566
+ return {
5567
+ event: new EventImpl(context, def, instance),
5568
+ start: context.dateEnv.toDate(range.start),
5569
+ end: context.dateEnv.toDate(range.end),
5570
+ isStart: seg.isStart,
5571
+ isEnd: seg.isEnd,
5572
+ };
5573
+ }
5574
+ if (typeof moreLinkClick === 'function') {
5575
+ moreLinkClick = moreLinkClick({
5576
+ date,
5577
+ allDay: Boolean(props.allDayDate),
5578
+ allSegs: props.segs.map(buildPublicSeg),
5579
+ hiddenSegs: props.hiddenSegs.map(buildPublicSeg),
5580
+ jsEvent: ev,
5581
+ view: context.viewApi,
5582
+ });
5583
+ }
5584
+ if (!moreLinkClick || moreLinkClick === 'popover') {
5585
+ this.setState({ isPopoverOpen: true });
5586
+ }
5587
+ else if (typeof moreLinkClick === 'string') { // a view name
5588
+ context.calendarApi.zoomTo(date, moreLinkClick);
5589
+ }
5590
+ };
5591
+ this.handlePopoverClose = () => {
5592
+ this.setState({ isPopoverOpen: false });
5593
+ };
6006
5594
  }
6007
- destroy() {
5595
+ render() {
5596
+ let { props, state } = this;
5597
+ return (createElement(ViewContextType.Consumer, null, (context) => {
5598
+ let { viewApi, options, calendarApi } = context;
5599
+ let { moreLinkText } = options;
5600
+ let moreCnt = props.hiddenSegs.length;
5601
+ let range = computeRange(props);
5602
+ let text = typeof moreLinkText === 'function' // TODO: eventually use formatWithOrdinals
5603
+ ? moreLinkText.call(calendarApi, moreCnt)
5604
+ : `+${moreCnt} ${moreLinkText}`;
5605
+ let hint = formatWithOrdinals(options.moreLinkHint, [moreCnt], text);
5606
+ let renderProps = {
5607
+ num: moreCnt,
5608
+ shortText: `+${moreCnt}`,
5609
+ text,
5610
+ view: viewApi,
5611
+ };
5612
+ return (createElement(Fragment, null,
5613
+ Boolean(moreCnt) && (createElement(ContentContainer, { elTag: props.elTag || 'a', elRef: this.handleLinkEl, elClasses: [
5614
+ ...(props.elClasses || []),
5615
+ 'fc-more-link',
5616
+ ], 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)),
5617
+ state.isPopoverOpen && (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 ?
5618
+ props.alignmentElRef.current :
5619
+ this.linkEl, alignGridTop: props.alignGridTop, forceTimed: props.forceTimed, onClose: this.handlePopoverClose }, props.popoverContent()))));
5620
+ }));
6008
5621
  }
6009
- setMirrorIsVisible(bool) {
6010
- // optional if subclass doesn't want to support a mirror
5622
+ componentDidMount() {
5623
+ this.updateParentEl();
6011
5624
  }
6012
- setMirrorNeedsRevert(bool) {
6013
- // optional if subclass doesn't want to support a mirror
5625
+ componentDidUpdate() {
5626
+ this.updateParentEl();
6014
5627
  }
6015
- setAutoScrollEnabled(bool) {
6016
- // optional
5628
+ updateParentEl() {
5629
+ if (this.linkEl) {
5630
+ this.parentEl = elementClosest(this.linkEl, '.fc-view-harness'); // HACK. reconsider
5631
+ }
6017
5632
  }
6018
5633
  }
6019
-
6020
- // TODO: get rid of this in favor of options system,
6021
- // tho it's really easy to access this globally rather than pass thru options.
6022
- const config = {};
6023
-
6024
- /*
6025
- Information about what will happen when an external element is dragged-and-dropped
6026
- onto a calendar. Contains information for creating an event.
6027
- */
6028
- const DRAG_META_REFINERS = {
6029
- startTime: createDuration,
6030
- duration: createDuration,
6031
- create: Boolean,
6032
- sourceId: String,
6033
- };
6034
- function parseDragMeta(raw) {
6035
- let { refined, extra } = refineProps(raw, DRAG_META_REFINERS);
5634
+ function renderMoreLinkInner(props) {
5635
+ return props.text;
5636
+ }
5637
+ function computeRange(props) {
5638
+ if (props.allDayDate) {
5639
+ return {
5640
+ start: props.allDayDate,
5641
+ end: addDays(props.allDayDate, 1),
5642
+ };
5643
+ }
6036
5644
  return {
6037
- startTime: refined.startTime || null,
6038
- duration: refined.duration || null,
6039
- create: refined.create != null ? refined.create : true,
6040
- sourceId: refined.sourceId,
6041
- leftoverProps: extra,
5645
+ start: computeEarliestSegStart(props.hiddenSegs),
5646
+ end: computeLatestSegEnd(props.hiddenSegs),
6042
5647
  };
6043
5648
  }
6044
-
6045
- // Computes a default column header formatting string if `colFormat` is not explicitly defined
6046
- function computeFallbackHeaderFormat(datesRepDistinctDays, dayCnt) {
6047
- // if more than one week row, or if there are a lot of columns with not much space,
6048
- // put just the day numbers will be in each cell
6049
- if (!datesRepDistinctDays || dayCnt > 10) {
6050
- return createFormatter({ weekday: 'short' }); // "Sat"
6051
- }
6052
- if (dayCnt > 1) {
6053
- return createFormatter({ weekday: 'short', month: 'numeric', day: 'numeric', omitCommas: true }); // "Sat 11/12"
6054
- }
6055
- return createFormatter({ weekday: 'long' }); // "Saturday"
5649
+ function computeEarliestSegStart(segs) {
5650
+ return segs.reduce(pickEarliestStart).eventRange.range.start;
6056
5651
  }
6057
-
6058
- const CLASS_NAME = 'fc-col-header-cell'; // do the cushion too? no
6059
- function renderInner$1(renderProps) {
6060
- return renderProps.text;
5652
+ function pickEarliestStart(seg0, seg1) {
5653
+ return seg0.eventRange.range.start < seg1.eventRange.range.start ? seg0 : seg1;
6061
5654
  }
6062
-
6063
- // BAD name for this class now. used in the Header
6064
- class TableDateCell extends BaseComponent {
6065
- render() {
6066
- let { dateEnv, options, theme, viewApi } = this.context;
6067
- let { props } = this;
6068
- let { date, dateProfile } = props;
6069
- let dayMeta = getDateMeta(date, props.todayRange, null, dateProfile);
6070
- let classNames = [CLASS_NAME].concat(getDayClassNames(dayMeta, theme));
6071
- let text = dateEnv.format(date, props.dayHeaderFormat);
6072
- // if colCnt is 1, we are already in a day-view and don't need a navlink
6073
- let navLinkAttrs = (!dayMeta.isDisabled && props.colCnt > 1)
6074
- ? buildNavLinkAttrs(this.context, date)
6075
- : {};
6076
- let renderProps = Object.assign(Object.assign(Object.assign({ date: dateEnv.toDate(date), view: viewApi }, props.extraRenderProps), { text }), dayMeta);
6077
- return (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) => (createElement("div", { className: "fc-scrollgrid-sync-inner" }, !dayMeta.isDisabled && (createElement(InnerContainer, { elTag: "a", elAttrs: navLinkAttrs, elClasses: [
6078
- 'fc-col-header-cell-cushion',
6079
- props.isSticky && 'fc-sticky',
6080
- ] }))))));
6081
- }
5655
+ function computeLatestSegEnd(segs) {
5656
+ return segs.reduce(pickLatestEnd).eventRange.range.end;
6082
5657
  }
6083
-
6084
- const WEEKDAY_FORMAT = createFormatter({ weekday: 'long' });
6085
- class TableDowCell extends BaseComponent {
6086
- render() {
6087
- let { props } = this;
6088
- let { dateEnv, theme, viewApi, options } = this.context;
6089
- let date = addDays(new Date(259200000), props.dow); // start with Sun, 04 Jan 1970 00:00:00 GMT
6090
- let dateMeta = {
6091
- dow: props.dow,
6092
- isDisabled: false,
6093
- isFuture: false,
6094
- isPast: false,
6095
- isToday: false,
6096
- isOther: false,
6097
- };
6098
- let text = dateEnv.format(date, props.dayHeaderFormat);
6099
- let renderProps = Object.assign(Object.assign(Object.assign(Object.assign({ // TODO: make this public?
6100
- date }, dateMeta), { view: viewApi }), props.extraRenderProps), { text });
6101
- return (createElement(ContentContainer, { elTag: "th", elClasses: [
6102
- CLASS_NAME,
6103
- ...getDayClassNames(dateMeta, theme),
6104
- ...(props.extraClassNames || []),
6105
- ], 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) => (createElement("div", { className: "fc-scrollgrid-sync-inner" },
6106
- createElement(InnerContent, { elTag: "a", elClasses: [
6107
- 'fc-col-header-cell-cushion',
6108
- props.isSticky && 'fc-sticky',
6109
- ], elAttrs: {
6110
- 'aria-label': dateEnv.format(date, WEEKDAY_FORMAT),
6111
- } })))));
6112
- }
5658
+ function pickLatestEnd(seg0, seg1) {
5659
+ return seg0.eventRange.range.end > seg1.eventRange.range.end ? seg0 : seg1;
6113
5660
  }
6114
5661
 
6115
- class NowTimer extends Component {
6116
- constructor(props, context) {
6117
- super(props, context);
6118
- this.initialNowDate = getNow(context.options.now, context.dateEnv);
6119
- this.initialNowQueriedMs = new Date().valueOf();
6120
- this.state = this.computeTiming().currentState;
6121
- }
6122
- render() {
6123
- let { props, state } = this;
6124
- return props.children(state.nowDate, state.todayRange);
6125
- }
6126
- componentDidMount() {
6127
- this.setTimeout();
5662
+ class SegHierarchy {
5663
+ constructor(getEntryThickness = (entry) => {
5664
+ // if no thickness known, assume 1 (if 0, so small it always fits)
5665
+ return entry.thickness;
5666
+ }) {
5667
+ this.getEntryThickness = getEntryThickness;
5668
+ // settings
5669
+ this.strictOrder = false;
5670
+ this.allowReslicing = false;
5671
+ this.maxCoord = -1; // -1 means no max
5672
+ this.maxStackCnt = -1; // -1 means no max
5673
+ this.levelCoords = []; // ordered
5674
+ this.entriesByLevel = []; // parallel with levelCoords
5675
+ this.stackCnts = {}; // TODO: use better technique!?
6128
5676
  }
6129
- componentDidUpdate(prevProps) {
6130
- if (prevProps.unit !== this.props.unit) {
6131
- this.clearTimeout();
6132
- this.setTimeout();
5677
+ addSegs(inputs) {
5678
+ let hiddenEntries = [];
5679
+ for (let input of inputs) {
5680
+ this.insertEntry(input, hiddenEntries);
6133
5681
  }
5682
+ return hiddenEntries;
6134
5683
  }
6135
- componentWillUnmount() {
6136
- this.clearTimeout();
6137
- }
6138
- computeTiming() {
6139
- let { props, context } = this;
6140
- let unroundedNow = addMs(this.initialNowDate, new Date().valueOf() - this.initialNowQueriedMs);
6141
- let currentUnitStart = context.dateEnv.startOf(unroundedNow, props.unit);
6142
- let nextUnitStart = context.dateEnv.add(currentUnitStart, createDuration(1, props.unit));
6143
- let waitMs = nextUnitStart.valueOf() - unroundedNow.valueOf();
6144
- // there is a max setTimeout ms value (https://stackoverflow.com/a/3468650/96342)
6145
- // ensure no longer than a day
6146
- waitMs = Math.min(1000 * 60 * 60 * 24, waitMs);
6147
- return {
6148
- currentState: { nowDate: currentUnitStart, todayRange: buildDayRange(currentUnitStart) },
6149
- nextState: { nowDate: nextUnitStart, todayRange: buildDayRange(nextUnitStart) },
6150
- waitMs,
6151
- };
5684
+ insertEntry(entry, hiddenEntries) {
5685
+ let entryThickness = this.getEntryThickness(entry);
5686
+ if (entryThickness == null) {
5687
+ hiddenEntries.push(entry);
5688
+ }
5689
+ else {
5690
+ let insertion = this.findInsertion(entry, entryThickness);
5691
+ if (this.isInsertionValid(insertion, entry, entryThickness)) {
5692
+ this.insertEntryAt(entry, insertion);
5693
+ }
5694
+ else {
5695
+ this.handleInvalidInsertion(insertion, entry, hiddenEntries);
5696
+ }
5697
+ }
6152
5698
  }
6153
- setTimeout() {
6154
- let { nextState, waitMs } = this.computeTiming();
6155
- this.timeoutId = setTimeout(() => {
6156
- this.setState(nextState, () => {
6157
- this.setTimeout();
6158
- });
6159
- }, waitMs);
5699
+ isInsertionValid(insertion, entry, entryThickness) {
5700
+ return (this.maxCoord === -1 || insertion.levelCoord + entryThickness <= this.maxCoord) &&
5701
+ (this.maxStackCnt === -1 || insertion.stackCnt < this.maxStackCnt);
6160
5702
  }
6161
- clearTimeout() {
6162
- if (this.timeoutId) {
6163
- clearTimeout(this.timeoutId);
5703
+ handleInvalidInsertion(insertion, entry, hiddenEntries) {
5704
+ if (this.allowReslicing && insertion.touchingEntry) {
5705
+ const hiddenEntry = Object.assign(Object.assign({}, entry), { span: intersectSpans(entry.span, insertion.touchingEntry.span) });
5706
+ hiddenEntries.push(hiddenEntry);
5707
+ this.splitEntry(entry, insertion.touchingEntry, hiddenEntries);
5708
+ }
5709
+ else {
5710
+ hiddenEntries.push(entry);
6164
5711
  }
6165
5712
  }
6166
- }
6167
- NowTimer.contextType = ViewContextType;
6168
- function buildDayRange(date) {
6169
- let start = startOfDay(date);
6170
- let end = addDays(start, 1);
6171
- return { start, end };
6172
- }
6173
-
6174
- class DayHeader extends BaseComponent {
6175
- constructor() {
6176
- super(...arguments);
6177
- this.createDayHeaderFormatter = memoize(createDayHeaderFormatter);
6178
- }
6179
- render() {
6180
- let { context } = this;
6181
- let { dates, dateProfile, datesRepDistinctDays, renderIntro } = this.props;
6182
- let dayHeaderFormat = this.createDayHeaderFormatter(context.options.dayHeaderFormat, datesRepDistinctDays, dates.length);
6183
- return (createElement(NowTimer, { unit: "day" }, (nowDate, todayRange) => (createElement("tr", { role: "row" },
6184
- renderIntro && renderIntro('day'),
6185
- dates.map((date) => (datesRepDistinctDays ? (createElement(TableDateCell, { key: date.toISOString(), date: date, dateProfile: dateProfile, todayRange: todayRange, colCnt: dates.length, dayHeaderFormat: dayHeaderFormat })) : (createElement(TableDowCell, { key: date.getUTCDay(), dow: date.getUTCDay(), dayHeaderFormat: dayHeaderFormat }))))))));
5713
+ /*
5714
+ Does NOT add what hit the `barrier` into hiddenEntries. Should already be done.
5715
+ */
5716
+ splitEntry(entry, barrier, hiddenEntries) {
5717
+ let entrySpan = entry.span;
5718
+ let barrierSpan = barrier.span;
5719
+ if (entrySpan.start < barrierSpan.start) {
5720
+ this.insertEntry({
5721
+ index: entry.index,
5722
+ seg: entry.seg,
5723
+ thickness: entry.thickness,
5724
+ span: { start: entrySpan.start, end: barrierSpan.start },
5725
+ }, hiddenEntries);
5726
+ }
5727
+ if (entrySpan.end > barrierSpan.end) {
5728
+ this.insertEntry({
5729
+ index: entry.index,
5730
+ seg: entry.seg,
5731
+ thickness: entry.thickness,
5732
+ span: { start: barrierSpan.end, end: entrySpan.end },
5733
+ }, hiddenEntries);
5734
+ }
5735
+ }
5736
+ insertEntryAt(entry, insertion) {
5737
+ let { entriesByLevel, levelCoords } = this;
5738
+ if (insertion.lateral === -1) {
5739
+ // create a new level
5740
+ insertAt(levelCoords, insertion.level, insertion.levelCoord);
5741
+ insertAt(entriesByLevel, insertion.level, [entry]);
5742
+ }
5743
+ else {
5744
+ // insert into existing level
5745
+ insertAt(entriesByLevel[insertion.level], insertion.lateral, entry);
5746
+ }
5747
+ this.stackCnts[buildEntryKey(entry)] = insertion.stackCnt;
5748
+ }
5749
+ /*
5750
+ does not care about limits
5751
+ */
5752
+ findInsertion(newEntry, newEntryThickness) {
5753
+ let { levelCoords, entriesByLevel, strictOrder, stackCnts } = this;
5754
+ let levelCnt = levelCoords.length;
5755
+ let candidateCoord = 0;
5756
+ let touchingLevel = -1;
5757
+ let touchingLateral = -1;
5758
+ let touchingEntry = null;
5759
+ let stackCnt = 0;
5760
+ for (let trackingLevel = 0; trackingLevel < levelCnt; trackingLevel += 1) {
5761
+ const trackingCoord = levelCoords[trackingLevel];
5762
+ // if the current level is past the placed entry, we have found a good empty space and can stop.
5763
+ // if strictOrder, keep finding more lateral intersections.
5764
+ if (!strictOrder && trackingCoord >= candidateCoord + newEntryThickness) {
5765
+ break;
5766
+ }
5767
+ let trackingEntries = entriesByLevel[trackingLevel];
5768
+ let trackingEntry;
5769
+ let searchRes = binarySearch(trackingEntries, newEntry.span.start, getEntrySpanEnd); // find first entry after newEntry's end
5770
+ let lateralIndex = searchRes[0] + searchRes[1]; // if exact match (which doesn't collide), go to next one
5771
+ while ( // loop through entries that horizontally intersect
5772
+ (trackingEntry = trackingEntries[lateralIndex]) && // but not past the whole entry list
5773
+ trackingEntry.span.start < newEntry.span.end // and not entirely past newEntry
5774
+ ) {
5775
+ let trackingEntryBottom = trackingCoord + this.getEntryThickness(trackingEntry);
5776
+ // intersects into the top of the candidate?
5777
+ if (trackingEntryBottom > candidateCoord) {
5778
+ candidateCoord = trackingEntryBottom;
5779
+ touchingEntry = trackingEntry;
5780
+ touchingLevel = trackingLevel;
5781
+ touchingLateral = lateralIndex;
5782
+ }
5783
+ // butts up against top of candidate? (will happen if just intersected as well)
5784
+ if (trackingEntryBottom === candidateCoord) {
5785
+ // accumulate the highest possible stackCnt of the trackingEntries that butt up
5786
+ stackCnt = Math.max(stackCnt, stackCnts[buildEntryKey(trackingEntry)] + 1);
5787
+ }
5788
+ lateralIndex += 1;
5789
+ }
5790
+ }
5791
+ // the destination level will be after touchingEntry's level. find it
5792
+ let destLevel = 0;
5793
+ if (touchingEntry) {
5794
+ destLevel = touchingLevel + 1;
5795
+ while (destLevel < levelCnt && levelCoords[destLevel] < candidateCoord) {
5796
+ destLevel += 1;
5797
+ }
5798
+ }
5799
+ // if adding to an existing level, find where to insert
5800
+ let destLateral = -1;
5801
+ if (destLevel < levelCnt && levelCoords[destLevel] === candidateCoord) {
5802
+ destLateral = binarySearch(entriesByLevel[destLevel], newEntry.span.end, getEntrySpanEnd)[0];
5803
+ }
5804
+ return {
5805
+ touchingLevel,
5806
+ touchingLateral,
5807
+ touchingEntry,
5808
+ stackCnt,
5809
+ levelCoord: candidateCoord,
5810
+ level: destLevel,
5811
+ lateral: destLateral,
5812
+ };
5813
+ }
5814
+ // sorted by levelCoord (lowest to highest)
5815
+ toRects() {
5816
+ let { entriesByLevel, levelCoords } = this;
5817
+ let levelCnt = entriesByLevel.length;
5818
+ let rects = [];
5819
+ for (let level = 0; level < levelCnt; level += 1) {
5820
+ let entries = entriesByLevel[level];
5821
+ let levelCoord = levelCoords[level];
5822
+ for (let entry of entries) {
5823
+ rects.push(Object.assign(Object.assign({}, entry), { thickness: this.getEntryThickness(entry), levelCoord }));
5824
+ }
5825
+ }
5826
+ return rects;
5827
+ }
5828
+ }
5829
+ function getEntrySpanEnd(entry) {
5830
+ return entry.span.end;
5831
+ }
5832
+ /*
5833
+ Generates a unique ID whose lifespan is a single run of SegHierarchy, so can be really specific
5834
+ without fear of accidentally busting the cache on subsequent rerenders
5835
+ */
5836
+ function buildEntryKey(entry) {
5837
+ return entry.index + ':' + entry.span.start;
5838
+ }
5839
+ /*
5840
+ returns groups with entries sorted by input order
5841
+ */
5842
+ function groupIntersectingEntries(entries) {
5843
+ let merges = [];
5844
+ for (let entry of entries) {
5845
+ let filteredMerges = [];
5846
+ let hungryMerge = {
5847
+ span: entry.span,
5848
+ entries: [entry],
5849
+ };
5850
+ for (let merge of merges) {
5851
+ if (intersectSpans(merge.span, hungryMerge.span)) {
5852
+ hungryMerge = {
5853
+ span: joinSpans(merge.span, hungryMerge.span),
5854
+ entries: merge.entries.concat(hungryMerge.entries), // keep preexisting merge's items first. maintains order
5855
+ };
5856
+ }
5857
+ else {
5858
+ filteredMerges.push(merge);
5859
+ }
5860
+ }
5861
+ filteredMerges.push(hungryMerge);
5862
+ merges = filteredMerges;
5863
+ }
5864
+ return merges.map((merge) => {
5865
+ const segs = merge.entries.map(extractEntrySeg);
5866
+ return {
5867
+ key: buildIsoString(computeEarliestSegStart(segs)),
5868
+ span: merge.span,
5869
+ segs,
5870
+ };
5871
+ });
5872
+ }
5873
+ function extractEntrySeg(entry) {
5874
+ return entry.seg;
5875
+ }
5876
+ function joinSpans(span0, span1) {
5877
+ return {
5878
+ start: Math.min(span0.start, span1.start),
5879
+ end: Math.max(span0.end, span1.end),
5880
+ };
5881
+ }
5882
+ function intersectSpans(span0, span1) {
5883
+ let start = Math.max(span0.start, span1.start);
5884
+ let end = Math.min(span0.end, span1.end);
5885
+ if (start < end) {
5886
+ return { start, end };
5887
+ }
5888
+ return null;
5889
+ }
5890
+ // general util
5891
+ // ---------------------------------------------------------------------------------------------------------------------
5892
+ function insertAt(arr, index, item) {
5893
+ arr.splice(index, 0, item);
5894
+ }
5895
+ function binarySearch(a, searchVal, getItemVal) {
5896
+ let startIndex = 0;
5897
+ let endIndex = a.length; // exclusive
5898
+ if (!endIndex || searchVal < getItemVal(a[startIndex])) { // no items OR before first item
5899
+ return [0, 0];
5900
+ }
5901
+ if (searchVal > getItemVal(a[endIndex - 1])) { // after last item
5902
+ return [endIndex, 0];
5903
+ }
5904
+ while (startIndex < endIndex) {
5905
+ let middleIndex = Math.floor(startIndex + (endIndex - startIndex) / 2);
5906
+ let middleVal = getItemVal(a[middleIndex]);
5907
+ if (searchVal < middleVal) {
5908
+ endIndex = middleIndex;
5909
+ }
5910
+ else if (searchVal > middleVal) {
5911
+ startIndex = middleIndex + 1;
5912
+ }
5913
+ else { // equal!
5914
+ return [middleIndex, 1];
5915
+ }
5916
+ }
5917
+ return [startIndex, 0];
5918
+ }
5919
+
5920
+ /*
5921
+ An abstraction for a dragging interaction originating on an event.
5922
+ Does higher-level things than PointerDragger, such as possibly:
5923
+ - a "mirror" that moves with the pointer
5924
+ - a minimum number of pixels or other criteria for a true drag to begin
5925
+
5926
+ subclasses must emit:
5927
+ - pointerdown
5928
+ - dragstart
5929
+ - dragmove
5930
+ - pointerup
5931
+ - dragend
5932
+ */
5933
+ class ElementDragging {
5934
+ constructor(el, selector) {
5935
+ this.emitter = new Emitter();
5936
+ }
5937
+ destroy() {
5938
+ }
5939
+ setMirrorIsVisible(bool) {
5940
+ // optional if subclass doesn't want to support a mirror
5941
+ }
5942
+ setMirrorNeedsRevert(bool) {
5943
+ // optional if subclass doesn't want to support a mirror
5944
+ }
5945
+ setAutoScrollEnabled(bool) {
5946
+ // optional
6186
5947
  }
6187
5948
  }
6188
- function createDayHeaderFormatter(explicitFormat, datesRepDistinctDays, dateCnt) {
6189
- return explicitFormat || computeFallbackHeaderFormat(datesRepDistinctDays, dateCnt);
5949
+
5950
+ // TODO: get rid of this in favor of options system,
5951
+ // tho it's really easy to access this globally rather than pass thru options.
5952
+ const config = {};
5953
+
5954
+ /*
5955
+ Information about what will happen when an external element is dragged-and-dropped
5956
+ onto a calendar. Contains information for creating an event.
5957
+ */
5958
+ const DRAG_META_REFINERS = {
5959
+ startTime: createDuration,
5960
+ duration: createDuration,
5961
+ create: Boolean,
5962
+ sourceId: String,
5963
+ };
5964
+ function parseDragMeta(raw) {
5965
+ let { refined, extra } = refineProps(raw, DRAG_META_REFINERS);
5966
+ return {
5967
+ startTime: refined.startTime || null,
5968
+ duration: refined.duration || null,
5969
+ create: refined.create != null ? refined.create : true,
5970
+ sourceId: refined.sourceId,
5971
+ leftoverProps: extra,
5972
+ };
6190
5973
  }
6191
5974
 
6192
5975
  class DaySeriesModel {
@@ -6270,7 +6053,7 @@ class DayTableModel {
6270
6053
  this.rowCnt = rowCnt;
6271
6054
  this.colCnt = daysPerRow;
6272
6055
  this.daySeries = daySeries;
6273
- this.cells = this.buildCells();
6056
+ this.cellRows = this.buildCells();
6274
6057
  this.headerDates = this.buildHeaderDates();
6275
6058
  }
6276
6059
  buildCells() {
@@ -6294,7 +6077,7 @@ class DayTableModel {
6294
6077
  buildHeaderDates() {
6295
6078
  let dates = [];
6296
6079
  for (let col = 0; col < this.colCnt; col += 1) {
6297
- dates.push(this.cells[0][col].date);
6080
+ dates.push(this.cellRows[0][col].date);
6298
6081
  }
6299
6082
  return dates;
6300
6083
  }
@@ -6322,18 +6105,272 @@ class DayTableModel {
6322
6105
  }
6323
6106
  }
6324
6107
 
6325
- class Slicer {
6326
- constructor() {
6327
- this.sliceBusinessHours = memoize(this._sliceBusinessHours);
6328
- this.sliceDateSelection = memoize(this._sliceDateSpan);
6329
- this.sliceEventStore = memoize(this._sliceEventStore);
6330
- this.sliceEventDrag = memoize(this._sliceInteraction);
6331
- this.sliceEventResize = memoize(this._sliceInteraction);
6332
- this.forceDayIfListItem = false; // hack
6333
- }
6334
- sliceProps(props, dateProfile, nextDayThreshold, context, ...extraArgs) {
6335
- let { eventUiBases } = props;
6336
- let eventSegs = this.sliceEventStore(props.eventStore, eventUiBases, dateProfile, nextDayThreshold, ...extraArgs);
6108
+ const WHEEL_EVENT_NAMES = 'wheel mousewheel DomMouseScroll MozMousePixelScroll'.split(' ');
6109
+ /*
6110
+ Fires:
6111
+ - scrollStart (always user)
6112
+ - scroll
6113
+ - scrollEnd (always user)
6114
+
6115
+ NOTE: detection is complicated (w/ touch and wheel) because ScrollerSyncer needs to know about it,
6116
+ but are we sure we can't just ignore programmatic scrollTo() calls with a flag? and determine the
6117
+ the scroll-master simply by who was the newest scroller? Does passive:true do things asynchronously?
6118
+ */
6119
+ class ScrollListener {
6120
+ constructor(el) {
6121
+ this.el = el;
6122
+ this.emitter = new Emitter();
6123
+ this.isScrolling = false;
6124
+ this.isTouching = false; // user currently has finger down?
6125
+ this.isRecentlyWheeled = false;
6126
+ this.isRecentlyScrolled = false;
6127
+ this.wheelWaiter = new DelayedRunner(this._handleWheelWaited.bind(this));
6128
+ this.scrollWaiter = new DelayedRunner(this._handleScrollWaited.bind(this));
6129
+ // Handlers
6130
+ // ----------------------------------------------------------------------------------------------
6131
+ this.handleScroll = () => {
6132
+ this.startScroll();
6133
+ this.emitter.trigger('scroll', this.isRecentlyWheeled, this.isTouching);
6134
+ this.isRecentlyScrolled = true;
6135
+ this.scrollWaiter.request(500);
6136
+ };
6137
+ // will fire *before* the scroll event is fired (might not cause a scroll)
6138
+ this.handleWheel = () => {
6139
+ this.isRecentlyWheeled = true;
6140
+ this.wheelWaiter.request(500);
6141
+ };
6142
+ // will fire *before* the scroll event is fired (might not cause a scroll)
6143
+ this.handleTouchStart = () => {
6144
+ this.isTouching = true;
6145
+ };
6146
+ this.handleTouchEnd = () => {
6147
+ this.isTouching = false;
6148
+ // if the user ended their touch, and the scroll area wasn't moving,
6149
+ // we consider this to be the end of the scroll.
6150
+ if (!this.isRecentlyScrolled) {
6151
+ this.endScroll(); // won't fire if already ended
6152
+ }
6153
+ };
6154
+ el.addEventListener('scroll', this.handleScroll);
6155
+ el.addEventListener('touchstart', this.handleTouchStart, { passive: true });
6156
+ el.addEventListener('touchend', this.handleTouchEnd);
6157
+ for (let eventName of WHEEL_EVENT_NAMES) {
6158
+ el.addEventListener(eventName, this.handleWheel, { passive: true });
6159
+ }
6160
+ }
6161
+ destroy() {
6162
+ let { el } = this;
6163
+ el.removeEventListener('scroll', this.handleScroll);
6164
+ el.removeEventListener('touchstart', this.handleTouchStart, { passive: true });
6165
+ el.removeEventListener('touchend', this.handleTouchEnd);
6166
+ for (let eventName of WHEEL_EVENT_NAMES) {
6167
+ el.removeEventListener(eventName, this.handleWheel, { passive: true });
6168
+ }
6169
+ }
6170
+ // Start / Stop
6171
+ // ----------------------------------------------------------------------------------------------
6172
+ startScroll() {
6173
+ if (!this.isScrolling) {
6174
+ this.isScrolling = true;
6175
+ this.emitter.trigger('scrollStart', this.isRecentlyWheeled, this.isTouching);
6176
+ }
6177
+ }
6178
+ endScroll() {
6179
+ if (this.isScrolling) {
6180
+ this.emitter.trigger('scrollEnd');
6181
+ this.isScrolling = false;
6182
+ this.isRecentlyScrolled = true;
6183
+ this.isRecentlyWheeled = false;
6184
+ this.scrollWaiter.clear();
6185
+ this.wheelWaiter.clear();
6186
+ }
6187
+ }
6188
+ _handleScrollWaited() {
6189
+ this.isRecentlyScrolled = false;
6190
+ // only end the scroll if not currently touching.
6191
+ // if touching, the scrolling will end later, on touchend.
6192
+ if (!this.isTouching) {
6193
+ this.endScroll(); // won't fire if already ended
6194
+ }
6195
+ }
6196
+ _handleWheelWaited() {
6197
+ this.isRecentlyWheeled = false;
6198
+ }
6199
+ }
6200
+
6201
+ class Scroller extends DateComponent {
6202
+ constructor() {
6203
+ super(...arguments);
6204
+ // ref
6205
+ this.elRef = createRef();
6206
+ }
6207
+ render() {
6208
+ const { props } = this;
6209
+ // if there's only one axis that needs scrolling, the other axis will unintentionally have
6210
+ // scrollbars too, so we must force to 'hidden'
6211
+ const fallbackOverflow = (props.horizontal || props.vertical) ? 'hidden' : '';
6212
+ return (createElement("div", { ref: this.elRef, className: [
6213
+ 'fc-scroller',
6214
+ props.hideScrollbars ? 'fc-scroller-nobars' : '',
6215
+ ...(props.elClassNames || []),
6216
+ ].join(' '), style: Object.assign(Object.assign({}, props.elStyle), { overflowX: props.horizontal ? 'auto' : fallbackOverflow, overflowY: props.vertical ? 'auto' : fallbackOverflow }) }, props.children));
6217
+ }
6218
+ componentDidMount() {
6219
+ const el = this.elRef.current; // TODO: make dynamic with useEffect
6220
+ this.listener = new ScrollListener(el);
6221
+ this.disconnectSize = watchSize(el, (contentWidth, contentHeight) => {
6222
+ const { props, context } = this;
6223
+ const bottomScrollbarWidth = el.offsetHeight - el.clientHeight;
6224
+ const horizontalScrollbarWidth = el.offsetWidth - el.clientWidth;
6225
+ let rightScrollbarWidth = 0;
6226
+ let leftScrollbarWidth = 0;
6227
+ if (context.isRtl && getRtlScrollerConfig().leftScrollbars) {
6228
+ leftScrollbarWidth = horizontalScrollbarWidth;
6229
+ }
6230
+ else {
6231
+ rightScrollbarWidth = horizontalScrollbarWidth;
6232
+ }
6233
+ if (!isDimsEqual(this.currentWidth, contentWidth)) {
6234
+ setRef(props.widthRef, this.currentWidth = contentWidth);
6235
+ }
6236
+ if (!isDimsEqual(this.currentHeight, contentHeight)) {
6237
+ setRef(props.heightRef, this.currentHeight = contentHeight);
6238
+ }
6239
+ if (!isDimsEqual(this.currentBottomScrollbarWidth, bottomScrollbarWidth)) {
6240
+ setRef(props.bottomScrollbarWidthRef, this.currentBottomScrollbarWidth = bottomScrollbarWidth);
6241
+ }
6242
+ if (!isDimsEqual(this.currentRightScrollbarWidth, rightScrollbarWidth)) {
6243
+ setRef(props.rightScrollbarWidthRef, this.currentRightScrollbarWidth = rightScrollbarWidth);
6244
+ }
6245
+ if (!isDimsEqual(this.currentLeftScrollbarWidth, leftScrollbarWidth)) {
6246
+ setRef(props.leftScrollbarWidthRef, this.currentLeftScrollbarWidth = leftScrollbarWidth);
6247
+ }
6248
+ });
6249
+ }
6250
+ componentWillUnmount() {
6251
+ const { props } = this;
6252
+ this.disconnectSize();
6253
+ this.listener.destroy();
6254
+ setRef(props.widthRef, null);
6255
+ setRef(props.heightRef, null);
6256
+ setRef(props.bottomScrollbarWidthRef, null);
6257
+ setRef(props.rightScrollbarWidthRef, null);
6258
+ setRef(props.leftScrollbarWidthRef, null);
6259
+ }
6260
+ endScroll() {
6261
+ this.listener.endScroll();
6262
+ }
6263
+ // Public API
6264
+ // -----------------------------------------------------------------------------------------------
6265
+ get x() {
6266
+ const { isRtl } = this.context;
6267
+ const el = this.elRef.current;
6268
+ return getNormalizedScrollX(el, isRtl);
6269
+ }
6270
+ get y() {
6271
+ const el = this.elRef.current;
6272
+ return el.scrollTop;
6273
+ }
6274
+ scrollTo({ x, y }) {
6275
+ const { isRtl } = this.context;
6276
+ const el = this.elRef.current;
6277
+ if (y != null) {
6278
+ el.scrollTop = y;
6279
+ }
6280
+ if (x != null) {
6281
+ setNormalizedScrollX(el, isRtl, x);
6282
+ }
6283
+ }
6284
+ addScrollEndListener(handler) {
6285
+ this.listener.emitter.on('scrollEnd', handler);
6286
+ }
6287
+ removeScrollEndListener(handler) {
6288
+ this.listener.emitter.off('scrollEnd', handler);
6289
+ }
6290
+ }
6291
+ // Public API
6292
+ // -------------------------------------------------------------------------------------------------
6293
+ // TODO: consolidate with scroll-left-norm.ts
6294
+ function getNormalizedScrollX(el, isRtl) {
6295
+ const { scrollLeft } = el;
6296
+ return isRtl ? getNormalizedRtlScrollX(scrollLeft, el) : scrollLeft;
6297
+ }
6298
+ function setNormalizedScrollX(el, isRtl, x) {
6299
+ el.scrollLeft = isRtl ? getNormalizedRtlScrollLeft(x, el) : x;
6300
+ }
6301
+ /*
6302
+ Returns a value in the 'reverse' system
6303
+ */
6304
+ function getNormalizedRtlScrollX(scrollLeft, el) {
6305
+ switch (getRtlScrollerConfig().system) {
6306
+ case 'positive':
6307
+ return el.scrollWidth - el.clientWidth - scrollLeft;
6308
+ case 'negative':
6309
+ return -scrollLeft;
6310
+ }
6311
+ return scrollLeft;
6312
+ }
6313
+ /*
6314
+ Receives a value in the 'reverse' system
6315
+ TODO: is this really the same equations as getNormalizedRtlScrollX??? I think so
6316
+ If so, consolidate. With isRtl check too
6317
+ */
6318
+ function getNormalizedRtlScrollLeft(x, el) {
6319
+ switch (getRtlScrollerConfig().system) {
6320
+ case 'positive':
6321
+ return el.scrollWidth - el.clientWidth - x;
6322
+ case 'negative':
6323
+ return -x;
6324
+ }
6325
+ return x;
6326
+ }
6327
+ let _rtlScrollerConfig;
6328
+ function getRtlScrollerConfig() {
6329
+ return _rtlScrollerConfig || (_rtlScrollerConfig = detectRtlScrollerConfig());
6330
+ }
6331
+ function detectRtlScrollerConfig() {
6332
+ let el = document.createElement('div');
6333
+ el.style.position = 'absolute';
6334
+ el.style.top = '-1000px';
6335
+ el.style.width = '100px'; // must be at least the side of scrollbars or you get inaccurate values (#7335)
6336
+ el.style.height = '100px'; // "
6337
+ el.style.overflow = 'scroll';
6338
+ el.style.direction = 'rtl';
6339
+ let innerEl = document.createElement('div');
6340
+ innerEl.style.width = '200px';
6341
+ innerEl.style.height = '200px';
6342
+ el.appendChild(innerEl);
6343
+ document.body.appendChild(el);
6344
+ let system;
6345
+ if (el.scrollLeft > 0) {
6346
+ system = 'positive'; // scroll is a positive number from the left edge
6347
+ }
6348
+ else {
6349
+ el.scrollLeft = 50;
6350
+ if (el.scrollLeft > 0) {
6351
+ system = 'reverse'; // scroll is a positive number from the right edge
6352
+ }
6353
+ else {
6354
+ system = 'negative'; // scroll is a negative number from the right edge
6355
+ }
6356
+ }
6357
+ let rightScrollbars = innerEl.getBoundingClientRect().right < el.getBoundingClientRect().right;
6358
+ removeElement(el);
6359
+ return { system, leftScrollbars: !rightScrollbars };
6360
+ }
6361
+
6362
+ class Slicer {
6363
+ constructor() {
6364
+ this.sliceBusinessHours = memoize(this._sliceBusinessHours);
6365
+ this.sliceDateSelection = memoize(this._sliceDateSpan);
6366
+ this.sliceEventStore = memoize(this._sliceEventStore);
6367
+ this.sliceEventDrag = memoize(this._sliceInteraction);
6368
+ this.sliceEventResize = memoize(this._sliceInteraction);
6369
+ this.forceDayIfListItem = false; // hack
6370
+ }
6371
+ sliceProps(props, dateProfile, nextDayThreshold, context, ...extraArgs) {
6372
+ let { eventUiBases } = props;
6373
+ let eventSegs = this.sliceEventStore(props.eventStore, eventUiBases, dateProfile, nextDayThreshold, ...extraArgs);
6337
6374
  return {
6338
6375
  dateSelectionSegs: this.sliceDateSelection(props.dateSelection, dateProfile, nextDayThreshold, eventUiBases, context, ...extraArgs),
6339
6376
  businessHourSegs: this.sliceBusinessHours(props.businessHours, dateProfile, nextDayThreshold, context, ...extraArgs),
@@ -6624,423 +6661,124 @@ function anyRangesContainRange(outerRanges, innerRange) {
6624
6661
  return false;
6625
6662
  }
6626
6663
 
6627
- const VISIBLE_HIDDEN_RE = /^(visible|hidden)$/;
6628
- class Scroller extends BaseComponent {
6629
- constructor() {
6630
- super(...arguments);
6631
- this.handleEl = (el) => {
6632
- this.el = el;
6633
- setRef(this.props.elRef, el);
6634
- };
6635
- }
6636
- render() {
6637
- let { props } = this;
6638
- let { liquid, liquidIsAbsolute } = props;
6639
- let isAbsolute = liquid && liquidIsAbsolute;
6640
- let className = ['fc-scroller'];
6641
- if (liquid) {
6642
- if (liquidIsAbsolute) {
6643
- className.push('fc-scroller-liquid-absolute');
6644
- }
6645
- else {
6646
- className.push('fc-scroller-liquid');
6647
- }
6648
- }
6649
- return (createElement("div", { ref: this.handleEl, className: className.join(' '), style: {
6650
- overflowX: props.overflowX,
6651
- overflowY: props.overflowY,
6652
- left: (isAbsolute && -(props.overcomeLeft || 0)) || '',
6653
- right: (isAbsolute && -(props.overcomeRight || 0)) || '',
6654
- bottom: (isAbsolute && -(props.overcomeBottom || 0)) || '',
6655
- marginLeft: (!isAbsolute && -(props.overcomeLeft || 0)) || '',
6656
- marginRight: (!isAbsolute && -(props.overcomeRight || 0)) || '',
6657
- marginBottom: (!isAbsolute && -(props.overcomeBottom || 0)) || '',
6658
- maxHeight: props.maxHeight || '',
6659
- } }, props.children));
6660
- }
6661
- needsXScrolling() {
6662
- if (VISIBLE_HIDDEN_RE.test(this.props.overflowX)) {
6663
- return false;
6664
- }
6665
- // testing scrollWidth>clientWidth is unreliable cross-browser when pixel heights aren't integers.
6666
- // much more reliable to see if children are taller than the scroller, even tho doesn't account for
6667
- // inner-child margins and absolute positioning
6668
- let { el } = this;
6669
- let realClientWidth = this.el.getBoundingClientRect().width - this.getYScrollbarWidth();
6670
- let { children } = el;
6671
- for (let i = 0; i < children.length; i += 1) {
6672
- let childEl = children[i];
6673
- if (childEl.getBoundingClientRect().width > realClientWidth) {
6674
- return true;
6675
- }
6676
- }
6677
- return false;
6678
- }
6679
- needsYScrolling() {
6680
- if (VISIBLE_HIDDEN_RE.test(this.props.overflowY)) {
6681
- return false;
6682
- }
6683
- // testing scrollHeight>clientHeight is unreliable cross-browser when pixel heights aren't integers.
6684
- // much more reliable to see if children are taller than the scroller, even tho doesn't account for
6685
- // inner-child margins and absolute positioning
6686
- let { el } = this;
6687
- let realClientHeight = this.el.getBoundingClientRect().height - this.getXScrollbarWidth();
6688
- let { children } = el;
6689
- for (let i = 0; i < children.length; i += 1) {
6690
- let childEl = children[i];
6691
- if (childEl.getBoundingClientRect().height > realClientHeight) {
6692
- return true;
6693
- }
6694
- }
6695
- return false;
6664
+ function getIsHeightAuto(options) {
6665
+ return options.height === 'auto' || options.viewHeight === 'auto';
6666
+ }
6667
+ function getStickyHeaderDates(options) {
6668
+ let { stickyHeaderDates } = options;
6669
+ if (stickyHeaderDates == null || stickyHeaderDates === 'auto') {
6670
+ stickyHeaderDates = getIsHeightAuto(options);
6696
6671
  }
6697
- getXScrollbarWidth() {
6698
- if (VISIBLE_HIDDEN_RE.test(this.props.overflowX)) {
6699
- return 0;
6700
- }
6701
- return this.el.offsetHeight - this.el.clientHeight; // only works because we guarantee no borders. TODO: add to CSS with important?
6672
+ return stickyHeaderDates;
6673
+ }
6674
+ function getStickyFooterScrollbar(options) {
6675
+ let { stickyFooterScrollbar } = options;
6676
+ if (stickyFooterScrollbar == null || stickyFooterScrollbar === 'auto') {
6677
+ stickyFooterScrollbar = getIsHeightAuto(options);
6702
6678
  }
6703
- getYScrollbarWidth() {
6704
- if (VISIBLE_HIDDEN_RE.test(this.props.overflowY)) {
6705
- return 0;
6706
- }
6707
- return this.el.offsetWidth - this.el.clientWidth; // only works because we guarantee no borders. TODO: add to CSS with important?
6679
+ return stickyFooterScrollbar;
6680
+ }
6681
+ function getScrollerSyncerClass(pluginHooks) {
6682
+ const ScrollerSyncer = pluginHooks.scrollerSyncerClass;
6683
+ if (!ScrollerSyncer) {
6684
+ throw new RangeError('Must import @fullcalendar/scrollgrid');
6708
6685
  }
6686
+ return ScrollerSyncer;
6709
6687
  }
6710
6688
 
6711
6689
  /*
6712
- TODO: somehow infer OtherArgs from masterCallback?
6713
- TODO: infer RefType from masterCallback if provided
6690
+ TODO: make API where createRefMap() called
6714
6691
  */
6715
6692
  class RefMap {
6716
6693
  constructor(masterCallback) {
6717
6694
  this.masterCallback = masterCallback;
6718
- this.currentMap = {};
6719
- this.depths = {};
6720
- this.callbackMap = {};
6695
+ this.rev = '';
6696
+ this.current = new Map();
6697
+ this.callbacks = new Map;
6721
6698
  this.handleValue = (val, key) => {
6722
- let { depths, currentMap } = this;
6723
- let removed = false;
6724
- let added = false;
6725
- if (val !== null) {
6726
- // for bug... ACTUALLY: can probably do away with this now that callers don't share numeric indices anymore
6727
- removed = (key in currentMap);
6728
- currentMap[key] = val;
6729
- depths[key] = (depths[key] || 0) + 1;
6730
- added = true;
6699
+ let { current, callbacks } = this;
6700
+ if (val === null) {
6701
+ current.delete(key);
6702
+ callbacks.delete(key);
6731
6703
  }
6732
6704
  else {
6733
- depths[key] -= 1;
6734
- if (!depths[key]) {
6735
- delete currentMap[key];
6736
- delete this.callbackMap[key];
6737
- removed = true;
6738
- }
6705
+ current.set(key, val);
6739
6706
  }
6707
+ this.rev = guid();
6740
6708
  if (this.masterCallback) {
6741
- if (removed) {
6742
- this.masterCallback(null, String(key));
6743
- }
6744
- if (added) {
6745
- this.masterCallback(val, String(key));
6746
- }
6709
+ this.masterCallback(val, key);
6747
6710
  }
6748
6711
  };
6749
6712
  }
6750
6713
  createRef(key) {
6751
- let refCallback = this.callbackMap[key];
6714
+ let refCallback = this.callbacks.get(key);
6752
6715
  if (!refCallback) {
6753
- refCallback = this.callbackMap[key] = (val) => {
6754
- this.handleValue(val, String(key));
6716
+ refCallback = (val) => {
6717
+ this.handleValue(val, key);
6755
6718
  };
6719
+ this.callbacks.set(key, refCallback);
6756
6720
  }
6757
6721
  return refCallback;
6758
6722
  }
6759
- // TODO: check callers that don't care about order. should use getAll instead
6760
- // NOTE: this method has become less valuable now that we are encouraged to map order by some other index
6761
- // TODO: provide ONE array-export function, buildArray, which fails on non-numeric indexes. caller can manipulate and "collect"
6762
- collect(startIndex, endIndex, step) {
6763
- return collectFromHash(this.currentMap, startIndex, endIndex, step);
6764
- }
6765
- getAll() {
6766
- return hashValuesToArray(this.currentMap);
6767
- }
6768
6723
  }
6769
6724
 
6770
- function computeShrinkWidth(chunkEls) {
6771
- let shrinkCells = findElements(chunkEls, '.fc-scrollgrid-shrink');
6772
- let largestWidth = 0;
6773
- for (let shrinkCell of shrinkCells) {
6774
- largestWidth = Math.max(largestWidth, computeSmallestCellWidth(shrinkCell));
6775
- }
6776
- return Math.ceil(largestWidth); // <table> elements work best with integers. round up to ensure contents fits
6777
- }
6778
- function getSectionHasLiquidHeight(props, sectionConfig) {
6779
- return props.liquid && sectionConfig.liquid; // does the section do liquid-height? (need to have whole scrollgrid liquid-height as well)
6780
- }
6781
- function getAllowYScrolling(props, sectionConfig) {
6782
- return sectionConfig.maxHeight != null || // if its possible for the height to max out, we might need scrollbars
6783
- getSectionHasLiquidHeight(props, sectionConfig); // if the section is liquid height, it might condense enough to require scrollbars
6784
- }
6785
- // TODO: ONLY use `arg`. force out internal function to use same API
6786
- function renderChunkContent(sectionConfig, chunkConfig, arg, isHeader) {
6787
- let { expandRows } = arg;
6788
- let content = typeof chunkConfig.content === 'function' ?
6789
- chunkConfig.content(arg) :
6790
- createElement('table', {
6791
- role: 'presentation',
6792
- className: [
6793
- chunkConfig.tableClassName,
6794
- sectionConfig.syncRowHeights ? 'fc-scrollgrid-sync-table' : '',
6795
- ].join(' '),
6796
- style: {
6797
- minWidth: arg.tableMinWidth,
6798
- width: arg.clientWidth,
6799
- height: expandRows ? arg.clientHeight : '', // css `height` on a <table> serves as a min-height
6800
- },
6801
- }, arg.tableColGroupNode, createElement(isHeader ? 'thead' : 'tbody', {
6802
- role: 'presentation',
6803
- }, typeof chunkConfig.rowContent === 'function'
6804
- ? chunkConfig.rowContent(arg)
6805
- : chunkConfig.rowContent));
6806
- return content;
6807
- }
6808
- function isColPropsEqual(cols0, cols1) {
6809
- return isArraysEqual(cols0, cols1, isPropsEqual);
6810
- }
6811
- function renderMicroColGroup(cols, shrinkWidth) {
6812
- let colNodes = [];
6813
- /*
6814
- for ColProps with spans, it would have been great to make a single <col span="">
6815
- HOWEVER, Chrome was getting messing up distributing the width to <td>/<th> elements with colspans.
6816
- SOLUTION: making individual <col> elements makes Chrome behave.
6817
- */
6818
- for (let colProps of cols) {
6819
- let span = colProps.span || 1;
6820
- for (let i = 0; i < span; i += 1) {
6821
- colNodes.push(createElement("col", { style: {
6822
- width: colProps.width === 'shrink' ? sanitizeShrinkWidth(shrinkWidth) : (colProps.width || ''),
6823
- minWidth: colProps.minWidth || '',
6824
- } }));
6825
- }
6826
- }
6827
- return createElement('colgroup', {}, ...colNodes);
6828
- }
6829
- function sanitizeShrinkWidth(shrinkWidth) {
6830
- /* why 4? if we do 0, it will kill any border, which are needed for computeSmallestCellWidth
6831
- 4 accounts for 2 2-pixel borders. TODO: better solution? */
6832
- return shrinkWidth == null ? 4 : shrinkWidth;
6833
- }
6834
- function hasShrinkWidth(cols) {
6835
- for (let col of cols) {
6836
- if (col.width === 'shrink') {
6837
- return true;
6838
- }
6839
- }
6840
- return false;
6841
- }
6842
- function getScrollGridClassNames(liquid, context) {
6843
- let classNames = [
6844
- 'fc-scrollgrid',
6845
- context.theme.getClass('table'),
6846
- ];
6847
- if (liquid) {
6848
- classNames.push('fc-scrollgrid-liquid');
6725
+ class NowTimer extends Component {
6726
+ constructor(props, context) {
6727
+ super(props, context);
6728
+ this.initialNowDate = getNow(context.options.now, context.dateEnv);
6729
+ this.initialNowQueriedMs = new Date().valueOf();
6730
+ this.state = this.computeTiming().currentState;
6849
6731
  }
6850
- return classNames;
6851
- }
6852
- function getSectionClassNames(sectionConfig, wholeTableVGrow) {
6853
- let classNames = [
6854
- 'fc-scrollgrid-section',
6855
- `fc-scrollgrid-section-${sectionConfig.type}`,
6856
- sectionConfig.className, // used?
6857
- ];
6858
- if (wholeTableVGrow && sectionConfig.liquid && sectionConfig.maxHeight == null) {
6859
- classNames.push('fc-scrollgrid-section-liquid');
6732
+ render() {
6733
+ let { props, state } = this;
6734
+ return props.children(state.nowDate, state.todayRange);
6860
6735
  }
6861
- if (sectionConfig.isSticky) {
6862
- classNames.push('fc-scrollgrid-section-sticky');
6736
+ componentDidMount() {
6737
+ this.setTimeout();
6863
6738
  }
6864
- return classNames;
6865
- }
6866
- function renderScrollShim(arg) {
6867
- return (createElement("div", { className: "fc-scrollgrid-sticky-shim", style: {
6868
- width: arg.clientWidth,
6869
- minWidth: arg.tableMinWidth,
6870
- } }));
6871
- }
6872
- function getStickyHeaderDates(options) {
6873
- let { stickyHeaderDates } = options;
6874
- if (stickyHeaderDates == null || stickyHeaderDates === 'auto') {
6875
- stickyHeaderDates = options.height === 'auto' || options.viewHeight === 'auto';
6739
+ componentDidUpdate(prevProps) {
6740
+ if (prevProps.unit !== this.props.unit) {
6741
+ this.clearTimeout();
6742
+ this.setTimeout();
6743
+ }
6876
6744
  }
6877
- return stickyHeaderDates;
6878
- }
6879
- function getStickyFooterScrollbar(options) {
6880
- let { stickyFooterScrollbar } = options;
6881
- if (stickyFooterScrollbar == null || stickyFooterScrollbar === 'auto') {
6882
- stickyFooterScrollbar = options.height === 'auto' || options.viewHeight === 'auto';
6745
+ componentWillUnmount() {
6746
+ this.clearTimeout();
6883
6747
  }
6884
- return stickyFooterScrollbar;
6885
- }
6886
-
6887
- class SimpleScrollGrid extends BaseComponent {
6888
- constructor() {
6889
- super(...arguments);
6890
- this.processCols = memoize((a) => a, isColPropsEqual); // so we get same `cols` props every time
6891
- // yucky to memoize VNodes, but much more efficient for consumers
6892
- this.renderMicroColGroup = memoize(renderMicroColGroup);
6893
- this.scrollerRefs = new RefMap();
6894
- this.scrollerElRefs = new RefMap(this._handleScrollerEl.bind(this));
6895
- this.state = {
6896
- shrinkWidth: null,
6897
- forceYScrollbars: false,
6898
- scrollerClientWidths: {},
6899
- scrollerClientHeights: {},
6900
- };
6901
- // TODO: can do a really simple print-view. dont need to join rows
6902
- this.handleSizing = () => {
6903
- this.safeSetState(Object.assign({ shrinkWidth: this.computeShrinkWidth() }, this.computeScrollerDims()));
6748
+ computeTiming() {
6749
+ let { props, context } = this;
6750
+ let unroundedNow = addMs(this.initialNowDate, new Date().valueOf() - this.initialNowQueriedMs);
6751
+ let currentUnitStart = context.dateEnv.startOf(unroundedNow, props.unit);
6752
+ let nextUnitStart = context.dateEnv.add(currentUnitStart, createDuration(1, props.unit));
6753
+ let waitMs = nextUnitStart.valueOf() - unroundedNow.valueOf();
6754
+ // there is a max setTimeout ms value (https://stackoverflow.com/a/3468650/96342)
6755
+ // ensure no longer than a day
6756
+ waitMs = Math.min(1000 * 60 * 60 * 24, waitMs);
6757
+ return {
6758
+ currentState: { nowDate: currentUnitStart, todayRange: buildDayRange(currentUnitStart) },
6759
+ nextState: { nowDate: nextUnitStart, todayRange: buildDayRange(nextUnitStart) },
6760
+ waitMs,
6904
6761
  };
6905
6762
  }
6906
- render() {
6907
- let { props, state, context } = this;
6908
- let sectionConfigs = props.sections || [];
6909
- let cols = this.processCols(props.cols);
6910
- let microColGroupNode = this.renderMicroColGroup(cols, state.shrinkWidth);
6911
- let classNames = getScrollGridClassNames(props.liquid, context);
6912
- if (props.collapsibleWidth) {
6913
- classNames.push('fc-scrollgrid-collapsible');
6914
- }
6915
- // TODO: make DRY
6916
- let configCnt = sectionConfigs.length;
6917
- let configI = 0;
6918
- let currentConfig;
6919
- let headSectionNodes = [];
6920
- let bodySectionNodes = [];
6921
- let footSectionNodes = [];
6922
- while (configI < configCnt && (currentConfig = sectionConfigs[configI]).type === 'header') {
6923
- headSectionNodes.push(this.renderSection(currentConfig, microColGroupNode, true));
6924
- configI += 1;
6925
- }
6926
- while (configI < configCnt && (currentConfig = sectionConfigs[configI]).type === 'body') {
6927
- bodySectionNodes.push(this.renderSection(currentConfig, microColGroupNode, false));
6928
- configI += 1;
6929
- }
6930
- while (configI < configCnt && (currentConfig = sectionConfigs[configI]).type === 'footer') {
6931
- footSectionNodes.push(this.renderSection(currentConfig, microColGroupNode, true));
6932
- configI += 1;
6933
- }
6934
- // firefox bug: when setting height on table and there is a thead or tfoot,
6935
- // the necessary height:100% on the liquid-height body section forces the *whole* table to be taller. (bug #5524)
6936
- // use getCanVGrowWithinCell as a way to detect table-stupid firefox.
6937
- // if so, use a simpler dom structure, jam everything into a lone tbody.
6938
- let isBuggy = !getCanVGrowWithinCell();
6939
- const roleAttrs = { role: 'rowgroup' };
6940
- return createElement('table', {
6941
- role: 'grid',
6942
- className: classNames.join(' '),
6943
- style: { height: props.height },
6944
- }, Boolean(!isBuggy && headSectionNodes.length) && createElement('thead', roleAttrs, ...headSectionNodes), Boolean(!isBuggy && bodySectionNodes.length) && createElement('tbody', roleAttrs, ...bodySectionNodes), Boolean(!isBuggy && footSectionNodes.length) && createElement('tfoot', roleAttrs, ...footSectionNodes), isBuggy && createElement('tbody', roleAttrs, ...headSectionNodes, ...bodySectionNodes, ...footSectionNodes));
6945
- }
6946
- renderSection(sectionConfig, microColGroupNode, isHeader) {
6947
- if ('outerContent' in sectionConfig) {
6948
- return (createElement(Fragment, { key: sectionConfig.key }, sectionConfig.outerContent));
6949
- }
6950
- return (createElement("tr", { key: sectionConfig.key, role: "presentation", className: getSectionClassNames(sectionConfig, this.props.liquid).join(' ') }, this.renderChunkTd(sectionConfig, microColGroupNode, sectionConfig.chunk, isHeader)));
6951
- }
6952
- renderChunkTd(sectionConfig, microColGroupNode, chunkConfig, isHeader) {
6953
- if ('outerContent' in chunkConfig) {
6954
- return chunkConfig.outerContent;
6955
- }
6956
- let { props } = this;
6957
- let { forceYScrollbars, scrollerClientWidths, scrollerClientHeights } = this.state;
6958
- let needsYScrolling = getAllowYScrolling(props, sectionConfig); // TODO: do lazily. do in section config?
6959
- let isLiquid = getSectionHasLiquidHeight(props, sectionConfig);
6960
- // for `!props.liquid` - is WHOLE scrollgrid natural height?
6961
- // TODO: do same thing in advanced scrollgrid? prolly not b/c always has horizontal scrollbars
6962
- let overflowY = !props.liquid ? 'visible' :
6963
- forceYScrollbars ? 'scroll' :
6964
- !needsYScrolling ? 'hidden' :
6965
- 'auto';
6966
- let sectionKey = sectionConfig.key;
6967
- let content = renderChunkContent(sectionConfig, chunkConfig, {
6968
- tableColGroupNode: microColGroupNode,
6969
- tableMinWidth: '',
6970
- clientWidth: (!props.collapsibleWidth && scrollerClientWidths[sectionKey] !== undefined) ? scrollerClientWidths[sectionKey] : null,
6971
- clientHeight: scrollerClientHeights[sectionKey] !== undefined ? scrollerClientHeights[sectionKey] : null,
6972
- expandRows: sectionConfig.expandRows,
6973
- syncRowHeights: false,
6974
- rowSyncHeights: [],
6975
- reportRowHeightChange: () => { },
6976
- }, isHeader);
6977
- return createElement(isHeader ? 'th' : 'td', {
6978
- ref: chunkConfig.elRef,
6979
- role: 'presentation',
6980
- }, createElement("div", { className: `fc-scroller-harness${isLiquid ? ' fc-scroller-harness-liquid' : ''}` },
6981
- 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
6982
- : true }, content)));
6983
- }
6984
- _handleScrollerEl(scrollerEl, key) {
6985
- let section = getSectionByKey(this.props.sections, key);
6986
- if (section) {
6987
- setRef(section.chunk.scrollerElRef, scrollerEl);
6988
- }
6989
- }
6990
- componentDidMount() {
6991
- this.handleSizing();
6992
- this.context.addResizeHandler(this.handleSizing);
6993
- }
6994
- componentDidUpdate() {
6995
- // TODO: need better solution when state contains non-sizing things
6996
- this.handleSizing();
6763
+ setTimeout() {
6764
+ let { nextState, waitMs } = this.computeTiming();
6765
+ this.timeoutId = setTimeout(() => {
6766
+ this.setState(nextState, () => {
6767
+ this.setTimeout();
6768
+ });
6769
+ }, waitMs);
6997
6770
  }
6998
- componentWillUnmount() {
6999
- this.context.removeResizeHandler(this.handleSizing);
7000
- }
7001
- computeShrinkWidth() {
7002
- return hasShrinkWidth(this.props.cols)
7003
- ? computeShrinkWidth(this.scrollerElRefs.getAll())
7004
- : 0;
7005
- }
7006
- computeScrollerDims() {
7007
- let scrollbarWidth = getScrollbarWidths();
7008
- let { scrollerRefs, scrollerElRefs } = this;
7009
- let forceYScrollbars = false;
7010
- let scrollerClientWidths = {};
7011
- let scrollerClientHeights = {};
7012
- for (let sectionKey in scrollerRefs.currentMap) {
7013
- let scroller = scrollerRefs.currentMap[sectionKey];
7014
- if (scroller && scroller.needsYScrolling()) {
7015
- forceYScrollbars = true;
7016
- break;
7017
- }
7018
- }
7019
- for (let section of this.props.sections) {
7020
- let sectionKey = section.key;
7021
- let scrollerEl = scrollerElRefs.currentMap[sectionKey];
7022
- if (scrollerEl) {
7023
- let harnessEl = scrollerEl.parentNode; // TODO: weird way to get this. need harness b/c doesn't include table borders
7024
- scrollerClientWidths[sectionKey] = Math.floor(harnessEl.getBoundingClientRect().width - (forceYScrollbars
7025
- ? scrollbarWidth.y // use global because scroller might not have scrollbars yet but will need them in future
7026
- : 0));
7027
- scrollerClientHeights[sectionKey] = Math.floor(harnessEl.getBoundingClientRect().height);
7028
- }
6771
+ clearTimeout() {
6772
+ if (this.timeoutId) {
6773
+ clearTimeout(this.timeoutId);
7029
6774
  }
7030
- return { forceYScrollbars, scrollerClientWidths, scrollerClientHeights };
7031
6775
  }
7032
6776
  }
7033
- SimpleScrollGrid.addStateEquality({
7034
- scrollerClientWidths: isPropsEqual,
7035
- scrollerClientHeights: isPropsEqual,
7036
- });
7037
- function getSectionByKey(sections, key) {
7038
- for (let section of sections) {
7039
- if (section.key === key) {
7040
- return section;
7041
- }
7042
- }
7043
- return null;
6777
+ NowTimer.contextType = ViewContextType;
6778
+ function buildDayRange(date) {
6779
+ let start = startOfDay(date);
6780
+ let end = addDays(start, 1);
6781
+ return { start, end };
7044
6782
  }
7045
6783
 
7046
6784
  class EventContainer extends BaseComponent {
@@ -7049,15 +6787,14 @@ class EventContainer extends BaseComponent {
7049
6787
  this.handleEl = (el) => {
7050
6788
  this.el = el;
7051
6789
  if (el) {
7052
- setElSeg(el, this.props.seg);
6790
+ setElEventRange(el, this.props.eventRange);
7053
6791
  }
7054
6792
  };
7055
6793
  }
7056
6794
  render() {
7057
6795
  const { props, context } = this;
7058
6796
  const { options } = context;
7059
- const { seg } = props;
7060
- const { eventRange } = seg;
6797
+ const { eventRange } = props;
7061
6798
  const { ui } = eventRange;
7062
6799
  const renderProps = {
7063
6800
  event: new EventImpl(context, eventRange.def, eventRange.instance),
@@ -7066,12 +6803,12 @@ class EventContainer extends BaseComponent {
7066
6803
  textColor: ui.textColor,
7067
6804
  backgroundColor: ui.backgroundColor,
7068
6805
  borderColor: ui.borderColor,
7069
- isDraggable: !props.disableDragging && computeSegDraggable(seg, context),
7070
- isStartResizable: !props.disableResizing && computeSegStartResizable(seg, context),
7071
- isEndResizable: !props.disableResizing && computeSegEndResizable(seg),
6806
+ isDraggable: !props.disableDragging && computeEventRangeDraggable(eventRange, context),
6807
+ isStartResizable: !props.disableResizing && props.isStart && eventRange.ui.durationEditable && options.eventResizableFromStart,
6808
+ isEndResizable: !props.disableResizing && props.isEnd && eventRange.ui.durationEditable,
7072
6809
  isMirror: Boolean(props.isDragging || props.isResizing || props.isDateSelecting),
7073
- isStart: Boolean(seg.isStart),
7074
- isEnd: Boolean(seg.isEnd),
6810
+ isStart: Boolean(props.isStart),
6811
+ isEnd: Boolean(props.isEnd),
7075
6812
  isPast: Boolean(props.isPast),
7076
6813
  isFuture: Boolean(props.isFuture),
7077
6814
  isToday: Boolean(props.isToday),
@@ -7081,13 +6818,13 @@ class EventContainer extends BaseComponent {
7081
6818
  };
7082
6819
  return (createElement(ContentContainer, Object.assign({}, props /* contains children */, { elRef: this.handleEl, elClasses: [
7083
6820
  ...getEventClassNames(renderProps),
7084
- ...seg.eventRange.ui.classNames,
6821
+ ...eventRange.ui.classNames,
7085
6822
  ...(props.elClasses || []),
7086
6823
  ], renderProps: renderProps, generatorName: "eventContent", customGenerator: options.eventContent, defaultGenerator: props.defaultGenerator, classNameGenerator: options.eventClassNames, didMount: options.eventDidMount, willUnmount: options.eventWillUnmount })));
7087
6824
  }
7088
6825
  componentDidUpdate(prevProps) {
7089
- if (this.el && this.props.seg !== prevProps.seg) {
7090
- setElSeg(this.el, this.props.seg);
6826
+ if (this.el && this.props.eventRange !== prevProps.eventRange) {
6827
+ setElEventRange(this.el, this.props.eventRange);
7091
6828
  }
7092
6829
  }
7093
6830
  }
@@ -7097,24 +6834,24 @@ class StandardEvent extends BaseComponent {
7097
6834
  render() {
7098
6835
  let { props, context } = this;
7099
6836
  let { options } = context;
7100
- let { seg } = props;
7101
- let { ui } = seg.eventRange;
6837
+ let { eventRange } = props;
6838
+ let { ui } = eventRange;
7102
6839
  let timeFormat = options.eventTimeFormat || props.defaultTimeFormat;
7103
- let timeText = buildSegTimeText(seg, timeFormat, context, props.defaultDisplayEventTime, props.defaultDisplayEventEnd);
6840
+ let timeText = buildEventRangeTimeText(eventRange, timeFormat, context, props.defaultDisplayEventTime, props.defaultDisplayEventEnd, props.startOverride, props.endOverride);
7104
6841
  return (createElement(EventContainer, Object.assign({}, props /* includes elRef */, { elTag: "a", elStyle: {
7105
6842
  borderColor: ui.borderColor,
7106
6843
  backgroundColor: ui.backgroundColor,
7107
- }, elAttrs: getSegAnchorAttrs(seg, context), defaultGenerator: renderInnerContent$1, timeText: timeText }), (InnerContent, eventContentArg) => (createElement(Fragment, null,
7108
- createElement(InnerContent, { elTag: "div", elClasses: ['fc-event-main'], elStyle: { color: eventContentArg.textColor } }),
6844
+ }, elAttrs: getEventRangeAnchorAttrs(eventRange, context), defaultGenerator: renderInnerContent$1, timeText: timeText }), (InnerContent, eventContentArg) => (createElement(Fragment, null,
6845
+ createElement(InnerContent, { elTag: "div", elClasses: ['fc-event-inner'], elStyle: { color: eventContentArg.textColor } }),
7109
6846
  Boolean(eventContentArg.isStartResizable) && (createElement("div", { className: "fc-event-resizer fc-event-resizer-start" })),
7110
6847
  Boolean(eventContentArg.isEndResizable) && (createElement("div", { className: "fc-event-resizer fc-event-resizer-end" }))))));
7111
6848
  }
7112
6849
  }
7113
6850
  function renderInnerContent$1(innerProps) {
7114
- return (createElement("div", { className: "fc-event-main-frame" },
6851
+ return (createElement(Fragment, null,
7115
6852
  innerProps.timeText && (createElement("div", { className: "fc-event-time" }, innerProps.timeText)),
7116
- createElement("div", { className: "fc-event-title-container" },
7117
- createElement("div", { className: "fc-event-title fc-sticky" }, innerProps.event.title || createElement(Fragment, null, "\u00A0")))));
6853
+ createElement("div", { className: "fc-event-title-outer" },
6854
+ createElement("div", { className: "fc-event-title" }, innerProps.event.title || createElement(Fragment, null, "\u00A0")))));
7118
6855
  }
7119
6856
 
7120
6857
  const NowIndicatorContainer = (props) => (createElement(ViewContextType.Consumer, null, (context) => {
@@ -7127,50 +6864,11 @@ const NowIndicatorContainer = (props) => (createElement(ViewContextType.Consumer
7127
6864
  return (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 })));
7128
6865
  }));
7129
6866
 
7130
- const DAY_NUM_FORMAT = createFormatter({ day: 'numeric' });
7131
- class DayCellContainer extends BaseComponent {
7132
- constructor() {
7133
- super(...arguments);
7134
- this.refineRenderProps = memoizeObjArg(refineRenderProps);
7135
- }
7136
- render() {
7137
- let { props, context } = this;
7138
- let { options } = context;
7139
- let renderProps = this.refineRenderProps({
7140
- date: props.date,
7141
- dateProfile: props.dateProfile,
7142
- todayRange: props.todayRange,
7143
- isMonthStart: props.isMonthStart || false,
7144
- showDayNumber: props.showDayNumber,
7145
- extraRenderProps: props.extraRenderProps,
7146
- viewApi: context.viewApi,
7147
- dateEnv: context.dateEnv,
7148
- monthStartFormat: options.monthStartFormat,
7149
- });
7150
- return (createElement(ContentContainer, Object.assign({}, props /* includes children */, { elClasses: [
7151
- ...getDayClassNames(renderProps, context.theme),
7152
- ...(props.elClasses || []),
7153
- ], 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:
7154
- // don't use custom classNames if disabled
7155
- renderProps.isDisabled ? undefined : options.dayCellClassNames, didMount: options.dayCellDidMount, willUnmount: options.dayCellWillUnmount })));
7156
- }
7157
- }
7158
- function hasCustomDayCellContent(options) {
7159
- return Boolean(options.dayCellContent || hasCustomRenderingHandler('dayCellContent', options));
7160
- }
7161
- function refineRenderProps(raw) {
7162
- let { date, dateEnv, dateProfile, isMonthStart } = raw;
7163
- let dayMeta = getDateMeta(date, raw.todayRange, null, dateProfile);
7164
- let dayNumberText = raw.showDayNumber ? (dateEnv.format(date, isMonthStart ? raw.monthStartFormat : DAY_NUM_FORMAT)) : '';
7165
- return Object.assign(Object.assign(Object.assign({ date: dateEnv.toDate(date), view: raw.viewApi }, dayMeta), { isMonthStart,
7166
- dayNumberText }), raw.extraRenderProps);
7167
- }
7168
-
7169
- class BgEvent extends BaseComponent {
6867
+ class BgEvent extends BaseComponent {
7170
6868
  render() {
7171
6869
  let { props } = this;
7172
- let { seg } = props;
7173
- return (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 }));
6870
+ let { eventRange } = props;
6871
+ return (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 }));
7174
6872
  }
7175
6873
  }
7176
6874
  function renderInnerContent(props) {
@@ -7195,250 +6893,384 @@ function renderInner(innerProps) {
7195
6893
  return innerProps.text;
7196
6894
  }
7197
6895
 
7198
- const PADDING_FROM_VIEWPORT = 10;
7199
- class Popover extends BaseComponent {
7200
- constructor() {
7201
- super(...arguments);
7202
- this.state = {
7203
- titleId: getUniqueDomId(),
7204
- };
7205
- this.handleRootEl = (el) => {
7206
- this.rootEl = el;
7207
- if (this.props.elRef) {
7208
- setRef(this.props.elRef, el);
7209
- }
7210
- };
7211
- // Triggered when the user clicks *anywhere* in the document, for the autoHide feature
7212
- this.handleDocumentMouseDown = (ev) => {
7213
- // only hide the popover if the click happened outside the popover
7214
- const target = getEventTargetViaRoot(ev);
7215
- if (!this.rootEl.contains(target)) {
7216
- this.handleCloseClick();
6896
+ /*
6897
+ Calendar instance for ALL frameworks
6898
+ */
6899
+ class CalendarImpl {
6900
+ getCurrentData() {
6901
+ return this.currentDataManager.getCurrentData();
6902
+ }
6903
+ dispatch(action) {
6904
+ this.currentDataManager.dispatch(action);
6905
+ }
6906
+ get view() { return this.getCurrentData().viewApi; }
6907
+ batchRendering(callback) {
6908
+ callback();
6909
+ }
6910
+ updateSize() {
6911
+ console.warn('Doesnt do anything!');
6912
+ }
6913
+ // Options
6914
+ // -----------------------------------------------------------------------------------------------------------------
6915
+ setOption(name, val) {
6916
+ this.dispatch({
6917
+ type: 'SET_OPTION',
6918
+ optionName: name,
6919
+ rawOptionValue: val,
6920
+ });
6921
+ }
6922
+ getOption(name) {
6923
+ return this.currentDataManager.currentCalendarOptionsInput[name];
6924
+ }
6925
+ getAvailableLocaleCodes() {
6926
+ return Object.keys(this.getCurrentData().availableRawLocales);
6927
+ }
6928
+ // Trigger
6929
+ // -----------------------------------------------------------------------------------------------------------------
6930
+ on(handlerName, handler) {
6931
+ let { currentDataManager } = this;
6932
+ if (currentDataManager.currentCalendarOptionsRefiners[handlerName]) {
6933
+ currentDataManager.emitter.on(handlerName, handler);
6934
+ }
6935
+ else {
6936
+ console.warn(`Unknown listener name '${handlerName}'`);
6937
+ }
6938
+ }
6939
+ off(handlerName, handler) {
6940
+ this.currentDataManager.emitter.off(handlerName, handler);
6941
+ }
6942
+ // not meant for public use
6943
+ trigger(handlerName, ...args) {
6944
+ this.currentDataManager.emitter.trigger(handlerName, ...args);
6945
+ }
6946
+ // View
6947
+ // -----------------------------------------------------------------------------------------------------------------
6948
+ changeView(viewType, dateOrRange) {
6949
+ this.batchRendering(() => {
6950
+ this.unselect();
6951
+ if (dateOrRange) {
6952
+ if (dateOrRange.start && dateOrRange.end) { // a range
6953
+ this.dispatch({
6954
+ type: 'CHANGE_VIEW_TYPE',
6955
+ viewType,
6956
+ });
6957
+ this.dispatch({
6958
+ type: 'SET_OPTION',
6959
+ optionName: 'visibleRange',
6960
+ rawOptionValue: dateOrRange,
6961
+ });
6962
+ }
6963
+ else {
6964
+ let { dateEnv } = this.getCurrentData();
6965
+ this.dispatch({
6966
+ type: 'CHANGE_VIEW_TYPE',
6967
+ viewType,
6968
+ dateMarker: dateEnv.createMarker(dateOrRange),
6969
+ });
6970
+ }
7217
6971
  }
7218
- };
7219
- this.handleDocumentKeyDown = (ev) => {
7220
- if (ev.key === 'Escape') {
7221
- this.handleCloseClick();
6972
+ else {
6973
+ this.dispatch({
6974
+ type: 'CHANGE_VIEW_TYPE',
6975
+ viewType,
6976
+ });
7222
6977
  }
7223
- };
7224
- this.handleCloseClick = () => {
7225
- let { onClose } = this.props;
7226
- if (onClose) {
7227
- onClose();
6978
+ });
6979
+ }
6980
+ // Forces navigation to a view for the given date.
6981
+ // `viewType` can be a specific view name or a generic one like "week" or "day".
6982
+ // needs to change
6983
+ zoomTo(dateMarker, viewType) {
6984
+ let state = this.getCurrentData();
6985
+ let spec;
6986
+ viewType = viewType || 'day'; // day is default zoom
6987
+ spec = state.viewSpecs[viewType] || this.getUnitViewSpec(viewType);
6988
+ this.unselect();
6989
+ if (spec) {
6990
+ this.dispatch({
6991
+ type: 'CHANGE_VIEW_TYPE',
6992
+ viewType: spec.type,
6993
+ dateMarker,
6994
+ });
6995
+ }
6996
+ else {
6997
+ this.dispatch({
6998
+ type: 'CHANGE_DATE',
6999
+ dateMarker,
7000
+ });
7001
+ }
7002
+ }
7003
+ // Given a duration singular unit, like "week" or "day", finds a matching view spec.
7004
+ // Preference is given to views that have corresponding buttons.
7005
+ getUnitViewSpec(unit) {
7006
+ let { viewSpecs, toolbarConfig } = this.getCurrentData();
7007
+ let viewTypes = [].concat(toolbarConfig.header ? toolbarConfig.header.viewsWithButtons : [], toolbarConfig.footer ? toolbarConfig.footer.viewsWithButtons : []);
7008
+ let i;
7009
+ let spec;
7010
+ for (let viewType in viewSpecs) {
7011
+ viewTypes.push(viewType);
7012
+ }
7013
+ for (i = 0; i < viewTypes.length; i += 1) {
7014
+ spec = viewSpecs[viewTypes[i]];
7015
+ if (spec) {
7016
+ if (spec.singleUnit === unit) {
7017
+ return spec;
7018
+ }
7228
7019
  }
7229
- };
7020
+ }
7021
+ return null;
7230
7022
  }
7231
- render() {
7232
- let { theme, options } = this.context;
7233
- let { props, state } = this;
7234
- let classNames = [
7235
- 'fc-popover',
7236
- theme.getClass('popover'),
7237
- ].concat(props.extraClassNames || []);
7238
- return createPortal(createElement("div", Object.assign({}, props.extraAttrs, { id: props.id, className: classNames.join(' '), "aria-labelledby": state.titleId, ref: this.handleRootEl }),
7239
- createElement("div", { className: 'fc-popover-header ' + theme.getClass('popoverHeader') },
7240
- createElement("span", { className: "fc-popover-title", id: state.titleId }, props.title),
7241
- createElement("span", { className: 'fc-popover-close ' + theme.getIconClass('close'), title: options.closeHint, onClick: this.handleCloseClick })),
7242
- createElement("div", { className: 'fc-popover-body ' + theme.getClass('popoverContent') }, props.children)), props.parentEl);
7023
+ // Current Date
7024
+ // -----------------------------------------------------------------------------------------------------------------
7025
+ prev() {
7026
+ this.unselect();
7027
+ this.dispatch({ type: 'PREV' });
7243
7028
  }
7244
- componentDidMount() {
7245
- document.addEventListener('mousedown', this.handleDocumentMouseDown);
7246
- document.addEventListener('keydown', this.handleDocumentKeyDown);
7247
- this.updateSize();
7029
+ next() {
7030
+ this.unselect();
7031
+ this.dispatch({ type: 'NEXT' });
7248
7032
  }
7249
- componentWillUnmount() {
7250
- document.removeEventListener('mousedown', this.handleDocumentMouseDown);
7251
- document.removeEventListener('keydown', this.handleDocumentKeyDown);
7033
+ prevYear() {
7034
+ let state = this.getCurrentData();
7035
+ this.unselect();
7036
+ this.dispatch({
7037
+ type: 'CHANGE_DATE',
7038
+ dateMarker: state.dateEnv.addYears(state.currentDate, -1),
7039
+ });
7252
7040
  }
7253
- updateSize() {
7254
- let { isRtl } = this.context;
7255
- let { alignmentEl, alignGridTop } = this.props;
7256
- let { rootEl } = this;
7257
- let alignmentRect = computeClippedClientRect(alignmentEl);
7258
- if (alignmentRect) {
7259
- let popoverDims = rootEl.getBoundingClientRect();
7260
- // position relative to viewport
7261
- let popoverTop = alignGridTop
7262
- ? elementClosest(alignmentEl, '.fc-scrollgrid').getBoundingClientRect().top
7263
- : alignmentRect.top;
7264
- let popoverLeft = isRtl ? alignmentRect.right - popoverDims.width : alignmentRect.left;
7265
- // constrain
7266
- popoverTop = Math.max(popoverTop, PADDING_FROM_VIEWPORT);
7267
- popoverLeft = Math.min(popoverLeft, document.documentElement.clientWidth - PADDING_FROM_VIEWPORT - popoverDims.width);
7268
- popoverLeft = Math.max(popoverLeft, PADDING_FROM_VIEWPORT);
7269
- let origin = rootEl.offsetParent.getBoundingClientRect();
7270
- applyStyle(rootEl, {
7271
- top: popoverTop - origin.top,
7272
- left: popoverLeft - origin.left,
7041
+ nextYear() {
7042
+ let state = this.getCurrentData();
7043
+ this.unselect();
7044
+ this.dispatch({
7045
+ type: 'CHANGE_DATE',
7046
+ dateMarker: state.dateEnv.addYears(state.currentDate, 1),
7047
+ });
7048
+ }
7049
+ today() {
7050
+ let state = this.getCurrentData();
7051
+ this.unselect();
7052
+ this.dispatch({
7053
+ type: 'CHANGE_DATE',
7054
+ dateMarker: getNow(state.calendarOptions.now, state.dateEnv),
7055
+ });
7056
+ }
7057
+ gotoDate(zonedDateInput) {
7058
+ let state = this.getCurrentData();
7059
+ this.unselect();
7060
+ this.dispatch({
7061
+ type: 'CHANGE_DATE',
7062
+ dateMarker: state.dateEnv.createMarker(zonedDateInput),
7063
+ });
7064
+ }
7065
+ incrementDate(deltaInput) {
7066
+ let state = this.getCurrentData();
7067
+ let delta = createDuration(deltaInput);
7068
+ if (delta) { // else, warn about invalid input?
7069
+ this.unselect();
7070
+ this.dispatch({
7071
+ type: 'CHANGE_DATE',
7072
+ dateMarker: state.dateEnv.add(state.currentDate, delta),
7073
+ });
7074
+ }
7075
+ }
7076
+ getDate() {
7077
+ let state = this.getCurrentData();
7078
+ return state.dateEnv.toDate(state.currentDate);
7079
+ }
7080
+ // Date Formatting Utils
7081
+ // -----------------------------------------------------------------------------------------------------------------
7082
+ formatDate(d, formatter) {
7083
+ let { dateEnv } = this.getCurrentData();
7084
+ return dateEnv.format(dateEnv.createMarker(d), createFormatter(formatter));
7085
+ }
7086
+ // `settings` is for formatter AND isEndExclusive
7087
+ formatRange(d0, d1, settings) {
7088
+ let { dateEnv } = this.getCurrentData();
7089
+ return dateEnv.formatRange(dateEnv.createMarker(d0), dateEnv.createMarker(d1), createFormatter(settings), settings);
7090
+ }
7091
+ formatIso(d, omitTime) {
7092
+ let { dateEnv } = this.getCurrentData();
7093
+ return dateEnv.formatIso(dateEnv.createMarker(d), { omitTime });
7094
+ }
7095
+ // Date Selection / Event Selection / DayClick
7096
+ // -----------------------------------------------------------------------------------------------------------------
7097
+ select(dateOrObj, endDate) {
7098
+ let selectionInput;
7099
+ if (endDate == null) {
7100
+ if (dateOrObj.start != null) {
7101
+ selectionInput = dateOrObj;
7102
+ }
7103
+ else {
7104
+ selectionInput = {
7105
+ start: dateOrObj,
7106
+ end: null,
7107
+ };
7108
+ }
7109
+ }
7110
+ else {
7111
+ selectionInput = {
7112
+ start: dateOrObj,
7113
+ end: endDate,
7114
+ };
7115
+ }
7116
+ let state = this.getCurrentData();
7117
+ let selection = parseDateSpan(selectionInput, state.dateEnv, createDuration({ days: 1 }));
7118
+ if (selection) { // throw parse error otherwise?
7119
+ this.dispatch({ type: 'SELECT_DATES', selection });
7120
+ triggerDateSelect(selection, null, state);
7121
+ }
7122
+ }
7123
+ unselect(pev) {
7124
+ let state = this.getCurrentData();
7125
+ if (state.dateSelection) {
7126
+ this.dispatch({ type: 'UNSELECT_DATES' });
7127
+ triggerDateUnselect(pev, state);
7128
+ }
7129
+ }
7130
+ // Public Events API
7131
+ // -----------------------------------------------------------------------------------------------------------------
7132
+ addEvent(eventInput, sourceInput) {
7133
+ if (eventInput instanceof EventImpl) {
7134
+ let def = eventInput._def;
7135
+ let instance = eventInput._instance;
7136
+ let currentData = this.getCurrentData();
7137
+ // not already present? don't want to add an old snapshot
7138
+ if (!currentData.eventStore.defs[def.defId]) {
7139
+ this.dispatch({
7140
+ type: 'ADD_EVENTS',
7141
+ eventStore: eventTupleToStore({ def, instance }), // TODO: better util for two args?
7142
+ });
7143
+ this.triggerEventAdd(eventInput);
7144
+ }
7145
+ return eventInput;
7146
+ }
7147
+ let state = this.getCurrentData();
7148
+ let eventSource;
7149
+ if (sourceInput instanceof EventSourceImpl) {
7150
+ eventSource = sourceInput.internalEventSource;
7151
+ }
7152
+ else if (typeof sourceInput === 'boolean') {
7153
+ if (sourceInput) { // true. part of the first event source
7154
+ [eventSource] = hashValuesToArray(state.eventSources);
7155
+ }
7156
+ }
7157
+ else if (sourceInput != null) { // an ID. accepts a number too
7158
+ let sourceApi = this.getEventSourceById(sourceInput); // TODO: use an internal function
7159
+ if (!sourceApi) {
7160
+ console.warn(`Could not find an event source with ID "${sourceInput}"`); // TODO: test
7161
+ return null;
7162
+ }
7163
+ eventSource = sourceApi.internalEventSource;
7164
+ }
7165
+ let tuple = parseEvent(eventInput, eventSource, state, false);
7166
+ if (tuple) {
7167
+ let newEventApi = new EventImpl(state, tuple.def, tuple.def.recurringDef ? null : tuple.instance);
7168
+ this.dispatch({
7169
+ type: 'ADD_EVENTS',
7170
+ eventStore: eventTupleToStore(tuple),
7273
7171
  });
7172
+ this.triggerEventAdd(newEventApi);
7173
+ return newEventApi;
7274
7174
  }
7175
+ return null;
7275
7176
  }
7276
- }
7277
-
7278
- class MorePopover extends DateComponent {
7279
- constructor() {
7280
- super(...arguments);
7281
- this.handleRootEl = (rootEl) => {
7282
- this.rootEl = rootEl;
7283
- if (rootEl) {
7284
- this.context.registerInteractiveComponent(this, {
7285
- el: rootEl,
7286
- useEventCenter: false,
7177
+ triggerEventAdd(eventApi) {
7178
+ let { emitter } = this.getCurrentData();
7179
+ emitter.trigger('eventAdd', {
7180
+ event: eventApi,
7181
+ relatedEvents: [],
7182
+ revert: () => {
7183
+ this.dispatch({
7184
+ type: 'REMOVE_EVENTS',
7185
+ eventStore: eventApiToStore(eventApi),
7287
7186
  });
7187
+ },
7188
+ });
7189
+ }
7190
+ // TODO: optimize
7191
+ getEventById(id) {
7192
+ let state = this.getCurrentData();
7193
+ let { defs, instances } = state.eventStore;
7194
+ id = String(id);
7195
+ for (let defId in defs) {
7196
+ let def = defs[defId];
7197
+ if (def.publicId === id) {
7198
+ if (def.recurringDef) {
7199
+ return new EventImpl(state, def, null);
7200
+ }
7201
+ for (let instanceId in instances) {
7202
+ let instance = instances[instanceId];
7203
+ if (instance.defId === def.defId) {
7204
+ return new EventImpl(state, def, instance);
7205
+ }
7206
+ }
7288
7207
  }
7289
- else {
7290
- this.context.unregisterInteractiveComponent(this);
7291
- }
7292
- };
7208
+ }
7209
+ return null;
7293
7210
  }
7294
- render() {
7295
- let { options, dateEnv } = this.context;
7296
- let { props } = this;
7297
- let { startDate, todayRange, dateProfile } = props;
7298
- let title = dateEnv.format(startDate, options.dayPopoverFormat);
7299
- return (createElement(DayCellContainer, { elRef: this.handleRootEl, date: startDate, dateProfile: dateProfile, todayRange: todayRange }, (InnerContent, renderProps, elAttrs) => (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 },
7300
- hasCustomDayCellContent(options) && (createElement(InnerContent, { elTag: "div", elClasses: ['fc-more-popover-misc'] })),
7301
- props.children))));
7211
+ getEvents() {
7212
+ let currentData = this.getCurrentData();
7213
+ return buildEventApis(currentData.eventStore, currentData);
7302
7214
  }
7303
- queryHit(positionLeft, positionTop, elWidth, elHeight) {
7304
- let { rootEl, props } = this;
7305
- if (positionLeft >= 0 && positionLeft < elWidth &&
7306
- positionTop >= 0 && positionTop < elHeight) {
7307
- return {
7308
- dateProfile: props.dateProfile,
7309
- dateSpan: Object.assign({ allDay: !props.forceTimed, range: {
7310
- start: props.startDate,
7311
- end: props.endDate,
7312
- } }, props.extraDateSpan),
7313
- dayEl: rootEl,
7314
- rect: {
7315
- left: 0,
7316
- top: 0,
7317
- right: elWidth,
7318
- bottom: elHeight,
7319
- },
7320
- layer: 1, // important when comparing with hits from other components
7321
- };
7215
+ removeAllEvents() {
7216
+ this.dispatch({ type: 'REMOVE_ALL_EVENTS' });
7217
+ }
7218
+ // Public Event Sources API
7219
+ // -----------------------------------------------------------------------------------------------------------------
7220
+ getEventSources() {
7221
+ let state = this.getCurrentData();
7222
+ let sourceHash = state.eventSources;
7223
+ let sourceApis = [];
7224
+ for (let internalId in sourceHash) {
7225
+ sourceApis.push(new EventSourceImpl(state, sourceHash[internalId]));
7322
7226
  }
7323
- return null;
7227
+ return sourceApis;
7324
7228
  }
7325
- }
7326
-
7327
- class MoreLinkContainer extends BaseComponent {
7328
- constructor() {
7329
- super(...arguments);
7330
- this.state = {
7331
- isPopoverOpen: false,
7332
- popoverId: getUniqueDomId(),
7333
- };
7334
- this.handleLinkEl = (linkEl) => {
7335
- this.linkEl = linkEl;
7336
- if (this.props.elRef) {
7337
- setRef(this.props.elRef, linkEl);
7338
- }
7339
- };
7340
- this.handleClick = (ev) => {
7341
- let { props, context } = this;
7342
- let { moreLinkClick } = context.options;
7343
- let date = computeRange(props).start;
7344
- function buildPublicSeg(seg) {
7345
- let { def, instance, range } = seg.eventRange;
7346
- return {
7347
- event: new EventImpl(context, def, instance),
7348
- start: context.dateEnv.toDate(range.start),
7349
- end: context.dateEnv.toDate(range.end),
7350
- isStart: seg.isStart,
7351
- isEnd: seg.isEnd,
7352
- };
7229
+ getEventSourceById(id) {
7230
+ let state = this.getCurrentData();
7231
+ let sourceHash = state.eventSources;
7232
+ id = String(id);
7233
+ for (let sourceId in sourceHash) {
7234
+ if (sourceHash[sourceId].publicId === id) {
7235
+ return new EventSourceImpl(state, sourceHash[sourceId]);
7353
7236
  }
7354
- if (typeof moreLinkClick === 'function') {
7355
- moreLinkClick = moreLinkClick({
7356
- date,
7357
- allDay: Boolean(props.allDayDate),
7358
- allSegs: props.allSegs.map(buildPublicSeg),
7359
- hiddenSegs: props.hiddenSegs.map(buildPublicSeg),
7360
- jsEvent: ev,
7361
- view: context.viewApi,
7237
+ }
7238
+ return null;
7239
+ }
7240
+ addEventSource(sourceInput) {
7241
+ let state = this.getCurrentData();
7242
+ if (sourceInput instanceof EventSourceImpl) {
7243
+ // not already present? don't want to add an old snapshot
7244
+ if (!state.eventSources[sourceInput.internalEventSource.sourceId]) {
7245
+ this.dispatch({
7246
+ type: 'ADD_EVENT_SOURCES',
7247
+ sources: [sourceInput.internalEventSource],
7362
7248
  });
7363
7249
  }
7364
- if (!moreLinkClick || moreLinkClick === 'popover') {
7365
- this.setState({ isPopoverOpen: true });
7366
- }
7367
- else if (typeof moreLinkClick === 'string') { // a view name
7368
- context.calendarApi.zoomTo(date, moreLinkClick);
7369
- }
7370
- };
7371
- this.handlePopoverClose = () => {
7372
- this.setState({ isPopoverOpen: false });
7373
- };
7374
- }
7375
- render() {
7376
- let { props, state } = this;
7377
- return (createElement(ViewContextType.Consumer, null, (context) => {
7378
- let { viewApi, options, calendarApi } = context;
7379
- let { moreLinkText } = options;
7380
- let { moreCnt } = props;
7381
- let range = computeRange(props);
7382
- let text = typeof moreLinkText === 'function' // TODO: eventually use formatWithOrdinals
7383
- ? moreLinkText.call(calendarApi, moreCnt)
7384
- : `+${moreCnt} ${moreLinkText}`;
7385
- let hint = formatWithOrdinals(options.moreLinkHint, [moreCnt], text);
7386
- let renderProps = {
7387
- num: moreCnt,
7388
- shortText: `+${moreCnt}`,
7389
- text,
7390
- view: viewApi,
7391
- };
7392
- return (createElement(Fragment, null,
7393
- Boolean(props.moreCnt) && (createElement(ContentContainer, { elTag: props.elTag || 'a', elRef: this.handleLinkEl, elClasses: [
7394
- ...(props.elClasses || []),
7395
- 'fc-more-link',
7396
- ], 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)),
7397
- state.isPopoverOpen && (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 ?
7398
- props.alignmentElRef.current :
7399
- this.linkEl, alignGridTop: props.alignGridTop, forceTimed: props.forceTimed, onClose: this.handlePopoverClose }, props.popoverContent()))));
7400
- }));
7250
+ return sourceInput;
7251
+ }
7252
+ let eventSource = parseEventSource(sourceInput, state);
7253
+ if (eventSource) { // TODO: error otherwise?
7254
+ this.dispatch({ type: 'ADD_EVENT_SOURCES', sources: [eventSource] });
7255
+ return new EventSourceImpl(state, eventSource);
7256
+ }
7257
+ return null;
7401
7258
  }
7402
- componentDidMount() {
7403
- this.updateParentEl();
7259
+ removeAllEventSources() {
7260
+ this.dispatch({ type: 'REMOVE_ALL_EVENT_SOURCES' });
7404
7261
  }
7405
- componentDidUpdate() {
7406
- this.updateParentEl();
7262
+ refetchEvents() {
7263
+ this.dispatch({ type: 'FETCH_EVENT_SOURCES', isRefetch: true });
7407
7264
  }
7408
- updateParentEl() {
7409
- if (this.linkEl) {
7410
- this.parentEl = elementClosest(this.linkEl, '.fc-view-harness');
7265
+ // Scroll
7266
+ // -----------------------------------------------------------------------------------------------------------------
7267
+ scrollToTime(timeInput) {
7268
+ let time = createDuration(timeInput);
7269
+ if (time) {
7270
+ this.trigger('_timeScrollRequest', time);
7411
7271
  }
7412
7272
  }
7413
7273
  }
7414
- function renderMoreLinkInner(props) {
7415
- return props.text;
7416
- }
7417
- function computeRange(props) {
7418
- if (props.allDayDate) {
7419
- return {
7420
- start: props.allDayDate,
7421
- end: addDays(props.allDayDate, 1),
7422
- };
7423
- }
7424
- let { hiddenSegs } = props;
7425
- return {
7426
- start: computeEarliestSegStart(hiddenSegs),
7427
- end: computeLatestSegEnd(hiddenSegs),
7428
- };
7429
- }
7430
- function computeEarliestSegStart(segs) {
7431
- return segs.reduce(pickEarliestStart).eventRange.range.start;
7432
- }
7433
- function pickEarliestStart(seg0, seg1) {
7434
- return seg0.eventRange.range.start < seg1.eventRange.range.start ? seg0 : seg1;
7435
- }
7436
- function computeLatestSegEnd(segs) {
7437
- return segs.reduce(pickLatestEnd).eventRange.range.end;
7438
- }
7439
- function pickLatestEnd(seg0, seg1) {
7440
- return seg0.eventRange.range.end > seg1.eventRange.range.end ? seg0 : seg1;
7441
- }
7442
7274
 
7443
7275
  class Store {
7444
7276
  constructor() {
@@ -7484,4 +7316,4 @@ class CustomRenderingStore extends Store {
7484
7316
  }
7485
7317
  }
7486
7318
 
7487
- export { elementClosest as $, memoizeObjArg as A, BaseComponent as B, ContentContainer as C, DelayedRunner as D, isPropsEqual as E, Emitter as F, getInitialDate as G, rangeContainsMarker as H, createEmptyEventStore as I, reduceCurrentDate as J, reduceEventStore as K, rezoneEventStoreDates as L, mergeRawOptions as M, BASE_OPTION_REFINERS as N, CALENDAR_LISTENER_REFINERS as O, CALENDAR_OPTION_REFINERS as P, COMPLEX_OPTION_COMPARATORS as Q, VIEW_OPTION_REFINERS as R, DateEnv as S, Theme as T, DateProfileGenerator as U, ViewContextType as V, createEventUi as W, parseBusinessHours as X, setRef as Y, Interaction as Z, getElSeg as _, mapHash as a, getSlotClassNames as a$, EventImpl as a0, listenBySelector as a1, listenToHoverBySelector as a2, PureComponent as a3, buildViewContext as a4, getUniqueDomId as a5, parseInteractionSettings as a6, interactionSettingsStore as a7, getNow as a8, CalendarImpl as a9, diffDates as aA, removeExact as aB, memoizeArraylike as aC, memoizeHashlike as aD, intersectRects as aE, pointInsideRect as aF, constrainPoint as aG, getRectCenter as aH, diffPoints as aI, translateRect as aJ, compareObjs as aK, collectFromHash as aL, findElements as aM, findDirectChildren as aN, removeElement as aO, applyStyle as aP, elementMatches as aQ, getEventTargetViaRoot as aR, parseClassNames as aS, getCanVGrowWithinCell as aT, mergeEventStores as aU, getRelevantEvents as aV, eventTupleToStore as aW, combineEventUis as aX, Splitter as aY, getDayClassNames as aZ, getDateMeta as a_, flushSync as aa, CalendarRoot as ab, RenderId as ac, ensureElHasStyles as ad, applyStyleProp as ae, sliceEventStore as af, JsonRequestError as ag, createContext as ah, refineProps as ai, createEventInstance as aj, parseEventDef as ak, refineEventDef as al, padStart as am, isInt as an, parseFieldSpecs as ao, compareByFieldSpecs as ap, flexibleCompare as aq, preventSelection as ar, allowSelection as as, preventContextMenu as at, allowContextMenu as au, compareNumbers as av, enableCursor as aw, disableCursor as ax, computeVisibleDayRange as ay, isMultiDayRange as az, buildViewClassNames as b, SimpleScrollGrid as b$, buildNavLinkAttrs as b0, preventDefault as b1, whenTransitionDone as b2, computeInnerRect as b3, computeEdges as b4, getClippingParents as b5, computeRect as b6, rangesEqual as b7, rangesIntersect as b8, rangeContainsRange as b9, SegHierarchy as bA, buildEntryKey as bB, getEntrySpanEnd as bC, binarySearch as bD, groupIntersectingEntries as bE, intersectSpans as bF, interactionSettingsToStore as bG, ElementDragging as bH, config as bI, parseDragMeta as bJ, DayHeader as bK, computeFallbackHeaderFormat as bL, TableDateCell as bM, TableDowCell as bN, DaySeriesModel as bO, hasBgRendering as bP, buildSegTimeText as bQ, sortEventSegs as bR, getSegMeta as bS, buildEventRangeKey as bT, getSegAnchorAttrs as bU, DayTableModel as bV, Slicer as bW, applyMutationToEventStore as bX, isPropsValid as bY, isInteractionValid as bZ, isDateSelectionValid as b_, PositionCache as ba, ScrollController as bb, ElementScrollController as bc, WindowScrollController as bd, DateComponent as be, isDateSpansEqual as bf, addMs as bg, addWeeks as bh, diffWeeks as bi, diffWholeWeeks as bj, diffDayAndTime as bk, diffDays as bl, isValidDate as bm, asCleanDays as bn, multiplyDuration as bo, addDurations as bp, asRoughMinutes as bq, asRoughSeconds as br, asRoughMs as bs, wholeDivideDurations as bt, formatIsoTimeString as bu, formatDayString as bv, buildIsoString as bw, formatIsoMonthStr as bx, NamedTimeZoneImpl as by, parse as bz, greatestDurationDenominator as c, hasShrinkWidth as c0, renderMicroColGroup as c1, getScrollGridClassNames as c2, getSectionClassNames as c3, getSectionHasLiquidHeight as c4, getAllowYScrolling as c5, renderChunkContent as c6, computeShrinkWidth as c7, sanitizeShrinkWidth as c8, isColPropsEqual as c9, renderScrollShim as ca, getStickyFooterScrollbar as cb, getStickyHeaderDates as cc, Scroller as cd, getScrollbarWidths as ce, RefMap as cf, getIsRtlScrollbarOnLeft as cg, NowTimer as ch, ScrollResponder as ci, StandardEvent as cj, NowIndicatorContainer as ck, DayCellContainer as cl, hasCustomDayCellContent as cm, EventContainer as cn, renderFill as co, BgEvent as cp, WeekNumberContainer as cq, MoreLinkContainer as cr, computeEarliestSegStart as cs, ViewContainer as ct, triggerDateSelect as cu, getDefaultEventEnd as cv, injectStyles as cw, buildElAttrs as cx, CustomRenderingStore as cy, createDuration as d, BASE_OPTION_DEFAULTS as e, arrayToHash as f, guid as g, filterHash as h, isArraysEqual as i, buildEventSourceRefiners as j, formatWithOrdinals as k, buildRangeApiWithTimeZone as l, mergeProps as m, identity as n, intersectRanges as o, parseEventSource as p, startOfDay as q, requestJson as r, subtractDurations as s, addDays as t, unpromisify as u, hashValuesToArray as v, buildEventApis as w, createFormatter as x, diffWholeDays as y, memoize as z };
7319
+ export { EventImpl as $, memoizeObjArg as A, BaseComponent as B, ContentContainer as C, DelayedRunner as D, isPropsEqual as E, Emitter as F, getInitialDate as G, rangeContainsMarker as H, createEmptyEventStore as I, reduceCurrentDate as J, reduceEventStore as K, rezoneEventStoreDates as L, mergeRawOptions as M, BASE_OPTION_REFINERS as N, CALENDAR_LISTENER_REFINERS as O, CALENDAR_OPTION_REFINERS as P, COMPLEX_OPTION_COMPARATORS as Q, VIEW_OPTION_REFINERS as R, DateEnv as S, Theme as T, DateProfileGenerator as U, ViewContextType as V, createEventUi as W, parseBusinessHours as X, Interaction as Y, getElEventRange as Z, elementClosest as _, mapHash as a, getSlotClassNames as a$, listenBySelector as a0, listenToHoverBySelector as a1, PureComponent as a2, buildViewContext as a3, parseInteractionSettings as a4, interactionSettingsStore as a5, getNow as a6, getIsHeightAuto as a7, CalendarImpl as a8, flushSync as a9, removeExact as aA, memoizeArraylike as aB, memoizeHashlike as aC, intersectRects as aD, pointInsideRect as aE, constrainPoint as aF, getRectCenter as aG, diffPoints as aH, translateRect as aI, compareObjs as aJ, collectFromHash as aK, findElements as aL, findDirectChildren as aM, removeElement as aN, applyStyle as aO, elementMatches as aP, getEventTargetViaRoot as aQ, getUniqueDomId as aR, parseClassNames as aS, fracToCssDim as aT, mergeEventStores as aU, getRelevantEvents as aV, eventTupleToStore as aW, combineEventUis as aX, Splitter as aY, getDayClassNames as aZ, getDateMeta as a_, CalendarRoot as aa, RenderId as ab, ensureElHasStyles as ac, applyStyleProp as ad, sliceEventStore as ae, JsonRequestError as af, createContext as ag, refineProps as ah, createEventInstance as ai, parseEventDef as aj, refineEventDef as ak, padStart as al, isInt as am, parseFieldSpecs as an, compareByFieldSpecs as ao, flexibleCompare as ap, preventSelection as aq, allowSelection as ar, preventContextMenu as as, allowContextMenu as at, compareNumbers as au, enableCursor as av, disableCursor as aw, computeVisibleDayRange as ax, isMultiDayRange as ay, diffDates as az, buildViewClassNames as b, setNormalizedScrollX as b$, setStateDimMap as b0, isDimMapsEqual as b1, isDimsEqual as b2, watchSize as b3, watchWidth as b4, watchHeight as b5, afterSize as b6, buildNavLinkAttrs as b7, preventDefault as b8, whenTransitionDone as b9, wholeDivideDurations as bA, formatIsoTimeString as bB, formatDayString as bC, buildIsoString as bD, formatIsoMonthStr as bE, NamedTimeZoneImpl as bF, parse as bG, SegHierarchy as bH, buildEntryKey as bI, getEntrySpanEnd as bJ, binarySearch as bK, groupIntersectingEntries as bL, intersectSpans as bM, interactionSettingsToStore as bN, ElementDragging as bO, config as bP, parseDragMeta as bQ, DaySeriesModel as bR, hasBgRendering as bS, buildEventRangeTimeText as bT, sortEventSegs as bU, getEventRangeMeta as bV, buildEventRangeKey as bW, getEventRangeAnchorAttrs as bX, DayTableModel as bY, Scroller as bZ, getNormalizedScrollX as b_, computeInnerRect as ba, computeEdges as bb, getClippingParents as bc, computeRect as bd, rangesEqual as be, rangesIntersect as bf, rangeContainsRange as bg, PositionCache as bh, ScrollController as bi, ElementScrollController as bj, WindowScrollController as bk, DateComponent as bl, isDateSpansEqual as bm, addMs as bn, addWeeks as bo, diffWeeks as bp, diffWholeWeeks as bq, diffDayAndTime as br, diffDays as bs, isValidDate as bt, asCleanDays as bu, multiplyDuration as bv, addDurations as bw, asRoughMinutes as bx, asRoughSeconds as by, asRoughMs as bz, greatestDurationDenominator as c, Slicer as c0, applyMutationToEventStore as c1, isPropsValid as c2, isInteractionValid as c3, isDateSelectionValid as c4, setRef as c5, getStickyFooterScrollbar as c6, getStickyHeaderDates as c7, getScrollerSyncerClass as c8, getScrollbarWidths as c9, RefMap as ca, getIsRtlScrollbarOnLeft as cb, NowTimer as cc, StandardEvent as cd, NowIndicatorContainer as ce, DayCellContainer as cf, hasCustomDayCellContent as cg, EventContainer as ch, renderFill as ci, BgEvent as cj, WeekNumberContainer as ck, MoreLinkContainer as cl, computeEarliestSegStart as cm, ViewContainer as cn, triggerDateSelect as co, getDefaultEventEnd as cp, injectStyles as cq, buildElAttrs as cr, CustomRenderingStore as cs, createDuration as d, BASE_OPTION_DEFAULTS as e, arrayToHash as f, guid as g, filterHash as h, isArraysEqual as i, buildEventSourceRefiners as j, formatWithOrdinals as k, buildRangeApiWithTimeZone as l, mergeProps as m, identity as n, intersectRanges as o, parseEventSource as p, startOfDay as q, requestJson as r, subtractDurations as s, addDays as t, unpromisify as u, hashValuesToArray as v, buildEventApis as w, createFormatter as x, diffWholeDays as y, memoize as z };