Chromium Code Reviews| Index: ui/login/account_picker/md_user_pod_row.js |
| diff --git a/ui/login/account_picker/md_user_pod_row.js b/ui/login/account_picker/md_user_pod_row.js |
| index e13d59bff035c8da4e0e56f043c5ef568d5febc2..2a75d1a980c8d91a0665340ad9f13837d792cf2f 100644 |
| --- a/ui/login/account_picker/md_user_pod_row.js |
| +++ b/ui/login/account_picker/md_user_pod_row.js |
| @@ -49,17 +49,29 @@ cr.define('login', function() { |
| * synced with computed CSS sizes of pods. |
| */ |
| var CROS_POD_WIDTH = 306; |
| + var CROS_SMALL_POD_WIDTH = 304; |
| + var CROS_EXTRA_SMALL_POD_WIDTH = 282; |
| var DESKTOP_POD_WIDTH = 180; |
| var MD_DESKTOP_POD_WIDTH = 160; |
| var PUBLIC_EXPANDED_BASIC_WIDTH = 500; |
| var PUBLIC_EXPANDED_ADVANCED_WIDTH = 610; |
| var CROS_POD_HEIGHT = 346; |
| + var CROS_SMALL_POD_HEIGHT = 74; |
| + var CROS_EXTRA_SMALL_POD_HEIGHT = 60; |
| var DESKTOP_POD_HEIGHT = 226; |
| var MD_DESKTOP_POD_HEIGHT = 200; |
| var POD_ROW_PADDING = 10; |
| var DESKTOP_ROW_PADDING = 32; |
| var CUSTOM_ICON_CONTAINER_SIZE = 40; |
| var CROS_PIN_POD_HEIGHT = 417; |
| + var SCROLL_MASK_HEIGHT = 112; |
| + |
| + /** |
| + * The maximum number of users that each pod placement method can handle. |
| + */ |
| + var POD_ROW_LIMIT = 2; |
| + var LANDSCAPE_MODE_LIMIT = 6; |
| + var PORTRAIT_MODE_LIMIT = 10; |
| /** |
| * Minimal padding between user pod and virtual keyboard. |
| @@ -89,12 +101,12 @@ cr.define('login', function() { |
| * @const |
| */ |
| var UserPodTabOrder = { |
| - POD_INPUT: 1, // Password input field, Action box menu button and |
| - // the pod itself. |
| - PIN_KEYBOARD: 2, // Pin keyboard below the password input field. |
| - POD_CUSTOM_ICON: 3, // Pod custom icon next to password input field. |
| - HEADER_BAR: 4, // Buttons on the header bar (Shutdown, Add User). |
| - POD_MENU_ITEM: 5 // User pad menu items (User info, Remove user). |
| + POD_INPUT: 1, // Password input field and the pod itself. |
| + PIN_KEYBOARD: 2, // Pin keyboard below the password input field. |
| + POD_CUSTOM_ICON: 3, // Pod custom icon next to password input field. |
| + HEADER_BAR: 4, // Buttons on the header bar (Shutdown, Add User). |
| + ACTION_BOX_BUTTON: 5, // Action box menu button. |
| + POD_MENU_ITEM: 6 // User pod menu items (User info, Remove user). |
| }; |
| /** |
| @@ -155,12 +167,13 @@ cr.define('login', function() { |
| // (1) all user pods have tab index 1 so they are traversed first; |
| // (2) when a user pod is activated, its tab index is set to -1 and its |
| // main input field gets focus and tab index 1; |
| - // (3) if user pod custom icon is interactive, it has tab index 2 so it |
| - // follows the input. |
| - // (4) buttons on the header bar have tab index 3 so they follow the custom |
| - // icon, or user pod if custom icon is not interactive; |
| - // (5) Action box buttons have tab index 4 and follow header bar buttons; |
| - // (6) lastly, focus jumps to the Status Area and back to user pods. |
| + // (3) if pin keyboard is present, it has tab index 2 so it follows the input; |
| + // (4) if user pod custom icon is interactive, it has tab index 3; |
| + // (5) buttons on the header bar have tab index 4; |
| + // (6) Action box buttons have tab index 5 and follow the buttons on the |
| + // header bar; |
| + // (7) User pod menu items (if present) have tab index 6; |
| + // (8) lastly, focus jumps to the Status Area and back to user pods. |
| // |
| // 'Focus' event is handled by a capture handler for the whole document |
| // and in some cases 'mousedown' event handlers are used instead of 'click' |
| @@ -187,6 +200,17 @@ cr.define('login', function() { |
| }); |
| /** |
| + * The display style of user pods. |
| + * @enum {number} |
| + * @const |
| + */ |
| + UserPod.Style = { |
| + LARGE: 0, |
| + SMALL: 1, |
| + EXTRA_SMALL: 2 |
| + }; |
| + |
| + /** |
| * Stops event propagation from the any user pod child element. |
| * @param {Event} e Event to handle. |
| */ |
| @@ -753,7 +777,7 @@ cr.define('login', function() { |
| /** @override */ |
| decorate: function() { |
| this.tabIndex = UserPodTabOrder.POD_INPUT; |
| - this.actionBoxAreaElement.tabIndex = UserPodTabOrder.POD_INPUT; |
| + this.actionBoxAreaElement.tabIndex = UserPodTabOrder.ACTION_BOX_BUTTON; |
| this.addEventListener('keydown', this.handlePodKeyDown_.bind(this)); |
| this.addEventListener('click', this.handleClickOnPod_.bind(this)); |
| @@ -823,6 +847,10 @@ cr.define('login', function() { |
| this.imageElement.addEventListener('load', |
| this.parentNode.handlePodImageLoad.bind(this.parentNode, this)); |
| + this.smallPodImageElement.addEventListener( |
| + 'load', |
| + this.parentNode.handlePodImageLoad.bind(this.parentNode, this)); |
| + |
| var initialAuthType = this.user.initialAuthType || |
| AUTH_TYPE.OFFLINE_PASSWORD; |
| this.setAuthType(initialAuthType, null); |
| @@ -952,6 +980,22 @@ cr.define('login', function() { |
| }, |
| /** |
| + * Gets image element of the small pod. |
| + * @type {!HTMLImageElement} |
| + */ |
| + get smallPodImageElement() { |
| + return this.querySelector('.small-pod-image'); |
| + }, |
| + |
| + /** |
| + * Gets name element of the small pod. |
| + * @type {!HTMLDivElement} |
| + */ |
| + get smallPodNameElement() { |
| + return this.querySelector('.small-pod-name'); |
| + }, |
| + |
| + /** |
| * Gets reauth name hint element. |
| * @type {!HTMLDivElement} |
| */ |
| @@ -1143,13 +1187,54 @@ cr.define('login', function() { |
| }, |
| /** |
| + * Sets the pod style. |
| + * @type {number} |
|
jdufault
2017/05/24 17:11:15
This doesn't return anything so there shouldn't be
Wenzhao (Colin) Zang
2017/05/24 19:23:43
Done.
jdufault
2017/05/24 20:44:52
Needs @param type annotation
Wenzhao (Colin) Zang
2017/05/24 21:36:37
Done.
|
| + */ |
| + set podStyle(style) { |
|
jdufault
2017/05/24 17:11:15
FYI: In the future I recommend making any non-triv
Wenzhao (Colin) Zang
2017/05/24 19:23:43
Done.
jdufault
2017/05/24 20:44:52
The idea here is to have it clear at the call site
Wenzhao (Colin) Zang
2017/05/24 21:36:37
Oh I see. I misunderstood earlier.
Wenzhao (Colin) Zang
2017/05/24 21:36:37
Done.
|
| + switch (style) { |
| + case UserPod.Style.LARGE: |
| + this.querySelector('.large-pod').hidden = false; |
| + this.querySelector('.small-pod').hidden = true; |
| + break; |
| + case UserPod.Style.SMALL: |
| + this.querySelector('.large-pod').hidden = true; |
| + this.querySelector('.small-pod').hidden = false; |
| + this.querySelector('.small-pod').classList.remove('extra-small'); |
| + break; |
| + case UserPod.Style.EXTRA_SMALL: |
| + this.querySelector('.large-pod').hidden = true; |
| + this.querySelector('.small-pod').hidden = false; |
| + this.querySelector('.small-pod').classList.add('extra-small'); |
| + break; |
| + default: |
| + console.error("Attempt to set an invalid pod style."); |
| + break; |
| + } |
| + }, |
| + |
| + /** |
| + * Gets the pod style. |
| + * @type {number} |
|
jdufault
2017/05/24 17:11:14
@type {UserPod.Style}
Wenzhao (Colin) Zang
2017/05/24 19:23:43
Done.
|
| + */ |
| + get podStyle() { |
| + if (this.querySelector('.small-pod').hidden) |
| + return UserPod.Style.LARGE; |
| + if (this.querySelector('.small-pod').classList.contains('extra-small')) |
| + return UserPod.Style.EXTRA_SMALL; |
| + return UserPod.Style.SMALL; |
| + }, |
| + |
| + /** |
| * Updates the user pod element. |
| */ |
| update: function() { |
| - this.imageElement.src = 'chrome://userimage/' + this.user.username + |
| + var imageSrc = 'chrome://userimage/' + this.user.username + |
| '?id=' + UserPod.userImageSalt_[this.user.username]; |
| + this.imageElement.src = imageSrc; |
| + this.smallPodImageElement.src = imageSrc; |
| this.nameElement.textContent = this.user_.displayName; |
| + this.smallPodNameElement.textContent = this.user_.displayName; |
| this.reauthNameHintElement.textContent = this.user_.displayName; |
| this.classList.toggle('signed-in', this.user_.signedIn); |
| @@ -1802,8 +1887,8 @@ cr.define('login', function() { |
| * @param {number|string=} count The number or string to replace $1 in |
| * |message|. Can be omitted if $1 is not present in |message|. |
| */ |
| - updateRemoveWarningDialogSetMessage_: function(profilePath, message, |
| - count) { |
| + updateRemoveWarningDialogSetMessage_: function( |
| + profilePath, message, count) { |
| if (profilePath !== this.user.profilePath) |
| return; |
| // Add localized messages where $1 will be replaced with |
| @@ -1987,7 +2072,9 @@ cr.define('login', function() { |
| * @param {Event} e The mouse down event. |
| */ |
| handlePodMouseDown_: function(e) { |
| - this.userClickAuthAllowed_ = this.parentNode.isFocused(this); |
| + // Small pods do not have mouse down event. |
|
jdufault
2017/05/24 17:11:15
The comment is a little confusing - I'd recommend
|
| + if (this.podStyle == UserPod.Style.LARGE) |
| + this.userClickAuthAllowed_ = this.parentNode.isFocused(this); |
| }, |
| /** |
| @@ -2044,6 +2131,12 @@ cr.define('login', function() { |
| if (this.parentNode.disabled) |
| return; |
| + if (this.podStyle != UserPod.Style.LARGE) { |
| + // It should trigger the switch between the small pod and the main pod. |
| + $('pod-row').changeMainPod(this); |
| + return; |
| + } |
| + |
| if (!this.isActionBoxMenuActive) { |
| if (this.isAuthTypeOnlineSignIn) { |
| this.showSigninUI(); |
| @@ -2073,6 +2166,10 @@ cr.define('login', function() { |
| * @param {Event} e Key event. |
| */ |
| handlePodKeyDown_: function(e) { |
| + if (this.podStyle != UserPod.Style.LARGE) { |
| + this.handleSmallPodKeyDown_(e); |
|
jdufault
2017/05/24 17:11:14
handleNonLargePodKeyDown?
Wenzhao (Colin) Zang
2017/05/24 19:23:44
Done.
|
| + return; |
| + } |
| if (!this.isAuthTypeUserClick || this.disabled) |
| return; |
| switch (e.key) { |
| @@ -2082,6 +2179,20 @@ cr.define('login', function() { |
| this.parentNode.setActivatedPod(this); |
| break; |
| } |
| + }, |
| + |
| + /** |
| + * Handles keydown event for a small user pod. |
| + * @param {Event} e Key event. |
| + */ |
| + handleSmallPodKeyDown_: function(e) { |
| + switch (e.key) { |
| + case 'Enter': |
| + case ' ': |
| + if ($('pod-row').isFocused(this)) |
| + $('pod-row').changeMainPod(this); |
| + break; |
| + } |
| } |
| }; |
| @@ -2293,6 +2404,12 @@ cr.define('login', function() { |
| if (this.parentNode.disabled) |
| return; |
| + if (this.podStyle != UserPod.Style.LARGE) { |
| + // It should trigger the switch between the small pod and the main pod. |
| + $('pod-row').changeMainPod(this); |
| + return; |
| + } |
| + |
| this.parentNode.focusPod(this); |
| this.parentNode.setActivatedPod(this, e); |
| // Prevent default so that we don't trigger 'focus' event. |
| @@ -2526,7 +2643,9 @@ cr.define('login', function() { |
| /** @override */ |
| update: function() { |
| this.imageElement.src = this.user.userImage; |
| + this.smallPodImageElement.src = this.user.userImage; |
| this.nameElement.textContent = this.user.displayName; |
| + this.smallPodNameElement.textContent = this.user.displayName; |
| this.reauthNameHintElement.textContent = this.user.displayName; |
| var isLockedUser = this.user.needsSignin; |
| @@ -2571,6 +2690,12 @@ cr.define('login', function() { |
| if (this.parentNode.disabled) |
| return; |
| + if (this.podStyle != UserPod.Style.LARGE) { |
| + // It should trigger the switch between the small pod and the main pod. |
|
jdufault
2017/05/24 17:11:15
This comment is a restatement of relatively obviou
Wenzhao (Colin) Zang
2017/05/24 19:23:43
Acknowledged.
Wenzhao (Colin) Zang
2017/05/24 21:36:37
Done.
|
| + $('pod-row').changeMainPod(this); |
| + return; |
| + } |
| + |
| Oobe.clearErrors(); |
| this.parentNode.lastFocusedPod_ = this; |
| @@ -2612,9 +2737,13 @@ cr.define('login', function() { |
| this.imageElement.src = this.user.iconUrl; |
| this.imageElement.alt = this.user.label; |
| this.imageElement.title = this.user.label; |
| + this.smallPodImageElement.src = this.user.iconUrl; |
| + this.smallPodImageElement.alt = this.user.label; |
| + this.smallPodImageElement.title = this.user.label; |
| this.passwordEntryContainerElement.hidden = true; |
| this.launchAppButtonContainerElement.hidden = false; |
| this.nameElement.textContent = this.user.label; |
| + this.smallPodNameElement.textContent = this.user.label; |
| this.reauthNameHintElement.textContent = this.user.label; |
| UserPod.prototype.updateActionBoxArea.call(this); |
| @@ -2651,6 +2780,12 @@ cr.define('login', function() { |
| if (this.parentNode.disabled) |
| return; |
| + if (this.podStyle != UserPod.Style.LARGE) { |
| + // It should trigger the switch between the small pod and the main pod. |
| + $('pod-row').changeMainPod(this); |
|
jdufault
2017/05/24 17:11:15
FYI: 'change' is an unusual word, typically 'set'
Wenzhao (Colin) Zang
2017/05/24 19:23:43
Done.
|
| + return; |
| + } |
| + |
| Oobe.clearErrors(); |
| this.parentNode.lastFocusedPod_ = this; |
| this.activate(e); |
| @@ -2712,6 +2847,9 @@ cr.define('login', function() { |
| // Pod that was most recently focused, if any. |
| lastFocusedPod_: undefined, |
| + // Pod that occupies the main spot. |
| + mainPod_: undefined, |
| + |
| // Pods whose initial images haven't been loaded yet. |
| podsWithPendingImages_: [], |
| @@ -2759,11 +2897,15 @@ cr.define('login', function() { |
| }, |
| /** |
| - * Returns all the pods in this pod row. |
| + * Returns all the pods in this pod row. Some pods may not be its direct |
| + * children, but the caller doesn't have to know this. |
| * @type {NodeList} |
| */ |
| get pods() { |
| - return Array.prototype.slice.call(this.children); |
| + var powRowChildren = Array.prototype.slice.call(this.children); |
| + var containerChildren = |
| + Array.prototype.slice.call(this.smallPodsContainer.children); |
| + return powRowChildren.concat(containerChildren); |
| }, |
| /** |
| @@ -2776,7 +2918,8 @@ cr.define('login', function() { |
| DISPLAY_TYPE.DESKTOP_USER_MANAGER; |
| return (isDesktopUserManager || this.touchViewEnabled_) ? |
| - false : this.children.length == 1; |
| + false : |
| + this.pods.length == 1; |
| }, |
| /** |
| @@ -2953,9 +3096,15 @@ cr.define('login', function() { |
| console.warn('Attempt to remove pod that does not exist'); |
| return; |
| } |
| - this.removeChild(podToRemove); |
| - if (this.pods.length > 0) |
| + // Its parent is not necessarily this pod row. |
| + podToRemove.parentNode.removeChild(podToRemove); |
| + this.mainPod_ = null; |
| + if (this.pods.length > 0) { |
| + // placePods_() will select a new main pod and re-append pods |
| + // to different parents if necessary. |
| this.placePods_(); |
| + this.maybePreselectPod(); |
| + } |
| }, |
| /** |
| @@ -3020,11 +3169,11 @@ cr.define('login', function() { |
| this.focusedPod_ = undefined; |
| this.activatedPod_ = undefined; |
| this.lastFocusedPod_ = undefined; |
| + this.mainPod_ = undefined; |
| // Switch off animation |
| Oobe.getInstance().toggleClass('flying-pods', false); |
| - // Populate the pod row. |
| for (var i = 0; i < this.users_.length; ++i) |
| this.addUserPod(this.users_[i]); |
| @@ -3070,6 +3219,14 @@ cr.define('login', function() { |
| }, |
| /** |
| + * Gets the container of small pods. |
| + * @type {!HTMLDivElement} |
| + */ |
| + get smallPodsContainer() { |
| + return document.querySelector('.small-pod-container'); |
| + }, |
| + |
| + /** |
| * Adds given apps to the pod row. |
| * @param {array} apps Array of apps. |
| */ |
| @@ -3322,10 +3479,8 @@ cr.define('login', function() { |
| * @param {boolean} multipleRecommendedLocales Whether |locales| contains |
| * two or more recommended locales |
| */ |
| - setPublicSessionLocales: function(userID, |
| - locales, |
| - defaultLocale, |
| - multipleRecommendedLocales) { |
| + setPublicSessionLocales: function( |
| + userID, locales, defaultLocale, multipleRecommendedLocales) { |
| var pod = this.getPodWithUsername_(userID); |
| if (pod != null) { |
| pod.populateLanguageSelect(locales, |
| @@ -3351,23 +3506,7 @@ cr.define('login', function() { |
| * Called when window was resized. |
| */ |
| onWindowResize: function() { |
| - var layout = this.calculateLayout_(); |
| - if (layout.columns != this.columns || layout.rows != this.rows) |
| - this.placePods_(); |
| - |
| - // Wrap this in a set timeout so the function is called after the pod is |
| - // finished transitioning so that we work with the final pod dimensions. |
| - // If there is no focused pod that may be transitioning when this function |
| - // is called, we can call scrollFocusedPodIntoView() right away. |
| - var timeOut = 0; |
| - if (this.focusedPod_) { |
| - var style = getComputedStyle(this.focusedPod_); |
| - timeOut = parseFloat(style.transitionDuration) * 1000; |
| - } |
| - |
| - setTimeout(function() { |
| - this.scrollFocusedPodIntoView(); |
| - }.bind(this), timeOut); |
| + this.placePods_(); |
|
jdufault
2017/05/24 17:11:14
Does this happen on orientation change?
Wenzhao (Colin) Zang
2017/05/24 19:23:43
Yes. onWindowResize is called when changing the sc
|
| }, |
| /** |
| @@ -3418,68 +3557,399 @@ cr.define('login', function() { |
| if (!Oobe.getInstance().newDesktopUserManager) { |
| var maxHeigth = Oobe.getInstance().clientAreaSize.height; |
| while (maxHeigth < this.rowsToHeight_(rows) && rows > 1) |
| - --rows; |
| + --rows; |
| } |
| // One more iteration if it's not enough cells to place all pods. |
| while (maxWidth >= this.columnsToWidth_(columns + 1) && |
| columns * rows < this.pods.length && |
| columns < MAX_NUMBER_OF_COLUMNS) { |
| - ++columns; |
| + ++columns; |
| } |
| return {columns: columns, rows: rows}; |
| }, |
| /** |
| - * Places pods onto their positions onto pod grid. |
| + * Places pods onto their positions in pod grid matching the new design. |
| * @private |
| */ |
| placePods_: function() { |
| - var isDesktopUserManager = Oobe.getInstance().displayType == |
| - DISPLAY_TYPE.DESKTOP_USER_MANAGER; |
| - if (isDesktopUserManager && !Oobe.getInstance().userPodsPageVisible) |
| + var pods = this.pods; |
| + if (pods.length == 0) { |
| + console.error('Attempt to place pods for an empty pod list.'); |
| return; |
| - |
| - var layout = this.calculateLayout_(); |
| - var columns = this.columns = layout.columns; |
| - var rows = this.rows = layout.rows; |
| - var maxPodsNumber = columns * rows; |
| - var margin = isDesktopUserManager ? DESKTOP_MARGIN_BY_COLUMNS[columns] : |
| - MARGIN_BY_COLUMNS[columns]; |
| + } |
| + // Append all pods to their proper parents. Small pods have parent other |
| + // than the pod row. The pods were all initialized with the pow row as a |
| + // temporary parent, which is intended to ensure that all event listeners |
| + // work properly. If the main pod already exists, it means we are in the |
| + // process of resizing the window, then there is no need to change parents |
| + // of any pod. |
| + if (!this.mainPod_) { |
| + this.mainPod_ = this.preselectedPod; |
| + this.appendPodsToParents(); |
| + } |
| + this.restoreInitialStates_(); |
| + // Make sure not to block the header bar. |
| this.parentNode.setPreferredSize( |
| - this.columnsToWidth_(columns), this.rowsToHeight_(rows)); |
| - var height = this.userPodHeight_; |
| - var width = this.userPodWidth_; |
| - var pinPodLocation = { column: columns + 1, row: rows + 1 }; |
| - if (this.focusedPod_ && this.focusedPod_.isPinShown()) |
| - pinPodLocation = this.findPodLocation_(this.focusedPod_, columns, rows); |
| + Oobe.getInstance().clientAreaSize.width, |
| + Oobe.getInstance().clientAreaSize.height); |
| - this.pods.forEach(function(pod, index) { |
| - if (index >= maxPodsNumber) { |
| - pod.hidden = true; |
| - return; |
| + if (pods.length == 1) { |
| + this.placeSinglePod_(); |
| + } else if (pods.length == POD_ROW_LIMIT) { |
| + this.placePodsOnPodRow_(); |
| + } else { |
| + this.placePodsOnContainer_(); |
| + } |
| + Oobe.getInstance().updateScreenSize(this.parentNode); |
| + this.updatePodNameArea(); |
| + }, |
| + |
| + /** |
| + * Append pods to proper parents. Called each time before pod placement. |
| + * @private |
| + */ |
| + appendPodsToParents: function() { |
| + var pods = this.pods; |
| + // Pod count may have changed, so the placement method may change |
| + // accordingly. Therefore, always remove all pods from their current |
| + // parents first. |
| + for (var pod of pods) { |
| + pod.parentNode.removeChild(pod); |
| + } |
| + if (pods.length <= POD_ROW_LIMIT) { |
| + for (var pod of pods) { |
| + this.appendChild(pod); |
| + } |
| + } else { |
| + // When the user count exceeds the limit (currently set to 2), only the |
| + // main pod still has pow row as parent, all other pods should be |
| + // appended to the container with scroll bar. |
| + for (var pod of pods) { |
| + if (pod == this.mainPod_) { |
| + this.appendChild(pod); |
| + } else { |
| + this.smallPodsContainer.appendChild(pod); |
| + } |
| + } |
| + } |
| + }, |
| + |
| + /** |
| + * Called when there is one user pod. |
| + * @private |
| + */ |
| + placeSinglePod_: function() { |
| + this.mainPod_.podStyle = UserPod.Style.LARGE; |
| + this.mainPod_.left = (this.screenSize.width - CROS_POD_WIDTH) / 2; |
| + this.mainPod_.top = (this.screenSize.height - CROS_POD_HEIGHT) / 2; |
| + }, |
| + |
| + /** |
| + * Called when there are two users pods. |
| + * @private |
| + */ |
| + placePodsOnPodRow_: function() { |
| + // Both pods have large size and are placed adjacently. |
| + var secondPod = |
| + this.pods[0] == this.mainPod_ ? this.pods[1] : this.pods[0]; |
| + this.mainPod_.podStyle = UserPod.Style.LARGE; |
| + secondPod.podStyle = UserPod.Style.LARGE; |
| + |
| + var DOUBLE_PODS_PADDING = this.isPortraitMode() ? 32 : 118; |
| + var leftPadding = (this.screenSize.width - (CROS_POD_WIDTH * 2 + DOUBLE_PODS_PADDING)) / 2; |
| + // Start actual positioning. |
| + this.mainPod_.left = leftPadding; |
| + this.mainPod_.top = (this.screenSize.height - CROS_POD_HEIGHT) / 2; |
| + secondPod.left = leftPadding + CROS_POD_WIDTH + DOUBLE_PODS_PADDING; |
| + secondPod.top = (this.screenSize.height - CROS_POD_HEIGHT) / 2; |
| + }, |
| + |
| + /** |
| + * Called when there are more than two user pods. |
| + * @private |
| + */ |
| + placePodsOnContainer_: function() { |
| + this.smallPodsContainer.hidden = false; |
| + var pods = this.pods; |
| + if ((pods.length > LANDSCAPE_MODE_LIMIT && !this.isPortraitMode()) || |
| + (pods.length > PORTRAIT_MODE_LIMIT && this.isPortraitMode())) { |
| + // If the pod count exceeds limits, they should be in extra small size |
| + // and the container will become scrollable. |
| + this.placePodsOnScrollableContainer_(); |
| + return; |
| + } |
| + this.mainPod_.podStyle = UserPod.Style.LARGE; |
| + for (var pod of pods) { |
| + if (pod != this.mainPod_) { |
| + // All pods except the main one must be set to the small style. |
| + pod.podStyle = UserPod.Style.SMALL; |
| + } |
| + } |
| + // The size of the smallPodsContainer must be updated to avoid overflow, |
| + // otherwise unnecessary scroll bar will show up. |
| + this.smallPodsContainer.style.height = |
| + cr.ui.toCssPx(this.screenSize.height); |
| + this.smallPodsContainer.style.width = cr.ui.toCssPx(CROS_SMALL_POD_WIDTH); |
| + |
| + var LEFT_PADDING = this.isPortraitMode() ? 0 : 98; |
| + var MIDDLE_PADDING = this.isPortraitMode() ? 84 : 220; |
| + var contentsWidth = LEFT_PADDING + |
| + CROS_POD_WIDTH + MIDDLE_PADDING + CROS_SMALL_POD_WIDTH; |
| + var blankWidth = this.screenSize.width - contentsWidth; |
| + var actualLeftPadding = LEFT_PADDING; |
| + actualLeftPadding += this.isPortraitMode() ? blankWidth * 2 / 3: |
| + blankWidth / 2; |
| + var SMALL_POD_PADDING = 54; |
| + var actualSmallPodPadding = SMALL_POD_PADDING; |
| + var smallPodsTotalHeight = (pods.length - 1) * CROS_SMALL_POD_HEIGHT + |
| + (pods.length - 2) * actualSmallPodPadding; |
| + if (smallPodsTotalHeight > this.screenSize.height) { |
| + // Edge case: when the virtual keyboard is present, the total height of |
| + // the smallPodsContainer may exceed the screen height. Decrease the |
| + // padding among small pods according to the design spec. |
| + actualSmallPodPadding = 32; |
| + smallPodsTotalHeight = (pods.length - 1) * CROS_SMALL_POD_HEIGHT + |
| + (pods.length - 2) * actualSmallPodPadding; |
| + } |
| + |
| + // Start positioning of the main pod and the smallPodsContainer. |
| + this.mainPod_.left = actualLeftPadding; |
| + this.mainPod_.top = (this.screenSize.height - CROS_POD_HEIGHT) / 2; |
| + this.smallPodsContainer.style.left = |
| + cr.ui.toCssPx(actualLeftPadding + CROS_POD_WIDTH + MIDDLE_PADDING); |
| + this.smallPodsContainer.style.top = cr.ui.toCssPx(0); |
| + // Start positioning of the small pods inside the smallPodsContainer. |
| + var smallPodsTopPadding = (this.screenSize.height - smallPodsTotalHeight) / 2; |
| + for (var pod of pods) { |
| + if (pod == this.mainPod_) { |
|
jdufault
2017/05/24 17:11:14
FYI: style guide says to drop the {} on single-lin
Wenzhao (Colin) Zang
2017/05/24 19:23:44
I will pay attention to it.
|
| + continue; |
| + } |
| + pod.left = 0; |
| + pod.top = smallPodsTopPadding; |
| + smallPodsTopPadding += CROS_SMALL_POD_HEIGHT + actualSmallPodPadding; |
| + } |
| + }, |
| + |
| + /** |
| + * Called when there are more than 6 user pods in landscape mode, or more |
| + * than 10 user pods in portrait mode. |
| + * @private |
| + */ |
| + placePodsOnScrollableContainer_: function() { |
| + this.smallPodsContainer.hidden = false; |
| + // Add a dark overlay. |
| + this.smallPodsContainer.classList.add('scroll'); |
| + var pods = this.pods; |
| + this.mainPod_.podStyle = UserPod.Style.LARGE; |
| + for (var pod of pods) { |
| + if (pod != this.mainPod_) { |
| + // All pods except the main one must be set to the extra small style. |
| + pod.podStyle = UserPod.Style.EXTRA_SMALL; |
| } |
| - pod.hidden = false; |
| - if (pod.offsetHeight != height && |
| - pod.offsetHeight != CROS_PIN_POD_HEIGHT) { |
| - console.error('Pod offsetHeight (' + pod.offsetHeight + |
| - ') and POD_HEIGHT (' + height + ') are not equal.'); |
| + } |
| + |
| + var SCROLL_LEFT_PADDING = this.isPortraitMode() ? 46 : 72; |
| + var SCROLL_RIGHT_PADDING = this.isPortraitMode() ? 12 : 72; |
| + // The offsetWidth of the smallPodsContainer. |
| + var scrollAreaWidth = SCROLL_LEFT_PADDING + CROS_EXTRA_SMALL_POD_WIDTH + |
| + SCROLL_RIGHT_PADDING; |
| + var mainPodPadding = (this.screenSize.width - |
| + scrollAreaWidth - CROS_POD_WIDTH) / 2; |
| + var SCROLL_TOP_PADDING = this.isPortraitMode() ? 66 : 72; |
| + var EXTRA_SMALL_POD_PADDING = 32; |
| + |
| + // Precalculate the total height of the scrollable container and check if |
| + // it indeed exceeds the screen height. |
| + var scrollHeight = 0; |
| + for (var pod of pods) { |
| + if (pod == this.mainPod_) { |
| + continue; |
| } |
| - if (pod.offsetWidth != width) { |
| - console.error('Pod offsetWidth (' + pod.offsetWidth + |
| - ') and POD_WIDTH (' + width + ') are not equal.'); |
| + scrollHeight += CROS_EXTRA_SMALL_POD_HEIGHT + EXTRA_SMALL_POD_PADDING; |
| + } |
| + scrollHeight -= EXTRA_SMALL_POD_PADDING; |
| + // The smallPodsContainer should occupy the full screen vertically. |
| + this.smallPodsContainer.style.height = cr.ui.toCssPx(this.screenSize.height); |
| + this.smallPodsContainer.style.width = cr.ui.toCssPx( |
| + SCROLL_LEFT_PADDING + CROS_EXTRA_SMALL_POD_WIDTH + |
| + SCROLL_RIGHT_PADDING); |
| + |
| + // SCROLL_TOP_PADDING denotes the smallest top padding we can tolerate |
| + // before allowing the container to overflow and show the scroll bar. |
| + var actualTopPadding = SCROLL_TOP_PADDING; |
| + if ((this.screenSize.height - scrollHeight) / 2 > actualTopPadding) { |
| + // Edge case: the total height of the scrollable container does not |
| + // exceed the screen height (minus the neceesary padding), so the |
| + // scroll bar will not appear. |
| + // In this case, we still want to keep the extra small pod size and |
| + // the overlay, but the top and bottom padding should be adjusted |
| + // to ensure a symmetric layout. |
| + actualTopPadding = (this.screenSize.height - scrollHeight) / 2; |
| + } else { |
| + // The scroll bar will definitely be shown if we reach here. A gradient |
| + // mask is applied to avoid blocking the header bar. |
| + actualTopPadding = SCROLL_MASK_HEIGHT; |
| + this.showScrollMask(); |
| + } |
| + // Start positioning of the main pod and the smallPodsContainer. |
| + this.mainPod_.left = mainPodPadding; |
| + this.mainPod_.top = (this.screenSize.height - CROS_POD_HEIGHT) / 2; |
| + this.smallPodsContainer.style.left = |
| + cr.ui.toCssPx(mainPodPadding * 2 + CROS_POD_WIDTH); |
| + this.smallPodsContainer.style.top = cr.ui.toCssPx(0); |
| + // Start positioning of the small pods inside the smallPodsContainer. |
| + var topPadding = actualTopPadding; |
| + var lastPod = undefined; |
| + for (var pod of pods) { |
| + if (pod == this.mainPod_) { |
| + continue; |
| } |
| - var column = index % columns; |
| - var row = Math.floor(index / columns); |
| + pod.left = SCROLL_LEFT_PADDING; |
| + pod.top = topPadding; |
| + topPadding += CROS_EXTRA_SMALL_POD_HEIGHT + EXTRA_SMALL_POD_PADDING; |
| + lastPod = pod; |
| + } |
| + // If the scroll bar and the gradient mask are shown, make sure the last |
| + // pod isn't blocked by the mask. |
| + if (!document.querySelector('.small-pod-container-mask').hidden) { |
| + lastPod.style.paddingBottom = cr.ui.toCssPx(SCROLL_MASK_HEIGHT); |
| + } |
| + }, |
| - var rowPadding = isDesktopUserManager ? DESKTOP_ROW_PADDING : |
| - POD_ROW_PADDING; |
| - pod.left = rowPadding + column * (width + margin); |
| + /** |
| + * Called each time before pod placement to ensure we start with the |
| + * initial state, which is ready to place only one user pod. The styles |
| + * of elements necessary for other placement methods must be set |
| + * explicitly each time. |
| + * @private |
| + */ |
| + restoreInitialStates_: function() { |
| + this.smallPodsContainer.hidden = true; |
| + document.querySelector('.small-pod-container-mask').hidden = true; |
| + document.querySelector('.small-pod-container-mask.rotate').hidden = true; |
| + this.smallPodsContainer.classList.remove('scroll'); |
| + var pods = this.pods; |
| + for (var pod of pods) { |
| + // There is a chance that one of the pods have a bottom padding, so |
| + // reset all of them to be safe. |
|
jdufault
2017/05/24 17:11:15
Any idea why? It'd be good to include that in the
Wenzhao (Colin) Zang
2017/05/24 19:23:44
Done.
|
| + pod.style.paddingBottom = cr.ui.toCssPx(0); |
| + } |
| + }, |
| - // On desktop, we want the rows to always be equally spaced. |
| - pod.top = isDesktopUserManager ? row * (height + rowPadding) : |
| - row * height + rowPadding; |
| + /** |
| + * Check if it's in portrait mode. |
|
jdufault
2017/05/24 17:11:15
Please clarify "it"
Wenzhao (Colin) Zang
2017/05/24 19:23:43
Done.
|
| + * @return {boolean} True if in portrait mode. |
| + */ |
| + isPortraitMode: function() { |
| + return this.screenSize.width < |
| + this.screenSize.height; |
| + }, |
| + |
| + /** |
| + * Called when scroll bar is shown and we need a mask for the header bar. |
| + * @private |
| + */ |
| + showScrollMask: function() { |
| + var topMask = document.querySelector('.small-pod-container-mask'); |
| + topMask.hidden = false; |
| + topMask.style.left = this.smallPodsContainer.style.left; |
| + topMask.style.width = this.smallPodsContainer.style.width; |
| + // The bottom mask is a rotation of the top mask. |
| + var bottomMask = |
| + document.querySelector('.small-pod-container-mask.rotate'); |
| + bottomMask.hidden = false; |
| + bottomMask.style.left = this.smallPodsContainer.style.left; |
| + bottomMask.style.width = this.smallPodsContainer.style.width; |
| + // The bottom mask should overlap with the header bar, and its z-index |
| + // is chosen to ensure it does not block users from using the header bar. |
| + bottomMask.style.top = cr.ui.toCssPx( |
| + document.querySelector('#scroll-container').offsetHeight - |
| + SCROLL_MASK_HEIGHT); |
| + }, |
| + |
| + /** |
| + * Makes sure that user name on each large pod is centered and extra long |
| + * name does not exceed max width. Names on small pods do not need to be |
| + * dynamically updated. |
| + * @private |
| + */ |
| + updatePodNameArea: function() { |
| + this.querySelectorAll('.name-container').forEach(function(nameArea) { |
| + var nameElement = nameArea.querySelector('.name'); |
| + var leftMargin = (CROS_POD_WIDTH - nameElement.offsetWidth) / 2; |
| + if (leftMargin > 0) |
| + nameArea.style.left = cr.ui.toCssPx(leftMargin); |
| + else |
| + nameElement.style.width = cr.ui.toCssPx(CROS_POD_WIDTH); |
| }); |
| - Oobe.getInstance().updateScreenSize(this.parentNode); |
| + }, |
| + |
| + /** |
| + * Called when a small pod is clicked to trigger the switch between the |
| + * small pod and the main pod. |
| + */ |
| + changeMainPod: function(pod) { |
| + if (this.disabled) { |
| + console.error('Cannot change main pod while sign-in UI is disabled.'); |
| + return; |
| + } |
| + if (!this.mainPod_) { |
| + console.error('Attempt to switch a non-existing main pod.'); |
| + return; |
| + } |
| + // Find the index of the small pod. |
| + var insert = 0; |
| + var children = pod.parentNode.children; |
| + while (insert < children.length && children[insert] != pod) |
| + insert++; |
| + if (insert >= children.length) { |
| + console.error('Attempt to switch a non-existing small pod.'); |
| + return; |
| + } |
| + // Switch style of the two pods. |
| + if (pod.podStyle == UserPod.Style.EXTRA_SMALL) { |
|
jdufault
2017/05/24 17:11:14
this.mainPod_.podStyle = pod.podStyle?
Then you c
Wenzhao (Colin) Zang
2017/05/24 19:23:44
Done.
|
| + this.mainPod_.podStyle = UserPod.Style.EXTRA_SMALL; |
| + } else { |
| + this.mainPod_.podStyle = UserPod.Style.SMALL; |
| + } |
| + pod.podStyle = UserPod.Style.LARGE; |
| + |
| + // Switch parent and position of the two pods. |
| + var left = pod.left; |
| + var top = pod.top; |
| + // Edge case: paddingBottom should be switched too because there's a |
| + // chance that the small pod was at the end of the scrollable container |
| + // and had a non-zero paddingBottom. |
| + var paddingBottom = pod.style.paddingBottom; |
| + var parent = pod.parentNode; |
| + parent.removeChild(pod); |
| + this.appendChild(pod); |
| + pod.left = this.mainPod_.left; |
| + pod.top = this.mainPod_.top; |
| + pod.style.paddingBottom = cr.ui.toCssPx(0); |
| + |
| + this.removeChild(this.mainPod_); |
| + // It must have the same index with the original small pod, instead |
| + // of being appended as the last child, in order to maintain the 'Tab' |
| + // order. |
| + parent.insertBefore(this.mainPod_, children[insert]); |
| + this.mainPod_.left = left; |
| + this.mainPod_.top = top; |
| + this.mainPod_.style.paddingBottom = paddingBottom; |
| + this.mainPod_ = pod; |
| + // Focus the new main pod. |
| + this.focusPod(this.mainPod_); |
| + this.updatePodNameArea(); |
| + }, |
| + |
| + /** |
| + * Returns dimensions of screen including the header bar. |
| + * @type {Object} |
| + */ |
| + get screenSize() { |
| + var container = $('scroll-container'); |
| + return {width: container.offsetWidth, height: container.offsetHeight}; |
| }, |
| /** |
| @@ -3803,6 +4273,12 @@ cr.define('login', function() { |
| return; |
| } |
| + // Small pods do not have input box. |
| + if (e.target.parentNode == this.smallPodsContainer) { |
| + this.focusPod(e.target, false, true /* opt_skipInputFocus */); |
| + return; |
| + } |
| + |
| var pod = findAncestorByClass(e.target, 'pod'); |
| if (pod && pod.parentNode == this) { |
| // Focus on a control of a pod but not on the action area button. |
| @@ -3963,33 +4439,18 @@ cr.define('login', function() { |
| }, |
| /** |
| - * Makes sure user name is centered in each pod and extra long name |
| - * does not exceed max width. |
| - */ |
| - updatePodNameArea: function() { |
| - this.querySelectorAll('.name-container').forEach(function(nameArea) { |
| - var nameElement = nameArea.querySelector('.name'); |
| - var leftMargin = (CROS_POD_WIDTH - nameElement.offsetWidth) / 2; |
| - if (leftMargin > 0) |
| - nameArea.style.left = leftMargin + 'px'; |
| - else |
| - nameElement.style.width = CROS_POD_WIDTH + 'px'; |
| - }); |
| - }, |
| - |
| - /** |
| * Preselects pod, if needed. |
| */ |
| - maybePreselectPod: function() { |
| - var pod = this.preselectedPod; |
| - this.focusPod(pod); |
| + maybePreselectPod: function() { |
| + var pod = this.preselectedPod; |
| + this.focusPod(pod); |
| - // Hide user-type-bubble in case all user pods are disabled and we focus |
| - // first pod. |
| - if (pod && pod.multiProfilesPolicyApplied) { |
| - pod.userTypeBubbleElement.classList.remove('bubble-shown'); |
| - } |
| - } |
| + // Hide user-type-bubble in case all user pods are disabled and we focus |
| + // first pod. |
| + if (pod && pod.multiProfilesPolicyApplied) { |
| + pod.userTypeBubbleElement.classList.remove('bubble-shown'); |
| + } |
| + } |
| }; |
| return { |