Merge "Add edit icons to sticky header"
This commit is contained in:
commit
29b35c25ac
|
@ -1,7 +1,7 @@
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"resourceModule": "skins.vector.styles.legacy",
|
"resourceModule": "skins.vector.styles.legacy",
|
||||||
"maxSize": "7.8 kB"
|
"maxSize": "7.9 kB"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"resourceModule": "skins.vector.styles",
|
"resourceModule": "skins.vector.styles",
|
||||||
|
|
|
@ -68,6 +68,30 @@ class SkinVector extends SkinMustache {
|
||||||
'tabindex' => '-1',
|
'tabindex' => '-1',
|
||||||
'class' => 'sticky-header-icon'
|
'class' => 'sticky-header-icon'
|
||||||
];
|
];
|
||||||
|
private const EDIT_VE_ICON = [
|
||||||
|
'href' => '#',
|
||||||
|
'id' => 'ca-ve-edit-sticky-header',
|
||||||
|
'event' => 've-edit-sticky-header',
|
||||||
|
'icon' => 'wikimedia-edit',
|
||||||
|
'is-quiet' => true,
|
||||||
|
'class' => 'sticky-header-icon'
|
||||||
|
];
|
||||||
|
private const EDIT_WIKITEXT_ICON = [
|
||||||
|
'href' => '#',
|
||||||
|
'id' => 'ca-edit-sticky-header',
|
||||||
|
'event' => 'wikitext-edit-sticky-header',
|
||||||
|
'icon' => 'wikimedia-wikiText',
|
||||||
|
'is-quiet' => true,
|
||||||
|
'class' => 'sticky-header-icon'
|
||||||
|
];
|
||||||
|
private const EDIT_PROTECTED_ICON = [
|
||||||
|
'href' => '#',
|
||||||
|
'id' => 'ca-viewsource-sticky-header',
|
||||||
|
'event' => 've-edit-protected-sticky-header',
|
||||||
|
'icon' => 'wikimedia-editLock',
|
||||||
|
'is-quiet' => true,
|
||||||
|
'class' => 'sticky-header-icon'
|
||||||
|
];
|
||||||
private const SEARCH_EXPANDING_CLASS = 'vector-search-box-show-thumbnail';
|
private const SEARCH_EXPANDING_CLASS = 'vector-search-box-show-thumbnail';
|
||||||
private const STICKY_HEADER_ENABLED_CLASS = 'vector-sticky-header-enabled';
|
private const STICKY_HEADER_ENABLED_CLASS = 'vector-sticky-header-enabled';
|
||||||
|
|
||||||
|
@ -353,7 +377,7 @@ class SkinVector extends SkinMustache {
|
||||||
* @param array $searchBoxData
|
* @param array $searchBoxData
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
private function getStickyHeaderData( $searchBoxData ) {
|
private function getStickyHeaderData( $searchBoxData ): array {
|
||||||
return [
|
return [
|
||||||
'data-primary-action' => !$this->shouldHideLanguages() ? $this->getULSButtonData() : null,
|
'data-primary-action' => !$this->shouldHideLanguages() ? $this->getULSButtonData() : null,
|
||||||
'data-button-start' => [
|
'data-button-start' => [
|
||||||
|
@ -365,7 +389,11 @@ class SkinVector extends SkinMustache {
|
||||||
],
|
],
|
||||||
'data-search' => $searchBoxData,
|
'data-search' => $searchBoxData,
|
||||||
'data-buttons' => [
|
'data-buttons' => [
|
||||||
self::TALK_ICON, self::HISTORY_ICON, self::NO_ICON, self::NO_ICON
|
self::TALK_ICON,
|
||||||
|
self::HISTORY_ICON,
|
||||||
|
self::EDIT_VE_ICON,
|
||||||
|
self::EDIT_PROTECTED_ICON,
|
||||||
|
self::EDIT_WIKITEXT_ICON
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
"Element": "https://developer.mozilla.org/docs/Web/API/Element",
|
"Element": "https://developer.mozilla.org/docs/Web/API/Element",
|
||||||
"Event": "https://developer.mozilla.org/docs/Web/API/Event",
|
"Event": "https://developer.mozilla.org/docs/Web/API/Event",
|
||||||
"HTMLElement": "https://developer.mozilla.org/docs/Web/API/HTMLElement",
|
"HTMLElement": "https://developer.mozilla.org/docs/Web/API/HTMLElement",
|
||||||
|
"Node": "https://developer.mozilla.org/docs/Web/API/Node",
|
||||||
"NodeList": "https://developer.mozilla.org/docs/Web/API/NodeList",
|
"NodeList": "https://developer.mozilla.org/docs/Web/API/NodeList",
|
||||||
"HTMLInputElement": "https://developer.mozilla.org/docs/Web/API/HTMLInputElement",
|
"HTMLInputElement": "https://developer.mozilla.org/docs/Web/API/HTMLInputElement",
|
||||||
"\"removeEventListener\"": "https://developer.mozilla.org/docs/Web/API/EventTarget/removeEventListener",
|
"\"removeEventListener\"": "https://developer.mozilla.org/docs/Web/API/EventTarget/removeEventListener",
|
||||||
|
|
|
@ -7,8 +7,7 @@ var
|
||||||
FIRST_HEADING_ID = 'firstHeading',
|
FIRST_HEADING_ID = 'firstHeading',
|
||||||
USER_MENU_ID = 'p-personal',
|
USER_MENU_ID = 'p-personal',
|
||||||
VECTOR_USER_LINKS_SELECTOR = '.vector-user-links',
|
VECTOR_USER_LINKS_SELECTOR = '.vector-user-links',
|
||||||
SEARCH_TOGGLE_SELECTOR = '.vector-sticky-header-search-toggle',
|
SEARCH_TOGGLE_SELECTOR = '.vector-sticky-header-search-toggle';
|
||||||
OTHER_STICKY_ELEMENT_SELECTORS = '.charts-stickyhead th';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copies attribute from an element to another.
|
* Copies attribute from an element to another.
|
||||||
|
@ -48,6 +47,23 @@ function makeNodeTrackable( node ) {
|
||||||
suffixStickyAttribute( node, 'data-event-name' );
|
suffixStickyAttribute( node, 'data-event-name' );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {null|HTMLElement|Node} node
|
||||||
|
* @return {HTMLElement}
|
||||||
|
*/
|
||||||
|
function toHTMLElement( node ) {
|
||||||
|
// @ts-ignore
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {HTMLElement} node
|
||||||
|
*/
|
||||||
|
function removeNode( node ) {
|
||||||
|
toHTMLElement( node.parentNode ).removeChild( node );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes sticky header icons functional for modern Vector.
|
* Makes sticky header icons functional for modern Vector.
|
||||||
*
|
*
|
||||||
|
@ -77,6 +93,76 @@ function prepareIcons( header, history, talk ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render sticky header edit or protected page icons for modern Vector.
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} header
|
||||||
|
* @param {HTMLElement|null} primaryEdit
|
||||||
|
* @param {boolean} isProtected
|
||||||
|
* @param {HTMLElement|null} secondaryEdit
|
||||||
|
*/
|
||||||
|
function prepareEditIcons(
|
||||||
|
header,
|
||||||
|
primaryEdit,
|
||||||
|
isProtected,
|
||||||
|
secondaryEdit
|
||||||
|
) {
|
||||||
|
var
|
||||||
|
primaryEditSticky = toHTMLElement(
|
||||||
|
header.querySelector(
|
||||||
|
'#ca-ve-edit-sticky-header'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
protectedSticky = toHTMLElement(
|
||||||
|
header.querySelector(
|
||||||
|
'#ca-viewsource-sticky-header'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
wikitextSticky = toHTMLElement(
|
||||||
|
header.querySelector(
|
||||||
|
'#ca-edit-sticky-header'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( !primaryEdit ) {
|
||||||
|
removeNode( protectedSticky );
|
||||||
|
removeNode( wikitextSticky );
|
||||||
|
removeNode( primaryEditSticky );
|
||||||
|
return;
|
||||||
|
} else if ( isProtected ) {
|
||||||
|
removeNode( wikitextSticky );
|
||||||
|
removeNode( primaryEditSticky );
|
||||||
|
copyAttribute( primaryEdit, protectedSticky, 'href' );
|
||||||
|
copyAttribute( primaryEdit, protectedSticky, 'title' );
|
||||||
|
} else {
|
||||||
|
removeNode( protectedSticky );
|
||||||
|
copyAttribute( primaryEdit, primaryEditSticky, 'href' );
|
||||||
|
copyAttribute( primaryEdit, primaryEditSticky, 'title' );
|
||||||
|
if ( secondaryEdit ) {
|
||||||
|
copyAttribute( secondaryEdit, wikitextSticky, 'href' );
|
||||||
|
copyAttribute( secondaryEdit, wikitextSticky, 'title' );
|
||||||
|
} else {
|
||||||
|
removeNode( wikitextSticky );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if element is in viewport.
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} element
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
function isInViewport( element ) {
|
||||||
|
var rect = element.getBoundingClientRect();
|
||||||
|
return (
|
||||||
|
rect.top >= 0 &&
|
||||||
|
rect.left >= 0 &&
|
||||||
|
rect.bottom <= ( window.innerHeight || document.documentElement.clientHeight ) &&
|
||||||
|
rect.right <= ( window.innerWidth || document.documentElement.clientWidth )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes sticky header functional for modern Vector.
|
* Makes sticky header functional for modern Vector.
|
||||||
*
|
*
|
||||||
|
@ -136,13 +222,42 @@ function makeStickyHeaderFunctional(
|
||||||
document.querySelector( '#ca-talk a' )
|
document.querySelector( '#ca-talk a' )
|
||||||
);
|
);
|
||||||
|
|
||||||
// Apply offset for other sticky elements on page if applicable.
|
var veEdit = document.querySelector( '#ca-ve-edit a' );
|
||||||
var otherStickyElements = document.querySelectorAll( OTHER_STICKY_ELEMENT_SELECTORS );
|
var ceEdit = document.querySelector( '#ca-edit a' );
|
||||||
Array.prototype.forEach.call( otherStickyElements, function ( el ) {
|
var protectedEdit = document.querySelector( '#ca-viewsource a' );
|
||||||
el.classList.add( 'mw-sticky-header-element' );
|
var isProtected = !!protectedEdit;
|
||||||
} );
|
var primaryEdit = protectedEdit || ( veEdit || ceEdit );
|
||||||
|
var secondaryEdit = veEdit ? ceEdit : null;
|
||||||
|
|
||||||
|
prepareEditIcons(
|
||||||
|
header,
|
||||||
|
toHTMLElement( primaryEdit ),
|
||||||
|
isProtected,
|
||||||
|
toHTMLElement( secondaryEdit )
|
||||||
|
);
|
||||||
|
|
||||||
stickyObserver.observe( stickyIntersection );
|
stickyObserver.observe( stickyIntersection );
|
||||||
|
|
||||||
|
// When Visual Editor is activated, hide sticky header.
|
||||||
|
mw.hook( 've.activationComplete' ).add( function () {
|
||||||
|
// eslint-disable-next-line mediawiki/class-doc
|
||||||
|
header.classList.remove( STICKY_HEADER_VISIBLE_CLASS );
|
||||||
|
stickyObserver.unobserve( stickyIntersection );
|
||||||
|
} );
|
||||||
|
|
||||||
|
// When Visual Editor is deactivated, by cliking "read" tab at top of page, show sticky header.
|
||||||
|
mw.hook( 've.deactivationComplete' ).add( function () {
|
||||||
|
stickyObserver.observe( stickyIntersection );
|
||||||
|
} );
|
||||||
|
|
||||||
|
// After saving edits, re-apply the sticky header if the target is not in the viewport.
|
||||||
|
mw.hook( 'postEdit.afterRemoval' ).add( function () {
|
||||||
|
if ( !isInViewport( stickyIntersection ) ) {
|
||||||
|
// eslint-disable-next-line mediawiki/class-doc
|
||||||
|
header.classList.add( STICKY_HEADER_VISIBLE_CLASS );
|
||||||
|
stickyObserver.observe( stickyIntersection );
|
||||||
|
}
|
||||||
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -56,31 +56,26 @@
|
||||||
//
|
//
|
||||||
// Layout
|
// Layout
|
||||||
//
|
//
|
||||||
&-start {
|
&-start,
|
||||||
|
&-end,
|
||||||
|
&-icons,
|
||||||
|
&-context-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-grow: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&-end {
|
&-start {
|
||||||
display: flex;
|
flex-grow: 1;
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Components
|
// Components
|
||||||
//
|
//
|
||||||
&-icons,
|
|
||||||
&-context-bar {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
white-space: nowrap;
|
|
||||||
margin: 0 15px;
|
|
||||||
padding-left: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-context-bar {
|
&-context-bar {
|
||||||
border-left: 1px solid #c8c8c8;
|
border-left: 1px solid #c8c8c8;
|
||||||
|
margin: 0 15px;
|
||||||
|
padding-left: 30px;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-context-bar-primary {
|
&-context-bar-primary {
|
||||||
|
@ -147,7 +142,8 @@
|
||||||
|
|
||||||
// T289817 Override other sticky element offsets to ensure that other
|
// T289817 Override other sticky element offsets to ensure that other
|
||||||
// sticky elements (i.e. table headers) appear below the sticky header.
|
// sticky elements (i.e. table headers) appear below the sticky header.
|
||||||
.mw-sticky-header-element {
|
.mw-sticky-header-element,
|
||||||
|
.charts-stickyhead th {
|
||||||
/* stylelint-disable-next-line declaration-no-important */
|
/* stylelint-disable-next-line declaration-no-important */
|
||||||
top: @height-sticky-header !important;
|
top: @height-sticky-header !important;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue