Explorar o código

remove jquery from popover

Adam Shaw %!s(int64=8) %!d(string=hai) anos
pai
achega
c8a999c6bc
Modificáronse 4 ficheiros con 81 adicións e 47 borrados
  1. 5 4
      src/basic/DayGrid.ts
  2. 58 43
      src/common/Popover.ts
  3. 1 0
      src/util.ts
  4. 17 0
      src/util/dom.ts

+ 5 - 4
src/basic/DayGrid.ts

@@ -647,6 +647,7 @@ export default class DayGrid extends InteractiveDateComponent {
     let moreWrap = moreLink.parent() // the <div> wrapper around the <a>
     let topEl // the element we want to match the top coordinate of
     let options
+    let themeClass = view.calendar.theme.getClass('popover')
 
     if (this.rowCnt === 1) {
       topEl = view.el // will cause the popover to cover any sort of header
@@ -655,9 +656,9 @@ export default class DayGrid extends InteractiveDateComponent {
     }
 
     options = {
-      className: 'fc-more-popover ' + view.calendar.theme.getClass('popover'),
-      content: this.renderSegPopoverContent(row, col, segs),
-      parentEl: view.el, // attach to root of view. guarantees outside of scrollbars.
+      className: 'fc-more-popover' + (themeClass ? ' ' + themeClass : ''),
+      content: this.renderSegPopoverContent(row, col, segs).toArray(),
+      parentEl: view.el[0], // attach to root of view. guarantees outside of scrollbars.
       top: topEl.offset().top,
       autoHide: true, // when the user clicks elsewhere, hide the popover
       viewportConstrain: this.opt('popoverViewportConstrain'),
@@ -686,7 +687,7 @@ export default class DayGrid extends InteractiveDateComponent {
 
     // the popover doesn't live within the grid's container element, and thus won't get the event
     // delegated-handlers for free. attach event-related handlers to the popover.
-    this.bindAllSegHandlersToEl(this.segPopover.el)
+    this.bindAllSegHandlersToEl($(this.segPopover.el))
 
     this.triggerAfterEventSegsRendered(segs)
   }

+ 58 - 43
src/common/Popover.ts

@@ -15,8 +15,19 @@ Options:
 
 import * as $ from 'jquery'
 import { getScrollParent } from '../util'
+import { listenViaDelegation } from '../util/dom'
 import { default as ListenerMixin, ListenerInterface } from './ListenerMixin'
 
+export interface PopoverOptions {
+  className?: string
+  content?: HTMLElement[]
+  parentEl: HTMLElement
+  autoHide?: boolean
+  top?: number
+  left?: number
+  right?: number
+  viewportConstrain?: boolean
+}
 
 export default class Popover {
 
@@ -24,13 +35,13 @@ export default class Popover {
   stopListeningTo: ListenerInterface['stopListeningTo']
 
   isHidden: boolean = true
-  options: any
-  el: JQuery // the container element for the popover. generated by this object
+  options: PopoverOptions
+  el: HTMLElement // the container element for the popover. generated by this object
   margin: number = 10 // the space required between the popover and the edges of the scroll container
 
 
-  constructor(options) {
-    this.options = options || {}
+  constructor(options: PopoverOptions) {
+    this.options = options
   }
 
 
@@ -40,7 +51,7 @@ export default class Popover {
       if (!this.el) {
         this.render()
       }
-      this.el.show()
+      this.el.style.display = ''
       this.position()
       this.isHidden = false
       this.trigger('show')
@@ -51,7 +62,7 @@ export default class Popover {
   // Hides the popover, through CSS, but does not remove it from the DOM
   hide() {
     if (!this.isHidden) {
-      this.el.hide()
+      this.el.style.display = 'none'
       this.isHidden = true
       this.trigger('hide')
     }
@@ -62,19 +73,23 @@ export default class Popover {
   render() {
     let options = this.options
 
-    this.el = $('<div class="fc-popover"/>')
-      .addClass(options.className || '')
-      .css({
-        // position initially to the top left to avoid creating scrollbars
-        top: 0,
-        left: 0
-      })
-      .append(options.content)
-      .appendTo(options.parentEl)
+    let el = this.el = document.createElement('div')
+    el.classList.add('fc-popover')
+    if (options.className) {
+      el.classList.add(options.className)
+    }
+    el.style.top = '0'
+    el.style.left = '0'
+
+    options.content.forEach(function(node) {
+      el.appendChild(node)
+    })
+
+    options.parentEl.appendChild(el)
 
     // when a click happens on anything inside with a 'fc-close' className, hide the popover
-    this.el.on('click', '.fc-close', () => {
-      this.hide()
+    listenViaDelegation(el, 'click', 'fc-close', function(ev) {
+      el.style.display = 'none'
     })
 
     if (options.autoHide) {
@@ -108,14 +123,10 @@ export default class Popover {
   // Positions the popover optimally, using the top/left/right options
   position() {
     let options = this.options
-    let origin = this.el.offsetParent().offset()
-    let width = this.el.outerWidth()
-    let height = this.el.outerHeight()
-    let windowEl = $(window)
-    let viewportEl = getScrollParent(this.el)
-    let viewportTop
-    let viewportLeft
-    let viewportOffset
+    let el = this.el
+    let rect = el.getBoundingClientRect()
+    let viewportEl = getScrollParent($(el))[0]
+    let viewportRect
     let top // the "position" (not "offset") values for the popover
     let left //
 
@@ -124,37 +135,41 @@ export default class Popover {
     if (options.left !== undefined) {
       left = options.left
     } else if (options.right !== undefined) {
-      left = options.right - width // derive the left value from the right value
+      left = options.right - rect.width // derive the left value from the right value
     } else {
       left = 0
     }
 
-    if (viewportEl.is(window) || viewportEl.is(document)) { // normalize getScrollParent's result
-      viewportEl = windowEl
-      viewportTop = 0 // the window is always at the top left
-      viewportLeft = 0 // (and .offset() won't work if called here)
+    // normalize getScrollParent's result
+    if (viewportEl === (document as any)) {
+      viewportEl = (window as any)
+    }
+
+    if (viewportEl === (window as any)) {
+      viewportRect = {
+        top: 0,
+        left: 0,
+        width: document.documentElement.clientWidth,
+        height: document.documentElement.clientHeight
+      }
     } else {
-      viewportOffset = viewportEl.offset()
-      viewportTop = viewportOffset.top
-      viewportLeft = viewportOffset.left
+      viewportRect = viewportEl.getBoundingClientRect()
     }
 
     // if the window is scrolled, it causes the visible area to be further down
-    viewportTop += windowEl.scrollTop()
-    viewportLeft += windowEl.scrollLeft()
+    viewportRect.top += window.scrollY
+    viewportRect.left += window.scrollX
 
     // constrain to the view port. if constrained by two edges, give precedence to top/left
     if (options.viewportConstrain !== false) {
-      top = Math.min(top, viewportTop + viewportEl.outerHeight() - height - this.margin)
-      top = Math.max(top, viewportTop + this.margin)
-      left = Math.min(left, viewportLeft + viewportEl.outerWidth() - width - this.margin)
-      left = Math.max(left, viewportLeft + this.margin)
+      top = Math.min(top, viewportRect.top + viewportRect.height - rect.height - this.margin)
+      top = Math.max(top, viewportRect.top + this.margin)
+      left = Math.min(left, viewportRect.left + viewportRect.width - rect.width - this.margin)
+      left = Math.max(left, viewportRect.left + this.margin)
     }
 
-    this.el.css({
-      top: top - origin.top,
-      left: left - origin.left
-    })
+    el.style.top = (top - rect.top) + 'px'
+    el.style.left = (left - rect.left) + 'px'
   }
 
 

+ 1 - 0
src/util.ts

@@ -152,6 +152,7 @@ export function subtractInnerElHeight(outerEl: JQuery, innerEl: JQuery) {
 
 
 // borrowed from https://github.com/jquery/jquery-ui/blob/1.11.0/ui/core.js#L51
+// TODO: normalize window/document?
 export function getScrollParent(el: JQuery) {
   let position = el.css('position')
   let scrollParent = el.parents().filter(function() {

+ 17 - 0
src/util/dom.ts

@@ -0,0 +1,17 @@
+
+export function htmlToElement(htmlString): HTMLElement {
+  let div = document.createElement('div')
+  div.innerHTML = htmlString.trim()
+  return div.firstChild as HTMLElement
+}
+
+export function listenViaDelegation(container: HTMLElement, eventType, childClassName, handler) {
+  container.addEventListener(eventType, function(ev: Event) {
+    for (let node = ev.target as HTMLElement; node !== container; node = node.parentNode as HTMLElement) {
+      if (node.classList.contains(childClassName)) {
+        handler.call(node, ev)
+        break
+      }
+    }
+  })
+}