diff --git a/.storybook/icons.less b/.storybook/icons.less index 6ed274cff..e7c9e1fe8 100644 --- a/.storybook/icons.less +++ b/.storybook/icons.less @@ -55,16 +55,12 @@ background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Ctitle%3Elog out%3C/title%3E%3Cpath d='M3 3h8V1H3a2 2 0 00-2 2v14a2 2 0 002 2h8v-2H3z'/%3E%3Cpath d='M13 5v4H5v2h8v4l6-5z'/%3E%3C/svg%3E"); } -.mw-ui-icon-wikimedia-userAvatar:before { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAx0lEQVQ4jdXSzQmEQAwFYEuYUixhSwgkA8mQgKXYgS3YgXZgCZagHWgHuxf14t8osssGcv145CVJvjk+hBRFK2TrkK1D0cqHkN7CUBRI7L21KAqXMIDModiwD9oAkLlH0i3L+ooGiTWPAPPfJQTIHLGOB9h46YZnKS+3PI8PISW2GkV7FO2Jrb79h4+ODyElsYJYm437NSRWRCWdylgj++U0u+UAZI5E22hsWW03UWQtr2NT66zlCjz8uzNQbFiDN7F5/xB8aj57Ynp2FKI0bAAAAABJRU5ErkJggg==") !important; -} - .mw-ui-icon-wikimedia-ellipsis:before { background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Ctitle%3Eellipsis%3C/title%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Ccircle cx='3' cy='10' r='2'/%3E%3Ccircle cx='17' cy='10' r='2'/%3E%3C/svg%3E%0A") !important; } +.mw-ui-icon-wikimedia-userAvatar:before, #pt-anonuserpage, -#pt-userpage a { +.vector-user-menu-legacy #pt-userpage a { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAx0lEQVQ4jdXSzQmEQAwFYEuYUixhSwgkA8mQgKXYgS3YgXZgCZagHWgHuxf14t8osssGcv145CVJvjk+hBRFK2TrkK1D0cqHkN7CUBRI7L21KAqXMIDModiwD9oAkLlH0i3L+ooGiTWPAPPfJQTIHLGOB9h46YZnKS+3PI8PISW2GkV7FO2Jrb79h4+ODyElsYJYm437NSRWRCWdylgj++U0u+UAZI5E22hsWW03UWQtr2NT66zlCjz8uzNQbFiDN7F5/xB8aj57Ynp2FKI0bAAAAABJRU5ErkJggg==") !important; - background-repeat: no-repeat; } diff --git a/i18n/en.json b/i18n/en.json index ac6d6c646..2c3332c14 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -29,5 +29,6 @@ "vector-more-actions": "More", "vector-search-loader": "Loading search suggestions", "vector-anon-user-menu-pages": "Pages for logged out editors", - "vector-anon-user-menu-pages-learn": "learn more" + "vector-anon-user-menu-pages-learn": "learn more", + "vector-personal-more-label": "User links" } diff --git a/i18n/qqq.json b/i18n/qqq.json index 2327009d3..ea9565be9 100644 --- a/i18n/qqq.json +++ b/i18n/qqq.json @@ -41,5 +41,6 @@ "vector-more-actions": "Label in the Vector skin's menu for the less-important or rarer actions which are not shown as tabs (like moving the page, or for sysops deleting or protecting the page), as well as (for users with a narrow viewing window in their browser) the less-important tab actions which the user's browser is unable to fit in. {{Identical|More}}", "vector-search-loader": "Text to display below search input while the search suggestion module is loading", "vector-anon-user-menu-pages": "Label describing the anon editor links in the anon user menu", - "vector-anon-user-menu-pages-learn": "Lowercase text of link that goes to Help:Introduction and helps the user learn more about editing" + "vector-anon-user-menu-pages-learn": "Lowercase text of link that goes to Help:Introduction and helps the user learn more about editing", + "vector-personal-more-label": "Label describing the user links next to the user links dropdown menu." } diff --git a/includes/Hooks.php b/includes/Hooks.php index eb83095be..0d8aa7786 100644 --- a/includes/Hooks.php +++ b/includes/Hooks.php @@ -192,6 +192,21 @@ class Hooks { // "Login" link is handled manually by Vector unset( $content_navigation['user-menu']['login'] ); } + + // ULS and user page links are hidden at lower resolutions. + if ( $content_navigation['user-interface-preferences'] ) { + self::appendClassToListItem( + $content_navigation['user-interface-preferences']['uls'], + $COLLAPSE_MENU_ITEM_CLASS + ); + } + if ( $content_navigation['user-page'] ) { + self::appendClassToListItem( + $content_navigation['user-page']['userpage'], + $COLLAPSE_MENU_ITEM_CLASS + ); + } + // Prefix user link items with associated icon. $user_menu = $content_navigation['user-menu']; // Loop through each menu to check/append its link classes. diff --git a/includes/SkinVector.php b/includes/SkinVector.php index d2f2d6077..5cb5e3178 100644 --- a/includes/SkinVector.php +++ b/includes/SkinVector.php @@ -246,8 +246,25 @@ class SkinVector extends SkinMustache { $returnto = $this->getReturnToParam(); $useCombinedLoginLink = $this->useCombinedLoginLink(); $htmlCreateAccount = $this->getCreateAccountHTML( $returnto ); - $userMenuData = $menuData[ 'data-user-menu' ]; + $templateParser = $this->getTemplateParser(); + $userMoreHtmlItems = $templateParser->processTemplate( 'UserLinks__more', [ + 'is-anon' => $isAnon, + 'html-create-account' => $htmlCreateAccount, + 'data-user-interface-preferences' => $menuData[ 'data-user-interface-preferences' ], + 'data-notifications' => $menuData[ 'data-notifications' ], + 'data-user-page' => $menuData[ 'data-user-page' ], + ] ); + $userMoreData = [ + "id" => 'p-personal-more', + "class" => 'mw-portlet mw-portlet-personal-more vector-menu vector-user-menu-more', + "html-items" => $userMoreHtmlItems, + "label" => $this->msg( 'vector-personal-more-label' ), + "heading-class" => 'vector-menu-heading', + "is-dropdown" => false, + ]; + + $userMenuData = $menuData[ 'data-user-menu' ]; if ( $isAnon ) { $userMenuData[ 'html-before-portal' ] .= $this->getLoginHTML( $returnto, $useCombinedLoginLink ); } else { @@ -256,11 +273,7 @@ class SkinVector extends SkinMustache { } return [ - 'is-anon' => $isAnon, - 'html-create-account' => $htmlCreateAccount, - 'data-user-interface-preferences' => $menuData[ 'data-user-interface-preferences' ], - 'data-notifications' => $menuData[ 'data-notifications' ], - 'data-user-page' => $menuData[ 'data-user-page' ], + 'data-user-more' => $userMoreData, 'data-user-menu' => $userMenuData ]; } diff --git a/includes/templates/UserLinks.mustache b/includes/templates/UserLinks.mustache index 07d65f760..85f96d99d 100644 --- a/includes/templates/UserLinks.mustache +++ b/includes/templates/UserLinks.mustache @@ -1,13 +1,4 @@ diff --git a/includes/templates/UserLinks__more.mustache b/includes/templates/UserLinks__more.mustache new file mode 100644 index 000000000..9a0f921d2 --- /dev/null +++ b/includes/templates/UserLinks__more.mustache @@ -0,0 +1,10 @@ +{{#data-user-interface-preferences}}{{{html-items}}}{{/data-user-interface-preferences}} +{{#is-anon}} + +{{/is-anon}} +{{^is-anon}} + {{#data-user-page}}{{{html-items}}}{{/data-user-page}} + {{#data-notifications}}{{{html-items}}}{{/data-notifications}} +{{/is-anon}} diff --git a/resources/skins.vector.styles/components/UserLinks.less b/resources/skins.vector.styles/components/UserLinks.less index 9b4c7824b..c76c5bf5c 100644 --- a/resources/skins.vector.styles/components/UserLinks.less +++ b/resources/skins.vector.styles/components/UserLinks.less @@ -8,6 +8,34 @@ justify-content: flex-end; flex-shrink: 1; + .vector-user-menu-more { + .vector-menu-content-list { + display: flex; + align-items: center; + + li { + padding-top: 0; + margin-left: 1em; + white-space: nowrap; + + a { + color: #000; + text-decoration: none; + } + + // Below tablet threshold, all menu items except the notification icons will collapse into the user menu + // This ensures a max of 4 icons on small screen sizes (i.e. search, 2 notification icons and the user avatar) + &.user-links-collapsible-item { + display: none; + + @media ( min-width: @width-breakpoint-tablet ) { + display: block; + } + } + } + } + } + // Overrides personal menu styles for consolidated user links. .vector-user-menu { margin: 0 0 0 12px; @@ -115,73 +143,4 @@ background-position: 100% 0%; } } - - .mw-portlet-notifications { - li { - float: left; - margin-left: 0; - } - } - - .mw-portlet-user-page { - // For logged-in users, below tablet threshold, the menu will collapse the user name/link to user page into the user menu - display: none; - - @media ( min-width: @width-breakpoint-tablet ) { - display: block; - } - - li { - margin-left: 1em; - padding-bottom: 0.5em; - - a { - background-position: left center; - color: #000; - text-decoration: none; - } - } - } - - .vector-user-links-createaccount { - margin-left: 0.75em; - // For logged-out users, below tablet threshold, the menu will collapse the create account link into the user menu - display: none; - - @media ( min-width: @width-breakpoint-tablet ) { - display: block; - } - } - - // Adjust the user-interface-preferences menu. - .mw-portlet-user-interface-preferences { - margin-top: -0.2em; - margin-right: 0; - padding: 0; - // The menu is collapsed into the p-personal menu at lower resolutions, because the features - // are accessible via Special:Preferences with a few more clicks and there is not space to - // accomodate this icon at this resolution. - display: none; - - @media ( min-width: @width-breakpoint-tablet ) { - display: block; - } - - // Hide the heading of the user-interface-preferences menu. - h3 { - display: none; - } - - li { - font-size: 0.75em; - - a { - padding-left: 2em; - } - } - - ul.vector-menu-content-list { - padding-top: 0; - } - } } diff --git a/skin.json b/skin.json index 6247fc30c..add53aa20 100644 --- a/skin.json +++ b/skin.json @@ -34,6 +34,7 @@ "createaccount", "vector-anon-user-menu-pages", "vector-anon-user-menu-pages-learn", + "vector-personal-more-label", "parentheses", "otherlanguages", "tooltip-p-logo", diff --git a/stories/UserLinks.stories.data.js b/stories/UserLinks.stories.data.js index b31810924..b5b239aaf 100644 --- a/stories/UserLinks.stories.data.js +++ b/stories/UserLinks.stories.data.js @@ -2,9 +2,11 @@ * @external MenuDefinition * @external UserLinksDefinition */ +import mustache from 'mustache'; import { menuTemplate } from './Menu.stories.data'; import userLinksTemplateLegacy from '!!raw-loader!../includes/templates/legacy/UserLinks.mustache'; import userLinksTemplate from '!!raw-loader!../includes/templates/UserLinks.mustache'; +import userLinksMoreTemplate from '!!raw-loader!../includes/templates/UserLinks__more.mustache'; import { helperClassName, helperMakeMenuData } from './utils'; /** @@ -61,6 +63,19 @@ const PERSONAL_MENU_TEMPLATE_DATA = { loggedInWithULS }; +const additionalUserMoreData = { + class: 'vector-menu vector-user-menu-more', + "heading-class": 'vector-menu-heading', + "is-dropdown": false +}; + +const userMoreHtmlItems = ( isAnon = true ) => mustache.render( userLinksMoreTemplate, { + 'is-anon': isAnon, + 'html-create-account': `Create account`, + 'data-user-page': helperMakeMenuData( 'user-page', USERNAME_ITEM ), + 'data-notifications': helperMakeMenuData( 'notifications', ECHO_ITEMS ) +} ); + const loggedInData = { class: 'vector-user-menu vector-menu-dropdown vector-user-menu-logged-in', 'heading-class': 'mw-ui-icon mw-ui-icon-element mw-ui-icon-wikimedia-userAvatar', @@ -94,9 +109,7 @@ const loggedOutData = { * @type {UserLinksDefinition} */ const USER_LINKS_LOGGED_IN_TEMPLATE_DATA = { - 'is-anon': false, - 'data-user-page': helperMakeMenuData( 'user-page', USERNAME_ITEM ), - 'data-notifications': helperMakeMenuData( 'notifications', ECHO_ITEMS ), + 'data-user-more': helperMakeMenuData( 'personal-more', userMoreHtmlItems( false ), additionalUserMoreData ), 'data-user-menu': helperMakeMenuData( 'new-personal', USER_LINKS_ITEMS, loggedInData ) }; @@ -104,8 +117,7 @@ const USER_LINKS_LOGGED_IN_TEMPLATE_DATA = { * @type {UserLinksDefinition} */ const USER_LINKS_LOGGED_OUT_TEMPLATE_DATA = { - 'is-anon': true, - 'html-create-account': `Create account`, + 'data-user-more': helperMakeMenuData( 'personal-more', userMoreHtmlItems( true ), additionalUserMoreData ), 'data-user-menu': helperMakeMenuData( 'new-personal', ANON_USER_LINKS_ITEMS, loggedOutData ) }; diff --git a/stories/types.js b/stories/types.js index d9a2fbb03..00f10a62c 100644 --- a/stories/types.js +++ b/stories/types.js @@ -71,10 +71,6 @@ /** * @typedef {Object} UserLinksDefinition - * @property {boolean} is-anon - * @property {string} [html-create-account] - * @property {MenuDefinition} [data-user-interface-preferences] - * @property {MenuDefinition} [data-notifications] - * @property {MenuDefinition} [data-user-page] + * @property {MenuDefinition} data-user-more * @property {MenuDefinition} data-user-menu */