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

Unified Diff: ui/login/account_picker/md_user_pod_row.js

Issue 2898283002: Multiple user pods implementation for new login screen (Closed)
Patch Set: Address comments Created 3 years, 7 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « ui/login/account_picker/md_user_pod_row.css ('k') | ui/login/account_picker/md_user_pod_template.html » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 {
« no previous file with comments | « ui/login/account_picker/md_user_pod_row.css ('k') | ui/login/account_picker/md_user_pod_template.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698