import * as React from "react" ; import { polyfill } from "react-lifecycles-compat" ; import { AbstractComponent2 , Props , IRef , setRef , Utils as CoreUtils } from "@blueprintjs/core" ; import * as Classes from "../common/classes" ; import { Grid } from "../common/grid" ; import * as ScrollUtils from "../common/internal/scrollUtils" ; import { Utils } from "../common/utils" ; import { TableLoadingOption } from "../regions" ; import { QuadrantType , TableQuadrant } from "./tableQuadrant" ; import { TableQuadrantStackCache } from "./tableQuadrantStackCache" ; interface IQuadrantRefMap < T > { columnHeader ? : T ; menu ? : T ; quadrant ? : T ; rowHeader ? : T ; scrollContainer ? : T ; } type QuadrantRefHandler = IRef < HTMLDivElement > ; type IQuadrantRefs = IQuadrantRefMap < HTMLDivElement > ; type IQuadrantRefHandlers = IQuadrantRefMap < QuadrantRefHandler > ; export interface ITableQuadrantStackProps extends Props { /**
     * A callback that receives a `ref` to the main quadrant's table-body element.
     */ bodyRef ? : IRef < HTMLDivElement > ; /**
     * A callback that receives a `ref` to the main quadrant's column-header container.
     */ columnHeaderRef ? : IRef < HTMLDivElement > ; /**
     * The grid computes sizes of cells, rows, or columns from the
     * configurable `columnWidths` and `rowHeights`.
     */ grid : Grid ; /**
     * An optional callback for reacting to column-resize events.
     */ handleColumnResizeGuide ? : ( verticalGuides : number [ ] ) => void ; /**
     * An optional callback for reacting to column-reordering events.
     */ handleColumnsReordering ? : ( verticalGuides : number [ ] ) => void ; /**
     * An optional callback for reacting to row-resize events.
     */ handleRowResizeGuide ? : ( horizontalGuides : number [ ] ) => void ; /**
     * An optional callback for reacting to column-reordering events.
     */ handleRowsReordering ? : ( horizontalGuides : number [ ] ) => void ; default isHorizontalScrollDisabled ? : boolean ; default enableRowHeader ? : boolean ; default isVerticalScrollDisabled ? : boolean ; /**
     * A list of `TableLoadingOption`. Loading cells may have different sizes
     * from potentially custom cells in the header or body, so we need to know
     * when the loading states change in order to synchronize quadrant sizes
     * properly.
     *
     * REQUIRES QUADRANT RESYNC
     */ loadingOptions ? : TableLoadingOption [ ] ; /**
     * The number of columns. Affects the layout of the table, so we need to
     * know when this changes in order to synchronize quadrant sizes properly.
     *
     * REQUIRES QUADRANT RESYNC
     */ numColumns ? : number ; /**
     * The number of frozen columns. Affects the layout of the table, so we need
     * to know when this changes in order to synchronize quadrant sizes
     * properly.
     *
     * REQUIRES QUADRANT RESYNC
     */ numFrozenColumns ? : number ; /**
     * The number of frozen rows. Affects the layout of the table, so we need to
     * know when this changes in order to synchronize quadrant sizes properly.
     *
     * REQUIRES QUADRANT RESYNC
     */ numFrozenRows ? : number ; /**
     * The number of rows. Affects the layout of the table, so we need to know
     * when this changes in order to synchronize quadrant sizes properly.
     *
     * REQUIRES QUADRANT RESYNC
     */ numRows ? : number ; /**
     * An optional callback invoked the quadrants are scrolled.
     */ onScroll ? : React . EventHandler < React . SyntheticEvent < HTMLElement > > ; /**
     * A callback that receives a `ref` to the main-quadrant element.
     */ quadrantRef ? : IRef < HTMLDivElement > ; /**
     * A callback that renders either all of or just frozen sections of the table body.
     */ bodyRenderer : ( quadrantType : QuadrantType , showFrozenRowsOnly ? : boolean , showFrozenColumnsOnly ? : boolean ) => JSX . Element ; /**
     * A callback that renders either all of or just the frozen section of the column header.
     */ columnHeaderCellRenderer ? : ( refHandler : IRef < HTMLDivElement > , resizeHandler : ( verticalGuides : number [ ] ) => void , reorderingHandler : ( oldIndex : number , newIndex : number , length : number ) => void , showFrozenColumnsOnly ? : boolean ) => JSX . Element ; /**
     * A callback that renders the table menu (the rectangle in the top-left corner).
     */ menuRenderer ? : ( refHandler : IRef < HTMLDivElement > ) => JSX . Element ; /**
     * A callback that renders either all of or just the frozen section of the row header.
     */ rowHeaderCellRenderer ? : ( refHandler : IRef < HTMLDivElement > , resizeHandler : ( verticalGuides : number [ ] ) => void , reorderingHandler : ( oldIndex : number , newIndex : number , length : number ) => void , showFrozenRowsOnly ? : boolean ) => JSX . Element ; /**
     * A callback that receives a `ref` to the main quadrant's row-header container.
     */ rowHeaderRef ? : IRef < HTMLDivElement > ; /**
     * A callback that receives a `ref` to the main quadrant's scroll-container element.
     */ scrollContainerRef ? : IRef < HTMLDivElement > ; default throttleScrolling ? : boolean ; default viewSyncDelay ? : number ; default enableColumnInteractionBar ? : boolean ; } const DEFAULT_COLUMN_HEADER_HEIGHT = 30 ; const DEFAULT_VIEW_SYNC_DELAY = 500 ; const QUADRANT_MIN_SIZE = 1 ; const SYNC_TRIGGER_PROP_KEYS = [ "enableRowHeader" , "loadingOptions" , "numFrozenColumns" , "numFrozenRows" , "numColumns" , "numRows" , "enableColumnInteractionBar" , ] ; @ polyfill export class TableQuadrantStack extends AbstractComponent2 < ITableQuadrantStackProps > { public static defaultProps : Partial < ITableQuadrantStackProps > = { enableColumnInteractionBar : undefined , enableRowHeader : true , isHorizontalScrollDisabled : false , isVerticalScrollDisabled : false , throttleScrolling : true , viewSyncDelay : DEFAULT_VIEW_SYNC_DELAY , } ; private quadrantRefs : Record < QuadrantType , IQuadrantRefs > = { [ QuadrantType . MAIN ] : { } , [ QuadrantType . TOP ] : { } , [ QuadrantType . LEFT ] : { } , [ QuadrantType . TOP_LEFT ] : { } , } ; private quadrantRefHandlers = { [ QuadrantType . MAIN ] : this . generateQuadrantRefHandlers ( QuadrantType . MAIN ) , [ QuadrantType . TOP ] : this . generateQuadrantRefHandlers ( QuadrantType . TOP ) , [ QuadrantType . LEFT ] : this . generateQuadrantRefHandlers ( QuadrantType . LEFT ) , [ QuadrantType . TOP_LEFT ] : this . generateQuadrantRefHandlers ( QuadrantType . TOP_LEFT ) , } ; private wasMainQuadrantScrollTriggeredByWheelEvent = false ; private throttledHandleMainQuadrantScroll : ( event : React . UIEvent < HTMLElement > ) => any ; private throttledHandleWheel : ( event : React . WheelEvent < HTMLElement > ) => any ; private debouncedViewSyncInterval : number ; private cache : TableQuadrantStackCache ; public constructor ( props , context ? ) { super ( props , context ) ; this . throttledHandleMainQuadrantScroll = CoreUtils . throttleReactEventCallback ( this . handleMainQuadrantScroll ) ; this . throttledHandleWheel = CoreUtils . throttleReactEventCallback ( this . handleWheel ) ; this . cache = new TableQuadrantStackCache ( ) ; } /**
     * Scroll the main quadrant to the specified scroll offset, keeping all other quadrants in sync.
     */ public scrollToPosition ( scrollLeft , scrollTop ) { const { scrollContainer } = this . quadrantRefs [ QuadrantType . MAIN ] ; this . wasMainQuadrantScrollTriggeredByWheelEvent = false ; scrollContainer . scrollLeft = scrollLeft ; scrollContainer . scrollTop = scrollTop ; this . syncQuadrantViews ( ) ; } /**
     * Synchronizes quadrant sizes and scroll offsets based on the current
     * column, row, and header sizes. Useful for correcting quadrant sizes after
     * explicitly resizing columns and rows, for instance.
     *
     * Invoking this method imperatively is cheaper than providing columnWidths
     * or rowHeights array props to TableQuadrantStack and forcing it to run
     * expensive array diffs upon every update.
     */ public synchronizeQuadrantViews ( ) { this . syncQuadrantViews ( ) ; } public componentDidMount ( ) { this . emitRefs ( ) ; this . syncQuadrantViews ( ) ; } public componentDidUpdate ( prevProps ) { if ( ! CoreUtils . shallowCompareKeys ( this . props , prevProps , { include : SYNC_TRIGGER_PROP_KEYS , } ) ) { this . emitRefs ( ) ; this . syncQuadrantViews ( ) ; } } public render ( ) { const { grid , enableRowHeader , bodyRenderer , throttleScrolling } = this . props ; const onMainQuadrantScroll = throttleScrolling ? this . throttledHandleMainQuadrantScroll : this . handleMainQuadrantScroll ; const onWheel = throttleScrolling ? this . throttledHandleWheel : this . handleWheel ; const baseProps = { bodyRenderer , enableRowHeader , grid , onWheel , } ; const shouldRenderLeftQuadrants = this . shouldRenderLeftQuadrants ( ) ; const maybeLeftQuadrant = shouldRenderLeftQuadrants ? ( < TableQuadrant { ... baseProps } quadrantRef = { this . quadrantRefHandlers [ QuadrantType . LEFT ] . quadrant } quadrantType = { QuadrantType . LEFT } columnHeaderCellRenderer = { this . renderLeftQuadrantColumnHeader } menuRenderer = { this . renderLeftQuadrantMenu } rowHeaderCellRenderer = { this . renderLeftQuadrantRowHeader } scrollContainerRef = { this . quadrantRefHandlers [ QuadrantType . LEFT ] . scrollContainer } / > ) : undefined ; const maybeTopLeftQuadrant = shouldRenderLeftQuadrants ? ( < TableQuadrant { ... baseProps } quadrantRef = { this . quadrantRefHandlers [ QuadrantType . TOP_LEFT ] . quadrant } quadrantType = { QuadrantType . TOP_LEFT } columnHeaderCellRenderer = { this . renderTopLeftQuadrantColumnHeader } menuRenderer = { this . renderTopLeftQuadrantMenu } rowHeaderCellRenderer = { this . renderTopLeftQuadrantRowHeader } scrollContainerRef = { this . quadrantRefHandlers [ QuadrantType . TOP_LEFT ] . scrollContainer } / > ) : undefined ; return ( < div className = { Classes . TABLE_QUADRANT_STACK } > < TableQuadrant { ... baseProps } bodyRef = { this . props . bodyRef } onScroll = { onMainQuadrantScroll } quadrantRef = { this . quadrantRefHandlers [ QuadrantType . MAIN ] . quadrant } quadrantType = { QuadrantType . MAIN } columnHeaderCellRenderer = { this . renderMainQuadrantColumnHeader } menuRenderer = { this . renderMainQuadrantMenu } rowHeaderCellRenderer = { this . renderMainQuadrantRowHeader } scrollContainerRef = { this . quadrantRefHandlers [ QuadrantType . MAIN ] . scrollContainer } / > < TableQuadrant { ... baseProps } quadrantRef = { this . quadrantRefHandlers [ QuadrantType . TOP ] . quadrant } quadrantType = { QuadrantType . TOP } columnHeaderCellRenderer = { this . renderTopQuadrantColumnHeader } menuRenderer = { this . renderTopQuadrantMenu } rowHeaderCellRenderer = { this . renderTopQuadrantRowHeader } scrollContainerRef = { this . quadrantRefHandlers [ QuadrantType . TOP ] . scrollContainer } / > { maybeLeftQuadrant } { maybeTopLeftQuadrant } < /div>); } private generateQuadrantRefHandlers ( quadrantType ) { const reducer = ( agg , key ) => { agg [ key ] = ( ref ) => ( this . quadrantRefs [ quadrantType ] [ key ] = ref ) ; return agg ; } ; const refHandlers = [ "columnHeader" , "menu" , "quadrant" , "rowHeader" , "scrollContainer" , ] ; return refHandlers . reduce ( reducer , { } ) ; } private renderMainQuadrantMenu = ( ) => { return this . props . menuRenderer ?. ( this . quadrantRefHandlers [ QuadrantType . MAIN ] . menu ) ; } ; private renderTopQuadrantMenu = ( ) => { return this . props . menuRenderer ?. ( this . quadrantRefHandlers [ QuadrantType . TOP ] . menu ) ; } ; private renderLeftQuadrantMenu = ( ) => { return this . props . menuRenderer ?. ( this . quadrantRefHandlers [ QuadrantType . LEFT ] . menu ) ; } ; private renderTopLeftQuadrantMenu = ( ) => { return this . props . menuRenderer ?. ( this . quadrantRefHandlers [ QuadrantType . TOP_LEFT ] . menu ) ; } ; private renderMainQuadrantColumnHeader = ( showFrozenColumnsOnly ) => { const refHandler = this . quadrantRefHandlers [ QuadrantType . MAIN ] . columnHeader ; const resizeHandler = this . handleColumnResizeGuideMain ; const reorderingHandler = this . handleColumnsReordering ; return this . props . columnHeaderCellRenderer ?. ( refHandler , resizeHandler , reorderingHandler , showFrozenColumnsOnly ) ; } ; private renderTopQuadrantColumnHeader = ( showFrozenColumnsOnly ) => { const refHandler = this . quadrantRefHandlers [ QuadrantType . TOP ] . columnHeader ; const resizeHandler = this . handleColumnResizeGuideTop ; const reorderingHandler = this . handleColumnsReordering ; return this . props . columnHeaderCellRenderer ?. ( refHandler , resizeHandler , reorderingHandler , showFrozenColumnsOnly ) ; } ; private renderLeftQuadrantColumnHeader = ( showFrozenColumnsOnly ) => { const refHandler = this . quadrantRefHandlers [ QuadrantType . LEFT ] . columnHeader ; const resizeHandler = this . handleColumnResizeGuideLeft ; const reorderingHandler = this . handleColumnsReordering ; return this . props . columnHeaderCellRenderer ?. ( refHandler , resizeHandler , reorderingHandler , showFrozenColumnsOnly ) ; } ; private renderTopLeftQuadrantColumnHeader = ( showFrozenColumnsOnly ) => { const refHandler = this . quadrantRefHandlers [ QuadrantType . TOP_LEFT ] . columnHeader ; const resizeHandler = this . handleColumnResizeGuideTopLeft ; const reorderingHandler = this . handleColumnsReordering ; return this . props . columnHeaderCellRenderer ?. ( refHandler , resizeHandler , reorderingHandler , showFrozenColumnsOnly ) ; } ; private renderMainQuadrantRowHeader = ( showFrozenRowsOnly ) => { const refHandler = this . quadrantRefHandlers [ QuadrantType . MAIN ] . rowHeader ; const resizeHandler = this . handleRowResizeGuideMain ; const reorderingHandler = this . handleRowsReordering ; return this . props . rowHeaderCellRenderer ?. ( refHandler , resizeHandler , reorderingHandler , showFrozenRowsOnly ) ; } ; private renderTopQuadrantRowHeader = ( showFrozenRowsOnly ) => { const refHandler = this . quadrantRefHandlers [ QuadrantType . TOP ] . rowHeader ; const resizeHandler = this . handleRowResizeGuideTop ; const reorderingHandler = this . handleRowsReordering ; return this . props . rowHeaderCellRenderer ?. ( refHandler , resizeHandler , reorderingHandler , showFrozenRowsOnly ) ; } ; private renderLeftQuadrantRowHeader = ( showFrozenRowsOnly ) => { const refHandler = this . quadrantRefHandlers [ QuadrantType . LEFT ] . rowHeader ; const resizeHandler = this . handleRowResizeGuideLeft ; const reorderingHandler = this . handleRowsReordering ; return this . props . rowHeaderCellRenderer ?. ( refHandler , resizeHandler , reorderingHandler , showFrozenRowsOnly ) ; } ; private renderTopLeftQuadrantRowHeader = ( showFrozenRowsOnly ) => { const refHandler = this . quadrantRefHandlers [ QuadrantType . TOP_LEFT ] . rowHeader ; const resizeHandler = this . handleRowResizeGuideTopLeft ; const reorderingHandler = this . handleRowsReordering ; return this . props . rowHeaderCellRenderer ?. ( refHandler , resizeHandler , reorderingHandler , showFrozenRowsOnly ) ; } ; private handleMainQuadrantScroll = ( event ) => { if ( this . wasMainQuadrantScrollTriggeredByWheelEvent ) { this . wasMainQuadrantScrollTriggeredByWheelEvent = false ; return ; } this . props . onScroll ?. ( event ) ; const mainScrollContainer = this . quadrantRefs [ QuadrantType . MAIN ] . scrollContainer ; const nextScrollLeft = mainScrollContainer . scrollLeft ; const nextScrollTop = mainScrollContainer . scrollTop ; this . handleScrollOffsetChange ( "scrollLeft" , nextScrollLeft ) ; this . handleScrollOffsetChange ( "scrollTop" , nextScrollTop ) ; this . syncQuadrantViewsDebounced ( ) ; } ; private handleWheel = ( event ) => { this . props . onScroll ?. ( event ) ; const nextScrollLeft = this . getNextScrollOffset ( "horizontal" , event . deltaX ) ; const nextScrollTop = this . getNextScrollOffset ( "vertical" , event . deltaY ) ; if ( nextScrollLeft != null || nextScrollTop != null ) { this . wasMainQuadrantScrollTriggeredByWheelEvent = true ; } this . quadrantRefs [ QuadrantType . MAIN ] . scrollContainer . scrollLeft = nextScrollLeft ; this . quadrantRefs [ QuadrantType . MAIN ] . scrollContainer . scrollTop = nextScrollTop ; this . handleScrollOffsetChange ( "scrollLeft" , nextScrollLeft ) ; this . handleScrollOffsetChange ( "scrollTop" , nextScrollTop ) ; this . syncQuadrantViewsDebounced ( ) ; } ; private getNextScrollOffset = ( direction , delta ) => { const { grid , isHorizontalScrollDisabled , isVerticalScrollDisabled } = this . props ; const isHorizontal = direction === "horizontal" ; const scrollKey = isHorizontal ? "scrollLeft" : "scrollTop" ; const isScrollDisabled = isHorizontal ? isHorizontalScrollDisabled : isVerticalScrollDisabled ; if ( isScrollDisabled ) { return undefined ; } let clientSize = isHorizontal ? this . cache . getScrollContainerClientWidth ( ) : this . cache . getScrollContainerClientHeight ( ) ; if ( clientSize == null ) { clientSize = this . updateScrollContainerClientSize ( isHorizontal ) ; } const containerSize = isHorizontal ? this . cache . getScrollContainerClientWidth ( ) - this . cache . getRowHeaderWidth ( ) : this . cache . getScrollContainerClientHeight ( ) - this . cache . getColumnHeaderHeight ( ) ; const gridSize = isHorizontal ? grid . getWidth ( ) : grid . getHeight ( ) ; const maxScrollOffset = Math . max ( 0 , gridSize - containerSize ) ; const currScrollOffset = this . cache . getScrollOffset ( scrollKey ) ; const nextScrollOffset = CoreUtils . clamp ( currScrollOffset + delta , 0 , maxScrollOffset ) ; return nextScrollOffset ; } ; private handleColumnResizeGuideMain = ( verticalGuides ) => { this . invokeColumnResizeHandler ( verticalGuides , QuadrantType . MAIN ) ; } ; private handleColumnResizeGuideTop = ( verticalGuides ) => { this . invokeColumnResizeHandler ( verticalGuides , QuadrantType . TOP ) ; } ; private handleColumnResizeGuideLeft = ( verticalGuides ) => { this . invokeColumnResizeHandler ( verticalGuides , QuadrantType . LEFT ) ; } ; private handleColumnResizeGuideTopLeft = ( verticalGuides ) => { this . invokeColumnResizeHandler ( verticalGuides , QuadrantType . TOP_LEFT ) ; } ; private invokeColumnResizeHandler = ( verticalGuides , quadrantType ) => { const adjustedGuides = this . adjustVerticalGuides ( verticalGuides , quadrantType ) ; this . props . handleColumnResizeGuide ?. ( adjustedGuides ) ; } ; private handleRowResizeGuideMain = ( horizontalGuides ) => { this . invokeRowResizeHandler ( horizontalGuides , QuadrantType . MAIN ) ; } ; private handleRowResizeGuideTop = ( horizontalGuides ) => { this . invokeRowResizeHandler ( horizontalGuides , QuadrantType . TOP ) ; } ; private handleRowResizeGuideLeft = ( horizontalGuides ) => { this . invokeRowResizeHandler ( horizontalGuides , QuadrantType . LEFT ) ; } ; private handleRowResizeGuideTopLeft = ( horizontalGuides ) => { this . invokeRowResizeHandler ( horizontalGuides , QuadrantType . TOP_LEFT ) ; } ; private invokeRowResizeHandler = ( horizontalGuides , quadrantType ) => { const adjustedGuides = this . adjustHorizontalGuides ( horizontalGuides , quadrantType ) ; this . props . handleRowResizeGuide ?. ( adjustedGuides ) ; } ; private handleColumnsReordering = ( oldIndex , newIndex , length ) => { const guideIndex = Utils . reorderedIndexToGuideIndex ( oldIndex , newIndex , length ) ; const leftOffset = this . props . grid . getCumulativeWidthBefore ( guideIndex ) ; const quadrantType = guideIndex <= this . props . numFrozenColumns ? QuadrantType . TOP_LEFT : QuadrantType . TOP ; const verticalGuides = this . adjustVerticalGuides ( [ leftOffset ] , quadrantType ) ; this . props . handleColumnsReordering ?. ( verticalGuides ) ; } ; private handleRowsReordering = ( oldIndex , newIndex , length ) => { const guideIndex = Utils . reorderedIndexToGuideIndex ( oldIndex , newIndex , length ) ; const topOffset = this . props . grid . getCumulativeHeightBefore ( guideIndex ) ; const quadrantType = guideIndex <= this . props . numFrozenRows ? QuadrantType . TOP_LEFT : QuadrantType . LEFT ; const horizontalGuides = this . adjustHorizontalGuides ( [ topOffset ] , quadrantType ) ; this . props . handleRowsReordering ?. ( horizontalGuides ) ; } ; private emitRefs ( ) { setRef ( this . props . quadrantRef , this . quadrantRefs [ QuadrantType . MAIN ] . quadrant ) ; setRef ( this . props . rowHeaderRef , this . quadrantRefs [ QuadrantType . MAIN ] . rowHeader ) ; setRef ( this . props . columnHeaderRef , this . quadrantRefs [ QuadrantType . MAIN ] . columnHeader ) ; setRef ( this . props . scrollContainerRef , this . quadrantRefs [ QuadrantType . MAIN ] . scrollContainer ) ; } private syncQuadrantViewsDebounced = ( ) => { const { viewSyncDelay } = this . props ; if ( viewSyncDelay < 0 ) { this . syncQuadrantViews ( ) ; } else { clearInterval ( this . debouncedViewSyncInterval ) ; this . debouncedViewSyncInterval = window . setTimeout ( this . syncQuadrantViews , viewSyncDelay ) ; } } ; private syncQuadrantViews = ( ) => { const mainRefs = this . quadrantRefs [ QuadrantType . MAIN ] ; const mainScrollContainer = mainRefs . scrollContainer ; const rowHeaderWidth = this . measureDesiredRowHeaderWidth ( ) ; const columnHeaderHeight = this . measureDesiredColumnHeaderHeight ( ) ; const leftQuadrantGridWidth = this . getSecondaryQuadrantGridSize ( "width" ) ; const topQuadrantGridHeight = this . getSecondaryQuadrantGridSize ( "height" ) ; const leftQuadrantWidth = rowHeaderWidth + leftQuadrantGridWidth ; const topQuadrantHeight = columnHeaderHeight + topQuadrantGridHeight ; const rightScrollBarWidth = ScrollUtils . measureScrollBarThickness ( mainScrollContainer , "vertical" ) ; const bottomScrollBarHeight = ScrollUtils . measureScrollBarThickness ( mainScrollContainer , "horizontal" ) ; const adjustedColumnHeaderHeight = this . maybeIncreaseToDefaultColumnHeaderHeight ( columnHeaderHeight ) ; const adjustedTopQuadrantHeight = this . maybeIncreaseToDefaultColumnHeaderHeight ( topQuadrantHeight ) ; this . cache . setRowHeaderWidth ( rowHeaderWidth ) ; this . cache . setColumnHeaderHeight ( columnHeaderHeight ) ; this . cache . setScrollContainerClientWidth ( undefined ) ; this . cache . setScrollContainerClientHeight ( undefined ) ; this . maybesSetQuadrantRowHeaderSizes ( rowHeaderWidth ) ; this . maybeSetQuadrantMenuElementSizes ( rowHeaderWidth , adjustedColumnHeaderHeight ) ; this . maybeSetQuadrantSizes ( leftQuadrantWidth , adjustedTopQuadrantHeight ) ; this . maybeSetQuadrantPositionOffset ( QuadrantType . TOP , "right" , rightScrollBarWidth ) ; this . maybeSetQuadrantPositionOffset ( QuadrantType . LEFT , "bottom" , bottomScrollBarHeight ) ; this . maybeSetQuadrantScrollOffset ( QuadrantType . LEFT , "scrollTop" ) ; this . maybeSetQuadrantScrollOffset ( QuadrantType . TOP , "scrollLeft" ) ; } ; private maybeSetQuadrantSizes = ( width , height ) => { this . maybesSetQuadrantSize ( QuadrantType . LEFT , "width" , width ) ; this . maybesSetQuadrantSize ( QuadrantType . TOP , "height" , height ) ; this . maybesSetQuadrantSize ( QuadrantType . TOP_LEFT , "width" , width ) ; this . maybesSetQuadrantSize ( QuadrantType . TOP_LEFT , "height" , height ) ; } ; private maybesSetQuadrantSize = ( quadrantType , dimension , value ) => { const { quadrant } = this . quadrantRefs [ quadrantType ] ; if ( quadrant != null ) { quadrant . style [ dimension ] = `${ value }px` ; } } ; private maybeSetQuadrantPositionOffset = ( quadrantType , side , value ) => { const { quadrant } = this . quadrantRefs [ quadrantType ] ; if ( quadrant != null ) { quadrant . style [ side ] = `${ value }px` ; } } ; private maybesSetQuadrantRowHeaderSizes = ( width ) => { this . maybeSetQuadrantRowHeaderSize ( QuadrantType . MAIN , width ) ; this . maybeSetQuadrantRowHeaderSize ( QuadrantType . TOP , width ) ; this . maybeSetQuadrantRowHeaderSize ( QuadrantType . LEFT , width ) ; this . maybeSetQuadrantRowHeaderSize ( QuadrantType . TOP_LEFT , width ) ; } ; private maybeSetQuadrantRowHeaderSize = ( quadrantType , width ) => { const { rowHeader } = this . quadrantRefs [ quadrantType ] ; if ( rowHeader != null ) { rowHeader . style . width = `${ width }px` ; } } ; private maybeSetQuadrantMenuElementSizes = ( width , height ) => { this . maybeSetQuadrantMenuElementSize ( QuadrantType . MAIN , width , height ) ; this . maybeSetQuadrantMenuElementSize ( QuadrantType . TOP , width , height ) ; this . maybeSetQuadrantMenuElementSize ( QuadrantType . LEFT , width , height ) ; this . maybeSetQuadrantMenuElementSize ( QuadrantType . TOP_LEFT , width , height ) ; } ; private maybeSetQuadrantMenuElementSize = ( quadrantType , width , height ) => { const { menu } = this . quadrantRefs [ quadrantType ] ; if ( menu != null ) { menu . style . width = `${ width }px` ; menu . style . height = `${ height }px` ; } } ; private maybeSetQuadrantScrollOffset = ( quadrantType , scrollKey , newOffset ? ) => { const { scrollContainer } = this . quadrantRefs [ quadrantType ] ; const scrollOffset = newOffset != null ? newOffset : this . cache . getScrollOffset ( scrollKey ) ; if ( scrollContainer != null ) { scrollContainer [ scrollKey ] = scrollOffset ; } } ; private handleScrollOffsetChange = ( scrollKey , offset ) => { this . cache . setScrollOffset ( scrollKey , offset ) ; const dependentQuadrantType = scrollKey === "scrollLeft" ? QuadrantType . TOP : QuadrantType . LEFT ; this . maybeSetQuadrantScrollOffset ( dependentQuadrantType , scrollKey ) ; } ; private updateScrollContainerClientSize ( isHorizontal ) { const mainScrollContainer = this . quadrantRefs [ QuadrantType . MAIN ] . scrollContainer ; if ( isHorizontal ) { this . cache . setScrollContainerClientWidth ( mainScrollContainer . clientWidth ) ; return this . cache . getScrollContainerClientWidth ( ) ; } else { this . cache . setScrollContainerClientHeight ( mainScrollContainer . clientHeight ) ; return this . cache . getScrollContainerClientHeight ( ) ; } } private maybeIncreaseToDefaultColumnHeaderHeight ( height ) { return height <= QUADRANT_MIN_SIZE ? DEFAULT_COLUMN_HEADER_HEIGHT : height ; } /**
     * Returns the width or height of *only the grid* in the secondary quadrants
     * (TOP, LEFT, TOP_LEFT), based on the number of frozen rows and columns.
     */ private getSecondaryQuadrantGridSize ( dimension ) { const { grid , numFrozenColumns , numFrozenRows } = this . props ; const numFrozen = dimension === "width" ? numFrozenColumns : numFrozenRows ; const getterFn = dimension === "width" ? grid . getCumulativeWidthAt : grid . getCumulativeHeightAt ; return numFrozen > 0 ? getterFn ( numFrozen - 1 ) : QUADRANT_MIN_SIZE ; } /**
     * Measures the desired width of the row header based on its tallest
     * contents.
     */ private measureDesiredRowHeaderWidth ( ) { const mainRowHeader = this . quadrantRefs [ QuadrantType . MAIN ] . rowHeader ; if ( mainRowHeader == null ) { return 0 ; } else { mainRowHeader . style . width = "auto" ; const desiredRowHeaderWidth = mainRowHeader . clientWidth ; return desiredRowHeaderWidth ; } } /**
     * Measures the desired height of the column header based on its tallest
     * contents.
     */ private measureDesiredColumnHeaderHeight ( ) { const mainColumnHeader = this . quadrantRefs [ QuadrantType . MAIN ] . columnHeader ; return mainColumnHeader == null ? 0 : mainColumnHeader . clientHeight ; } private shouldRenderLeftQuadrants ( props = this . props ) { const { enableRowHeader , numFrozenColumns } = props ; return enableRowHeader || ( numFrozenColumns != null && numFrozenColumns > 0 ) ; } private adjustVerticalGuides ( verticalGuides , quadrantType ) { const isFrozenQuadrant = quadrantType === QuadrantType . LEFT || quadrantType === QuadrantType . TOP_LEFT ; const scrollAmount = isFrozenQuadrant ? 0 : this . cache . getScrollOffset ( "scrollLeft" ) ; const rowHeaderWidth = this . cache . getRowHeaderWidth ( ) ; const adjustedVerticalGuides = verticalGuides != null ? verticalGuides . map ( verticalGuide => verticalGuide - scrollAmount + rowHeaderWidth ) : verticalGuides ; return adjustedVerticalGuides ; } private adjustHorizontalGuides ( horizontalGuides , quadrantType ) { const isFrozenQuadrant = quadrantType === QuadrantType . TOP || quadrantType === QuadrantType . TOP_LEFT ; const scrollAmount = isFrozenQuadrant ? 0 : this . cache . getScrollOffset ( "scrollTop" ) ; const columnHeaderHeight = this . cache . getColumnHeaderHeight ( ) ; const adjustedHorizontalGuides = horizontalGuides != null ? horizontalGuides . map ( horizontalGuide => horizontalGuide - scrollAmount + columnHeaderHeight ) : horizontalGuides ; return adjustedHorizontalGuides ; } }