88*/
99
1010import React = require( 'react' ) ;
11+ import ReactDOM = require( 'react-dom' ) ;
1112
1213import AccessibilityUtil from './AccessibilityUtil' ;
1314import AnimateListEdits from './listAnimations/AnimateListEdits' ;
@@ -24,6 +25,32 @@ const _styles = {
2425 flex : '0 0 auto' ,
2526 overflow : 'hidden' ,
2627 alignItems : 'stretch'
28+ } ,
29+
30+ // See resize detector comments in renderResizeDetectorIfNeeded() method below.
31+ resizeDetectorContainerStyles : {
32+ position : 'absolute' ,
33+ left : '0' ,
34+ top : '0' ,
35+ right : '0' ,
36+ bottom : '0' ,
37+ overflow : 'scroll' ,
38+ zIndex : '-1' ,
39+ visibility : 'hidden'
40+ } ,
41+
42+ resizeGrowDetectorStyles : {
43+ position : 'absolute' ,
44+ left : '100500px' ,
45+ top : '100500px' ,
46+ width : '1px' ,
47+ height : '1px'
48+ } ,
49+
50+ resizeShrinkDetectorStyles : {
51+ position : 'absolute' ,
52+ width : '150%' ,
53+ height : '150%'
2754 }
2855} ;
2956
@@ -50,6 +77,106 @@ export class View extends ViewBase<Types.ViewProps, {}> {
5077 isRxParentAText : React . PropTypes . bool . isRequired
5178 } ;
5279
80+ private resizeDetectorAnimationFrame : number ;
81+ private resizeDetectorNodes : { grow ?: HTMLElement , shrink ?: HTMLElement } = { } ;
82+
83+ private renderResizeDetectorIfNeeded ( containerStyles : any ) : React . ReactNode {
84+ // If needed, additional invisible DOM elements will be added inside the
85+ // view to track the size changes that are performed behind our back by
86+ // the browser's layout engine faster (ViewBase checks for the layout
87+ // updates once a second and sometimes it's not fast enough).
88+
89+ // Unfortunately <div> doesn't have `resize` event, so we're trying to
90+ // detect the fact that the view has been resized with `scroll` events.
91+ // To do that, we create two scrollable <div>s and we put them into a
92+ // state in which `scroll` event is triggered by the browser when the
93+ // container gets resized (one element triggers `scroll` when the
94+ // container gets bigger, another triggers `scroll` when the container
95+ // gets smaller).
96+
97+ if ( ! this . props . importantForLayout ) {
98+ return null ;
99+ }
100+
101+ if ( containerStyles . position !== 'relative' ) {
102+ console . error ( 'View: importantForLayout property is applicable only for a view with relative position' ) ;
103+ return null ;
104+ }
105+
106+ let initResizer = ( key : 'grow' | 'shrink' , ref : React . DOMComponent < React . HTMLAttributes > ) => {
107+ const cur : HTMLElement = this . resizeDetectorNodes [ key ] ;
108+ const element = ReactDOM . findDOMNode < HTMLElement > ( ref ) ;
109+
110+ if ( cur ) {
111+ delete this . resizeDetectorNodes [ key ] ;
112+ }
113+
114+ if ( element ) {
115+ this . resizeDetectorNodes [ key ] = element ;
116+ }
117+
118+ this . resizeDetectorOnScroll ( ) ;
119+ } ;
120+
121+ return [
122+ (
123+ < div
124+ key = { 'grow' }
125+ style = { _styles . resizeDetectorContainerStyles }
126+ ref = { ( ref ) => initResizer ( 'grow' , ref ) }
127+ onScroll = { ( ) => this . resizeDetectorOnScroll ( ) } >
128+
129+ < div style = { _styles . resizeGrowDetectorStyles } > </ div >
130+ </ div >
131+ ) ,
132+ (
133+ < div
134+ key = { 'shrink' }
135+ style = { _styles . resizeDetectorContainerStyles }
136+ ref = { ( ref ) => initResizer ( 'shrink' , ref ) }
137+ onScroll = { ( ) => this . resizeDetectorOnScroll ( ) } >
138+
139+ < div style = { _styles . resizeShrinkDetectorStyles } > </ div >
140+ </ div >
141+ )
142+ ] ;
143+ }
144+
145+ private resizeDetectorReset ( ) {
146+ // Scroll the detectors to the bottom-right corner so
147+ // that `scroll` events will be triggered when the container
148+ // is resized.
149+ const scrollMax = 100500 ;
150+
151+ let node = this . resizeDetectorNodes . grow ;
152+
153+ if ( node ) {
154+ node . scrollLeft = scrollMax ;
155+ node . scrollTop = scrollMax ;
156+ }
157+
158+ node = this . resizeDetectorNodes . shrink ;
159+
160+ if ( node ) {
161+ node . scrollLeft = scrollMax ;
162+ node . scrollTop = scrollMax ;
163+ }
164+ }
165+
166+ private resizeDetectorOnScroll ( ) {
167+ if ( this . resizeDetectorAnimationFrame ) {
168+ // Do not execute action more often than once per animation frame.
169+ return ;
170+ }
171+
172+ this . resizeDetectorAnimationFrame = window . requestAnimationFrame ( ( ) => {
173+ this . resizeDetectorReset ( ) ;
174+ this . resizeDetectorAnimationFrame = undefined ;
175+ ViewBase . _checkViews ( ) ;
176+ } ) ;
177+
178+ }
179+
53180 getChildContext ( ) {
54181 // Let descendant Types components know that their nearest Types ancestor is not an Types.Text.
55182 // Because they're in an Types.View, they should use their normal styling rather than their
@@ -66,7 +193,7 @@ export class View extends ViewBase<Types.ViewProps, {}> {
66193 const ariaRole = AccessibilityUtil . accessibilityTraitToString ( this . props . accessibilityTraits ) ;
67194 const ariaSelected = AccessibilityUtil . accessibilityTraitToAriaSelected ( this . props . accessibilityTraits ) ;
68195 const isAriaHidden = AccessibilityUtil . isHidden ( this . props . importantForAccessibility ) ;
69-
196+
70197 let props : Types . AccessibilityHtmlAttributes = {
71198 role : ariaRole ,
72199 tabIndex : this . props . tabIndex ,
@@ -100,18 +227,19 @@ export class View extends ViewBase<Types.ViewProps, {}> {
100227 if ( childAnimationsEnabled ) {
101228 reactElement = (
102229 < AnimateListEdits
103- { ...props }
104- animateChildEnter = { this . props . animateChildEnter }
105- animateChildMove = { this . props . animateChildMove }
106- animateChildLeave = { this . props . animateChildLeave }
230+ { ...props }
231+ animateChildEnter = { this . props . animateChildEnter }
232+ animateChildMove = { this . props . animateChildMove }
233+ animateChildLeave = { this . props . animateChildLeave }
107234 >
108- { this . props . children }
235+ { this . props . children }
109236 </ AnimateListEdits >
110237 ) ;
111238 } else {
112239 reactElement = (
113- < div { ...props } >
114- { this . props . children }
240+ < div { ...props } >
241+ { this . renderResizeDetectorIfNeeded ( combinedStyles ) }
242+ { this . props . children }
115243 </ div >
116244 ) ;
117245 }
0 commit comments