BasicView.ts 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. import * as $ from 'jquery'
  2. import {
  3. matchCellWidths,
  4. uncompensateScroll,
  5. compensateScroll,
  6. subtractInnerElHeight,
  7. distributeHeight,
  8. undistributeHeight,
  9. htmlEscape
  10. } from '../util'
  11. import Scroller from '../common/Scroller'
  12. import View from '../View'
  13. import BasicViewDateProfileGenerator from './BasicViewDateProfileGenerator'
  14. import DayGrid from './DayGrid'
  15. /* An abstract class for the "basic" views, as well as month view. Renders one or more rows of day cells.
  16. ----------------------------------------------------------------------------------------------------------------------*/
  17. // It is a manager for a DayGrid subcomponent, which does most of the heavy lifting.
  18. // It is responsible for managing width/height.
  19. export default class BasicView extends View {
  20. // initialized after class
  21. dateProfileGeneratorClass: any
  22. dayGridClass: any // class the dayGrid will be instantiated from (overridable by subclasses)
  23. scroller: any
  24. dayGrid: any // the main subcomponent that does most of the heavy lifting
  25. weekNumberWidth: any // width of all the week-number cells running down the side
  26. constructor(calendar, viewSpec) {
  27. super(calendar, viewSpec);
  28. this.dayGrid = this.instantiateDayGrid();
  29. this.dayGrid.isRigid = this.hasRigidRows();
  30. if (this.opt('weekNumbers')) {
  31. if (this.opt('weekNumbersWithinDays')) {
  32. this.dayGrid.cellWeekNumbersVisible = true;
  33. this.dayGrid.colWeekNumbersVisible = false;
  34. }
  35. else {
  36. this.dayGrid.cellWeekNumbersVisible = false;
  37. this.dayGrid.colWeekNumbersVisible = true;
  38. };
  39. }
  40. this.addChild(this.dayGrid);
  41. this.scroller = new Scroller({
  42. overflowX: 'hidden',
  43. overflowY: 'auto'
  44. });
  45. }
  46. // Generates the DayGrid object this view needs. Draws from this.dayGridClass
  47. instantiateDayGrid() {
  48. // generate a subclass on the fly with BasicView-specific behavior
  49. // TODO: cache this subclass
  50. var subclass: any = makeDayGridSubclass(this.dayGridClass)
  51. return new subclass(this);
  52. }
  53. executeDateRender(dateProfile) {
  54. this.dayGrid.breakOnWeeks = /year|month|week/.test(dateProfile.currentRangeUnit);
  55. super.executeDateRender(dateProfile);
  56. }
  57. renderSkeleton() {
  58. var dayGridContainerEl;
  59. var dayGridEl;
  60. this.el.addClass('fc-basic-view').html(this.renderSkeletonHtml());
  61. this.scroller.render();
  62. dayGridContainerEl = this.scroller.el.addClass('fc-day-grid-container');
  63. dayGridEl = $('<div class="fc-day-grid" />').appendTo(dayGridContainerEl);
  64. this.el.find('.fc-body > tr > td').append(dayGridContainerEl);
  65. this.dayGrid.headContainerEl = this.el.find('.fc-head-container');
  66. this.dayGrid.setElement(dayGridEl);
  67. }
  68. unrenderSkeleton() {
  69. this.dayGrid.removeElement();
  70. this.scroller.destroy();
  71. }
  72. // Builds the HTML skeleton for the view.
  73. // The day-grid component will render inside of a container defined by this HTML.
  74. renderSkeletonHtml() {
  75. var theme = this.calendar.theme;
  76. return '' +
  77. '<table class="' + theme.getClass('tableGrid') + '">' +
  78. (this.opt('columnHeader') ?
  79. '<thead class="fc-head">' +
  80. '<tr>' +
  81. '<td class="fc-head-container ' + theme.getClass('widgetHeader') + '">&nbsp;</td>' +
  82. '</tr>' +
  83. '</thead>' :
  84. ''
  85. ) +
  86. '<tbody class="fc-body">' +
  87. '<tr>' +
  88. '<td class="' + theme.getClass('widgetContent') + '"></td>' +
  89. '</tr>' +
  90. '</tbody>' +
  91. '</table>';
  92. }
  93. // Generates an HTML attribute string for setting the width of the week number column, if it is known
  94. weekNumberStyleAttr() {
  95. if (this.weekNumberWidth != null) {
  96. return 'style="width:' + this.weekNumberWidth + 'px"';
  97. }
  98. return '';
  99. }
  100. // Determines whether each row should have a constant height
  101. hasRigidRows() {
  102. var eventLimit = this.opt('eventLimit');
  103. return eventLimit && typeof eventLimit !== 'number';
  104. }
  105. /* Dimensions
  106. ------------------------------------------------------------------------------------------------------------------*/
  107. // Refreshes the horizontal dimensions of the view
  108. updateSize(totalHeight, isAuto, isResize) {
  109. var eventLimit = this.opt('eventLimit');
  110. var headRowEl = this.dayGrid.headContainerEl.find('.fc-row');
  111. var scrollerHeight;
  112. var scrollbarWidths;
  113. // hack to give the view some height prior to dayGrid's columns being rendered
  114. // TODO: separate setting height from scroller VS dayGrid.
  115. if (!this.dayGrid.rowEls) {
  116. if (!isAuto) {
  117. scrollerHeight = this.computeScrollerHeight(totalHeight);
  118. this.scroller.setHeight(scrollerHeight);
  119. }
  120. return;
  121. }
  122. super.updateSize(totalHeight, isAuto, isResize);
  123. if (this.dayGrid.colWeekNumbersVisible) {
  124. // Make sure all week number cells running down the side have the same width.
  125. // Record the width for cells created later.
  126. this.weekNumberWidth = matchCellWidths(
  127. this.el.find('.fc-week-number')
  128. );
  129. }
  130. // reset all heights to be natural
  131. this.scroller.clear();
  132. uncompensateScroll(headRowEl);
  133. this.dayGrid.removeSegPopover(); // kill the "more" popover if displayed
  134. // is the event limit a constant level number?
  135. if (eventLimit && typeof eventLimit === 'number') {
  136. this.dayGrid.limitRows(eventLimit); // limit the levels first so the height can redistribute after
  137. }
  138. // distribute the height to the rows
  139. // (totalHeight is a "recommended" value if isAuto)
  140. scrollerHeight = this.computeScrollerHeight(totalHeight);
  141. this.setGridHeight(scrollerHeight, isAuto);
  142. // is the event limit dynamically calculated?
  143. if (eventLimit && typeof eventLimit !== 'number') {
  144. this.dayGrid.limitRows(eventLimit); // limit the levels after the grid's row heights have been set
  145. }
  146. if (!isAuto) { // should we force dimensions of the scroll container?
  147. this.scroller.setHeight(scrollerHeight);
  148. scrollbarWidths = this.scroller.getScrollbarWidths();
  149. if (scrollbarWidths.left || scrollbarWidths.right) { // using scrollbars?
  150. compensateScroll(headRowEl, scrollbarWidths);
  151. // doing the scrollbar compensation might have created text overflow which created more height. redo
  152. scrollerHeight = this.computeScrollerHeight(totalHeight);
  153. this.scroller.setHeight(scrollerHeight);
  154. }
  155. // guarantees the same scrollbar widths
  156. this.scroller.lockOverflow(scrollbarWidths);
  157. }
  158. }
  159. // given a desired total height of the view, returns what the height of the scroller should be
  160. computeScrollerHeight(totalHeight) {
  161. return totalHeight -
  162. subtractInnerElHeight(this.el, this.scroller.el); // everything that's NOT the scroller
  163. }
  164. // Sets the height of just the DayGrid component in this view
  165. setGridHeight(height, isAuto) {
  166. if (isAuto) {
  167. undistributeHeight(this.dayGrid.rowEls); // let the rows be their natural height with no expanding
  168. }
  169. else {
  170. distributeHeight(this.dayGrid.rowEls, height, true); // true = compensate for height-hogging rows
  171. }
  172. }
  173. /* Scroll
  174. ------------------------------------------------------------------------------------------------------------------*/
  175. computeInitialDateScroll() {
  176. return { top: 0 };
  177. }
  178. queryDateScroll() {
  179. return { top: this.scroller.getScrollTop() };
  180. }
  181. applyDateScroll(scroll) {
  182. if (scroll.top !== undefined) {
  183. this.scroller.setScrollTop(scroll.top);
  184. }
  185. }
  186. }
  187. BasicView.prototype.dateProfileGeneratorClass = BasicViewDateProfileGenerator
  188. BasicView.prototype.dayGridClass = DayGrid
  189. // customize the rendering behavior of BasicView's dayGrid
  190. function makeDayGridSubclass(SuperClass) {
  191. return class SubClass extends SuperClass {
  192. colWeekNumbersVisible: boolean = false // display week numbers along the side?
  193. // Generates the HTML that will go before the day-of week header cells
  194. renderHeadIntroHtml() {
  195. var view = this.view;
  196. if (this.colWeekNumbersVisible) {
  197. return '' +
  198. '<th class="fc-week-number ' + view.calendar.theme.getClass('widgetHeader') + '" ' + view.weekNumberStyleAttr() + '>' +
  199. '<span>' + // needed for matchCellWidths
  200. htmlEscape(this.opt('weekNumberTitle')) +
  201. '</span>' +
  202. '</th>';
  203. }
  204. return '';
  205. }
  206. // Generates the HTML that will go before content-skeleton cells that display the day/week numbers
  207. renderNumberIntroHtml(row) {
  208. var view = this.view;
  209. var weekStart = this.getCellDate(row, 0);
  210. if (this.colWeekNumbersVisible) {
  211. return '' +
  212. '<td class="fc-week-number" ' + view.weekNumberStyleAttr() + '>' +
  213. view.buildGotoAnchorHtml( // aside from link, important for matchCellWidths
  214. { date: weekStart, type: 'week', forceOff: this.colCnt === 1 },
  215. weekStart.format('w') // inner HTML
  216. ) +
  217. '</td>';
  218. }
  219. return '';
  220. }
  221. // Generates the HTML that goes before the day bg cells for each day-row
  222. renderBgIntroHtml() {
  223. var view = this.view;
  224. if (this.colWeekNumbersVisible) {
  225. return '<td class="fc-week-number ' + view.calendar.theme.getClass('widgetContent') + '" ' +
  226. view.weekNumberStyleAttr() + '></td>';
  227. }
  228. return '';
  229. }
  230. // Generates the HTML that goes before every other type of row generated by DayGrid.
  231. // Affects helper-skeleton and highlight-skeleton rows.
  232. renderIntroHtml() {
  233. var view = this.view;
  234. if (this.colWeekNumbersVisible) {
  235. return '<td class="fc-week-number" ' + view.weekNumberStyleAttr() + '></td>';
  236. }
  237. return '';
  238. }
  239. getIsNumbersVisible() {
  240. return DayGrid.prototype.getIsNumbersVisible.apply(this, arguments) || this.colWeekNumbersVisible;
  241. }
  242. }
  243. }