main.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. /*!
  2. FullCalendar List View Plugin v4.4.2
  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')) :
  8. typeof define === 'function' && define.amd ? define(['exports', '@fullcalendar/core'], factory) :
  9. (global = global || self, factory(global.FullCalendarList = {}, global.FullCalendar));
  10. }(this, function (exports, core) { 'use strict';
  11. /*! *****************************************************************************
  12. Copyright (c) Microsoft Corporation.
  13. Permission to use, copy, modify, and/or distribute this software for any
  14. purpose with or without fee is hereby granted.
  15. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  16. REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  17. AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  18. INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  19. LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  20. OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  21. PERFORMANCE OF THIS SOFTWARE.
  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 ListEventRenderer = /** @class */ (function (_super) {
  36. __extends(ListEventRenderer, _super);
  37. function ListEventRenderer(listView) {
  38. var _this = _super.call(this) || this;
  39. _this.listView = listView;
  40. return _this;
  41. }
  42. ListEventRenderer.prototype.attachSegs = function (segs) {
  43. if (!segs.length) {
  44. this.listView.renderEmptyMessage();
  45. }
  46. else {
  47. this.listView.renderSegList(segs);
  48. }
  49. };
  50. ListEventRenderer.prototype.detachSegs = function () {
  51. };
  52. // generates the HTML for a single event row
  53. ListEventRenderer.prototype.renderSegHtml = function (seg) {
  54. var _a = this.context, theme = _a.theme, options = _a.options;
  55. var eventRange = seg.eventRange;
  56. var eventDef = eventRange.def;
  57. var eventInstance = eventRange.instance;
  58. var eventUi = eventRange.ui;
  59. var url = eventDef.url;
  60. var classes = ['fc-list-item'].concat(eventUi.classNames);
  61. var bgColor = eventUi.backgroundColor;
  62. var timeHtml;
  63. if (eventDef.allDay) {
  64. timeHtml = core.getAllDayHtml(options);
  65. }
  66. else if (core.isMultiDayRange(eventRange.range)) {
  67. if (seg.isStart) {
  68. timeHtml = core.htmlEscape(this._getTimeText(eventInstance.range.start, seg.end, false // allDay
  69. ));
  70. }
  71. else if (seg.isEnd) {
  72. timeHtml = core.htmlEscape(this._getTimeText(seg.start, eventInstance.range.end, false // allDay
  73. ));
  74. }
  75. else { // inner segment that lasts the whole day
  76. timeHtml = core.getAllDayHtml(options);
  77. }
  78. }
  79. else {
  80. // Display the normal time text for the *event's* times
  81. timeHtml = core.htmlEscape(this.getTimeText(eventRange));
  82. }
  83. if (url) {
  84. classes.push('fc-has-url');
  85. }
  86. return '<tr class="' + classes.join(' ') + '">' +
  87. (this.displayEventTime ?
  88. '<td class="fc-list-item-time ' + theme.getClass('widgetContent') + '">' +
  89. (timeHtml || '') +
  90. '</td>' :
  91. '') +
  92. '<td class="fc-list-item-marker ' + theme.getClass('widgetContent') + '">' +
  93. '<span class="fc-event-dot"' +
  94. (bgColor ?
  95. ' style="background-color:' + bgColor + '"' :
  96. '') +
  97. '></span>' +
  98. '</td>' +
  99. '<td class="fc-list-item-title ' + theme.getClass('widgetContent') + '">' +
  100. '<a' + (url ? ' href="' + core.htmlEscape(url) + '"' : '') + '>' +
  101. core.htmlEscape(eventDef.title || '') +
  102. '</a>' +
  103. '</td>' +
  104. '</tr>';
  105. };
  106. // like "4:00am"
  107. ListEventRenderer.prototype.computeEventTimeFormat = function () {
  108. return {
  109. hour: 'numeric',
  110. minute: '2-digit',
  111. meridiem: 'short'
  112. };
  113. };
  114. return ListEventRenderer;
  115. }(core.FgEventRenderer));
  116. /*
  117. Responsible for the scroller, and forwarding event-related actions into the "grid".
  118. */
  119. var ListView = /** @class */ (function (_super) {
  120. __extends(ListView, _super);
  121. function ListView(viewSpec, parentEl) {
  122. var _this = _super.call(this, viewSpec, parentEl) || this;
  123. _this.computeDateVars = core.memoize(computeDateVars);
  124. _this.eventStoreToSegs = core.memoize(_this._eventStoreToSegs);
  125. _this.renderSkeleton = core.memoizeRendering(_this._renderSkeleton, _this._unrenderSkeleton);
  126. var eventRenderer = _this.eventRenderer = new ListEventRenderer(_this);
  127. _this.renderContent = core.memoizeRendering(eventRenderer.renderSegs.bind(eventRenderer), eventRenderer.unrender.bind(eventRenderer), [_this.renderSkeleton]);
  128. return _this;
  129. }
  130. ListView.prototype.firstContext = function (context) {
  131. context.calendar.registerInteractiveComponent(this, {
  132. el: this.el
  133. // TODO: make aware that it doesn't do Hits
  134. });
  135. };
  136. ListView.prototype.render = function (props, context) {
  137. _super.prototype.render.call(this, props, context);
  138. var _a = this.computeDateVars(props.dateProfile), dayDates = _a.dayDates, dayRanges = _a.dayRanges;
  139. this.dayDates = dayDates;
  140. this.renderSkeleton(context);
  141. this.renderContent(context, this.eventStoreToSegs(props.eventStore, props.eventUiBases, dayRanges));
  142. };
  143. ListView.prototype.destroy = function () {
  144. _super.prototype.destroy.call(this);
  145. this.renderSkeleton.unrender();
  146. this.renderContent.unrender();
  147. this.context.calendar.unregisterInteractiveComponent(this);
  148. };
  149. ListView.prototype._renderSkeleton = function (context) {
  150. var theme = context.theme;
  151. this.el.classList.add('fc-list-view');
  152. var listViewClassNames = (theme.getClass('listView') || '').split(' '); // wish we didn't have to do this
  153. for (var _i = 0, listViewClassNames_1 = listViewClassNames; _i < listViewClassNames_1.length; _i++) {
  154. var listViewClassName = listViewClassNames_1[_i];
  155. if (listViewClassName) { // in case input was empty string
  156. this.el.classList.add(listViewClassName);
  157. }
  158. }
  159. this.scroller = new core.ScrollComponent('hidden', // overflow x
  160. 'auto' // overflow y
  161. );
  162. this.el.appendChild(this.scroller.el);
  163. this.contentEl = this.scroller.el; // shortcut
  164. };
  165. ListView.prototype._unrenderSkeleton = function () {
  166. // TODO: remove classNames
  167. this.scroller.destroy(); // will remove the Grid too
  168. };
  169. ListView.prototype.updateSize = function (isResize, viewHeight, isAuto) {
  170. _super.prototype.updateSize.call(this, isResize, viewHeight, isAuto);
  171. this.eventRenderer.computeSizes(isResize);
  172. this.eventRenderer.assignSizes(isResize);
  173. this.scroller.clear(); // sets height to 'auto' and clears overflow
  174. if (!isAuto) {
  175. this.scroller.setHeight(this.computeScrollerHeight(viewHeight));
  176. }
  177. };
  178. ListView.prototype.computeScrollerHeight = function (viewHeight) {
  179. return viewHeight -
  180. core.subtractInnerElHeight(this.el, this.scroller.el); // everything that's NOT the scroller
  181. };
  182. ListView.prototype._eventStoreToSegs = function (eventStore, eventUiBases, dayRanges) {
  183. return this.eventRangesToSegs(core.sliceEventStore(eventStore, eventUiBases, this.props.dateProfile.activeRange, this.context.nextDayThreshold).fg, dayRanges);
  184. };
  185. ListView.prototype.eventRangesToSegs = function (eventRanges, dayRanges) {
  186. var segs = [];
  187. for (var _i = 0, eventRanges_1 = eventRanges; _i < eventRanges_1.length; _i++) {
  188. var eventRange = eventRanges_1[_i];
  189. segs.push.apply(segs, this.eventRangeToSegs(eventRange, dayRanges));
  190. }
  191. return segs;
  192. };
  193. ListView.prototype.eventRangeToSegs = function (eventRange, dayRanges) {
  194. var _a = this.context, dateEnv = _a.dateEnv, nextDayThreshold = _a.nextDayThreshold;
  195. var range = eventRange.range;
  196. var allDay = eventRange.def.allDay;
  197. var dayIndex;
  198. var segRange;
  199. var seg;
  200. var segs = [];
  201. for (dayIndex = 0; dayIndex < dayRanges.length; dayIndex++) {
  202. segRange = core.intersectRanges(range, dayRanges[dayIndex]);
  203. if (segRange) {
  204. seg = {
  205. component: this,
  206. eventRange: eventRange,
  207. start: segRange.start,
  208. end: segRange.end,
  209. isStart: eventRange.isStart && segRange.start.valueOf() === range.start.valueOf(),
  210. isEnd: eventRange.isEnd && segRange.end.valueOf() === range.end.valueOf(),
  211. dayIndex: dayIndex
  212. };
  213. segs.push(seg);
  214. // detect when range won't go fully into the next day,
  215. // and mutate the latest seg to the be the end.
  216. if (!seg.isEnd && !allDay &&
  217. dayIndex + 1 < dayRanges.length &&
  218. range.end <
  219. dateEnv.add(dayRanges[dayIndex + 1].start, nextDayThreshold)) {
  220. seg.end = range.end;
  221. seg.isEnd = true;
  222. break;
  223. }
  224. }
  225. }
  226. return segs;
  227. };
  228. ListView.prototype.renderEmptyMessage = function () {
  229. this.contentEl.innerHTML =
  230. '<div class="fc-list-empty-wrap2">' + // TODO: try less wraps
  231. '<div class="fc-list-empty-wrap1">' +
  232. '<div class="fc-list-empty">' +
  233. core.htmlEscape(this.context.options.noEventsMessage) +
  234. '</div>' +
  235. '</div>' +
  236. '</div>';
  237. };
  238. // called by ListEventRenderer
  239. ListView.prototype.renderSegList = function (allSegs) {
  240. var theme = this.context.theme;
  241. var segsByDay = this.groupSegsByDay(allSegs); // sparse array
  242. var dayIndex;
  243. var daySegs;
  244. var i;
  245. var tableEl = core.htmlToElement('<table class="fc-list-table ' + theme.getClass('tableList') + '"><tbody></tbody></table>');
  246. var tbodyEl = tableEl.querySelector('tbody');
  247. for (dayIndex = 0; dayIndex < segsByDay.length; dayIndex++) {
  248. daySegs = segsByDay[dayIndex];
  249. if (daySegs) { // sparse array, so might be undefined
  250. // append a day header
  251. tbodyEl.appendChild(this.buildDayHeaderRow(this.dayDates[dayIndex]));
  252. daySegs = this.eventRenderer.sortEventSegs(daySegs);
  253. for (i = 0; i < daySegs.length; i++) {
  254. tbodyEl.appendChild(daySegs[i].el); // append event row
  255. }
  256. }
  257. }
  258. this.contentEl.innerHTML = '';
  259. this.contentEl.appendChild(tableEl);
  260. };
  261. // Returns a sparse array of arrays, segs grouped by their dayIndex
  262. ListView.prototype.groupSegsByDay = function (segs) {
  263. var segsByDay = []; // sparse array
  264. var i;
  265. var seg;
  266. for (i = 0; i < segs.length; i++) {
  267. seg = segs[i];
  268. (segsByDay[seg.dayIndex] || (segsByDay[seg.dayIndex] = []))
  269. .push(seg);
  270. }
  271. return segsByDay;
  272. };
  273. // generates the HTML for the day headers that live amongst the event rows
  274. ListView.prototype.buildDayHeaderRow = function (dayDate) {
  275. var _a = this.context, theme = _a.theme, dateEnv = _a.dateEnv, options = _a.options;
  276. var mainFormat = core.createFormatter(options.listDayFormat); // TODO: cache
  277. var altFormat = core.createFormatter(options.listDayAltFormat); // TODO: cache
  278. return core.createElement('tr', {
  279. className: 'fc-list-heading',
  280. 'data-date': dateEnv.formatIso(dayDate, { omitTime: true })
  281. }, '<td class="' + (theme.getClass('tableListHeading') ||
  282. theme.getClass('widgetHeader')) + '" colspan="3">' +
  283. (mainFormat ?
  284. core.buildGotoAnchorHtml(options, dateEnv, dayDate, { 'class': 'fc-list-heading-main' }, core.htmlEscape(dateEnv.format(dayDate, mainFormat)) // inner HTML
  285. ) :
  286. '') +
  287. (altFormat ?
  288. core.buildGotoAnchorHtml(options, dateEnv, dayDate, { 'class': 'fc-list-heading-alt' }, core.htmlEscape(dateEnv.format(dayDate, altFormat)) // inner HTML
  289. ) :
  290. '') +
  291. '</td>');
  292. };
  293. return ListView;
  294. }(core.View));
  295. ListView.prototype.fgSegSelector = '.fc-list-item'; // which elements accept event actions
  296. function computeDateVars(dateProfile) {
  297. var dayStart = core.startOfDay(dateProfile.renderRange.start);
  298. var viewEnd = dateProfile.renderRange.end;
  299. var dayDates = [];
  300. var dayRanges = [];
  301. while (dayStart < viewEnd) {
  302. dayDates.push(dayStart);
  303. dayRanges.push({
  304. start: dayStart,
  305. end: core.addDays(dayStart, 1)
  306. });
  307. dayStart = core.addDays(dayStart, 1);
  308. }
  309. return { dayDates: dayDates, dayRanges: dayRanges };
  310. }
  311. var main = core.createPlugin({
  312. views: {
  313. list: {
  314. class: ListView,
  315. buttonTextKey: 'list',
  316. listDayFormat: { month: 'long', day: 'numeric', year: 'numeric' } // like "January 1, 2016"
  317. },
  318. listDay: {
  319. type: 'list',
  320. duration: { days: 1 },
  321. listDayFormat: { weekday: 'long' } // day-of-week is all we need. full date is probably in header
  322. },
  323. listWeek: {
  324. type: 'list',
  325. duration: { weeks: 1 },
  326. listDayFormat: { weekday: 'long' },
  327. listDayAltFormat: { month: 'long', day: 'numeric', year: 'numeric' }
  328. },
  329. listMonth: {
  330. type: 'list',
  331. duration: { month: 1 },
  332. listDayAltFormat: { weekday: 'long' } // day-of-week is nice-to-have
  333. },
  334. listYear: {
  335. type: 'list',
  336. duration: { year: 1 },
  337. listDayAltFormat: { weekday: 'long' } // day-of-week is nice-to-have
  338. }
  339. }
  340. });
  341. exports.ListView = ListView;
  342. exports.default = main;
  343. Object.defineProperty(exports, '__esModule', { value: true });
  344. }));