DayTile.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. import DateComponent, { Seg } from '../component/DateComponent'
  2. import SimpleDayGridEventRenderer from './SimpleDayGridEventRenderer'
  3. import { htmlEscape } from '../util/html'
  4. import { createFormatter } from '../datelib/formatting'
  5. import { Hit } from '../interactions/HitDragging'
  6. import OffsetTracker from '../common/OffsetTracker'
  7. import { computeRect } from '../util/dom-geom'
  8. import { Rect, pointInsideRect } from '../util/geom'
  9. import { addDays, DateMarker } from '../datelib/marker'
  10. import { removeElement } from '../util/dom-manip'
  11. import { ComponentContext } from '../component/Component'
  12. import { EventInstanceHash } from '../structs/event'
  13. import { memoizeRendering, MemoizedRendering } from '../component/memoized-rendering'
  14. export interface DayTileProps {
  15. date: DateMarker
  16. fgSegs: Seg[]
  17. eventSelection: string
  18. eventDragInstances: EventInstanceHash
  19. eventResizeInstances: EventInstanceHash
  20. }
  21. export default class DayTile extends DateComponent<DayTileProps> {
  22. segContainerEl: HTMLElement
  23. width: number
  24. height: number
  25. offsetTracker: OffsetTracker // TODO: abstraction for tracking dims of whole element rect
  26. private renderFrame: MemoizedRendering<[DateMarker]>
  27. private renderFgEvents: MemoizedRendering<[Seg[]]>
  28. private renderEventSelection: MemoizedRendering<[string]>
  29. private renderEventDrag: MemoizedRendering<[EventInstanceHash]>
  30. private renderEventResize: MemoizedRendering<[EventInstanceHash]>
  31. constructor(context: ComponentContext, el: HTMLElement) {
  32. super(context, el)
  33. let eventRenderer = this.eventRenderer = new DayTileEventRenderer(this)
  34. let renderFrame = this.renderFrame = memoizeRendering(
  35. this._renderFrame
  36. )
  37. this.renderFgEvents = memoizeRendering(
  38. eventRenderer.renderSegs.bind(eventRenderer),
  39. eventRenderer.unrender.bind(eventRenderer),
  40. [ renderFrame ]
  41. )
  42. this.renderEventSelection = memoizeRendering(
  43. eventRenderer.selectByInstanceId.bind(eventRenderer),
  44. eventRenderer.unselectByInstanceId.bind(eventRenderer),
  45. [ this.renderFgEvents ]
  46. )
  47. this.renderEventDrag = memoizeRendering(
  48. eventRenderer.hideByHash.bind(eventRenderer),
  49. eventRenderer.showByHash.bind(eventRenderer),
  50. [ renderFrame ]
  51. )
  52. this.renderEventResize = memoizeRendering(
  53. eventRenderer.hideByHash.bind(eventRenderer),
  54. eventRenderer.showByHash.bind(eventRenderer),
  55. [ renderFrame ]
  56. )
  57. }
  58. render(props: DayTileProps) {
  59. this.renderFrame(props.date)
  60. this.renderFgEvents(props.fgSegs)
  61. this.renderEventSelection(props.eventSelection)
  62. this.renderEventDrag(props.eventDragInstances)
  63. this.renderEventResize(props.eventResizeInstances)
  64. }
  65. destroy() {
  66. super.destroy()
  67. this.renderFrame.unrender() // should unrender everything else
  68. }
  69. _renderFrame(date: DateMarker) {
  70. let { theme, dateEnv } = this
  71. let title = dateEnv.format(
  72. date,
  73. createFormatter(this.opt('dayPopoverFormat')) // TODO: cache
  74. )
  75. this.el.innerHTML =
  76. '<div class="fc-header ' + theme.getClass('popoverHeader') + '">' +
  77. '<span class="fc-close ' + theme.getIconClass('close') + '"></span>' +
  78. '<span class="fc-title">' +
  79. htmlEscape(title) +
  80. '</span>' +
  81. '<div class="fc-clear"></div>' +
  82. '</div>' +
  83. '<div class="fc-body ' + theme.getClass('popoverContent') + '">' +
  84. '<div class="fc-event-container"></div>' +
  85. '</div>'
  86. this.segContainerEl = this.el.querySelector('.fc-event-container')
  87. }
  88. prepareHits() {
  89. let rect = computeRect(this.el)
  90. this.width = rect.right - rect.left
  91. this.height = rect.bottom - rect.top
  92. this.offsetTracker = new OffsetTracker(this.el)
  93. }
  94. releaseHits() {
  95. this.offsetTracker.destroy()
  96. }
  97. queryHit(leftOffset, topOffset): Hit | null {
  98. let rectLeft = this.offsetTracker.computeLeft()
  99. let rectTop = this.offsetTracker.computeTop()
  100. let rect: Rect = {
  101. left: rectLeft,
  102. right: rectLeft + this.width,
  103. top: rectTop,
  104. bottom: rectTop + this.height
  105. }
  106. if (pointInsideRect({ left: leftOffset, top: topOffset }, rect)) {
  107. let date = (this.props as any).date // HACK
  108. return {
  109. component: this,
  110. dateSpan: {
  111. allDay: true,
  112. range: { start: date, end: addDays(date, 1) }
  113. },
  114. dayEl: this.el,
  115. rect: rect,
  116. layer: 1
  117. }
  118. }
  119. return null
  120. }
  121. }
  122. DayTile.prototype.isInteractable = true
  123. DayTile.prototype.useEventCenter = false
  124. export class DayTileEventRenderer extends SimpleDayGridEventRenderer {
  125. dayTile: DayTile
  126. constructor(dayTile) {
  127. super(dayTile.context)
  128. this.dayTile = dayTile
  129. }
  130. attachSegs(segs: Seg[]) {
  131. for (let seg of segs) {
  132. this.dayTile.segContainerEl.appendChild(seg.el)
  133. }
  134. }
  135. detachSegs(segs: Seg[]) {
  136. for (let seg of segs) {
  137. removeElement(seg.el)
  138. }
  139. }
  140. }