main.js 67 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354
  1. /*!
  2. FullCalendar Time Grid Plugin v4.2.0
  3. Docs & License: https://fullcalendar.io/
  4. (c) 2019 Adam Shaw
  5. */
  6. (function (global, factory) {
  7. typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@fullcalendar/core'), require('@fullcalendar/daygrid')) :
  8. typeof define === 'function' && define.amd ? define(['exports', '@fullcalendar/core', '@fullcalendar/daygrid'], factory) :
  9. (global = global || self, factory(global.FullCalendarTimeGrid = {}, global.FullCalendar, global.FullCalendarDayGrid));
  10. }(this, function (exports, core, daygrid) { 'use strict';
  11. /*! *****************************************************************************
  12. Copyright (c) Microsoft Corporation. All rights reserved.
  13. Licensed under the Apache License, Version 2.0 (the "License"); you may not use
  14. this file except in compliance with the License. You may obtain a copy of the
  15. License at http://www.apache.org/licenses/LICENSE-2.0
  16. THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  17. KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
  18. WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
  19. MERCHANTABLITY OR NON-INFRINGEMENT.
  20. See the Apache Version 2.0 License for specific language governing permissions
  21. and limitations under the License.
  22. ***************************************************************************** */
  23. /* global Reflect, Promise */
  24. var extendStatics = function(d, b) {
  25. extendStatics = Object.setPrototypeOf ||
  26. ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
  27. function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  28. return extendStatics(d, b);
  29. };
  30. function __extends(d, b) {
  31. extendStatics(d, b);
  32. function __() { this.constructor = d; }
  33. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  34. }
  35. var __assign = function() {
  36. __assign = Object.assign || function __assign(t) {
  37. for (var s, i = 1, n = arguments.length; i < n; i++) {
  38. s = arguments[i];
  39. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
  40. }
  41. return t;
  42. };
  43. return __assign.apply(this, arguments);
  44. };
  45. /*
  46. Only handles foreground segs.
  47. Does not own rendering. Use for low-level util methods by TimeGrid.
  48. */
  49. var TimeGridEventRenderer = /** @class */ (function (_super) {
  50. __extends(TimeGridEventRenderer, _super);
  51. function TimeGridEventRenderer(timeGrid) {
  52. var _this = _super.call(this, timeGrid.context) || this;
  53. _this.timeGrid = timeGrid;
  54. _this.fullTimeFormat = core.createFormatter({
  55. hour: 'numeric',
  56. minute: '2-digit',
  57. separator: _this.context.options.defaultRangeSeparator
  58. });
  59. return _this;
  60. }
  61. // Given an array of foreground segments, render a DOM element for each, computes position,
  62. // and attaches to the column inner-container elements.
  63. TimeGridEventRenderer.prototype.attachSegs = function (segs, mirrorInfo) {
  64. var segsByCol = this.timeGrid.groupSegsByCol(segs);
  65. // order the segs within each column
  66. // TODO: have groupSegsByCol do this?
  67. for (var col = 0; col < segsByCol.length; col++) {
  68. segsByCol[col] = this.sortEventSegs(segsByCol[col]);
  69. }
  70. this.segsByCol = segsByCol;
  71. this.timeGrid.attachSegsByCol(segsByCol, this.timeGrid.fgContainerEls);
  72. };
  73. TimeGridEventRenderer.prototype.detachSegs = function (segs) {
  74. segs.forEach(function (seg) {
  75. core.removeElement(seg.el);
  76. });
  77. this.segsByCol = null;
  78. };
  79. TimeGridEventRenderer.prototype.computeSegSizes = function (allSegs) {
  80. var _a = this, timeGrid = _a.timeGrid, segsByCol = _a.segsByCol;
  81. var colCnt = timeGrid.colCnt;
  82. timeGrid.computeSegVerticals(allSegs); // horizontals relies on this
  83. if (segsByCol) {
  84. for (var col = 0; col < colCnt; col++) {
  85. this.computeSegHorizontals(segsByCol[col]); // compute horizontal coordinates, z-index's, and reorder the array
  86. }
  87. }
  88. };
  89. TimeGridEventRenderer.prototype.assignSegSizes = function (allSegs) {
  90. var _a = this, timeGrid = _a.timeGrid, segsByCol = _a.segsByCol;
  91. var colCnt = timeGrid.colCnt;
  92. timeGrid.assignSegVerticals(allSegs); // horizontals relies on this
  93. if (segsByCol) {
  94. for (var col = 0; col < colCnt; col++) {
  95. this.assignSegCss(segsByCol[col]);
  96. }
  97. }
  98. };
  99. // Computes a default event time formatting string if `eventTimeFormat` is not explicitly defined
  100. TimeGridEventRenderer.prototype.computeEventTimeFormat = function () {
  101. return {
  102. hour: 'numeric',
  103. minute: '2-digit',
  104. meridiem: false
  105. };
  106. };
  107. // Computes a default `displayEventEnd` value if one is not expliclty defined
  108. TimeGridEventRenderer.prototype.computeDisplayEventEnd = function () {
  109. return true;
  110. };
  111. // Renders the HTML for a single event segment's default rendering
  112. TimeGridEventRenderer.prototype.renderSegHtml = function (seg, mirrorInfo) {
  113. var eventRange = seg.eventRange;
  114. var eventDef = eventRange.def;
  115. var eventUi = eventRange.ui;
  116. var allDay = eventDef.allDay;
  117. var isDraggable = eventUi.startEditable;
  118. var isResizableFromStart = seg.isStart && eventUi.durationEditable && this.context.options.eventResizableFromStart;
  119. var isResizableFromEnd = seg.isEnd && eventUi.durationEditable;
  120. var classes = this.getSegClasses(seg, isDraggable, isResizableFromStart || isResizableFromEnd, mirrorInfo);
  121. var skinCss = core.cssToStr(this.getSkinCss(eventUi));
  122. var timeText;
  123. var fullTimeText; // more verbose time text. for the print stylesheet
  124. var startTimeText; // just the start time text
  125. classes.unshift('fc-time-grid-event');
  126. // if the event appears to span more than one day...
  127. if (core.isMultiDayRange(eventRange.range)) {
  128. // Don't display time text on segments that run entirely through a day.
  129. // That would appear as midnight-midnight and would look dumb.
  130. // Otherwise, display the time text for the *segment's* times (like 6pm-midnight or midnight-10am)
  131. if (seg.isStart || seg.isEnd) {
  132. var unzonedStart = seg.start;
  133. var unzonedEnd = seg.end;
  134. timeText = this._getTimeText(unzonedStart, unzonedEnd, allDay); // TODO: give the timezones
  135. fullTimeText = this._getTimeText(unzonedStart, unzonedEnd, allDay, this.fullTimeFormat);
  136. startTimeText = this._getTimeText(unzonedStart, unzonedEnd, allDay, null, false); // displayEnd=false
  137. }
  138. }
  139. else {
  140. // Display the normal time text for the *event's* times
  141. timeText = this.getTimeText(eventRange);
  142. fullTimeText = this.getTimeText(eventRange, this.fullTimeFormat);
  143. startTimeText = this.getTimeText(eventRange, null, false); // displayEnd=false
  144. }
  145. return '<a class="' + classes.join(' ') + '"' +
  146. (eventDef.url ?
  147. ' href="' + core.htmlEscape(eventDef.url) + '"' :
  148. '') +
  149. (skinCss ?
  150. ' style="' + skinCss + '"' :
  151. '') +
  152. '>' +
  153. '<div class="fc-content">' +
  154. (timeText ?
  155. '<div class="fc-time"' +
  156. ' data-start="' + core.htmlEscape(startTimeText) + '"' +
  157. ' data-full="' + core.htmlEscape(fullTimeText) + '"' +
  158. '>' +
  159. '<span>' + core.htmlEscape(timeText) + '</span>' +
  160. '</div>' :
  161. '') +
  162. (eventDef.title ?
  163. '<div class="fc-title">' +
  164. core.htmlEscape(eventDef.title) +
  165. '</div>' :
  166. '') +
  167. '</div>' +
  168. /* TODO: write CSS for this
  169. (isResizableFromStart ?
  170. '<div class="fc-resizer fc-start-resizer"></div>' :
  171. ''
  172. ) +
  173. */
  174. (isResizableFromEnd ?
  175. '<div class="fc-resizer fc-end-resizer"></div>' :
  176. '') +
  177. '</a>';
  178. };
  179. // Given an array of segments that are all in the same column, sets the backwardCoord and forwardCoord on each.
  180. // Assumed the segs are already ordered.
  181. // NOTE: Also reorders the given array by date!
  182. TimeGridEventRenderer.prototype.computeSegHorizontals = function (segs) {
  183. var levels;
  184. var level0;
  185. var i;
  186. levels = buildSlotSegLevels(segs);
  187. computeForwardSlotSegs(levels);
  188. if ((level0 = levels[0])) {
  189. for (i = 0; i < level0.length; i++) {
  190. computeSlotSegPressures(level0[i]);
  191. }
  192. for (i = 0; i < level0.length; i++) {
  193. this.computeSegForwardBack(level0[i], 0, 0);
  194. }
  195. }
  196. };
  197. // Calculate seg.forwardCoord and seg.backwardCoord for the segment, where both values range
  198. // from 0 to 1. If the calendar is left-to-right, the seg.backwardCoord maps to "left" and
  199. // seg.forwardCoord maps to "right" (via percentage). Vice-versa if the calendar is right-to-left.
  200. //
  201. // The segment might be part of a "series", which means consecutive segments with the same pressure
  202. // who's width is unknown until an edge has been hit. `seriesBackwardPressure` is the number of
  203. // segments behind this one in the current series, and `seriesBackwardCoord` is the starting
  204. // coordinate of the first segment in the series.
  205. TimeGridEventRenderer.prototype.computeSegForwardBack = function (seg, seriesBackwardPressure, seriesBackwardCoord) {
  206. var forwardSegs = seg.forwardSegs;
  207. var i;
  208. if (seg.forwardCoord === undefined) { // not already computed
  209. if (!forwardSegs.length) {
  210. // if there are no forward segments, this segment should butt up against the edge
  211. seg.forwardCoord = 1;
  212. }
  213. else {
  214. // sort highest pressure first
  215. this.sortForwardSegs(forwardSegs);
  216. // this segment's forwardCoord will be calculated from the backwardCoord of the
  217. // highest-pressure forward segment.
  218. this.computeSegForwardBack(forwardSegs[0], seriesBackwardPressure + 1, seriesBackwardCoord);
  219. seg.forwardCoord = forwardSegs[0].backwardCoord;
  220. }
  221. // calculate the backwardCoord from the forwardCoord. consider the series
  222. seg.backwardCoord = seg.forwardCoord -
  223. (seg.forwardCoord - seriesBackwardCoord) / // available width for series
  224. (seriesBackwardPressure + 1); // # of segments in the series
  225. // use this segment's coordinates to computed the coordinates of the less-pressurized
  226. // forward segments
  227. for (i = 0; i < forwardSegs.length; i++) {
  228. this.computeSegForwardBack(forwardSegs[i], 0, seg.forwardCoord);
  229. }
  230. }
  231. };
  232. TimeGridEventRenderer.prototype.sortForwardSegs = function (forwardSegs) {
  233. var objs = forwardSegs.map(buildTimeGridSegCompareObj);
  234. var specs = [
  235. // put higher-pressure first
  236. { field: 'forwardPressure', order: -1 },
  237. // put segments that are closer to initial edge first (and favor ones with no coords yet)
  238. { field: 'backwardCoord', order: 1 }
  239. ].concat(this.context.view.eventOrderSpecs);
  240. objs.sort(function (obj0, obj1) {
  241. return core.compareByFieldSpecs(obj0, obj1, specs);
  242. });
  243. return objs.map(function (c) {
  244. return c._seg;
  245. });
  246. };
  247. // Given foreground event segments that have already had their position coordinates computed,
  248. // assigns position-related CSS values to their elements.
  249. TimeGridEventRenderer.prototype.assignSegCss = function (segs) {
  250. for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) {
  251. var seg = segs_1[_i];
  252. core.applyStyle(seg.el, this.generateSegCss(seg));
  253. if (seg.level > 0) {
  254. seg.el.classList.add('fc-time-grid-event-inset');
  255. }
  256. // if the event is short that the title will be cut off,
  257. // attach a className that condenses the title into the time area.
  258. if (seg.eventRange.def.title && seg.bottom - seg.top < 30) {
  259. seg.el.classList.add('fc-short'); // TODO: "condensed" is a better name
  260. }
  261. }
  262. };
  263. // Generates an object with CSS properties/values that should be applied to an event segment element.
  264. // Contains important positioning-related properties that should be applied to any event element, customized or not.
  265. TimeGridEventRenderer.prototype.generateSegCss = function (seg) {
  266. var shouldOverlap = this.context.options.slotEventOverlap;
  267. var backwardCoord = seg.backwardCoord; // the left side if LTR. the right side if RTL. floating-point
  268. var forwardCoord = seg.forwardCoord; // the right side if LTR. the left side if RTL. floating-point
  269. var props = this.timeGrid.generateSegVerticalCss(seg); // get top/bottom first
  270. var isRtl = this.timeGrid.isRtl;
  271. var left; // amount of space from left edge, a fraction of the total width
  272. var right; // amount of space from right edge, a fraction of the total width
  273. if (shouldOverlap) {
  274. // double the width, but don't go beyond the maximum forward coordinate (1.0)
  275. forwardCoord = Math.min(1, backwardCoord + (forwardCoord - backwardCoord) * 2);
  276. }
  277. if (isRtl) {
  278. left = 1 - forwardCoord;
  279. right = backwardCoord;
  280. }
  281. else {
  282. left = backwardCoord;
  283. right = 1 - forwardCoord;
  284. }
  285. props.zIndex = seg.level + 1; // convert from 0-base to 1-based
  286. props.left = left * 100 + '%';
  287. props.right = right * 100 + '%';
  288. if (shouldOverlap && seg.forwardPressure) {
  289. // add padding to the edge so that forward stacked events don't cover the resizer's icon
  290. props[isRtl ? 'marginLeft' : 'marginRight'] = 10 * 2; // 10 is a guesstimate of the icon's width
  291. }
  292. return props;
  293. };
  294. return TimeGridEventRenderer;
  295. }(core.FgEventRenderer));
  296. // Builds an array of segments "levels". The first level will be the leftmost tier of segments if the calendar is
  297. // left-to-right, or the rightmost if the calendar is right-to-left. Assumes the segments are already ordered by date.
  298. function buildSlotSegLevels(segs) {
  299. var levels = [];
  300. var i;
  301. var seg;
  302. var j;
  303. for (i = 0; i < segs.length; i++) {
  304. seg = segs[i];
  305. // go through all the levels and stop on the first level where there are no collisions
  306. for (j = 0; j < levels.length; j++) {
  307. if (!computeSlotSegCollisions(seg, levels[j]).length) {
  308. break;
  309. }
  310. }
  311. seg.level = j;
  312. (levels[j] || (levels[j] = [])).push(seg);
  313. }
  314. return levels;
  315. }
  316. // For every segment, figure out the other segments that are in subsequent
  317. // levels that also occupy the same vertical space. Accumulate in seg.forwardSegs
  318. function computeForwardSlotSegs(levels) {
  319. var i;
  320. var level;
  321. var j;
  322. var seg;
  323. var k;
  324. for (i = 0; i < levels.length; i++) {
  325. level = levels[i];
  326. for (j = 0; j < level.length; j++) {
  327. seg = level[j];
  328. seg.forwardSegs = [];
  329. for (k = i + 1; k < levels.length; k++) {
  330. computeSlotSegCollisions(seg, levels[k], seg.forwardSegs);
  331. }
  332. }
  333. }
  334. }
  335. // Figure out which path forward (via seg.forwardSegs) results in the longest path until
  336. // the furthest edge is reached. The number of segments in this path will be seg.forwardPressure
  337. function computeSlotSegPressures(seg) {
  338. var forwardSegs = seg.forwardSegs;
  339. var forwardPressure = 0;
  340. var i;
  341. var forwardSeg;
  342. if (seg.forwardPressure === undefined) { // not already computed
  343. for (i = 0; i < forwardSegs.length; i++) {
  344. forwardSeg = forwardSegs[i];
  345. // figure out the child's maximum forward path
  346. computeSlotSegPressures(forwardSeg);
  347. // either use the existing maximum, or use the child's forward pressure
  348. // plus one (for the forwardSeg itself)
  349. forwardPressure = Math.max(forwardPressure, 1 + forwardSeg.forwardPressure);
  350. }
  351. seg.forwardPressure = forwardPressure;
  352. }
  353. }
  354. // Find all the segments in `otherSegs` that vertically collide with `seg`.
  355. // Append into an optionally-supplied `results` array and return.
  356. function computeSlotSegCollisions(seg, otherSegs, results) {
  357. if (results === void 0) { results = []; }
  358. for (var i = 0; i < otherSegs.length; i++) {
  359. if (isSlotSegCollision(seg, otherSegs[i])) {
  360. results.push(otherSegs[i]);
  361. }
  362. }
  363. return results;
  364. }
  365. // Do these segments occupy the same vertical space?
  366. function isSlotSegCollision(seg1, seg2) {
  367. return seg1.bottom > seg2.top && seg1.top < seg2.bottom;
  368. }
  369. function buildTimeGridSegCompareObj(seg) {
  370. var obj = core.buildSegCompareObj(seg);
  371. obj.forwardPressure = seg.forwardPressure;
  372. obj.backwardCoord = seg.backwardCoord;
  373. return obj;
  374. }
  375. var TimeGridMirrorRenderer = /** @class */ (function (_super) {
  376. __extends(TimeGridMirrorRenderer, _super);
  377. function TimeGridMirrorRenderer() {
  378. return _super !== null && _super.apply(this, arguments) || this;
  379. }
  380. TimeGridMirrorRenderer.prototype.attachSegs = function (segs, mirrorInfo) {
  381. this.segsByCol = this.timeGrid.groupSegsByCol(segs);
  382. this.timeGrid.attachSegsByCol(this.segsByCol, this.timeGrid.mirrorContainerEls);
  383. this.sourceSeg = mirrorInfo.sourceSeg;
  384. };
  385. TimeGridMirrorRenderer.prototype.generateSegCss = function (seg) {
  386. var props = _super.prototype.generateSegCss.call(this, seg);
  387. var sourceSeg = this.sourceSeg;
  388. if (sourceSeg && sourceSeg.col === seg.col) {
  389. var sourceSegProps = _super.prototype.generateSegCss.call(this, sourceSeg);
  390. props.left = sourceSegProps.left;
  391. props.right = sourceSegProps.right;
  392. props.marginLeft = sourceSegProps.marginLeft;
  393. props.marginRight = sourceSegProps.marginRight;
  394. }
  395. return props;
  396. };
  397. return TimeGridMirrorRenderer;
  398. }(TimeGridEventRenderer));
  399. var TimeGridFillRenderer = /** @class */ (function (_super) {
  400. __extends(TimeGridFillRenderer, _super);
  401. function TimeGridFillRenderer(timeGrid) {
  402. var _this = _super.call(this, timeGrid.context) || this;
  403. _this.timeGrid = timeGrid;
  404. return _this;
  405. }
  406. TimeGridFillRenderer.prototype.attachSegs = function (type, segs) {
  407. var timeGrid = this.timeGrid;
  408. var containerEls;
  409. // TODO: more efficient lookup
  410. if (type === 'bgEvent') {
  411. containerEls = timeGrid.bgContainerEls;
  412. }
  413. else if (type === 'businessHours') {
  414. containerEls = timeGrid.businessContainerEls;
  415. }
  416. else if (type === 'highlight') {
  417. containerEls = timeGrid.highlightContainerEls;
  418. }
  419. timeGrid.attachSegsByCol(timeGrid.groupSegsByCol(segs), containerEls);
  420. return segs.map(function (seg) {
  421. return seg.el;
  422. });
  423. };
  424. TimeGridFillRenderer.prototype.computeSegSizes = function (segs) {
  425. this.timeGrid.computeSegVerticals(segs);
  426. };
  427. TimeGridFillRenderer.prototype.assignSegSizes = function (segs) {
  428. this.timeGrid.assignSegVerticals(segs);
  429. };
  430. return TimeGridFillRenderer;
  431. }(core.FillRenderer));
  432. /* A component that renders one or more columns of vertical time slots
  433. ----------------------------------------------------------------------------------------------------------------------*/
  434. // potential nice values for the slot-duration and interval-duration
  435. // from largest to smallest
  436. var AGENDA_STOCK_SUB_DURATIONS = [
  437. { hours: 1 },
  438. { minutes: 30 },
  439. { minutes: 15 },
  440. { seconds: 30 },
  441. { seconds: 15 }
  442. ];
  443. var TimeGrid = /** @class */ (function (_super) {
  444. __extends(TimeGrid, _super);
  445. function TimeGrid(context, el, renderProps) {
  446. var _this = _super.call(this, context, el) || this;
  447. _this.isSlatSizesDirty = false;
  448. _this.isColSizesDirty = false;
  449. _this.renderSlats = core.memoizeRendering(_this._renderSlats);
  450. var eventRenderer = _this.eventRenderer = new TimeGridEventRenderer(_this);
  451. var fillRenderer = _this.fillRenderer = new TimeGridFillRenderer(_this);
  452. _this.mirrorRenderer = new TimeGridMirrorRenderer(_this);
  453. var renderColumns = _this.renderColumns = core.memoizeRendering(_this._renderColumns, _this._unrenderColumns);
  454. _this.renderBusinessHours = core.memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'businessHours'), fillRenderer.unrender.bind(fillRenderer, 'businessHours'), [renderColumns]);
  455. _this.renderDateSelection = core.memoizeRendering(_this._renderDateSelection, _this._unrenderDateSelection, [renderColumns]);
  456. _this.renderFgEvents = core.memoizeRendering(eventRenderer.renderSegs.bind(eventRenderer), eventRenderer.unrender.bind(eventRenderer), [renderColumns]);
  457. _this.renderBgEvents = core.memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'bgEvent'), fillRenderer.unrender.bind(fillRenderer, 'bgEvent'), [renderColumns]);
  458. _this.renderEventSelection = core.memoizeRendering(eventRenderer.selectByInstanceId.bind(eventRenderer), eventRenderer.unselectByInstanceId.bind(eventRenderer), [_this.renderFgEvents]);
  459. _this.renderEventDrag = core.memoizeRendering(_this._renderEventDrag, _this._unrenderEventDrag, [renderColumns]);
  460. _this.renderEventResize = core.memoizeRendering(_this._renderEventResize, _this._unrenderEventResize, [renderColumns]);
  461. _this.processOptions();
  462. el.innerHTML =
  463. '<div class="fc-bg"></div>' +
  464. '<div class="fc-slats"></div>' +
  465. '<hr class="fc-divider ' + _this.theme.getClass('widgetHeader') + '" style="display:none" />';
  466. _this.rootBgContainerEl = el.querySelector('.fc-bg');
  467. _this.slatContainerEl = el.querySelector('.fc-slats');
  468. _this.bottomRuleEl = el.querySelector('.fc-divider');
  469. _this.renderProps = renderProps;
  470. return _this;
  471. }
  472. /* Options
  473. ------------------------------------------------------------------------------------------------------------------*/
  474. // Parses various options into properties of this object
  475. TimeGrid.prototype.processOptions = function () {
  476. var slotDuration = this.opt('slotDuration');
  477. var snapDuration = this.opt('snapDuration');
  478. var snapsPerSlot;
  479. var input;
  480. slotDuration = core.createDuration(slotDuration);
  481. snapDuration = snapDuration ? core.createDuration(snapDuration) : slotDuration;
  482. snapsPerSlot = core.wholeDivideDurations(slotDuration, snapDuration);
  483. if (snapsPerSlot === null) {
  484. snapDuration = slotDuration;
  485. snapsPerSlot = 1;
  486. // TODO: say warning?
  487. }
  488. this.slotDuration = slotDuration;
  489. this.snapDuration = snapDuration;
  490. this.snapsPerSlot = snapsPerSlot;
  491. // might be an array value (for TimelineView).
  492. // if so, getting the most granular entry (the last one probably).
  493. input = this.opt('slotLabelFormat');
  494. if (Array.isArray(input)) {
  495. input = input[input.length - 1];
  496. }
  497. this.labelFormat = core.createFormatter(input || {
  498. hour: 'numeric',
  499. minute: '2-digit',
  500. omitZeroMinute: true,
  501. meridiem: 'short'
  502. });
  503. input = this.opt('slotLabelInterval');
  504. this.labelInterval = input ?
  505. core.createDuration(input) :
  506. this.computeLabelInterval(slotDuration);
  507. };
  508. // Computes an automatic value for slotLabelInterval
  509. TimeGrid.prototype.computeLabelInterval = function (slotDuration) {
  510. var i;
  511. var labelInterval;
  512. var slotsPerLabel;
  513. // find the smallest stock label interval that results in more than one slots-per-label
  514. for (i = AGENDA_STOCK_SUB_DURATIONS.length - 1; i >= 0; i--) {
  515. labelInterval = core.createDuration(AGENDA_STOCK_SUB_DURATIONS[i]);
  516. slotsPerLabel = core.wholeDivideDurations(labelInterval, slotDuration);
  517. if (slotsPerLabel !== null && slotsPerLabel > 1) {
  518. return labelInterval;
  519. }
  520. }
  521. return slotDuration; // fall back
  522. };
  523. /* Rendering
  524. ------------------------------------------------------------------------------------------------------------------*/
  525. TimeGrid.prototype.render = function (props) {
  526. var cells = props.cells;
  527. this.colCnt = cells.length;
  528. this.renderSlats(props.dateProfile);
  529. this.renderColumns(props.cells, props.dateProfile);
  530. this.renderBusinessHours(props.businessHourSegs);
  531. this.renderDateSelection(props.dateSelectionSegs);
  532. this.renderFgEvents(props.fgEventSegs);
  533. this.renderBgEvents(props.bgEventSegs);
  534. this.renderEventSelection(props.eventSelection);
  535. this.renderEventDrag(props.eventDrag);
  536. this.renderEventResize(props.eventResize);
  537. };
  538. TimeGrid.prototype.destroy = function () {
  539. _super.prototype.destroy.call(this);
  540. // should unrender everything else too
  541. this.renderSlats.unrender();
  542. this.renderColumns.unrender();
  543. };
  544. TimeGrid.prototype.updateSize = function (isResize) {
  545. var _a = this, fillRenderer = _a.fillRenderer, eventRenderer = _a.eventRenderer, mirrorRenderer = _a.mirrorRenderer;
  546. if (isResize || this.isSlatSizesDirty) {
  547. this.buildSlatPositions();
  548. this.isSlatSizesDirty = false;
  549. }
  550. if (isResize || this.isColSizesDirty) {
  551. this.buildColPositions();
  552. this.isColSizesDirty = false;
  553. }
  554. fillRenderer.computeSizes(isResize);
  555. eventRenderer.computeSizes(isResize);
  556. mirrorRenderer.computeSizes(isResize);
  557. fillRenderer.assignSizes(isResize);
  558. eventRenderer.assignSizes(isResize);
  559. mirrorRenderer.assignSizes(isResize);
  560. };
  561. TimeGrid.prototype._renderSlats = function (dateProfile) {
  562. var theme = this.theme;
  563. this.slatContainerEl.innerHTML =
  564. '<table class="' + theme.getClass('tableGrid') + '">' +
  565. this.renderSlatRowHtml(dateProfile) +
  566. '</table>';
  567. this.slatEls = core.findElements(this.slatContainerEl, 'tr');
  568. this.slatPositions = new core.PositionCache(this.el, this.slatEls, false, true // vertical
  569. );
  570. this.isSlatSizesDirty = true;
  571. };
  572. // Generates the HTML for the horizontal "slats" that run width-wise. Has a time axis on a side. Depends on RTL.
  573. TimeGrid.prototype.renderSlatRowHtml = function (dateProfile) {
  574. var _a = this, dateEnv = _a.dateEnv, theme = _a.theme, isRtl = _a.isRtl;
  575. var html = '';
  576. var dayStart = core.startOfDay(dateProfile.renderRange.start);
  577. var slotTime = dateProfile.minTime;
  578. var slotIterator = core.createDuration(0);
  579. var slotDate; // will be on the view's first day, but we only care about its time
  580. var isLabeled;
  581. var axisHtml;
  582. // Calculate the time for each slot
  583. while (core.asRoughMs(slotTime) < core.asRoughMs(dateProfile.maxTime)) {
  584. slotDate = dateEnv.add(dayStart, slotTime);
  585. isLabeled = core.wholeDivideDurations(slotIterator, this.labelInterval) !== null;
  586. axisHtml =
  587. '<td class="fc-axis fc-time ' + theme.getClass('widgetContent') + '">' +
  588. (isLabeled ?
  589. '<span>' + // for matchCellWidths
  590. core.htmlEscape(dateEnv.format(slotDate, this.labelFormat)) +
  591. '</span>' :
  592. '') +
  593. '</td>';
  594. html +=
  595. '<tr data-time="' + core.formatIsoTimeString(slotDate) + '"' +
  596. (isLabeled ? '' : ' class="fc-minor"') +
  597. '>' +
  598. (!isRtl ? axisHtml : '') +
  599. '<td class="' + theme.getClass('widgetContent') + '"></td>' +
  600. (isRtl ? axisHtml : '') +
  601. '</tr>';
  602. slotTime = core.addDurations(slotTime, this.slotDuration);
  603. slotIterator = core.addDurations(slotIterator, this.slotDuration);
  604. }
  605. return html;
  606. };
  607. TimeGrid.prototype._renderColumns = function (cells, dateProfile) {
  608. var _a = this, theme = _a.theme, dateEnv = _a.dateEnv, view = _a.view;
  609. var bgRow = new daygrid.DayBgRow(this.context);
  610. this.rootBgContainerEl.innerHTML =
  611. '<table class="' + theme.getClass('tableGrid') + '">' +
  612. bgRow.renderHtml({
  613. cells: cells,
  614. dateProfile: dateProfile,
  615. renderIntroHtml: this.renderProps.renderBgIntroHtml
  616. }) +
  617. '</table>';
  618. this.colEls = core.findElements(this.el, '.fc-day, .fc-disabled-day');
  619. for (var col = 0; col < this.colCnt; col++) {
  620. this.publiclyTrigger('dayRender', [
  621. {
  622. date: dateEnv.toDate(cells[col].date),
  623. el: this.colEls[col],
  624. view: view
  625. }
  626. ]);
  627. }
  628. if (this.isRtl) {
  629. this.colEls.reverse();
  630. }
  631. this.colPositions = new core.PositionCache(this.el, this.colEls, true, // horizontal
  632. false);
  633. this.renderContentSkeleton();
  634. this.isColSizesDirty = true;
  635. };
  636. TimeGrid.prototype._unrenderColumns = function () {
  637. this.unrenderContentSkeleton();
  638. };
  639. /* Content Skeleton
  640. ------------------------------------------------------------------------------------------------------------------*/
  641. // Renders the DOM that the view's content will live in
  642. TimeGrid.prototype.renderContentSkeleton = function () {
  643. var parts = [];
  644. var skeletonEl;
  645. parts.push(this.renderProps.renderIntroHtml());
  646. for (var i = 0; i < this.colCnt; i++) {
  647. parts.push('<td>' +
  648. '<div class="fc-content-col">' +
  649. '<div class="fc-event-container fc-mirror-container"></div>' +
  650. '<div class="fc-event-container"></div>' +
  651. '<div class="fc-highlight-container"></div>' +
  652. '<div class="fc-bgevent-container"></div>' +
  653. '<div class="fc-business-container"></div>' +
  654. '</div>' +
  655. '</td>');
  656. }
  657. if (this.isRtl) {
  658. parts.reverse();
  659. }
  660. skeletonEl = this.contentSkeletonEl = core.htmlToElement('<div class="fc-content-skeleton">' +
  661. '<table>' +
  662. '<tr>' + parts.join('') + '</tr>' +
  663. '</table>' +
  664. '</div>');
  665. this.colContainerEls = core.findElements(skeletonEl, '.fc-content-col');
  666. this.mirrorContainerEls = core.findElements(skeletonEl, '.fc-mirror-container');
  667. this.fgContainerEls = core.findElements(skeletonEl, '.fc-event-container:not(.fc-mirror-container)');
  668. this.bgContainerEls = core.findElements(skeletonEl, '.fc-bgevent-container');
  669. this.highlightContainerEls = core.findElements(skeletonEl, '.fc-highlight-container');
  670. this.businessContainerEls = core.findElements(skeletonEl, '.fc-business-container');
  671. if (this.isRtl) {
  672. this.colContainerEls.reverse();
  673. this.mirrorContainerEls.reverse();
  674. this.fgContainerEls.reverse();
  675. this.bgContainerEls.reverse();
  676. this.highlightContainerEls.reverse();
  677. this.businessContainerEls.reverse();
  678. }
  679. this.el.appendChild(skeletonEl);
  680. };
  681. TimeGrid.prototype.unrenderContentSkeleton = function () {
  682. core.removeElement(this.contentSkeletonEl);
  683. };
  684. // Given a flat array of segments, return an array of sub-arrays, grouped by each segment's col
  685. TimeGrid.prototype.groupSegsByCol = function (segs) {
  686. var segsByCol = [];
  687. var i;
  688. for (i = 0; i < this.colCnt; i++) {
  689. segsByCol.push([]);
  690. }
  691. for (i = 0; i < segs.length; i++) {
  692. segsByCol[segs[i].col].push(segs[i]);
  693. }
  694. return segsByCol;
  695. };
  696. // Given segments grouped by column, insert the segments' elements into a parallel array of container
  697. // elements, each living within a column.
  698. TimeGrid.prototype.attachSegsByCol = function (segsByCol, containerEls) {
  699. var col;
  700. var segs;
  701. var i;
  702. for (col = 0; col < this.colCnt; col++) { // iterate each column grouping
  703. segs = segsByCol[col];
  704. for (i = 0; i < segs.length; i++) {
  705. containerEls[col].appendChild(segs[i].el);
  706. }
  707. }
  708. };
  709. /* Now Indicator
  710. ------------------------------------------------------------------------------------------------------------------*/
  711. TimeGrid.prototype.getNowIndicatorUnit = function () {
  712. return 'minute'; // will refresh on the minute
  713. };
  714. TimeGrid.prototype.renderNowIndicator = function (segs, date) {
  715. // HACK: if date columns not ready for some reason (scheduler)
  716. if (!this.colContainerEls) {
  717. return;
  718. }
  719. var top = this.computeDateTop(date);
  720. var nodes = [];
  721. var i;
  722. // render lines within the columns
  723. for (i = 0; i < segs.length; i++) {
  724. var lineEl = core.createElement('div', { className: 'fc-now-indicator fc-now-indicator-line' });
  725. lineEl.style.top = top + 'px';
  726. this.colContainerEls[segs[i].col].appendChild(lineEl);
  727. nodes.push(lineEl);
  728. }
  729. // render an arrow over the axis
  730. if (segs.length > 0) { // is the current time in view?
  731. var arrowEl = core.createElement('div', { className: 'fc-now-indicator fc-now-indicator-arrow' });
  732. arrowEl.style.top = top + 'px';
  733. this.contentSkeletonEl.appendChild(arrowEl);
  734. nodes.push(arrowEl);
  735. }
  736. this.nowIndicatorEls = nodes;
  737. };
  738. TimeGrid.prototype.unrenderNowIndicator = function () {
  739. if (this.nowIndicatorEls) {
  740. this.nowIndicatorEls.forEach(core.removeElement);
  741. this.nowIndicatorEls = null;
  742. }
  743. };
  744. /* Coordinates
  745. ------------------------------------------------------------------------------------------------------------------*/
  746. TimeGrid.prototype.getTotalSlatHeight = function () {
  747. return this.slatContainerEl.offsetHeight;
  748. };
  749. // Computes the top coordinate, relative to the bounds of the grid, of the given date.
  750. // A `startOfDayDate` must be given for avoiding ambiguity over how to treat midnight.
  751. TimeGrid.prototype.computeDateTop = function (when, startOfDayDate) {
  752. if (!startOfDayDate) {
  753. startOfDayDate = core.startOfDay(when);
  754. }
  755. return this.computeTimeTop(when.valueOf() - startOfDayDate.valueOf());
  756. };
  757. // Computes the top coordinate, relative to the bounds of the grid, of the given time (a Duration).
  758. TimeGrid.prototype.computeTimeTop = function (timeMs) {
  759. var len = this.slatEls.length;
  760. var dateProfile = this.props.dateProfile;
  761. var slatCoverage = (timeMs - core.asRoughMs(dateProfile.minTime)) / core.asRoughMs(this.slotDuration); // floating-point value of # of slots covered
  762. var slatIndex;
  763. var slatRemainder;
  764. // compute a floating-point number for how many slats should be progressed through.
  765. // from 0 to number of slats (inclusive)
  766. // constrained because minTime/maxTime might be customized.
  767. slatCoverage = Math.max(0, slatCoverage);
  768. slatCoverage = Math.min(len, slatCoverage);
  769. // an integer index of the furthest whole slat
  770. // from 0 to number slats (*exclusive*, so len-1)
  771. slatIndex = Math.floor(slatCoverage);
  772. slatIndex = Math.min(slatIndex, len - 1);
  773. // how much further through the slatIndex slat (from 0.0-1.0) must be covered in addition.
  774. // could be 1.0 if slatCoverage is covering *all* the slots
  775. slatRemainder = slatCoverage - slatIndex;
  776. return this.slatPositions.tops[slatIndex] +
  777. this.slatPositions.getHeight(slatIndex) * slatRemainder;
  778. };
  779. // For each segment in an array, computes and assigns its top and bottom properties
  780. TimeGrid.prototype.computeSegVerticals = function (segs) {
  781. var eventMinHeight = this.opt('timeGridEventMinHeight');
  782. var i;
  783. var seg;
  784. var dayDate;
  785. for (i = 0; i < segs.length; i++) {
  786. seg = segs[i];
  787. dayDate = this.props.cells[seg.col].date;
  788. seg.top = this.computeDateTop(seg.start, dayDate);
  789. seg.bottom = Math.max(seg.top + eventMinHeight, this.computeDateTop(seg.end, dayDate));
  790. }
  791. };
  792. // Given segments that already have their top/bottom properties computed, applies those values to
  793. // the segments' elements.
  794. TimeGrid.prototype.assignSegVerticals = function (segs) {
  795. var i;
  796. var seg;
  797. for (i = 0; i < segs.length; i++) {
  798. seg = segs[i];
  799. core.applyStyle(seg.el, this.generateSegVerticalCss(seg));
  800. }
  801. };
  802. // Generates an object with CSS properties for the top/bottom coordinates of a segment element
  803. TimeGrid.prototype.generateSegVerticalCss = function (seg) {
  804. return {
  805. top: seg.top,
  806. bottom: -seg.bottom // flipped because needs to be space beyond bottom edge of event container
  807. };
  808. };
  809. /* Sizing
  810. ------------------------------------------------------------------------------------------------------------------*/
  811. TimeGrid.prototype.buildPositionCaches = function () {
  812. this.buildColPositions();
  813. this.buildSlatPositions();
  814. };
  815. TimeGrid.prototype.buildColPositions = function () {
  816. this.colPositions.build();
  817. };
  818. TimeGrid.prototype.buildSlatPositions = function () {
  819. this.slatPositions.build();
  820. };
  821. /* Hit System
  822. ------------------------------------------------------------------------------------------------------------------*/
  823. TimeGrid.prototype.positionToHit = function (positionLeft, positionTop) {
  824. var _a = this, dateEnv = _a.dateEnv, snapsPerSlot = _a.snapsPerSlot, slatPositions = _a.slatPositions, colPositions = _a.colPositions;
  825. var colIndex = colPositions.leftToIndex(positionLeft);
  826. var slatIndex = slatPositions.topToIndex(positionTop);
  827. if (colIndex != null && slatIndex != null) {
  828. var slatTop = slatPositions.tops[slatIndex];
  829. var slatHeight = slatPositions.getHeight(slatIndex);
  830. var partial = (positionTop - slatTop) / slatHeight; // floating point number between 0 and 1
  831. var localSnapIndex = Math.floor(partial * snapsPerSlot); // the snap # relative to start of slat
  832. var snapIndex = slatIndex * snapsPerSlot + localSnapIndex;
  833. var dayDate = this.props.cells[colIndex].date;
  834. var time = core.addDurations(this.props.dateProfile.minTime, core.multiplyDuration(this.snapDuration, snapIndex));
  835. var start = dateEnv.add(dayDate, time);
  836. var end = dateEnv.add(start, this.snapDuration);
  837. return {
  838. col: colIndex,
  839. dateSpan: {
  840. range: { start: start, end: end },
  841. allDay: false
  842. },
  843. dayEl: this.colEls[colIndex],
  844. relativeRect: {
  845. left: colPositions.lefts[colIndex],
  846. right: colPositions.rights[colIndex],
  847. top: slatTop,
  848. bottom: slatTop + slatHeight
  849. }
  850. };
  851. }
  852. };
  853. /* Event Drag Visualization
  854. ------------------------------------------------------------------------------------------------------------------*/
  855. TimeGrid.prototype._renderEventDrag = function (state) {
  856. if (state) {
  857. this.eventRenderer.hideByHash(state.affectedInstances);
  858. if (state.isEvent) {
  859. this.mirrorRenderer.renderSegs(state.segs, { isDragging: true, sourceSeg: state.sourceSeg });
  860. }
  861. else {
  862. this.fillRenderer.renderSegs('highlight', state.segs);
  863. }
  864. }
  865. };
  866. TimeGrid.prototype._unrenderEventDrag = function (state) {
  867. if (state) {
  868. this.eventRenderer.showByHash(state.affectedInstances);
  869. this.mirrorRenderer.unrender(state.segs, { isDragging: true, sourceSeg: state.sourceSeg });
  870. this.fillRenderer.unrender('highlight');
  871. }
  872. };
  873. /* Event Resize Visualization
  874. ------------------------------------------------------------------------------------------------------------------*/
  875. TimeGrid.prototype._renderEventResize = function (state) {
  876. if (state) {
  877. this.eventRenderer.hideByHash(state.affectedInstances);
  878. this.mirrorRenderer.renderSegs(state.segs, { isResizing: true, sourceSeg: state.sourceSeg });
  879. }
  880. };
  881. TimeGrid.prototype._unrenderEventResize = function (state) {
  882. if (state) {
  883. this.eventRenderer.showByHash(state.affectedInstances);
  884. this.mirrorRenderer.unrender(state.segs, { isResizing: true, sourceSeg: state.sourceSeg });
  885. }
  886. };
  887. /* Selection
  888. ------------------------------------------------------------------------------------------------------------------*/
  889. // Renders a visual indication of a selection. Overrides the default, which was to simply render a highlight.
  890. TimeGrid.prototype._renderDateSelection = function (segs) {
  891. if (segs) {
  892. if (this.opt('selectMirror')) {
  893. this.mirrorRenderer.renderSegs(segs, { isSelecting: true });
  894. }
  895. else {
  896. this.fillRenderer.renderSegs('highlight', segs);
  897. }
  898. }
  899. };
  900. TimeGrid.prototype._unrenderDateSelection = function (segs) {
  901. this.mirrorRenderer.unrender(segs, { isSelecting: true });
  902. this.fillRenderer.unrender('highlight');
  903. };
  904. return TimeGrid;
  905. }(core.DateComponent));
  906. var AllDaySplitter = /** @class */ (function (_super) {
  907. __extends(AllDaySplitter, _super);
  908. function AllDaySplitter() {
  909. return _super !== null && _super.apply(this, arguments) || this;
  910. }
  911. AllDaySplitter.prototype.getKeyInfo = function () {
  912. return {
  913. allDay: {},
  914. timed: {}
  915. };
  916. };
  917. AllDaySplitter.prototype.getKeysForDateSpan = function (dateSpan) {
  918. if (dateSpan.allDay) {
  919. return ['allDay'];
  920. }
  921. else {
  922. return ['timed'];
  923. }
  924. };
  925. AllDaySplitter.prototype.getKeysForEventDef = function (eventDef) {
  926. if (!eventDef.allDay) {
  927. return ['timed'];
  928. }
  929. else if (core.hasBgRendering(eventDef)) {
  930. return ['timed', 'allDay'];
  931. }
  932. else {
  933. return ['allDay'];
  934. }
  935. };
  936. return AllDaySplitter;
  937. }(core.Splitter));
  938. var TIMEGRID_ALL_DAY_EVENT_LIMIT = 5;
  939. var WEEK_HEADER_FORMAT = core.createFormatter({ week: 'short' });
  940. /* An abstract class for all timegrid-related views. Displays one more columns with time slots running vertically.
  941. ----------------------------------------------------------------------------------------------------------------------*/
  942. // Is a manager for the TimeGrid subcomponent and possibly the DayGrid subcomponent (if allDaySlot is on).
  943. // Responsible for managing width/height.
  944. var TimeGridView = /** @class */ (function (_super) {
  945. __extends(TimeGridView, _super);
  946. function TimeGridView(context, viewSpec, dateProfileGenerator, parentEl) {
  947. var _this = _super.call(this, context, viewSpec, dateProfileGenerator, parentEl) || this;
  948. _this.splitter = new AllDaySplitter();
  949. /* Header Render Methods
  950. ------------------------------------------------------------------------------------------------------------------*/
  951. // Generates the HTML that will go before the day-of week header cells
  952. _this.renderHeadIntroHtml = function () {
  953. var _a = _this, theme = _a.theme, dateEnv = _a.dateEnv;
  954. var range = _this.props.dateProfile.renderRange;
  955. var dayCnt = core.diffDays(range.start, range.end);
  956. var weekText;
  957. if (_this.opt('weekNumbers')) {
  958. weekText = dateEnv.format(range.start, WEEK_HEADER_FORMAT);
  959. return '' +
  960. '<th class="fc-axis fc-week-number ' + theme.getClass('widgetHeader') + '" ' + _this.axisStyleAttr() + '>' +
  961. core.buildGotoAnchorHtml(// aside from link, important for matchCellWidths
  962. _this, { date: range.start, type: 'week', forceOff: dayCnt > 1 }, core.htmlEscape(weekText) // inner HTML
  963. ) +
  964. '</th>';
  965. }
  966. else {
  967. return '<th class="fc-axis ' + theme.getClass('widgetHeader') + '" ' + _this.axisStyleAttr() + '></th>';
  968. }
  969. };
  970. /* Time Grid Render Methods
  971. ------------------------------------------------------------------------------------------------------------------*/
  972. // Generates the HTML that goes before the bg of the TimeGrid slot area. Long vertical column.
  973. _this.renderTimeGridBgIntroHtml = function () {
  974. var theme = _this.theme;
  975. return '<td class="fc-axis ' + theme.getClass('widgetContent') + '" ' + _this.axisStyleAttr() + '></td>';
  976. };
  977. // Generates the HTML that goes before all other types of cells.
  978. // Affects content-skeleton, mirror-skeleton, highlight-skeleton for both the time-grid and day-grid.
  979. _this.renderTimeGridIntroHtml = function () {
  980. return '<td class="fc-axis" ' + _this.axisStyleAttr() + '></td>';
  981. };
  982. /* Day Grid Render Methods
  983. ------------------------------------------------------------------------------------------------------------------*/
  984. // Generates the HTML that goes before the all-day cells
  985. _this.renderDayGridBgIntroHtml = function () {
  986. var theme = _this.theme;
  987. return '' +
  988. '<td class="fc-axis ' + theme.getClass('widgetContent') + '" ' + _this.axisStyleAttr() + '>' +
  989. '<span>' + // needed for matchCellWidths
  990. core.getAllDayHtml(_this) +
  991. '</span>' +
  992. '</td>';
  993. };
  994. // Generates the HTML that goes before all other types of cells.
  995. // Affects content-skeleton, mirror-skeleton, highlight-skeleton for both the time-grid and day-grid.
  996. _this.renderDayGridIntroHtml = function () {
  997. return '<td class="fc-axis" ' + _this.axisStyleAttr() + '></td>';
  998. };
  999. _this.el.classList.add('fc-timeGrid-view');
  1000. _this.el.innerHTML = _this.renderSkeletonHtml();
  1001. _this.scroller = new core.ScrollComponent('hidden', // overflow x
  1002. 'auto' // overflow y
  1003. );
  1004. var timeGridWrapEl = _this.scroller.el;
  1005. _this.el.querySelector('.fc-body > tr > td').appendChild(timeGridWrapEl);
  1006. timeGridWrapEl.classList.add('fc-time-grid-container');
  1007. var timeGridEl = core.createElement('div', { className: 'fc-time-grid' });
  1008. timeGridWrapEl.appendChild(timeGridEl);
  1009. _this.timeGrid = new TimeGrid(_this.context, timeGridEl, {
  1010. renderBgIntroHtml: _this.renderTimeGridBgIntroHtml,
  1011. renderIntroHtml: _this.renderTimeGridIntroHtml
  1012. });
  1013. if (_this.opt('allDaySlot')) { // should we display the "all-day" area?
  1014. _this.dayGrid = new daygrid.DayGrid(// the all-day subcomponent of this view
  1015. _this.context, _this.el.querySelector('.fc-day-grid'), {
  1016. renderNumberIntroHtml: _this.renderDayGridIntroHtml,
  1017. renderBgIntroHtml: _this.renderDayGridBgIntroHtml,
  1018. renderIntroHtml: _this.renderDayGridIntroHtml,
  1019. colWeekNumbersVisible: false,
  1020. cellWeekNumbersVisible: false
  1021. });
  1022. // have the day-grid extend it's coordinate area over the <hr> dividing the two grids
  1023. _this.dayGrid.bottomCoordPadding = _this.el.querySelector('.fc-divider').offsetHeight;
  1024. }
  1025. return _this;
  1026. }
  1027. TimeGridView.prototype.destroy = function () {
  1028. _super.prototype.destroy.call(this);
  1029. this.timeGrid.destroy();
  1030. if (this.dayGrid) {
  1031. this.dayGrid.destroy();
  1032. }
  1033. this.scroller.destroy();
  1034. };
  1035. /* Rendering
  1036. ------------------------------------------------------------------------------------------------------------------*/
  1037. // Builds the HTML skeleton for the view.
  1038. // The day-grid and time-grid components will render inside containers defined by this HTML.
  1039. TimeGridView.prototype.renderSkeletonHtml = function () {
  1040. var theme = this.theme;
  1041. return '' +
  1042. '<table class="' + theme.getClass('tableGrid') + '">' +
  1043. (this.opt('columnHeader') ?
  1044. '<thead class="fc-head">' +
  1045. '<tr>' +
  1046. '<td class="fc-head-container ' + theme.getClass('widgetHeader') + '">&nbsp;</td>' +
  1047. '</tr>' +
  1048. '</thead>' :
  1049. '') +
  1050. '<tbody class="fc-body">' +
  1051. '<tr>' +
  1052. '<td class="' + theme.getClass('widgetContent') + '">' +
  1053. (this.opt('allDaySlot') ?
  1054. '<div class="fc-day-grid"></div>' +
  1055. '<hr class="fc-divider ' + theme.getClass('widgetHeader') + '" />' :
  1056. '') +
  1057. '</td>' +
  1058. '</tr>' +
  1059. '</tbody>' +
  1060. '</table>';
  1061. };
  1062. /* Now Indicator
  1063. ------------------------------------------------------------------------------------------------------------------*/
  1064. TimeGridView.prototype.getNowIndicatorUnit = function () {
  1065. return this.timeGrid.getNowIndicatorUnit();
  1066. };
  1067. // subclasses should implement
  1068. // renderNowIndicator(date: DateMarker) {
  1069. // }
  1070. TimeGridView.prototype.unrenderNowIndicator = function () {
  1071. this.timeGrid.unrenderNowIndicator();
  1072. };
  1073. /* Dimensions
  1074. ------------------------------------------------------------------------------------------------------------------*/
  1075. TimeGridView.prototype.updateSize = function (isResize, viewHeight, isAuto) {
  1076. _super.prototype.updateSize.call(this, isResize, viewHeight, isAuto); // will call updateBaseSize. important that executes first
  1077. this.timeGrid.updateSize(isResize);
  1078. if (this.dayGrid) {
  1079. this.dayGrid.updateSize(isResize);
  1080. }
  1081. };
  1082. // Adjusts the vertical dimensions of the view to the specified values
  1083. TimeGridView.prototype.updateBaseSize = function (isResize, viewHeight, isAuto) {
  1084. var _this = this;
  1085. var eventLimit;
  1086. var scrollerHeight;
  1087. var scrollbarWidths;
  1088. // make all axis cells line up
  1089. this.axisWidth = core.matchCellWidths(core.findElements(this.el, '.fc-axis'));
  1090. // hack to give the view some height prior to timeGrid's columns being rendered
  1091. // TODO: separate setting height from scroller VS timeGrid.
  1092. if (!this.timeGrid.colEls) {
  1093. if (!isAuto) {
  1094. scrollerHeight = this.computeScrollerHeight(viewHeight);
  1095. this.scroller.setHeight(scrollerHeight);
  1096. }
  1097. return;
  1098. }
  1099. // set of fake row elements that must compensate when scroller has scrollbars
  1100. var noScrollRowEls = core.findElements(this.el, '.fc-row').filter(function (node) {
  1101. return !_this.scroller.el.contains(node);
  1102. });
  1103. // reset all dimensions back to the original state
  1104. this.timeGrid.bottomRuleEl.style.display = 'none'; // will be shown later if this <hr> is necessary
  1105. this.scroller.clear(); // sets height to 'auto' and clears overflow
  1106. noScrollRowEls.forEach(core.uncompensateScroll);
  1107. // limit number of events in the all-day area
  1108. if (this.dayGrid) {
  1109. this.dayGrid.removeSegPopover(); // kill the "more" popover if displayed
  1110. eventLimit = this.opt('eventLimit');
  1111. if (eventLimit && typeof eventLimit !== 'number') {
  1112. eventLimit = TIMEGRID_ALL_DAY_EVENT_LIMIT; // make sure "auto" goes to a real number
  1113. }
  1114. if (eventLimit) {
  1115. this.dayGrid.limitRows(eventLimit);
  1116. }
  1117. }
  1118. if (!isAuto) { // should we force dimensions of the scroll container?
  1119. scrollerHeight = this.computeScrollerHeight(viewHeight);
  1120. this.scroller.setHeight(scrollerHeight);
  1121. scrollbarWidths = this.scroller.getScrollbarWidths();
  1122. if (scrollbarWidths.left || scrollbarWidths.right) { // using scrollbars?
  1123. // make the all-day and header rows lines up
  1124. noScrollRowEls.forEach(function (rowEl) {
  1125. core.compensateScroll(rowEl, scrollbarWidths);
  1126. });
  1127. // the scrollbar compensation might have changed text flow, which might affect height, so recalculate
  1128. // and reapply the desired height to the scroller.
  1129. scrollerHeight = this.computeScrollerHeight(viewHeight);
  1130. this.scroller.setHeight(scrollerHeight);
  1131. }
  1132. // guarantees the same scrollbar widths
  1133. this.scroller.lockOverflow(scrollbarWidths);
  1134. // if there's any space below the slats, show the horizontal rule.
  1135. // this won't cause any new overflow, because lockOverflow already called.
  1136. if (this.timeGrid.getTotalSlatHeight() < scrollerHeight) {
  1137. this.timeGrid.bottomRuleEl.style.display = '';
  1138. }
  1139. }
  1140. };
  1141. // given a desired total height of the view, returns what the height of the scroller should be
  1142. TimeGridView.prototype.computeScrollerHeight = function (viewHeight) {
  1143. return viewHeight -
  1144. core.subtractInnerElHeight(this.el, this.scroller.el); // everything that's NOT the scroller
  1145. };
  1146. /* Scroll
  1147. ------------------------------------------------------------------------------------------------------------------*/
  1148. // Computes the initial pre-configured scroll state prior to allowing the user to change it
  1149. TimeGridView.prototype.computeDateScroll = function (timeMs) {
  1150. var top = this.timeGrid.computeTimeTop(timeMs);
  1151. // zoom can give weird floating-point values. rather scroll a little bit further
  1152. top = Math.ceil(top);
  1153. if (top) {
  1154. top++; // to overcome top border that slots beyond the first have. looks better
  1155. }
  1156. return { top: top };
  1157. };
  1158. TimeGridView.prototype.queryDateScroll = function () {
  1159. return { top: this.scroller.getScrollTop() };
  1160. };
  1161. TimeGridView.prototype.applyDateScroll = function (scroll) {
  1162. if (scroll.top !== undefined) {
  1163. this.scroller.setScrollTop(scroll.top);
  1164. }
  1165. };
  1166. // Generates an HTML attribute string for setting the width of the axis, if it is known
  1167. TimeGridView.prototype.axisStyleAttr = function () {
  1168. if (this.axisWidth != null) {
  1169. return 'style="width:' + this.axisWidth + 'px"';
  1170. }
  1171. return '';
  1172. };
  1173. return TimeGridView;
  1174. }(core.View));
  1175. TimeGridView.prototype.usesMinMaxTime = true; // indicates that minTime/maxTime affects rendering
  1176. var SimpleTimeGrid = /** @class */ (function (_super) {
  1177. __extends(SimpleTimeGrid, _super);
  1178. function SimpleTimeGrid(context, timeGrid) {
  1179. var _this = _super.call(this, context, timeGrid.el) || this;
  1180. _this.buildDayRanges = core.memoize(buildDayRanges);
  1181. _this.slicer = new TimeGridSlicer();
  1182. _this.timeGrid = timeGrid;
  1183. context.calendar.registerInteractiveComponent(_this, {
  1184. el: _this.timeGrid.el
  1185. });
  1186. return _this;
  1187. }
  1188. SimpleTimeGrid.prototype.destroy = function () {
  1189. _super.prototype.destroy.call(this);
  1190. this.calendar.unregisterInteractiveComponent(this);
  1191. };
  1192. SimpleTimeGrid.prototype.render = function (props) {
  1193. var dateProfile = props.dateProfile, dayTable = props.dayTable;
  1194. var dayRanges = this.dayRanges = this.buildDayRanges(dayTable, dateProfile, this.dateEnv);
  1195. this.timeGrid.receiveProps(__assign({}, this.slicer.sliceProps(props, dateProfile, null, this.timeGrid, dayRanges), { dateProfile: dateProfile, cells: dayTable.cells[0] }));
  1196. };
  1197. SimpleTimeGrid.prototype.renderNowIndicator = function (date) {
  1198. this.timeGrid.renderNowIndicator(this.slicer.sliceNowDate(date, this.timeGrid, this.dayRanges), date);
  1199. };
  1200. SimpleTimeGrid.prototype.buildPositionCaches = function () {
  1201. this.timeGrid.buildPositionCaches();
  1202. };
  1203. SimpleTimeGrid.prototype.queryHit = function (positionLeft, positionTop) {
  1204. var rawHit = this.timeGrid.positionToHit(positionLeft, positionTop);
  1205. if (rawHit) {
  1206. return {
  1207. component: this.timeGrid,
  1208. dateSpan: rawHit.dateSpan,
  1209. dayEl: rawHit.dayEl,
  1210. rect: {
  1211. left: rawHit.relativeRect.left,
  1212. right: rawHit.relativeRect.right,
  1213. top: rawHit.relativeRect.top,
  1214. bottom: rawHit.relativeRect.bottom
  1215. },
  1216. layer: 0
  1217. };
  1218. }
  1219. };
  1220. return SimpleTimeGrid;
  1221. }(core.DateComponent));
  1222. function buildDayRanges(dayTable, dateProfile, dateEnv) {
  1223. var ranges = [];
  1224. for (var _i = 0, _a = dayTable.headerDates; _i < _a.length; _i++) {
  1225. var date = _a[_i];
  1226. ranges.push({
  1227. start: dateEnv.add(date, dateProfile.minTime),
  1228. end: dateEnv.add(date, dateProfile.maxTime)
  1229. });
  1230. }
  1231. return ranges;
  1232. }
  1233. var TimeGridSlicer = /** @class */ (function (_super) {
  1234. __extends(TimeGridSlicer, _super);
  1235. function TimeGridSlicer() {
  1236. return _super !== null && _super.apply(this, arguments) || this;
  1237. }
  1238. TimeGridSlicer.prototype.sliceRange = function (range, dayRanges) {
  1239. var segs = [];
  1240. for (var col = 0; col < dayRanges.length; col++) {
  1241. var segRange = core.intersectRanges(range, dayRanges[col]);
  1242. if (segRange) {
  1243. segs.push({
  1244. start: segRange.start,
  1245. end: segRange.end,
  1246. isStart: segRange.start.valueOf() === range.start.valueOf(),
  1247. isEnd: segRange.end.valueOf() === range.end.valueOf(),
  1248. col: col
  1249. });
  1250. }
  1251. }
  1252. return segs;
  1253. };
  1254. return TimeGridSlicer;
  1255. }(core.Slicer));
  1256. var TimeGridView$1 = /** @class */ (function (_super) {
  1257. __extends(TimeGridView, _super);
  1258. function TimeGridView(_context, viewSpec, dateProfileGenerator, parentEl) {
  1259. var _this = _super.call(this, _context, viewSpec, dateProfileGenerator, parentEl) || this;
  1260. _this.buildDayTable = core.memoize(buildDayTable);
  1261. if (_this.opt('columnHeader')) {
  1262. _this.header = new core.DayHeader(_this.context, _this.el.querySelector('.fc-head-container'));
  1263. }
  1264. _this.simpleTimeGrid = new SimpleTimeGrid(_this.context, _this.timeGrid);
  1265. if (_this.dayGrid) {
  1266. _this.simpleDayGrid = new daygrid.SimpleDayGrid(_this.context, _this.dayGrid);
  1267. }
  1268. return _this;
  1269. }
  1270. TimeGridView.prototype.destroy = function () {
  1271. _super.prototype.destroy.call(this);
  1272. if (this.header) {
  1273. this.header.destroy();
  1274. }
  1275. this.simpleTimeGrid.destroy();
  1276. if (this.simpleDayGrid) {
  1277. this.simpleDayGrid.destroy();
  1278. }
  1279. };
  1280. TimeGridView.prototype.render = function (props) {
  1281. _super.prototype.render.call(this, props); // for flags for updateSize
  1282. var dateProfile = this.props.dateProfile;
  1283. var dayTable = this.buildDayTable(dateProfile, this.dateProfileGenerator);
  1284. var splitProps = this.splitter.splitProps(props);
  1285. if (this.header) {
  1286. this.header.receiveProps({
  1287. dateProfile: dateProfile,
  1288. dates: dayTable.headerDates,
  1289. datesRepDistinctDays: true,
  1290. renderIntroHtml: this.renderHeadIntroHtml
  1291. });
  1292. }
  1293. this.simpleTimeGrid.receiveProps(__assign({}, splitProps['timed'], { dateProfile: dateProfile,
  1294. dayTable: dayTable }));
  1295. if (this.simpleDayGrid) {
  1296. this.simpleDayGrid.receiveProps(__assign({}, splitProps['allDay'], { dateProfile: dateProfile,
  1297. dayTable: dayTable, nextDayThreshold: this.nextDayThreshold, isRigid: false }));
  1298. }
  1299. };
  1300. TimeGridView.prototype.renderNowIndicator = function (date) {
  1301. this.simpleTimeGrid.renderNowIndicator(date);
  1302. };
  1303. return TimeGridView;
  1304. }(TimeGridView));
  1305. function buildDayTable(dateProfile, dateProfileGenerator) {
  1306. var daySeries = new core.DaySeries(dateProfile.renderRange, dateProfileGenerator);
  1307. return new core.DayTable(daySeries, false);
  1308. }
  1309. var main = core.createPlugin({
  1310. defaultView: 'timeGridWeek',
  1311. views: {
  1312. timeGrid: {
  1313. class: TimeGridView$1,
  1314. allDaySlot: true,
  1315. slotDuration: '00:30:00',
  1316. slotEventOverlap: true // a bad name. confused with overlap/constraint system
  1317. },
  1318. timeGridDay: {
  1319. type: 'timeGrid',
  1320. duration: { days: 1 }
  1321. },
  1322. timeGridWeek: {
  1323. type: 'timeGrid',
  1324. duration: { weeks: 1 }
  1325. }
  1326. }
  1327. });
  1328. exports.AbstractTimeGridView = TimeGridView;
  1329. exports.TimeGrid = TimeGrid;
  1330. exports.TimeGridSlicer = TimeGridSlicer;
  1331. exports.TimeGridView = TimeGridView$1;
  1332. exports.buildDayRanges = buildDayRanges;
  1333. exports.buildDayTable = buildDayTable;
  1334. exports.default = main;
  1335. Object.defineProperty(exports, '__esModule', { value: true });
  1336. }));