@@ -14,6 +14,8 @@ import flowtip, {
1414import type { RectLike , Region , Align , Dimensions , Result } from 'flowtip-core' ;
1515
1616import getContainingBlock from './util/getContainingBlock' ;
17+ import getClippingBlock from './util/getClippingBlock' ;
18+ import getContentRect from './util/getContentRect' ;
1719import findDOMNode from './util/findDOMNode' ;
1820
1921// Static `flowtip` layout calculation result mock for use during initial client
@@ -30,8 +32,8 @@ const STATIC_RESULT: Result = {
3032} ;
3133
3234export type State = {
33- containingBlock : RectLike ,
34- bounds : RectLike | null ,
35+ containingBlock : Rect ,
36+ bounds : Rect | null ,
3537 content : Dimensions | null ,
3638 tail : Dimensions | null ,
3739 result : Result | null ,
@@ -140,11 +142,12 @@ class FlowTip extends React.Component<Props, State> {
140142
141143 _nextContent : Dimensions | null = null ;
142144 _nextTail : Dimensions | null = null ;
143- _nextContainingBlock : RectLike = Rect . zero ;
144- _nextBounds : RectLike | null = Rect . zero ;
145+ _nextContainingBlock : Rect = Rect . zero ;
146+ _nextBounds : Rect | null = null ;
145147 _lastRegion : Region | void ;
146148 _isMounted : boolean = false ;
147149 _containingBlockNode : HTMLElement | null = null ;
150+ _clippingBlockNode : HTMLElement | null = null ;
148151 _node : HTMLElement | null = null ;
149152 state = this . _getState ( this . props ) ;
150153
@@ -153,7 +156,7 @@ class FlowTip extends React.Component<Props, State> {
153156 _handleScroll = this . _handleScroll . bind ( this ) ;
154157
155158 // ===========================================================================
156- // Lifecycle Methods.
159+ // Lifecycle Methods
157160 // ===========================================================================
158161 componentDidMount ( ) : void {
159162 this . _isMounted = true ;
@@ -169,6 +172,7 @@ class FlowTip extends React.Component<Props, State> {
169172 }
170173
171174 componentWillReceiveProps ( nextProps : Props ) : void {
175+ this . _nextContainingBlock = this . _getContainingBlockRect ( ) ;
172176 this . _nextBounds = this . _getBoundsRect ( nextProps ) ;
173177
174178 this . _updateState ( nextProps ) ;
@@ -182,14 +186,15 @@ class FlowTip extends React.Component<Props, State> {
182186 this . _isMounted = false ;
183187
184188 this . _containingBlockNode = null ;
189+ this . _clippingBlockNode = null ;
185190 this . _node = null ;
186191
187192 window . removeEventListener ( 'scroll' , this . _handleScroll ) ;
188193 window . removeEventListener ( 'resize' , this . _handleScroll ) ;
189194 }
190195
191196 // ===========================================================================
192- // State Management.
197+ // State Management
193198 // ===========================================================================
194199
195200 _getLastRegion ( nextProps : Props ) : Region | void {
@@ -402,59 +407,89 @@ class FlowTip extends React.Component<Props, State> {
402407 }
403408
404409 // ===========================================================================
405- // DOM Measurement Methods.
410+ // DOM Measurement Methods
406411 // ===========================================================================
407412
408- _getBoundsRect ( nextProps : Props ) : RectLike | null {
409- const viewport = new Rect (
410- 0 ,
411- 0 ,
412- window . document . documentElement . clientWidth ,
413- window . document . documentElement . clientHeight ,
414- ) ;
413+ _getBoundsRect ( nextProps : Props ) : Rect | null {
414+ const viewportRect = new Rect ( 0 , 0 , window . innerWidth , window . innerHeight ) ;
415415
416- const bounds = Rect . grow (
417- nextProps . bounds ? Rect . intersect ( viewport , nextProps . bounds ) : viewport ,
418- - nextProps . edgeOffset ,
419- ) ;
416+ const processBounds = ( boundsRect : RectLike ) => {
417+ const visibleBounds = Rect . grow (
418+ Rect . intersect ( viewportRect , boundsRect ) ,
419+ - nextProps . edgeOffset ,
420+ ) ;
421+
422+ // A rect with negative dimensions doesn't make sense here.
423+ // Returning null will disable rendering content.
424+ if ( visibleBounds . width < 0 || visibleBounds . height < 0 ) {
425+ return Rect . zero ;
426+ }
427+
428+ return visibleBounds ;
429+ } ;
430+
431+ if ( nextProps . bounds ) {
432+ return processBounds ( nextProps . bounds ) ;
433+ }
434+
435+ if ( document . body && this . _clippingBlockNode === document . documentElement ) {
436+ return processBounds (
437+ new Rect (
438+ - document . body . scrollLeft ,
439+ - document . body . scrollTop ,
440+ document . body . scrollWidth ,
441+ document . body . scrollHeight ,
442+ ) ,
443+ ) ;
444+ }
420445
421- // A rect with neagitve dimensions doesn't make sense here.
422- // Returning null disable rendering of any content.
423- if ( bounds . width >= 0 && bounds . height >= 0 ) {
424- return bounds ;
446+ if ( this . _clippingBlockNode ) {
447+ return processBounds ( getContentRect ( this . _clippingBlockNode ) ) ;
425448 }
426449
427450 return null ;
428451 }
429452
430- _getContainingBlockRect ( ) : RectLike {
431- if ( ! this . _containingBlockNode ) return Rect . zero ;
432- return Rect . from ( this . _containingBlockNode . getBoundingClientRect ( ) ) ;
453+ _getContainingBlockRect ( ) : Rect {
454+ if ( ! this . _containingBlockNode ) {
455+ return Rect . zero ;
456+ }
457+
458+ if (
459+ document . body &&
460+ this . _containingBlockNode === document . documentElement
461+ ) {
462+ return new Rect (
463+ - document . body . scrollLeft ,
464+ - document . body . scrollTop ,
465+ document . body . scrollWidth ,
466+ document . body . scrollHeight ,
467+ ) ;
468+ }
469+
470+ return getContentRect ( this . _containingBlockNode ) ;
433471 }
434472
435473 // ===========================================================================
436- // DOM Element Accessors.
474+ // DOM Element Accessors
437475 // ===========================================================================
438476
439477 _updateDOMNodes ( ) : void {
440478 const node = findDOMNode ( this ) ;
441- this . _node = node instanceof HTMLElement ? node : null ;
442-
443- const block = this . _node && getContainingBlock ( this . _node . parentNode ) ;
444- if ( block ) {
445- this . _containingBlockNode = block ;
446- } else {
447- // Refine nullable `document.body`.
448- // see: https://stackoverflow.com/questions/42377663
449- if ( document . body === null ) {
450- throw new Error ( 'document.body is null' ) ;
451- }
452- this . _containingBlockNode = document . body ;
479+
480+ if ( node instanceof HTMLElement ) {
481+ this . _node = node ;
482+
483+ this . _containingBlockNode =
484+ getContainingBlock ( node . parentNode ) || document . documentElement ;
485+
486+ this . _clippingBlockNode =
487+ getClippingBlock ( node . parentNode ) || document . documentElement ;
453488 }
454489 }
455490
456491 // ===========================================================================
457- // Event Handlers.
492+ // Event Handlers
458493 // ===========================================================================
459494
460495 /**
@@ -501,7 +536,7 @@ class FlowTip extends React.Component<Props, State> {
501536 }
502537
503538 // ===========================================================================
504- // Render Methods.
539+ // Render Methods
505540 // ===========================================================================
506541
507542 /**
0 commit comments