Icons: Watchstar and wikilove are upgraded
Can be tested by appending ?vectorvisualenhancementnext=1 to URL Bug: T310838 Bug: T234990 Bug: T234550 Depends-On: I76d0d94c9006cc5f5680849ecdd1c382c16e34ba Depends-On: Ib7c3021db014827b4b88cac855afc0b54a360f8c Change-Id: Ie2ffa5c3ecf270c1bb1f315937023ae7ace5ed30
This commit is contained in:
parent
e629775aff
commit
97cf735de2
|
@ -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
|
* @param array &$content_navigation
|
||||||
*/
|
*/
|
||||||
|
@ -168,15 +168,60 @@ class Hooks implements
|
||||||
|
|
||||||
// Promote watch link from actions to views and add an icon
|
// Promote watch link from actions to views and add an icon
|
||||||
if ( $key !== null ) {
|
if ( $key !== null ) {
|
||||||
self::appendClassToItem(
|
|
||||||
$content_navigation['actions'][$key]['class'],
|
|
||||||
[ 'icon' ]
|
|
||||||
);
|
|
||||||
$content_navigation['views'][$key] = $content_navigation['actions'][$key];
|
$content_navigation['views'][$key] = $content_navigation['actions'][$key];
|
||||||
unset( $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
|
* Adds class to a property
|
||||||
*
|
*
|
||||||
|
@ -376,9 +421,10 @@ class Hooks implements
|
||||||
* @param array $item data to update
|
* @param array $item data to update
|
||||||
* @param string $buttonClassProp property to append button classes
|
* @param string $buttonClassProp property to append button classes
|
||||||
* @param string $iconHtmlProp property to set icon HTML
|
* @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
|
* @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;
|
$hasButton = $item['button'] ?? false;
|
||||||
$hideText = $item['text-hidden'] ?? false;
|
$hideText = $item['text-hidden'] ?? false;
|
||||||
$isCollapsible = $item['collapsible'] ?? false;
|
$isCollapsible = $item['collapsible'] ?? false;
|
||||||
|
@ -403,6 +449,9 @@ class Hooks implements
|
||||||
// We should seek to remove all these instances.
|
// We should seek to remove all these instances.
|
||||||
'mw-ui-icon-wikimedia-' . $icon
|
'mw-ui-icon-wikimedia-' . $icon
|
||||||
];
|
];
|
||||||
|
if ( $isSmallIcon ) {
|
||||||
|
$iconElementClasses[] = 'mw-ui-icon-small';
|
||||||
|
}
|
||||||
self::appendClassToItem( $item[ $buttonClassProp ], $iconElementClasses );
|
self::appendClassToItem( $item[ $buttonClassProp ], $iconElementClasses );
|
||||||
} else {
|
} else {
|
||||||
$item[ $iconHtmlProp ] = self::makeIcon( $icon );
|
$item[ $iconHtmlProp ] = self::makeIcon( $icon );
|
||||||
|
@ -439,12 +488,13 @@ class Hooks implements
|
||||||
* Updates template data for Vector menu items.
|
* Updates template data for Vector menu items.
|
||||||
*
|
*
|
||||||
* @param array $item menu item data to update
|
* @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
|
* @return array $item Updated menu item data
|
||||||
*/
|
*/
|
||||||
public static function updateMenuItemData( $item ) {
|
public static function updateMenuItemData( $item, $isSmallIcon = false ) {
|
||||||
$buttonClassProp = 'link-class';
|
$buttonClassProp = 'link-class';
|
||||||
$iconHtmlProp = 'link-html';
|
$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();
|
$title = $sk->getRelevantTitle();
|
||||||
|
|
||||||
$skinName = $sk->getSkinName();
|
$skinName = $sk->getSkinName();
|
||||||
if ( self::isVectorSkin( $skinName ) ) {
|
// These changes should only happen in Vector.
|
||||||
if (
|
if ( !$skinName || !self::isVectorSkin( $skinName ) ) {
|
||||||
$sk->getConfig()->get( 'VectorUseIconWatch' ) &&
|
return;
|
||||||
$title && $title->canExist()
|
|
||||||
) {
|
|
||||||
self::updateActionsMenu( $content_navigation );
|
|
||||||
}
|
|
||||||
|
|
||||||
self::updateUserLinksItems( $sk, $content_navigation );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
$sk->getConfig()->get( 'VectorUseIconWatch' ) &&
|
||||||
|
$title && $title->canExist()
|
||||||
|
) {
|
||||||
|
self::updateActionsMenu( $content_navigation );
|
||||||
|
}
|
||||||
|
|
||||||
|
self::updateUserLinksItems( $sk, $content_navigation );
|
||||||
if ( $skinName === Constants::SKIN_NAME_MODERN ) {
|
if ( $skinName === Constants::SKIN_NAME_MODERN ) {
|
||||||
self::createMoreOverflowMenu( $content_navigation );
|
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 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2,6 +2,7 @@ var languageButton = require( './languageButton.js' ),
|
||||||
initSearchLoader = require( './searchLoader.js' ).initSearchLoader,
|
initSearchLoader = require( './searchLoader.js' ).initSearchLoader,
|
||||||
dropdownMenus = require( './dropdownMenus.js' ).dropdownMenus,
|
dropdownMenus = require( './dropdownMenus.js' ).dropdownMenus,
|
||||||
sidebarPersistence = require( './sidebarPersistence.js' ),
|
sidebarPersistence = require( './sidebarPersistence.js' ),
|
||||||
|
watchstar = require( './watchstar.js' ),
|
||||||
checkbox = require( './checkbox.js' );
|
checkbox = require( './checkbox.js' );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -74,6 +75,9 @@ function main( window ) {
|
||||||
languageButton();
|
languageButton();
|
||||||
dropdownMenus();
|
dropdownMenus();
|
||||||
addNamespacesGadgetSupport();
|
addNamespacesGadgetSupport();
|
||||||
|
if ( document.body.classList.contains( 'vector-feature-visual-enhancement-next-enabled' ) ) {
|
||||||
|
watchstar();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
|
@ -15,8 +15,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/* focus and hover have outlines. Text underline interferes with bottom border */
|
/* focus and hover have outlines. Text underline interferes with bottom border */
|
||||||
.mw-list-item a:focus,
|
/* FIXME: Remove 2 not selectors when cache has cleared for Ie2ffa5c3ecf270c1bb1f315937023ae7ace5ed30 */
|
||||||
.mw-list-item a:hover {
|
.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;
|
text-decoration: none;
|
||||||
border-bottom: @border-width-base @border-style-base;
|
border-bottom: @border-width-base @border-style-base;
|
||||||
}
|
}
|
||||||
|
@ -37,11 +40,18 @@
|
||||||
* Tab list item appearance. Applies to both <li>'s inside .vector-menu-tabs
|
* Tab list item appearance. Applies to both <li>'s inside .vector-menu-tabs
|
||||||
* and dropdown menus inside the article toolbar
|
* and dropdown menus inside the article toolbar
|
||||||
*/
|
*/
|
||||||
|
// FIXME: Remove the body selector once Ie2ffa5c3ecf270c1bb1f315937023ae7ace5ed30 is in production
|
||||||
|
/* for cached HTML */ body:not( .vector-feature-visual-enhancement-next-enabled ) .vector-menu-tabs .mw-list-item,
|
||||||
|
.vector-menu-tabs .mw-list-item.vector-tab-noicon,
|
||||||
|
.mw-article-toolbar-container .vector-menu-dropdown {
|
||||||
|
margin: 0 @padding-horizontal-tabs;
|
||||||
|
}
|
||||||
|
|
||||||
.vector-menu-tabs .mw-list-item,
|
.vector-menu-tabs .mw-list-item,
|
||||||
.mw-article-toolbar-container .vector-menu-dropdown {
|
.mw-article-toolbar-container .vector-menu-dropdown {
|
||||||
float: left;
|
float: left;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
margin: 0 @padding-horizontal-tabs;
|
margin-bottom: 0; /* overrides default `li` styling */
|
||||||
|
|
||||||
// target links inside of .vector-tab-menu
|
// target links inside of .vector-tab-menu
|
||||||
// and dropdown menu headings inside the article toolbar.
|
// and dropdown menu headings inside the article toolbar.
|
||||||
|
@ -51,19 +61,42 @@
|
||||||
.vector-menu-heading {
|
.vector-menu-heading {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
// Top & bottom padding to increase clickable area.
|
|
||||||
padding: 18px 0 7px 0;
|
|
||||||
// bottom margin to overlap border with toolbar border.
|
|
||||||
margin-bottom: -1px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-bottom: @border-width-base @border-style-base transparent;
|
|
||||||
// max-height & box-sizing to make link, watchstar & dropdown height consistent.
|
// max-height & box-sizing to make link, watchstar & dropdown height consistent.
|
||||||
// NOTE: Was 40px instead of 41, but changed to avoid visual regressions.
|
// NOTE: Was 40px instead of 41, but changed to avoid visual regressions.
|
||||||
max-height: unit( 41 / @font-size-tabs / @font-size-browser, em );
|
max-height: unit( 41 / @font-size-tabs / @font-size-browser, em );
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vector-menu-heading {
|
||||||
// For better compatibility with gadgets (like Twinkle) that append
|
// For better compatibility with gadgets (like Twinkle) that append
|
||||||
// <H3> elements as dropdown headings (which was the convention in legacy Vector).
|
// <H3> elements as dropdown headings (which was the convention in legacy Vector).
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
font-weight: normal;
|
}
|
||||||
|
|
||||||
|
/* FIXME: Remove cached HTML selector (> a:not( .mw-ui-icon ))
|
||||||
|
when Ie2ffa5c3ecf270c1bb1f315937023ae7ace5ed30 is in production */
|
||||||
|
&.vector-tab-noicon > a,
|
||||||
|
& > a:not( .mw-ui-icon ),
|
||||||
|
.vector-menu-heading {
|
||||||
|
// Top & bottom padding to increase clickable area.
|
||||||
|
padding: 18px 0 7px 0;
|
||||||
|
// bottom margin to overlap border with toolbar border.
|
||||||
|
margin-bottom: -1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// With mw-ui-icons, expand watchstar and wikilove links it to cover full touch area
|
||||||
|
.vector-feature-visual-enhancement-next-enabled {
|
||||||
|
.vector-menu-tabs {
|
||||||
|
.mw-list-item {
|
||||||
|
.mw-ui-icon {
|
||||||
|
// Align small icons with the bottom of the tabs.
|
||||||
|
// Height of tab is 41px, and small icon is 36px,
|
||||||
|
// With 1px border, 41 - 36 + 1;
|
||||||
|
margin: 4px 0 0 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
/* Watch/Unwatch Icon Styling */
|
/* Watch/Unwatch Icon Styling */
|
||||||
/* Only use icon if the menu item is not collapsed into the "More" dropdown
|
/* Only use icon if the menu item is not collapsed into the "More" dropdown
|
||||||
* (in which case it is inside `.vector-menu-dropdown` instead of `.vector-menu-tabs`). */
|
* (in which case it is inside `.vector-menu-dropdown` instead of `.vector-menu-tabs`). */
|
||||||
.vector-menu-tabs {
|
// Note: there's no watchstar for anon users so no need to worry about cached HTML when changing this class
|
||||||
|
.vector-feature-visual-enhancement-next-disabled .vector-menu-tabs {
|
||||||
@size-watchlink-icon: unit( 16 / @font-size-tabs / @font-size-browser, em );
|
@size-watchlink-icon: unit( 16 / @font-size-tabs / @font-size-browser, em );
|
||||||
|
|
||||||
.mw-watchlink.icon a {
|
.mw-watchlink.icon a {
|
||||||
|
@ -59,3 +60,15 @@
|
||||||
transform-origin: 50% 50%;
|
transform-origin: 50% 50%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Loading watchstar link class.
|
||||||
|
.vector-feature-visual-enhancement-next-enabled {
|
||||||
|
.mw-watchlink .loading:before {
|
||||||
|
.rotation( 500ms );
|
||||||
|
/* Suppress the hilarious rotating focus outline on Firefox */
|
||||||
|
outline: 0;
|
||||||
|
cursor: default;
|
||||||
|
pointer-events: none;
|
||||||
|
transform-origin: 50% 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -311,6 +311,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"icons": [
|
"icons": [
|
||||||
|
"heart",
|
||||||
"language",
|
"language",
|
||||||
"ellipsis",
|
"ellipsis",
|
||||||
"userAvatar",
|
"userAvatar",
|
||||||
|
@ -387,6 +388,7 @@
|
||||||
"name": "resources/skins.vector.js/config.json",
|
"name": "resources/skins.vector.js/config.json",
|
||||||
"callback": "MediaWiki\\Skins\\Vector\\Hooks::getVectorResourceLoaderConfig"
|
"callback": "MediaWiki\\Skins\\Vector\\Hooks::getVectorResourceLoaderConfig"
|
||||||
},
|
},
|
||||||
|
"resources/skins.vector.js/watchstar.js",
|
||||||
"resources/skins.vector.js/dropdownMenus.js",
|
"resources/skins.vector.js/dropdownMenus.js",
|
||||||
"resources/skins.vector.js/checkbox.js",
|
"resources/skins.vector.js/checkbox.js",
|
||||||
"resources/skins.vector.js/sidebarPersistence.js",
|
"resources/skins.vector.js/sidebarPersistence.js",
|
||||||
|
|
|
@ -394,19 +394,24 @@ class VectorHooksTest extends MediaWikiIntegrationTestCase {
|
||||||
*/
|
*/
|
||||||
public function testOnSkinTemplateNavigation() {
|
public function testOnSkinTemplateNavigation() {
|
||||||
$this->setMwGlobals( [
|
$this->setMwGlobals( [
|
||||||
'wgVectorUseIconWatch' => true
|
'wgVectorUseIconWatch' => true,
|
||||||
|
'wgVectorVisualEnhancementNext' => false,
|
||||||
] );
|
] );
|
||||||
$skin = new SkinVector22( [ 'name' => 'vector' ] );
|
$skin = new SkinVector22( [ 'name' => 'vector' ] );
|
||||||
$skin->getContext()->setTitle( Title::newFromText( 'Foo' ) );
|
$skin->getContext()->setTitle( Title::newFromText( 'Foo' ) );
|
||||||
$contentNavWatch = [
|
$contentNavWatch = [
|
||||||
|
'associated-pages' => [],
|
||||||
|
'views' => [],
|
||||||
'actions' => [
|
'actions' => [
|
||||||
'watch' => [ 'class' => [ 'watch' ] ],
|
'watch' => [ 'class' => [ 'watch' ], 'icon' => 'star' ],
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
$contentNavUnWatch = [
|
$contentNavUnWatch = [
|
||||||
|
'associated-pages' => [],
|
||||||
|
'views' => [],
|
||||||
'actions' => [
|
'actions' => [
|
||||||
'move' => [ 'class' => [ 'move' ] ],
|
'move' => [ 'class' => [ 'move' ] ],
|
||||||
'unwatch' => [],
|
'unwatch' => [ 'icon' => 'unStar' ],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -433,6 +438,8 @@ class VectorHooksTest extends MediaWikiIntegrationTestCase {
|
||||||
public function testUpdateUserLinksItems() {
|
public function testUpdateUserLinksItems() {
|
||||||
$vector2022Skin = new SkinVector22( [ 'name' => 'vector-2022' ] );
|
$vector2022Skin = new SkinVector22( [ 'name' => 'vector-2022' ] );
|
||||||
$contentNav = [
|
$contentNav = [
|
||||||
|
'associated-pages' => [],
|
||||||
|
'views' => [],
|
||||||
'user-page' => [
|
'user-page' => [
|
||||||
'userpage' => [ 'class' => [], 'icon' => 'userpage' ],
|
'userpage' => [ 'class' => [], 'icon' => 'userpage' ],
|
||||||
],
|
],
|
||||||
|
@ -442,6 +449,8 @@ class VectorHooksTest extends MediaWikiIntegrationTestCase {
|
||||||
];
|
];
|
||||||
$vectorLegacySkin = new SkinVectorLegacy( [ 'name' => 'vector' ] );
|
$vectorLegacySkin = new SkinVectorLegacy( [ 'name' => 'vector' ] );
|
||||||
$contentNavLegacy = [
|
$contentNavLegacy = [
|
||||||
|
'associated-pages' => [],
|
||||||
|
'views' => [],
|
||||||
'user-page' => [
|
'user-page' => [
|
||||||
'userpage' => [ 'class' => [], 'icon' => 'userpage' ],
|
'userpage' => [ 'class' => [], 'icon' => 'userpage' ],
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in New Issue