No Description

main.esm.js 14KB

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