OLD | NEW |
| (Empty) |
1 | |
2 | |
3 /** | |
4 Polymer.IronFitBehavior fits an element in another element using `max-height` an
d `max-width`, and | |
5 optionally centers it in the window or another element. | |
6 | |
7 The element will only be sized and/or positioned if it has not already been size
d and/or positioned | |
8 by CSS. | |
9 | |
10 CSS properties | Action | |
11 -----------------------------|------------------------------------------- | |
12 `position` set | Element is not centered horizontally or verticall
y | |
13 `top` or `bottom` set | Element is not vertically centered | |
14 `left` or `right` set | Element is not horizontally centered | |
15 `max-height` or `height` set | Element respects `max-height` or `height` | |
16 `max-width` or `width` set | Element respects `max-width` or `width` | |
17 | |
18 @demo demo/index.html | |
19 @polymerBehavior | |
20 */ | |
21 | |
22 Polymer.IronFitBehavior = { | |
23 | |
24 properties: { | |
25 | |
26 /** | |
27 * The element that will receive a `max-height`/`width`. By default it is
the same as `this`, | |
28 * but it can be set to a child element. This is useful, for example, for
implementing a | |
29 * scrolling region inside the element. | |
30 */ | |
31 sizingTarget: { | |
32 type: Object, | |
33 value: function() { | |
34 return this; | |
35 } | |
36 }, | |
37 | |
38 /** | |
39 * The element to fit `this` into. | |
40 */ | |
41 fitInto: { | |
42 type: Object, | |
43 value: window | |
44 }, | |
45 | |
46 /** | |
47 * Set to true to auto-fit on attach. | |
48 */ | |
49 autoFitOnAttach: { | |
50 type: Boolean, | |
51 value: false | |
52 }, | |
53 | |
54 _fitInfo: { | |
55 type: Object | |
56 } | |
57 | |
58 }, | |
59 | |
60 get _fitWidth() { | |
61 var fitWidth; | |
62 if (this.fitInto === window) { | |
63 fitWidth = this.fitInto.innerWidth; | |
64 } else { | |
65 fitWidth = this.fitInto.getBoundingClientRect().width; | |
66 } | |
67 return fitWidth; | |
68 }, | |
69 | |
70 get _fitHeight() { | |
71 var fitHeight; | |
72 if (this.fitInto === window) { | |
73 fitHeight = this.fitInto.innerHeight; | |
74 } else { | |
75 fitHeight = this.fitInto.getBoundingClientRect().height; | |
76 } | |
77 return fitHeight; | |
78 }, | |
79 | |
80 attached: function() { | |
81 if (this.autoFitOnAttach) { | |
82 if (window.getComputedStyle(this).display === 'none') { | |
83 setTimeout(function() { | |
84 this.fit(); | |
85 }.bind(this)); | |
86 } else { | |
87 this.fit(); | |
88 } | |
89 } | |
90 }, | |
91 | |
92 /** | |
93 * Fits and optionally centers the element into the window, or `fitInfo` if
specified. | |
94 */ | |
95 fit: function() { | |
96 this._discoverInfo(); | |
97 this.constrain(); | |
98 this.center(); | |
99 }, | |
100 | |
101 /** | |
102 * Memoize information needed to position and size the target element. | |
103 */ | |
104 _discoverInfo: function() { | |
105 if (this._fitInfo) { | |
106 return; | |
107 } | |
108 var target = window.getComputedStyle(this); | |
109 var sizer = window.getComputedStyle(this.sizingTarget); | |
110 this._fitInfo = { | |
111 positionedBy: { | |
112 vertically: target.top !== 'auto' ? 'top' : (target.bottom !== 'auto'
? | |
113 'bottom' : null), | |
114 horizontally: target.left !== 'auto' ? 'left' : (target.right !== 'aut
o' ? | |
115 'right' : null), | |
116 css: target.position | |
117 }, | |
118 sizedBy: { | |
119 height: sizer.maxHeight !== 'none', | |
120 width: sizer.maxWidth !== 'none' | |
121 }, | |
122 margin: { | |
123 top: parseInt(target.marginTop, 10) || 0, | |
124 right: parseInt(target.marginRight, 10) || 0, | |
125 bottom: parseInt(target.marginBottom, 10) || 0, | |
126 left: parseInt(target.marginLeft, 10) || 0 | |
127 } | |
128 }; | |
129 }, | |
130 | |
131 /** | |
132 * Resets the target element's position and size constraints, and clear | |
133 * the memoized data. | |
134 */ | |
135 resetFit: function() { | |
136 if (!this._fitInfo || !this._fitInfo.sizedBy.height) { | |
137 this.sizingTarget.style.maxHeight = ''; | |
138 this.style.top = ''; | |
139 } | |
140 if (!this._fitInfo || !this._fitInfo.sizedBy.width) { | |
141 this.sizingTarget.style.maxWidth = ''; | |
142 this.style.left = ''; | |
143 } | |
144 if (this._fitInfo) { | |
145 this.style.position = this._fitInfo.positionedBy.css; | |
146 } | |
147 this._fitInfo = null; | |
148 }, | |
149 | |
150 /** | |
151 * Equivalent to calling `resetFit()` and `fit()`. Useful to call this after
the element, | |
152 * the window, or the `fitInfo` element has been resized. | |
153 */ | |
154 refit: function() { | |
155 this.resetFit(); | |
156 this.fit(); | |
157 }, | |
158 | |
159 /** | |
160 * Constrains the size of the element to the window or `fitInfo` by setting
`max-height` | |
161 * and/or `max-width`. | |
162 */ | |
163 constrain: function() { | |
164 var info = this._fitInfo; | |
165 // position at (0px, 0px) if not already positioned, so we can measure the
natural size. | |
166 if (!this._fitInfo.positionedBy.vertically) { | |
167 this.style.top = '0px'; | |
168 } | |
169 if (!this._fitInfo.positionedBy.horizontally) { | |
170 this.style.left = '0px'; | |
171 } | |
172 // need border-box for margin/padding | |
173 this.sizingTarget.style.boxSizing = 'border-box'; | |
174 // constrain the width and height if not already set | |
175 var rect = this.getBoundingClientRect(); | |
176 if (!info.sizedBy.height) { | |
177 this._sizeDimension(rect, info.positionedBy.vertically, 'top', 'bottom',
'Height'); | |
178 } | |
179 if (!info.sizedBy.width) { | |
180 this._sizeDimension(rect, info.positionedBy.horizontally, 'left', 'right
', 'Width'); | |
181 } | |
182 }, | |
183 | |
184 _sizeDimension: function(rect, positionedBy, start, end, extent) { | |
185 var info = this._fitInfo; | |
186 var max = extent === 'Width' ? this._fitWidth : this._fitHeight; | |
187 var flip = (positionedBy === end); | |
188 var offset = flip ? max - rect[end] : rect[start]; | |
189 var margin = info.margin[flip ? start : end]; | |
190 var offsetExtent = 'offset' + extent; | |
191 var sizingOffset = this[offsetExtent] - this.sizingTarget[offsetExtent]; | |
192 this.sizingTarget.style['max' + extent] = (max - margin - offset - sizingO
ffset) + 'px'; | |
193 }, | |
194 | |
195 /** | |
196 * Centers horizontally and vertically if not already positioned. This also
sets | |
197 * `position:fixed`. | |
198 */ | |
199 center: function() { | |
200 if (!this._fitInfo.positionedBy.vertically || !this._fitInfo.positionedBy.
horizontally) { | |
201 // need position:fixed to center | |
202 this.style.position = 'fixed'; | |
203 } | |
204 if (!this._fitInfo.positionedBy.vertically) { | |
205 var top = (this._fitHeight - this.offsetHeight) / 2; | |
206 top -= this._fitInfo.margin.top; | |
207 this.style.top = top + 'px'; | |
208 } | |
209 if (!this._fitInfo.positionedBy.horizontally) { | |
210 var left = (this._fitWidth - this.offsetWidth) / 2; | |
211 left -= this._fitInfo.margin.left; | |
212 this.style.left = left + 'px'; | |
213 } | |
214 } | |
215 | |
216 }; | |
217 | |
OLD | NEW |