main.js 16 KB

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