Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(85)

Side by Side Diff: third_party/polymer/v1_0/components-chromium/app-layout/app-drawer/app-drawer-extracted.js

Issue 2633633002: Polymer: Remove unused app-layout element (Closed)
Patch Set: Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 Polymer({
2 is: 'app-drawer',
3
4 properties: {
5 /**
6 * The opened state of the drawer.
7 */
8 opened: {
9 type: Boolean,
10 value: false,
11 notify: true,
12 reflectToAttribute: true
13 },
14
15 /**
16 * The drawer does not have a scrim and cannot be swiped close.
17 */
18 persistent: {
19 type: Boolean,
20 value: false,
21 reflectToAttribute: true
22 },
23
24 /**
25 * The alignment of the drawer on the screen ('left', 'right', 'start' o r 'end').
26 * 'start' computes to left and 'end' to right in LTR layout and vice ve rsa in RTL
27 * layout.
28 */
29 align: {
30 type: String,
31 value: 'left'
32 },
33
34 /**
35 * The computed, read-only position of the drawer on the screen ('left' or 'right').
36 */
37 position: {
38 type: String,
39 readOnly: true,
40 value: 'left',
41 reflectToAttribute: true
42 },
43
44 /**
45 * Create an area at the edge of the screen to swipe open the drawer.
46 */
47 swipeOpen: {
48 type: Boolean,
49 value: false,
50 reflectToAttribute: true
51 },
52
53 /**
54 * Trap keyboard focus when the drawer is opened and not persistent.
55 */
56 noFocusTrap: {
57 type: Boolean,
58 value: false
59 }
60 },
61
62 observers: [
63 'resetLayout(position)',
64 '_resetPosition(align, isAttached)'
65 ],
66
67 _translateOffset: 0,
68
69 _trackDetails: null,
70
71 _drawerState: 0,
72
73 _boundEscKeydownHandler: null,
74
75 _firstTabStop: null,
76
77 _lastTabStop: null,
78
79 ready: function() {
80 // Set the scroll direction so you can vertically scroll inside the draw er.
81 this.setScrollDirection('y');
82
83 // Only transition the drawer after its first render (e.g. app-drawer-la yout
84 // may need to set the initial opened state which should not be transiti oned).
85 this._setTransitionDuration('0s');
86 },
87
88 attached: function() {
89 // Only transition the drawer after its first render (e.g. app-drawer-la yout
90 // may need to set the initial opened state which should not be transiti oned).
91 Polymer.RenderStatus.afterNextRender(this, function() {
92 this._setTransitionDuration('');
93 this._boundEscKeydownHandler = this._escKeydownHandler.bind(this);
94 this._resetDrawerState();
95
96 this.listen(this, 'track', '_track');
97 this.addEventListener('transitionend', this._transitionend.bind(this)) ;
98 this.addEventListener('keydown', this._tabKeydownHandler.bind(this))
99 });
100 },
101
102 detached: function() {
103 document.removeEventListener('keydown', this._boundEscKeydownHandler);
104 },
105
106 /**
107 * Opens the drawer.
108 */
109 open: function() {
110 this.opened = true;
111 },
112
113 /**
114 * Closes the drawer.
115 */
116 close: function() {
117 this.opened = false;
118 },
119
120 /**
121 * Toggles the drawer open and close.
122 */
123 toggle: function() {
124 this.opened = !this.opened;
125 },
126
127 /**
128 * Gets the width of the drawer.
129 *
130 * @return {number} The width of the drawer in pixels.
131 */
132 getWidth: function() {
133 return this.$.contentContainer.offsetWidth;
134 },
135
136 /**
137 * Resets the layout. If you changed the size of app-header via CSS
138 * you can notify the changes by either firing the `iron-resize` event
139 * or calling `resetLayout` directly.
140 *
141 * @method resetLayout
142 */
143 resetLayout: function() {
144 this.debounce('_resetLayout', function() {
145 this.fire('app-drawer-reset-layout');
146 }, 1);
147 },
148
149 _isRTL: function() {
150 return window.getComputedStyle(this).direction === 'rtl';
151 },
152
153 _resetPosition: function() {
154 switch (this.align) {
155 case 'start':
156 this._setPosition(this._isRTL() ? 'right' : 'left');
157 return;
158 case 'end':
159 this._setPosition(this._isRTL() ? 'left' : 'right');
160 return;
161 }
162 this._setPosition(this.align);
163 },
164
165 _escKeydownHandler: function(event) {
166 var ESC_KEYCODE = 27;
167 if (event.keyCode === ESC_KEYCODE) {
168 // Prevent any side effects if app-drawer closes.
169 event.preventDefault();
170 this.close();
171 }
172 },
173
174 _track: function(event) {
175 if (this.persistent) {
176 return;
177 }
178
179 // Disable user selection on desktop.
180 event.preventDefault();
181
182 switch (event.detail.state) {
183 case 'start':
184 this._trackStart(event);
185 break;
186 case 'track':
187 this._trackMove(event);
188 break;
189 case 'end':
190 this._trackEnd(event);
191 break;
192 }
193 },
194
195 _trackStart: function(event) {
196 this._drawerState = this._DRAWER_STATE.TRACKING;
197
198 // Disable transitions since style attributes will reflect user track ev ents.
199 this._setTransitionDuration('0s');
200 this.style.visibility = 'visible';
201
202 var rect = this.$.contentContainer.getBoundingClientRect();
203 if (this.position === 'left') {
204 this._translateOffset = rect.left;
205 } else {
206 this._translateOffset = rect.right - window.innerWidth;
207 }
208
209 this._trackDetails = [];
210 },
211
212 _trackMove: function(event) {
213 this._translateDrawer(event.detail.dx + this._translateOffset);
214
215 // Use Date.now() since event.timeStamp is inconsistent across browsers (e.g. most
216 // browsers use milliseconds but FF 44 uses microseconds).
217 this._trackDetails.push({
218 dx: event.detail.dx,
219 timeStamp: Date.now()
220 });
221 },
222
223 _trackEnd: function(event) {
224 var x = event.detail.dx + this._translateOffset;
225 var drawerWidth = this.getWidth();
226 var isPositionLeft = this.position === 'left';
227 var isInEndState = isPositionLeft ? (x >= 0 || x <= -drawerWidth) :
228 (x <= 0 || x >= drawerWidth);
229
230 if (!isInEndState) {
231 // No longer need the track events after this method returns - allow t hem to be GC'd.
232 var trackDetails = this._trackDetails;
233 this._trackDetails = null;
234
235 this._flingDrawer(event, trackDetails);
236 if (this._drawerState === this._DRAWER_STATE.FLINGING) {
237 return;
238 }
239 }
240
241 // If the drawer is not flinging, toggle the opened state based on the p osition of
242 // the drawer.
243 var halfWidth = drawerWidth / 2;
244 if (event.detail.dx < -halfWidth) {
245 this.opened = this.position === 'right';
246 } else if (event.detail.dx > halfWidth) {
247 this.opened = this.position === 'left';
248 }
249
250 // Trigger app-drawer-transitioned now since there will be no transition end event.
251 if (isInEndState) {
252 this._resetDrawerState();
253 }
254
255 this._setTransitionDuration('');
256 this._resetDrawerTranslate();
257 this.style.visibility = '';
258 },
259
260 _calculateVelocity: function(event, trackDetails) {
261 // Find the oldest track event that is within 100ms using binary search.
262 var now = Date.now();
263 var timeLowerBound = now - 100;
264 var trackDetail;
265 var min = 0;
266 var max = trackDetails.length - 1;
267
268 while (min <= max) {
269 // Floor of average of min and max.
270 var mid = (min + max) >> 1;
271 var d = trackDetails[mid];
272 if (d.timeStamp >= timeLowerBound) {
273 trackDetail = d;
274 max = mid - 1;
275 } else {
276 min = mid + 1;
277 }
278 }
279
280 if (trackDetail) {
281 var dx = event.detail.dx - trackDetail.dx;
282 var dt = (now - trackDetail.timeStamp) || 1;
283 return dx / dt;
284 }
285 return 0;
286 },
287
288 _flingDrawer: function(event, trackDetails) {
289 var velocity = this._calculateVelocity(event, trackDetails);
290
291 // Do not fling if velocity is not above a threshold.
292 if (Math.abs(velocity) < this._MIN_FLING_THRESHOLD) {
293 return;
294 }
295
296 this._drawerState = this._DRAWER_STATE.FLINGING;
297
298 var x = event.detail.dx + this._translateOffset;
299 var drawerWidth = this.getWidth();
300 var isPositionLeft = this.position === 'left';
301 var isVelocityPositive = velocity > 0;
302 var isClosingLeft = !isVelocityPositive && isPositionLeft;
303 var isClosingRight = isVelocityPositive && !isPositionLeft;
304 var dx;
305 if (isClosingLeft) {
306 dx = -(x + drawerWidth);
307 } else if (isClosingRight) {
308 dx = (drawerWidth - x);
309 } else {
310 dx = -x;
311 }
312
313 // Enforce a minimum transition velocity to make the drawer feel snappy.
314 if (isVelocityPositive) {
315 velocity = Math.max(velocity, this._MIN_TRANSITION_VELOCITY);
316 this.opened = this.position === 'left';
317 } else {
318 velocity = Math.min(velocity, -this._MIN_TRANSITION_VELOCITY);
319 this.opened = this.position === 'right';
320 }
321
322 // Calculate the amount of time needed to finish the transition based on the
323 // initial slope of the timing function.
324 this._setTransitionDuration((this._FLING_INITIAL_SLOPE * dx / velocity) + 'ms');
325 this._setTransitionTimingFunction(this._FLING_TIMING_FUNCTION);
326
327 this._resetDrawerTranslate();
328 },
329
330 _transitionend: function(event) {
331 // contentContainer will transition on opened state changed, and scrim w ill
332 // transition on persistent state changed when opened - these are the
333 // transitions we are interested in.
334 var target = Polymer.dom(event).rootTarget;
335 if (target === this.$.contentContainer || target === this.$.scrim) {
336
337 // If the drawer was flinging, we need to reset the style attributes.
338 if (this._drawerState === this._DRAWER_STATE.FLINGING) {
339 this._setTransitionDuration('');
340 this._setTransitionTimingFunction('');
341 this.style.visibility = '';
342 }
343
344 this._resetDrawerState();
345 }
346 },
347
348 _setTransitionDuration: function(duration) {
349 this.$.contentContainer.style.transitionDuration = duration;
350 this.$.scrim.style.transitionDuration = duration;
351 },
352
353 _setTransitionTimingFunction: function(timingFunction) {
354 this.$.contentContainer.style.transitionTimingFunction = timingFunction;
355 this.$.scrim.style.transitionTimingFunction = timingFunction;
356 },
357
358 _translateDrawer: function(x) {
359 var drawerWidth = this.getWidth();
360
361 if (this.position === 'left') {
362 x = Math.max(-drawerWidth, Math.min(x, 0));
363 this.$.scrim.style.opacity = 1 + x / drawerWidth;
364 } else {
365 x = Math.max(0, Math.min(x, drawerWidth));
366 this.$.scrim.style.opacity = 1 - x / drawerWidth;
367 }
368
369 this.translate3d(x + 'px', '0', '0', this.$.contentContainer);
370 },
371
372 _resetDrawerTranslate: function() {
373 this.$.scrim.style.opacity = '';
374 this.transform('', this.$.contentContainer);
375 },
376
377 _resetDrawerState: function() {
378 var oldState = this._drawerState;
379 if (this.opened) {
380 this._drawerState = this.persistent ?
381 this._DRAWER_STATE.OPENED_PERSISTENT : this._DRAWER_STATE.OPENED;
382 } else {
383 this._drawerState = this._DRAWER_STATE.CLOSED;
384 }
385
386 if (oldState !== this._drawerState) {
387 if (this._drawerState === this._DRAWER_STATE.OPENED) {
388 this._setKeyboardFocusTrap();
389 document.addEventListener('keydown', this._boundEscKeydownHandler);
390 document.body.style.overflow = 'hidden';
391 } else {
392 document.removeEventListener('keydown', this._boundEscKeydownHandler );
393 document.body.style.overflow = '';
394 }
395
396 // Don't fire the event on initial load.
397 if (oldState !== this._DRAWER_STATE.INIT) {
398 this.fire('app-drawer-transitioned');
399 }
400 }
401 },
402
403 _setKeyboardFocusTrap: function() {
404 if (this.noFocusTrap) {
405 return;
406 }
407
408 // NOTE: Unless we use /deep/ (which we shouldn't since it's deprecated) , this will
409 // not select focusable elements inside shadow roots.
410 var focusableElementsSelector = [
411 'a[href]:not([tabindex="-1"])',
412 'area[href]:not([tabindex="-1"])',
413 'input:not([disabled]):not([tabindex="-1"])',
414 'select:not([disabled]):not([tabindex="-1"])',
415 'textarea:not([disabled]):not([tabindex="-1"])',
416 'button:not([disabled]):not([tabindex="-1"])',
417 'iframe:not([tabindex="-1"])',
418 '[tabindex]:not([tabindex="-1"])',
419 '[contentEditable=true]:not([tabindex="-1"])'
420 ].join(',');
421 var focusableElements = Polymer.dom(this).querySelectorAll(focusableElem entsSelector);
422
423 if (focusableElements.length > 0) {
424 this._firstTabStop = focusableElements[0];
425 this._lastTabStop = focusableElements[focusableElements.length - 1];
426 } else {
427 // Reset saved tab stops when there are no focusable elements in the d rawer.
428 this._firstTabStop = null;
429 this._lastTabStop = null;
430 }
431
432 // Focus on app-drawer if it has non-zero tabindex. Otherwise, focus the first focusable
433 // element in the drawer, if it exists. Use the tabindex attribute since the this.tabIndex
434 // property in IE/Edge returns 0 (instead of -1) when the attribute is n ot set.
435 var tabindex = this.getAttribute('tabindex');
436 if (tabindex && parseInt(tabindex, 10) > -1) {
437 this.focus();
438 } else if (this._firstTabStop) {
439 this._firstTabStop.focus();
440 }
441 },
442
443 _tabKeydownHandler: function(event) {
444 if (this.noFocusTrap) {
445 return;
446 }
447
448 var TAB_KEYCODE = 9;
449 if (this._drawerState === this._DRAWER_STATE.OPENED && event.keyCode === TAB_KEYCODE) {
450 if (event.shiftKey) {
451 if (this._firstTabStop && Polymer.dom(event).localTarget === this._f irstTabStop) {
452 event.preventDefault();
453 this._lastTabStop.focus();
454 }
455 } else {
456 if (this._lastTabStop && Polymer.dom(event).localTarget === this._la stTabStop) {
457 event.preventDefault();
458 this._firstTabStop.focus();
459 }
460 }
461 }
462 },
463
464 _MIN_FLING_THRESHOLD: 0.2,
465
466 _MIN_TRANSITION_VELOCITY: 1.2,
467
468 _FLING_TIMING_FUNCTION: 'cubic-bezier(0.667, 1, 0.667, 1)',
469
470 _FLING_INITIAL_SLOPE: 1.5,
471
472 _DRAWER_STATE: {
473 INIT: 0,
474 OPENED: 1,
475 OPENED_PERSISTENT: 2,
476 CLOSED: 3,
477 TRACKING: 4,
478 FLINGING: 5
479 }
480
481 /**
482 * Fired when the layout of app-drawer has changed.
483 *
484 * @event app-drawer-reset-layout
485 */
486
487 /**
488 * Fired when app-drawer has finished transitioning.
489 *
490 * @event app-drawer-transitioned
491 */
492 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698