diff --git a/includes/Hooks.php b/includes/Hooks.php index 1e3db76c..78cb6520 100644 --- a/includes/Hooks.php +++ b/includes/Hooks.php @@ -153,7 +153,7 @@ class Hooks implements } /** - * Transforms watch item inside the action navigation menu + * Moves watch item from actions to views menu. * * @param array &$content_navigation */ @@ -168,15 +168,60 @@ class Hooks implements // Promote watch link from actions to views and add an icon if ( $key !== null ) { - self::appendClassToItem( - $content_navigation['actions'][$key]['class'], - [ 'icon' ] - ); $content_navigation['views'][$key] = $content_navigation['actions'][$key]; unset( $content_navigation['actions'][$key] ); } } + /** + * Adds icons to items in the "views" menu. + * + * @param array &$content_navigation + * @param bool $isLegacy is this the legacy Vector skin? + */ + private static function updateViewsMenuIcons( &$content_navigation, $isLegacy ) { + $featureManager = VectorServices::getFeatureManager(); + $visualEnhancements = $featureManager->isFeatureEnabled( Constants::FEATURE_VISUAL_ENHANCEMENTS ); + + foreach ( $content_navigation['views'] as $key => $item ) { + $icon = $item['icon'] ?? null; + if ( $icon ) { + if ( $isLegacy || !$featureManager->isFeatureEnabled( Constants::FEATURE_VISUAL_ENHANCEMENTS ) ) { + self::appendClassToItem( + $item['class'], + [ 'icon' ] + ); + } else { + // Force the item as a button with hidden text. + $item['button'] = true; + $item['text-hidden'] = true; + $item = self::updateMenuItemData( $item, true ); + } + } else { + self::appendClassToItem( + $item['class'], + [ 'vector-tab-noicon' ] + ); + } + $content_navigation['views'][$key] = $item; + } + } + + /** + * All associated pages menu items do not have icons so are given the vector-tab-noicon class. + * + * @param array &$content_navigation + */ + private static function updateAssociatedPagesMenuIcons( &$content_navigation ) { + foreach ( $content_navigation['associated-pages'] as $key => $item ) { + self::appendClassToItem( + $item['class'], + [ 'vector-tab-noicon' ] + ); + $content_navigation['associated-pages'][$key] = $item; + } + } + /** * Adds class to a property * @@ -376,9 +421,10 @@ class Hooks implements * @param array $item data to update * @param string $buttonClassProp property to append button classes * @param string $iconHtmlProp property to set icon HTML + * @param bool $isSmallIcon when set a small icon will be applied rather than the standard icon size * @return array $item Updated data */ - private static function updateItemData( $item, $buttonClassProp, $iconHtmlProp ) { + private static function updateItemData( $item, $buttonClassProp, $iconHtmlProp, $isSmallIcon = false ) { $hasButton = $item['button'] ?? false; $hideText = $item['text-hidden'] ?? false; $isCollapsible = $item['collapsible'] ?? false; @@ -403,6 +449,9 @@ class Hooks implements // We should seek to remove all these instances. 'mw-ui-icon-wikimedia-' . $icon ]; + if ( $isSmallIcon ) { + $iconElementClasses[] = 'mw-ui-icon-small'; + } self::appendClassToItem( $item[ $buttonClassProp ], $iconElementClasses ); } else { $item[ $iconHtmlProp ] = self::makeIcon( $icon ); @@ -439,12 +488,13 @@ class Hooks implements * Updates template data for Vector menu items. * * @param array $item menu item data to update + * @param bool $isSmallIcon when set a small icon will be applied rather than the standard icon size * @return array $item Updated menu item data */ - public static function updateMenuItemData( $item ) { + public static function updateMenuItemData( $item, $isSmallIcon = false ) { $buttonClassProp = 'link-class'; $iconHtmlProp = 'link-html'; - return self::updateItemData( $item, $buttonClassProp, $iconHtmlProp ); + return self::updateItemData( $item, $buttonClassProp, $iconHtmlProp, $isSmallIcon ); } /** @@ -500,19 +550,27 @@ class Hooks implements $title = $sk->getRelevantTitle(); $skinName = $sk->getSkinName(); - if ( self::isVectorSkin( $skinName ) ) { - if ( - $sk->getConfig()->get( 'VectorUseIconWatch' ) && - $title && $title->canExist() - ) { - self::updateActionsMenu( $content_navigation ); - } - - self::updateUserLinksItems( $sk, $content_navigation ); + // These changes should only happen in Vector. + if ( !$skinName || !self::isVectorSkin( $skinName ) ) { + return; } + + if ( + $sk->getConfig()->get( 'VectorUseIconWatch' ) && + $title && $title->canExist() + ) { + self::updateActionsMenu( $content_navigation ); + } + + self::updateUserLinksItems( $sk, $content_navigation ); if ( $skinName === Constants::SKIN_NAME_MODERN ) { self::createMoreOverflowMenu( $content_navigation ); } + + // The updating of the views menu happens /after/ the overflow menu has been created + // this avoids icons showing in the more overflow menu. + self::updateViewsMenuIcons( $content_navigation, self::isSkinVersionLegacy( $skinName ) ); + self::updateAssociatedPagesMenuIcons( $content_navigation ); } /** diff --git a/resources/skins.vector.js/skin.js b/resources/skins.vector.js/skin.js index ec2b4132..7118df61 100644 --- a/resources/skins.vector.js/skin.js +++ b/resources/skins.vector.js/skin.js @@ -2,6 +2,7 @@ var languageButton = require( './languageButton.js' ), initSearchLoader = require( './searchLoader.js' ).initSearchLoader, dropdownMenus = require( './dropdownMenus.js' ).dropdownMenus, sidebarPersistence = require( './sidebarPersistence.js' ), + watchstar = require( './watchstar.js' ), checkbox = require( './checkbox.js' ); /** @@ -74,6 +75,9 @@ function main( window ) { languageButton(); dropdownMenus(); addNamespacesGadgetSupport(); + if ( document.body.classList.contains( 'vector-feature-visual-enhancement-next-enabled' ) ) { + watchstar(); + } } /** diff --git a/resources/skins.vector.js/watchstar.js b/resources/skins.vector.js/watchstar.js new file mode 100644 index 00000000..a6d6e20d --- /dev/null +++ b/resources/skins.vector.js/watchstar.js @@ -0,0 +1,24 @@ +module.exports = function () { + mw.hook( 'wikipage.watchlistChange' ).add( + function ( /** @type {boolean} */ isWatched, /** @type {string} */ expiry ) { + var watchElement = document.querySelectorAll( '#ca-watch a, #ca-unwatch a' )[ 0 ]; + if ( !watchElement ) { + return; + } + watchElement.classList.remove( + 'mw-ui-icon-wikimedia-unStar', + 'mw-ui-icon-wikimedia-star', + 'mw-ui-icon-wikimedia-halfStar' + ); + if ( isWatched ) { + if ( expiry === 'infinity' ) { + watchElement.classList.add( 'mw-ui-icon-wikimedia-unStar' ); + } else { + watchElement.classList.add( 'mw-ui-icon-wikimedia-halfStar' ); + } + } else { + watchElement.classList.add( 'mw-ui-icon-wikimedia-star' ); + } + } + ); +}; diff --git a/resources/skins.vector.styles/components/MenuTabs.less b/resources/skins.vector.styles/components/MenuTabs.less index ae027839..0cad6b6d 100644 --- a/resources/skins.vector.styles/components/MenuTabs.less +++ b/resources/skins.vector.styles/components/MenuTabs.less @@ -15,8 +15,11 @@ } /* focus and hover have outlines. Text underline interferes with bottom border */ - .mw-list-item a:focus, - .mw-list-item a:hover { + /* FIXME: Remove 2 not selectors when cache has cleared for Ie2ffa5c3ecf270c1bb1f315937023ae7ace5ed30 */ + .mw-list-item a:not( .mw-ui-icon ):focus, + .mw-list-item a:not( .mw-ui-icon ):hover, + .mw-list-item.vector-tab-noicon a:focus, + .mw-list-item.vector-tab-noicon a:hover { text-decoration: none; border-bottom: @border-width-base @border-style-base; } @@ -37,11 +40,18 @@ * Tab list item appearance. Applies to both