| 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..8a03bd902f31700709697ef4635569db27ceb41e 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 = 9;
|
|
|
| /**
|
| * 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.
|
| + * @param {UserPod.Style} style Style set to the pod.
|
| + */
|
| + setPodStyle: function(style) {
|
| + 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.
|
| + * @return {UserPod.Style} Pod style.
|
| + */
|
| + getPodStyle: function() {
|
| + 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);
|
| + // Only large pods have mouse down event.
|
| + if (this.getPodStyle() == UserPod.Style.LARGE)
|
| + this.userClickAuthAllowed_ = this.parentNode.isFocused(this);
|
| },
|
|
|
| /**
|
| @@ -2044,6 +2131,11 @@ cr.define('login', function() {
|
| if (this.parentNode.disabled)
|
| return;
|
|
|
| + if (this.getPodStyle() != UserPod.Style.LARGE) {
|
| + $('pod-row').switchMainPod(this);
|
| + return;
|
| + }
|
| +
|
| if (!this.isActionBoxMenuActive) {
|
| if (this.isAuthTypeOnlineSignIn) {
|
| this.showSigninUI();
|
| @@ -2073,6 +2165,10 @@ cr.define('login', function() {
|
| * @param {Event} e Key event.
|
| */
|
| handlePodKeyDown_: function(e) {
|
| + if (this.getPodStyle() != UserPod.Style.LARGE) {
|
| + this.handleNonLargePodKeyDown_(e);
|
| + return;
|
| + }
|
| if (!this.isAuthTypeUserClick || this.disabled)
|
| return;
|
| switch (e.key) {
|
| @@ -2082,6 +2178,20 @@ cr.define('login', function() {
|
| this.parentNode.setActivatedPod(this);
|
| break;
|
| }
|
| + },
|
| +
|
| + /**
|
| + * Handles keydown event for a small or extra small user pod.
|
| + * @param {Event} e Key event.
|
| + */
|
| + handleNonLargePodKeyDown_: function(e) {
|
| + switch (e.key) {
|
| + case 'Enter':
|
| + case ' ':
|
| + if ($('pod-row').isFocused(this))
|
| + $('pod-row').switchMainPod(this);
|
| + break;
|
| + }
|
| }
|
| };
|
|
|
| @@ -2293,6 +2403,11 @@ cr.define('login', function() {
|
| if (this.parentNode.disabled)
|
| return;
|
|
|
| + if (this.getPodStyle() != UserPod.Style.LARGE) {
|
| + $('pod-row').switchMainPod(this);
|
| + return;
|
| + }
|
| +
|
| this.parentNode.focusPod(this);
|
| this.parentNode.setActivatedPod(this, e);
|
| // Prevent default so that we don't trigger 'focus' event.
|
| @@ -2526,7 +2641,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 +2688,11 @@ cr.define('login', function() {
|
| if (this.parentNode.disabled)
|
| return;
|
|
|
| + if (this.getPodStyle() != UserPod.Style.LARGE) {
|
| + $('pod-row').switchMainPod(this);
|
| + return;
|
| + }
|
| +
|
| Oobe.clearErrors();
|
| this.parentNode.lastFocusedPod_ = this;
|
|
|
| @@ -2612,9 +2734,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 +2777,11 @@ cr.define('login', function() {
|
| if (this.parentNode.disabled)
|
| return;
|
|
|
| + if (this.getPodStyle() != UserPod.Style.LARGE) {
|
| + $('pod-row').switchMainPod(this);
|
| + return;
|
| + }
|
| +
|
| Oobe.clearErrors();
|
| this.parentNode.lastFocusedPod_ = this;
|
| this.activate(e);
|
| @@ -2712,6 +2843,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 +2893,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 +2914,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 +3092,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 +3165,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 +3215,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 +3475,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,
|
| @@ -3348,26 +3499,30 @@ cr.define('login', function() {
|
| },
|
|
|
| /**
|
| - * Called when window was resized.
|
| + * Called when window was resized. The two common use cases are changing
|
| + * screen orientation and showing the virtual keyboard.
|
| */
|
| 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;
|
| + var screen = document.querySelector('#scroll-container');
|
| + var clientArea = document.querySelector('#inner-container');
|
| + if (Oobe.getInstance().virtualKeyboardShown) {
|
| + // Edge case: when virtual keyboard is shown, although the screen size
|
| + // is reduced properly, the size of the outer container remains the
|
| + // same because its min-height is applied. Users can scroll the screen
|
| + // upward and see empty areas beyond the pod row and the scroll bar,
|
| + // which should be avoided.
|
| + // This is a hacky solution: we can make the scroll container hide
|
| + // the overflow area and manully position the client area.
|
| + screen.style.overflowY = "hidden";
|
| + clientArea.style.position = "absolute";
|
| + clientArea.style.left = cr.ui.toCssPx(0);
|
| + clientArea.style.top = cr.ui.toCssPx(0);
|
| + } else {
|
| + // Sets the values to default when virtual keyboard is not shown.
|
| + screen.style.overflowY = "auto";
|
| + clientArea.style.position = "relative";
|
| }
|
| -
|
| - setTimeout(function() {
|
| - this.scrollFocusedPodIntoView();
|
| - }.bind(this), timeOut);
|
| + this.placePods_();
|
| },
|
|
|
| /**
|
| @@ -3418,68 +3573,407 @@ 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;
|
| + }
|
| + // 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_();
|
| + if (Oobe.getInstance().virtualKeyboardShown) {
|
| + // When virtual keyboard is shown, the account picker should occupy
|
| + // all the remaining screen. Screen size was already updated to exclude
|
| + // the virtual keyboard.
|
| + this.parentNode.setPreferredSize(
|
| + this.screenSize.width,
|
| + this.screenSize.height);
|
| + } else {
|
| + // Make sure not to block the header bar when virtual keyboard is absent.
|
| + this.parentNode.setPreferredSize(
|
| + Oobe.getInstance().clientAreaSize.width,
|
| + Oobe.getInstance().clientAreaSize.height);
|
| + }
|
|
|
| - 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];
|
| - 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);
|
| + 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();
|
| + },
|
|
|
| - this.pods.forEach(function(pod, index) {
|
| - if (index >= maxPodsNumber) {
|
| - pod.hidden = true;
|
| - return;
|
| + /**
|
| + * 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);
|
| }
|
| - 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.');
|
| + } 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_.setPodStyle(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_.setPodStyle(UserPod.Style.LARGE);
|
| + secondPod.setPodStyle(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_.setPodStyle(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.setPodStyle(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_) {
|
| + continue;
|
| }
|
| - if (pod.offsetWidth != width) {
|
| - console.error('Pod offsetWidth (' + pod.offsetWidth +
|
| - ') and POD_WIDTH (' + width + ') are not equal.');
|
| + 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_.setPodStyle(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.setPodStyle(UserPod.Style.EXTRA_SMALL);
|
| }
|
| - var column = index % columns;
|
| - var row = Math.floor(index / columns);
|
| + }
|
|
|
| - var rowPadding = isDesktopUserManager ? DESKTOP_ROW_PADDING :
|
| - POD_ROW_PADDING;
|
| - pod.left = rowPadding + column * (width + margin);
|
| + 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;
|
| + // 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);
|
| +
|
| + // 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;
|
| + }
|
| + 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 if (!Oobe.getInstance().virtualKeyboardShown) {
|
| + // The scroll bar will definitely be shown if we reach here. A gradient
|
| + // mask is applied to avoid blocking the header bar if the virtual
|
| + // keyboard is not shown. When the keyboard is shown, there's no need
|
| + // to add the mask and the original top padding value should be kept.
|
| + actualTopPadding = SCROLL_MASK_HEIGHT;
|
| + this.showScrollMask();
|
| + }
|
| +
|
| + // 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;
|
| + }
|
| + pod.left = SCROLL_LEFT_PADDING;
|
| + pod.top = topPadding;
|
| + topPadding += CROS_EXTRA_SMALL_POD_HEIGHT + EXTRA_SMALL_POD_PADDING;
|
| + lastPod = pod;
|
| + }
|
| + // Make sure the last pod has a proper bottom padding for a symmetric
|
| + // layout.
|
| + lastPod.style.paddingBottom = cr.ui.toCssPx(actualTopPadding);
|
| + },
|
|
|
| - // On desktop, we want the rows to always be equally spaced.
|
| - pod.top = isDesktopUserManager ? row * (height + rowPadding) :
|
| - row * height + rowPadding;
|
| + /**
|
| + * 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 has a bottom padding, so
|
| + // reset all of them to be safe. This is because if the pod was at
|
| + // the last position in the scrollable container, a bottom padding
|
| + // was added to ensure a symmetric layout.
|
| + pod.style.paddingBottom = cr.ui.toCssPx(0);
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * Check if the screen is in portrait mode.
|
| + * @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(
|
| + this.screenSize.height -
|
| + 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 or extra small pod is clicked to trigger the switch
|
| + * with the main pod.
|
| + */
|
| + switchMainPod: 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.
|
| + this.mainPod_.setPodStyle(pod.getPodStyle());
|
| + pod.setPodStyle(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 +4297,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 +4463,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 {
|
|
|