Parcourir la source

handleSizing system

Adam Shaw il y a 6 ans
Parent
commit
b60822572d

+ 1 - 1
packages-premium

@@ -1 +1 @@
-Subproject commit 67f58a5dc1cddc22e3e68b43897fed5cda3a310b
+Subproject commit 5017202274e60fe8e6d66c6588488465c4e20886

+ 1 - 21
packages/core/src/Calendar.tsx

@@ -107,7 +107,6 @@ export default class Calendar {
   renderRunner: DelayedRunner
   actionRunner: TaskRunner<Action> // for reducer. bad name
   afterSizingTriggers: any = {}
-  afterSizingCallbacks = []
   isViewUpdated: boolean = false
   isDatesUpdated: boolean = false
   isEventsUpdated: boolean = false
@@ -351,9 +350,6 @@ export default class Calendar {
     let calendarComponent = this.component
     let viewComponent = calendarComponent.view
 
-    // calendarComponent.updateSize(false)
-    this.drainAfterSizingCallbacks()
-
     if (this.isViewUpdated) {
       this.isViewUpdated = false
       this.publiclyTrigger('viewSkeletonRender', [
@@ -594,20 +590,6 @@ export default class Calendar {
   // -----------------------------------------------------------------------------------------------------------------
 
 
-  afterSizing(callback) {
-    this.afterSizingCallbacks.push(callback)
-  }
-
-
-  drainAfterSizingCallbacks() {
-    let { afterSizingCallbacks } = this
-
-    while (afterSizingCallbacks.length) {
-      (afterSizingCallbacks.shift())()
-    }
-  }
-
-
   publiclyTriggerAfterSizing<T extends EventHandlerName>(name: T, args: EventHandlerArgs<T>) {
     let { afterSizingTriggers } = this;
 
@@ -827,7 +809,7 @@ export default class Calendar {
 
   updateSize() { // public
     if (this.component) {
-      this.component.updateSize(true)
+      this.component.triggerResizeHandlers(true)
     }
   }
 
@@ -1198,8 +1180,6 @@ export default class Calendar {
     if (duration) {
       this.component.view.scrollToTime(duration)
     }
-
-    this.drainAfterSizingCallbacks() // hack
   }
 
 }

+ 40 - 26
packages/core/src/CalendarComponent.tsx

@@ -18,6 +18,7 @@ import { capitaliseFirstLetter } from './util/misc'
 import { DelayedRunner } from './util/runner'
 import { applyStyleProp } from './util/dom-manip'
 import ViewContainer from './ViewContainer'
+import { removeExact } from './util/array'
 
 
 export interface CalendarComponentProps extends CalendarState {
@@ -28,6 +29,8 @@ export interface CalendarComponentProps extends CalendarState {
   title: string
 }
 
+export type ResizeHandler = (force: boolean) => void
+
 export default class CalendarComponent extends BaseComponent<CalendarComponentProps> {
 
   private buildViewContext = memoize(buildContext)
@@ -37,11 +40,10 @@ export default class CalendarComponent extends BaseComponent<CalendarComponentPr
   private updateOuterClassNames = subrenderer(setClassNames, unsetClassNames)
   private updateOuterHeight = subrenderer(setHeight, unsetHeight)
   private handleNavLinkClick = buildDelegationHandler('a[data-goto]', this._handleNavLinkClick.bind(this))
-
-  headerRef = createRef<Toolbar>()
-  footerRef = createRef<Toolbar>()
-  viewRef = createRef<View>()
-  viewContainerEl: HTMLElement
+  private headerRef = createRef<Toolbar>()
+  private footerRef = createRef<Toolbar>()
+  private viewRef = createRef<View>()
+  private resizeHandlers: ResizeHandler[] = []
 
   get view() { return this.viewRef.current }
 
@@ -105,13 +107,6 @@ export default class CalendarComponent extends BaseComponent<CalendarComponentPr
   }
 
 
-  resizeRunner = new DelayedRunner(() => {
-    // TODO: call updateSize or something
-    let { calendar, view } = this.context
-    calendar.publiclyTrigger('windowResize', [ view ])
-  })
-
-
   componentDidMount() {
     window.addEventListener('resize', this.handleWindowResize)
   }
@@ -124,14 +119,6 @@ export default class CalendarComponent extends BaseComponent<CalendarComponentPr
   }
 
 
-  handleWindowResize = (ev: UIEvent) => {
-    if (ev.target === window) { // avoid jqui events
-      let { options } = this.context
-      this.resizeRunner.request(options.windowResizeDelay)
-    }
-  }
-
-
   _handleNavLinkClick(ev: UIEvent, anchorEl: HTMLElement) {
     let { dateEnv, calendar } = this.context
 
@@ -161,8 +148,6 @@ export default class CalendarComponent extends BaseComponent<CalendarComponentPr
     let { pluginHooks, calendar } = this.context
 
     if (viewContainerEl) {
-      this.viewContainerEl = viewContainerEl
-
       for (let modifyViewContainer of pluginHooks.viewContainerModifiers) {
         modifyViewContainer(viewContainerEl, calendar)
       }
@@ -223,10 +208,39 @@ export default class CalendarComponent extends BaseComponent<CalendarComponentPr
   // -----------------------------------------------------------------------------------------------------------------
 
 
-  updateSize(isResize = false) {
-    // TODO
-    // this.resizeRunner.whilePaused(() => {
-    // })
+  resizeRunner = new DelayedRunner(() => {
+    this.triggerResizeHandlers(false)
+    let { calendar, view } = this.context
+    calendar.publiclyTrigger('windowResize', [ view ])
+  })
+
+
+  handleWindowResize = (ev: UIEvent) => {
+    let { options } = this.context
+
+    if (
+      options.handleWindowResize &&
+      ev.target === window // avoid jqui events
+    ) {
+      this.resizeRunner.request(options.windowResizeDelay)
+    }
+  }
+
+
+  addResizeHandler = (handler: ResizeHandler) => {
+    this.resizeHandlers.push(handler)
+  }
+
+
+  removeResizeHandler = (handler: ResizeHandler) => {
+    removeExact(this.resizeHandlers, handler)
+  }
+
+
+  triggerResizeHandlers(forced: boolean) {
+    for (let handler of this.resizeHandlers) {
+      handler(forced)
+    }
   }
 
 }

+ 0 - 9
packages/core/src/View.ts

@@ -182,15 +182,6 @@ export default abstract class View<State={}> extends DateComponent<ViewProps, St
     // subclasses can implement
   }
 
-
-  // HACKs
-  // -----------------------------------------------------------------------------------------------------------------
-
-
-  afterSizing(callback) {
-    this.context.calendar.afterSizing(callback)
-  }
-
 }
 
 EmitterMixin.mixInto(View)

+ 6 - 1
packages/core/src/component/ComponentContext.ts

@@ -1,4 +1,5 @@
 import Calendar from '../Calendar'
+import { ResizeHandler } from '../CalendarComponent'
 import ViewApi from '../ViewApi'
 import Theme from '../theme/Theme'
 import { DateEnv } from '../datelib/env'
@@ -25,6 +26,8 @@ export default interface ComponentContext {
   header: ToolbarModel | null
   footer: ToolbarModel | null
   viewsWithButtons: string[]
+  addResizeHandler: (handler: ResizeHandler) => void
+  removeResizeHandler: (handler: ResizeHandler) => void
 }
 
 
@@ -43,7 +46,9 @@ export function buildContext(
     theme,
     view,
     options,
-    ...computeContextProps(options, theme, calendar)
+    ...computeContextProps(options, theme, calendar),
+    addResizeHandler: calendar.component.addResizeHandler,
+    removeResizeHandler: calendar.component.removeResizeHandler
   }
 }
 

+ 2 - 2
packages/core/src/main.ts

@@ -169,13 +169,13 @@ export { default as EventApi } from './api/EventApi'
 export { default as requestJson } from './util/requestJson'
 
 export * from './vdom'
-export { subrenderer, SubRenderer, BaseComponent, setRef, renderVNodes, buildMapSubRenderer } from './vdom-util'
+export { subrenderer, SubRenderer, BaseComponent, setRef, renderVNodes, buildMapSubRenderer, componentNeedsResize } from './vdom-util'
 export { DelayedRunner } from './util/runner'
 
 export { default as SimpleScrollGrid, SimpleScrollGridSection } from './scrollgrid/SimpleScrollGrid'
 export {
   CssDimValue, ScrollerLike, SectionConfig, ColCss, ChunkConfig, doSizingHacks, hasShrinkWidth, renderMicroColGroup,
-  getScrollGridClassNames, getSectionClassNames, getChunkVGrow, getNeedsYScrolling, renderChunkContent, getForceScrollbars, getShrinkWidth,
+  getScrollGridClassNames, getSectionClassNames, getChunkVGrow, getNeedsYScrolling, renderChunkContent, computeForceScrollbars, computeShrinkWidth,
   getChunkClassNames, ChunkContentCallbackArgs
 } from './scrollgrid/util'
 export { default as Scroller, ScrollerProps, OverflowValue } from './scrollgrid/Scroller'

+ 29 - 16
packages/core/src/scrollgrid/SimpleScrollGrid.tsx

@@ -1,10 +1,10 @@
 import { VNode, h } from '../vdom'
 import ComponentContext from '../component/ComponentContext'
-import { BaseComponent, setRef } from '../vdom-util'
+import { BaseComponent, setRef, componentNeedsResize } from '../vdom-util'
 import Scroller, { OverflowValue } from './Scroller'
 import RefMap from '../util/RefMap'
-import { ColCss, SectionConfig, renderMicroColGroup, getShrinkWidth, getScrollGridClassNames, getSectionClassNames, getNeedsYScrolling,
-  renderChunkContent, getChunkVGrow, doSizingHacks, getForceScrollbars, ChunkConfig, hasShrinkWidth, CssDimValue, getChunkClassNames } from './util'
+import { ColCss, SectionConfig, renderMicroColGroup, computeShrinkWidth, getScrollGridClassNames, getSectionClassNames, getNeedsYScrolling,
+  renderChunkContent, getChunkVGrow, doSizingHacks, computeForceScrollbars, ChunkConfig, hasShrinkWidth, CssDimValue, getChunkClassNames } from './util'
 
 
 export interface SimpleScrollGridProps {
@@ -23,6 +23,11 @@ interface SimpleScrollGridState {
   forceYScrollbars: boolean
 }
 
+const STATE_IS_SIZING = {
+  shrinkWidth: true,
+  forceYScrollbars: true
+}
+
 
 export default class SimpleScrollGrid extends BaseComponent<SimpleScrollGridProps> {
 
@@ -81,36 +86,44 @@ export default class SimpleScrollGrid extends BaseComponent<SimpleScrollGridProp
   }
 
 
+  _handleScrollerElRef(scrollerEl: HTMLElement | null, key: string, chunkConfig: ChunkConfig) {
+    setRef(chunkConfig.scrollerElRef, scrollerEl)
+  }
+
+
   componentDidMount() {
-    this.componentDidRender()
+    this.handleSizing()
+    this.context.addResizeHandler(this.handleSizing)
+  }
+
+
+  componentDidUpdate(prevProps: SimpleScrollGridProps, prevState: SimpleScrollGridState) {
+    if (componentNeedsResize(prevProps, this.props, prevState, this.state, STATE_IS_SIZING)) {
+      this.handleSizing()
+    }
   }
 
 
-  componentDidUpdate() {
-    this.componentDidRender()
+  componentWillUnmount() {
+    this.context.removeResizeHandler(this.handleSizing)
   }
 
 
-  componentDidRender() {
-    doSizingHacks(this.base as HTMLElement) // TODO: not every time
+  handleSizing = () => {
+    doSizingHacks(this.base as HTMLElement)
 
     if (hasShrinkWidth(this.props.cols)) {
       this.setState({
-        shrinkWidth: getShrinkWidth(this.scrollerElRefs.getCurrents())
+        shrinkWidth: computeShrinkWidth(this.scrollerElRefs.getCurrents())
       })
     }
 
-    this.setState({ // TODO: only for resizing
-      forceYScrollbars: getForceScrollbars(this.scrollerRefs.getCurrents(), 'Y')
+    this.setState({
+      forceYScrollbars: computeForceScrollbars(this.scrollerRefs.getCurrents(), 'Y')
     })
   }
 
 
-  _handleScrollerElRef(scrollerEl: HTMLElement | null, key: string, chunkConfig: ChunkConfig) {
-    setRef(chunkConfig.scrollerElRef, scrollerEl)
-  }
-
-
   // TODO: can do a really simple print-view. dont need to join rows
 
 }

+ 2 - 2
packages/core/src/scrollgrid/util.tsx

@@ -42,7 +42,7 @@ export interface ChunkContentCallbackArgs {
 }
 
 
-export function getShrinkWidth(chunkEls: HTMLElement[]) { // all in same COL!
+export function computeShrinkWidth(chunkEls: HTMLElement[]) { // all in same COL!
   let shrinkEls = findElements(chunkEls, '.shrink')
   let largestWidth = 0
 
@@ -94,7 +94,7 @@ export interface ScrollerLike { // have scrollers implement?
 }
 
 
-export function getForceScrollbars(scrollers: ScrollerLike[], axis: 'X' | 'Y') {
+export function computeForceScrollbars(scrollers: ScrollerLike[], axis: 'X' | 'Y') {
   let methodName = 'needs' + axis + 'Scrolling'
   let needsScrollbars = false
 

+ 23 - 0
packages/core/src/util/object.ts

@@ -120,3 +120,26 @@ export function isPropsEqual(obj0, obj1) {
 
   return true
 }
+
+
+export function getUnequalProps(obj0, obj1) {
+  let keys: string[] = []
+
+  for (let key in obj0) {
+    if (hasOwnProperty.call(obj0, key)) {
+      if (!(key in obj1)) {
+        keys.push(key)
+      }
+    }
+  }
+
+  for (let key in obj1) {
+    if (hasOwnProperty.call(obj1, key)) {
+      if (obj0[key] !== obj1[key]) {
+        keys.push(key)
+      }
+    }
+  }
+
+  return keys
+}

+ 22 - 0
packages/core/src/vdom-util.tsx

@@ -1,6 +1,7 @@
 import { Component, h, Fragment, Ref, ComponentChildren, render } from './vdom'
 import ComponentContext, { ComponentContextType } from './component/ComponentContext'
 import { __assign } from 'tslib'
+import { isPropsEqual, getUnequalProps } from './util/object'
 
 
 export type EqualityFuncs<ObjType> = {
@@ -315,3 +316,24 @@ export function renderVNodes(children: ComponentChildren, context: ComponentCont
 
   return Array.prototype.slice.call(containerEl.childNodes)
 }
+
+
+export function componentNeedsResize<P, S>(prevProps: P, props: P, prevState: S, state: S, stateIsSizing: { [K in keyof S]?: boolean }) {
+  if (!isPropsEqual(prevProps, props)) {
+    return true
+  }
+
+  let unequalState = getUnequalProps(prevState, state)
+
+  if (!unequalState.length) {
+    return true // if neither props nor state changed, that means context changed, so definitely do a resize!
+  }
+
+  for (let key of unequalState) {
+    if (!stateIsSizing[key]) {
+      return true
+    }
+  }
+
+  return false
+}

+ 10 - 4
packages/daygrid/src/Table.tsx

@@ -160,13 +160,19 @@ export default class Table extends BaseComponent<TableProps, TableState> {
 
   componentDidMount() {
     this.subrender()
-    this.resize()
+    this.handleSizing(false)
+    this.context.addResizeHandler(this.handleSizing)
   }
 
 
   componentDidUpdate() {
     this.subrender()
-    this.resize()
+    this.handleSizing(false)
+  }
+
+
+  componentWillUnmount() {
+    this.context.removeResizeHandler(this.handleSizing)
   }
 
 
@@ -260,12 +266,12 @@ export default class Table extends BaseComponent<TableProps, TableState> {
   ------------------------------------------------------------------------------------------------------------------*/
 
 
-  resize(isResize?: boolean) { // gaahhhhhhhhh
+  handleSizing = (forced: boolean) => {
     let { calendar } = this.context
     let popover = this.popoverRef.current
 
     if (
-      isResize ||
+      forced ||
       this.isCellSizesDirty ||
       calendar.isEventsUpdated // hack
     ) {

+ 11 - 5
packages/list/src/ListView.tsx

@@ -68,13 +68,19 @@ export default class ListView extends View {
 
   componentDidMount() {
     this.subrender()
-    this.resize()
+    this.handleSizing(false)
+    this.context.addResizeHandler(this.handleSizing)
   }
 
 
   componentDidUpdate() {
     this.subrender()
-    this.resize() // called too often!!!
+    this.handleSizing(false)
+  }
+
+
+  componentWillUnmount() {
+    this.context.removeResizeHandler(this.handleSizing)
   }
 
 
@@ -97,9 +103,9 @@ export default class ListView extends View {
   }
 
 
-  resize(isResize?: boolean) { // TODO: have caller use this flag!!!!!!
-    this.eventRenderer.computeSizes(isResize, this)
-    this.eventRenderer.assignSizes(isResize, this)
+  handleSizing = (forced: boolean) => {
+    this.eventRenderer.computeSizes(forced, this)
+    this.eventRenderer.assignSizes(forced, this)
   }
 
 

+ 13 - 7
packages/timegrid/src/TimeCols.tsx

@@ -185,13 +185,19 @@ export default class TimeCols extends BaseComponent<TimeColsProps> {
 
   componentDidMount() {
     this.subrender()
-    this.resize()
+    this.handleSizing(false)
+    this.context.addResizeHandler(this.handleSizing)
   }
 
 
   componentDidUpdate() {
     this.subrender()
-    this.resize()
+    this.handleSizing(false)
+  }
+
+
+  componentWillUnmount() {
+    this.context.removeResizeHandler(this.handleSizing)
   }
 
 
@@ -267,28 +273,28 @@ export default class TimeCols extends BaseComponent<TimeColsProps> {
   }
 
 
-  resize(isResize?: boolean) { // gahhhhhhhh
+  handleSizing = (forced: boolean) => {
     let { segRenderers } = this
 
-    if (isResize || this.isSlatSizesDirty) {
+    if (forced || this.isSlatSizesDirty) {
       this.buildSlatPositions()
       this.isSlatSizesDirty = false
     }
 
-    if (isResize || this.isColSizesDirty) {
+    if (forced || this.isColSizesDirty) {
       this.buildColPositions()
       this.isColSizesDirty = false
     }
 
     for (let segRenderer of segRenderers) {
       if (segRenderer) {
-        segRenderer.computeSizes(isResize, this)
+        segRenderer.computeSizes(forced, this)
       }
     }
 
     for (let segRenderer of segRenderers) {
       if (segRenderer) {
-        segRenderer.assignSizes(isResize, this)
+        segRenderer.assignSizes(forced, this)
       }
     }
   }

+ 4 - 19
packages/timegrid/src/TimeColsView.tsx

@@ -118,14 +118,9 @@ export default abstract class TimeColsView extends View {
   }
 
 
-  componentDidUpdate(prevProps: ViewProps, prevState: {}, snapshot) {
-    // not working on window resize!!!??
-
+  componentDidUpdate(prevProps: ViewProps) {
     if (prevProps.dateProfile !== this.props.dateProfile) {
       this.scrollToInitialTime()
-
-    } else {
-      this.scrollTop(snapshot.scrollTop)
     }
   }
 
@@ -174,20 +169,10 @@ export default abstract class TimeColsView extends View {
 
 
   scrollToTime(duration: Duration) {
-    this.afterSizing(() => { // hack
-      let top = this.computeDateScroll(duration)
-
-      this.scrollTop(top)
-    })
-  }
-
+    let scrollTop = this.computeDateScroll(duration)
+    let scrollerEl = this.scrollerElRef.current
 
-  scrollTop(top: number) {
-    this.afterSizing(() => { // hack
-      let scrollerEl = this.scrollerElRef.current
-
-      scrollerEl.scrollTop = top
-    })
+    scrollerEl.scrollTop = scrollTop
   }