mirror of
https://gerrit.wikimedia.org/r/mediawiki/skins/Vector.git
synced 2024-11-24 15:53:46 +00:00
Merge "Allow multiple search components on the same page"
This commit is contained in:
commit
84023ff39c
|
@ -5,7 +5,7 @@
|
|||
},
|
||||
{
|
||||
"resourceModule": "skins.vector.styles",
|
||||
"maxSize": "9.8 kB"
|
||||
"maxSize": "10.1 kB"
|
||||
},
|
||||
{
|
||||
"resourceModule": "skins.vector.legacy.js",
|
||||
|
|
|
@ -411,7 +411,9 @@ class SkinVector extends SkinMustache {
|
|||
|
||||
$commonSkinData['data-search-box'] = $this->getSearchData(
|
||||
$commonSkinData['data-search-box'],
|
||||
!$this->isLegacy()
|
||||
!$this->isLegacy(),
|
||||
true,
|
||||
'searchform'
|
||||
);
|
||||
|
||||
return $commonSkinData;
|
||||
|
@ -422,22 +424,31 @@ class SkinVector extends SkinMustache {
|
|||
*
|
||||
* @param array $searchBoxData
|
||||
* @param bool $isCollapsible
|
||||
* @param bool $isPrimary
|
||||
* @param string $formId
|
||||
* @return array modified version of $searchBoxData
|
||||
*/
|
||||
private function getSearchData( array $searchBoxData, bool $isCollapsible ) {
|
||||
$searchClass = 'vector-search-box';
|
||||
private function getSearchData( array $searchBoxData, bool $isCollapsible, bool $isPrimary, string $formId ) {
|
||||
$searchClass = '';
|
||||
|
||||
// Determine the search widget treatment to send to the user
|
||||
if ( VectorServices::getFeatureManager()->isFeatureEnabled( Constants::FEATURE_USE_WVUI_SEARCH ) ) {
|
||||
$searchClass .= 'vector-search-box-vue ';
|
||||
}
|
||||
|
||||
if ( $isCollapsible ) {
|
||||
$searchClass .= ' vector-search-box-collapses';
|
||||
$searchClass .= ' vector-search-box-collapses ';
|
||||
}
|
||||
|
||||
if ( $this->shouldSearchExpand() ) {
|
||||
$searchClass .= " " . self::SEARCH_EXPANDING_CLASS;
|
||||
$searchClass .= ' ' . self::SEARCH_EXPANDING_CLASS;
|
||||
}
|
||||
|
||||
// Annotate search box with a component class.
|
||||
$searchBoxData['class'] = $searchClass;
|
||||
$searchBoxData['class'] = trim( $searchClass );
|
||||
$searchBoxData['is-collapsible'] = $isCollapsible;
|
||||
$searchBoxData['is-primary'] = $isPrimary;
|
||||
$searchBoxData['form-id'] = $formId;
|
||||
|
||||
// At lower resolutions the search input is hidden search and only the submit button is shown.
|
||||
// It should behave like a form submit link (e.g. submit the form with no input value).
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
{{!
|
||||
See @typedef SearchData
|
||||
}}
|
||||
<div id="p-search" role="search" class="{{class}}">
|
||||
<div {{#is-primary}}id="p-search"{{/is-primary}} role="search" class="{{class}} vector-search-box">
|
||||
<div>
|
||||
{{#is-legacy}}
|
||||
<h3 {{{html-user-language-attributes}}}>
|
||||
<label for="searchInput">{{msg-search}}</label>
|
||||
<label {{#is-primary}}for="searchInput"{{/is-primary}}>{{msg-search}}</label>
|
||||
</h3>
|
||||
{{/is-legacy}}
|
||||
<form action="{{form-action}}" id="searchform">
|
||||
<div id="simpleSearch"{{#input-location}} data-search-loc="{{.}}"{{/input-location}}>
|
||||
{{{html-input}}}
|
||||
<form action="{{form-action}}" id="{{form-id}}"
|
||||
class="vector-search-box-form">
|
||||
<div {{#is-primary}}id="simpleSearch"{{/is-primary}}
|
||||
class="vector-search-box-inner"
|
||||
{{#input-location}} data-search-loc="{{.}}"{{/input-location}}>
|
||||
<input class="vector-search-box-input"
|
||||
{{{html-input-attributes}}} {{#is-primary}}id="searchInput"{{/is-primary}} />
|
||||
<input type="hidden" name="title" value="{{page-title}}"/>
|
||||
{{! We construct two buttons (for 'go' and 'fulltext' search modes), but only one will be
|
||||
visible and actionable at a time (they are overlaid on top of each other in CSS).
|
||||
|
@ -20,8 +24,10 @@
|
|||
* The mediawiki.searchSuggest module, after doing tests for the broken browsers, removes
|
||||
the 'fulltext' button and handles 'fulltext' search itself; this will reveal the 'go'
|
||||
button and cause it to be used. !}}
|
||||
{{{html-button-search-fallback}}}
|
||||
{{{html-button-search}}}
|
||||
<input {{#is-primary}}id="mw-searchButton"{{/is-primary}}
|
||||
{{{html-button-fulltext-attributes}}} value="{{msg-searchbutton}}" />
|
||||
<input {{#is-primary}}id="searchButton"{{/is-primary}}
|
||||
{{{html-button-go-attributes}}} value="{{msg-searcharticle}}" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -4,13 +4,20 @@
|
|||
|
||||
// Defined as `div`.
|
||||
// Provide extra element for gadgets due to `form` already carrying an `id`.
|
||||
// FIXME: Remove #simpleSearch when cache has cleared
|
||||
.vector-search-box-inner,
|
||||
#simpleSearch {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// The search input.
|
||||
#searchInput {
|
||||
// Note that these rules only apply to the non-Vue enabled search input field.
|
||||
// When Vue.js has loaded this element will no longer be in the page and subsituted with
|
||||
// a WVUI element.
|
||||
// FIXME: Remove searchInput selector when cache has cleared.
|
||||
#searchInput,
|
||||
.vector-search-box-input {
|
||||
background-color: rgba( 255, 255, 255, 0.5 );
|
||||
color: @color-base--emphasized;
|
||||
width: 100%;
|
||||
|
@ -34,11 +41,15 @@
|
|||
// Support: Firefox.
|
||||
-moz-appearance: textfield;
|
||||
|
||||
// FIXME: Remove #simpleSearch when cache has cleared
|
||||
.vector-search-box-inner:hover &,
|
||||
#simpleSearch:hover & {
|
||||
border-color: @colorGray7;
|
||||
}
|
||||
|
||||
// FIXME: #simpleSearch can be removed when cache has cleared.
|
||||
&:focus,
|
||||
.vector-search-box-inner:hover &:focus,
|
||||
#simpleSearch:hover &:focus {
|
||||
outline: 0;
|
||||
border-color: @border-color-base--focus;
|
||||
|
@ -60,8 +71,7 @@
|
|||
|
||||
// The search buttons. Fallback and search button are displayed in the same position,
|
||||
// and if both are present the fulltext search one obscures the 'Go' one.
|
||||
#searchButton,
|
||||
#mw-searchButton {
|
||||
.searchButton {
|
||||
background-color: transparent;
|
||||
position: absolute;
|
||||
top: @border-width-base;
|
||||
|
@ -85,7 +95,7 @@
|
|||
z-index: @z-index-search-button;
|
||||
}
|
||||
|
||||
#searchButton {
|
||||
.searchButton[ name='go' ] {
|
||||
background: no-repeat center/unit( 16 / @font-size-browser / @font-size-search-input, em ) url( images/search.svg );
|
||||
opacity: 0.67;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ var /** @type {VectorResourceLoaderVirtualConfig} */
|
|||
LOAD_START_MARK = 'mwVectorVueSearchLoadStart',
|
||||
LOAD_END_MARK = 'mwVectorVueSearchLoadEnd',
|
||||
LOAD_MEASURE = 'mwVectorVueSearchLoadStartToLoadEnd',
|
||||
SEARCH_FORM_ID = 'simpleSearch',
|
||||
SEARCH_INPUT_ID = 'searchInput',
|
||||
SEARCH_LOADING_CLASS = 'search-form__loader';
|
||||
|
||||
|
@ -34,18 +33,22 @@ var /** @type {VectorResourceLoaderVirtualConfig} */
|
|||
* After the search module is loaded, executes a function to remove
|
||||
* the loading indicator.
|
||||
*
|
||||
* @param {HTMLElement} element search input.
|
||||
* @param {Element} element search input.
|
||||
* @param {string} moduleName resourceLoader module to load.
|
||||
* @param {function(): void} afterLoadFn function to execute after search module loads.
|
||||
* @param {string|null} startMarker
|
||||
* @param {null|function(): void} afterLoadFn function to execute after search module loads.
|
||||
*/
|
||||
function loadSearchModule( element, moduleName, afterLoadFn ) {
|
||||
var SHOULD_TEST_SEARCH = CAN_TEST_SEARCH && moduleName === 'skins.vector.search';
|
||||
function loadSearchModule( element, moduleName, startMarker, afterLoadFn ) {
|
||||
var SHOULD_TEST_SEARCH = CAN_TEST_SEARCH &&
|
||||
moduleName === 'skins.vector.search';
|
||||
|
||||
function requestSearchModule() {
|
||||
if ( SHOULD_TEST_SEARCH ) {
|
||||
performance.mark( LOAD_START_MARK );
|
||||
if ( SHOULD_TEST_SEARCH && startMarker !== null && afterLoadFn !== null ) {
|
||||
performance.mark( startMarker );
|
||||
mw.loader.using( moduleName, afterLoadFn );
|
||||
} else {
|
||||
mw.loader.load( moduleName );
|
||||
}
|
||||
mw.loader.using( moduleName, afterLoadFn );
|
||||
element.removeEventListener( 'focus', requestSearchModule );
|
||||
}
|
||||
|
||||
|
@ -96,7 +99,7 @@ function renderSearchLoadingIndicator( event ) {
|
|||
* Attaches or detaches the event listeners responsible for activating
|
||||
* the loading indicator.
|
||||
*
|
||||
* @param {HTMLElement} element
|
||||
* @param {Element} element
|
||||
* @param {boolean} attach
|
||||
* @param {function(Event): void} eventCallback
|
||||
*/
|
||||
|
@ -116,11 +119,15 @@ function setLoadingIndicatorListeners( element, attach, eventCallback ) {
|
|||
|
||||
/**
|
||||
* Marks when the lazy load has completed.
|
||||
*
|
||||
* @param {string} startMarker
|
||||
* @param {string} endMarker
|
||||
* @param {string} measureMarker
|
||||
*/
|
||||
function markLoadEnd() {
|
||||
if ( performance.getEntriesByName( LOAD_START_MARK ).length ) {
|
||||
performance.mark( LOAD_END_MARK );
|
||||
performance.measure( LOAD_MEASURE, LOAD_START_MARK, LOAD_END_MARK );
|
||||
function markLoadEnd( startMarker, endMarker, measureMarker ) {
|
||||
if ( performance.getEntriesByName( startMarker ).length ) {
|
||||
performance.mark( endMarker );
|
||||
performance.measure( measureMarker, startMarker, endMarker );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,8 +138,7 @@ function markLoadEnd() {
|
|||
* @param {Document} document
|
||||
*/
|
||||
function initSearchLoader( document ) {
|
||||
var searchForm = document.getElementById( SEARCH_FORM_ID ),
|
||||
searchInput = document.getElementById( SEARCH_INPUT_ID ),
|
||||
var searchBoxes = document.querySelectorAll( '.vector-search-box' ),
|
||||
shouldUseCoreSearch;
|
||||
|
||||
// Allow developers to defined $wgVectorSearchHost in LocalSettings to target different APIs
|
||||
|
@ -140,7 +146,7 @@ function initSearchLoader( document ) {
|
|||
mw.config.set( 'wgVectorSearchHost', config.wgVectorSearchHost );
|
||||
}
|
||||
|
||||
if ( !searchForm || !searchInput ) {
|
||||
if ( !searchBoxes.length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -155,27 +161,46 @@ function initSearchLoader( document ) {
|
|||
* before the search module loads.
|
||||
**/
|
||||
if ( shouldUseCoreSearch || !window.fetch ) {
|
||||
loadSearchModule( searchInput, 'mediawiki.searchSuggest', function () {} );
|
||||
} else {
|
||||
searchBoxes.forEach( function ( searchBox ) {
|
||||
var input = searchBox.querySelector( 'input[name="search"]' );
|
||||
if ( input ) {
|
||||
loadSearchModule(
|
||||
input,
|
||||
'mediawiki.searchSuggest',
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
} );
|
||||
return;
|
||||
}
|
||||
searchBoxes.forEach( function ( searchBox ) {
|
||||
var searchInner = searchBox.querySelector( 'form > div' ),
|
||||
searchInput = searchBox.querySelector( 'input[name="search"]' ),
|
||||
isPrimarySearch = searchInput && searchInput.getAttribute( 'id' ) === 'searchInput';
|
||||
|
||||
if ( !searchInput || !searchInner ) {
|
||||
return;
|
||||
}
|
||||
// Remove tooltips while Vue search is still loading
|
||||
searchInput.setAttribute( 'autocomplete', 'off' );
|
||||
searchInput.removeAttribute( 'title' );
|
||||
setLoadingIndicatorListeners( searchForm, true, renderSearchLoadingIndicator );
|
||||
setLoadingIndicatorListeners( searchInner, true, renderSearchLoadingIndicator );
|
||||
loadSearchModule(
|
||||
searchInput,
|
||||
'skins.vector.search',
|
||||
function () {
|
||||
markLoadEnd();
|
||||
|
||||
isPrimarySearch ? LOAD_START_MARK : null,
|
||||
isPrimarySearch ? function () {
|
||||
markLoadEnd( LOAD_START_MARK, LOAD_END_MARK, LOAD_MEASURE );
|
||||
setLoadingIndicatorListeners(
|
||||
/** @type {HTMLElement} */ ( searchForm ),
|
||||
// @ts-ignore
|
||||
searchInner,
|
||||
false,
|
||||
renderSearchLoadingIndicator
|
||||
);
|
||||
}
|
||||
} : null
|
||||
);
|
||||
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
|
|
@ -42,7 +42,7 @@ function bindSearchBoxHandler( searchBox, header ) {
|
|||
*
|
||||
* @param {HTMLElement} searchBox
|
||||
* @param {HTMLElement} header
|
||||
* @param {HTMLElement} searchToggle
|
||||
* @param {Element} searchToggle
|
||||
*/
|
||||
function bindToggleClickHandler( searchBox, header, searchToggle ) {
|
||||
/**
|
||||
|
@ -88,7 +88,7 @@ function bindToggleClickHandler( searchBox, header, searchToggle ) {
|
|||
* elements. When the user clicks outside of SEARCH_BOX_SELECTOR, the class will
|
||||
* be removed.
|
||||
*
|
||||
* @param {HTMLElement|null} searchToggle
|
||||
* @param {HTMLElement|null|Element} searchToggle
|
||||
*/
|
||||
module.exports = function initSearchToggle( searchToggle ) {
|
||||
// Check if .closest API is available (IE11 does not support it).
|
||||
|
|
|
@ -89,8 +89,8 @@ function makeStickyHeaderFunctional(
|
|||
userMenu,
|
||||
userMenuStickyContainer
|
||||
) {
|
||||
/* eslint-disable-next-line compat/compat */
|
||||
var
|
||||
/* eslint-disable-next-line compat/compat */
|
||||
stickyObserver = new IntersectionObserver( function ( entries ) {
|
||||
if ( !entries[ 0 ].isIntersecting && entries[ 0 ].boundingClientRect.top < 0 ) {
|
||||
// Viewport has crossed the bottom edge of firstHeading so show sticky header.
|
||||
|
@ -105,7 +105,8 @@ function makeStickyHeaderFunctional(
|
|||
// Type declaration needed because of https://github.com/Microsoft/TypeScript/issues/3734#issuecomment-118934518
|
||||
userMenuClone = /** @type {HTMLElement} */( userMenu.cloneNode( true ) ),
|
||||
userMenuStickyElementsWithIds = userMenuClone.querySelectorAll( '[ id ], [ data-event-name ]' ),
|
||||
userMenuStickyContainerInner = userMenuStickyContainer.querySelector( VECTOR_USER_LINKS_SELECTOR );
|
||||
userMenuStickyContainerInner = userMenuStickyContainer
|
||||
.querySelector( VECTOR_USER_LINKS_SELECTOR );
|
||||
|
||||
// Update all ids of the cloned user menu to make them unique.
|
||||
makeNodeTrackable( userMenuClone );
|
||||
|
|
|
@ -5,53 +5,52 @@ var
|
|||
config = require( './config.json' );
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} searchForm
|
||||
* @param {NodeList} secondarySearchElements
|
||||
* @param {HTMLInputElement} search
|
||||
* @param {string|null} searchPageTitle title of page used for searching e.g. Special:Search
|
||||
* If null then this will default to Special:Search.
|
||||
* @param {Function} createElement
|
||||
* @param {Element} searchForm
|
||||
* @return {Vue.VNode}
|
||||
* @throws {Error} if the searchForm does not
|
||||
* contain input[name=title] and input[name="search"] elements.
|
||||
*/
|
||||
function renderFn( createElement, searchForm ) {
|
||||
var
|
||||
titleInput = /** @type {HTMLInputElement|null} */ (
|
||||
searchForm.querySelector( 'input[name=title]' )
|
||||
),
|
||||
search = /** @type {HTMLInputElement|null} */ ( searchForm.querySelector( 'input[name="search"]' ) ),
|
||||
searchPageTitle = titleInput && titleInput.value;
|
||||
|
||||
if ( !search || !titleInput ) {
|
||||
throw new Error( 'Attempted to create Vue search element from an incompatible element.' );
|
||||
}
|
||||
|
||||
return createElement( App, {
|
||||
props: $.extend( {
|
||||
id: searchForm.id,
|
||||
autofocusInput: search === document.activeElement,
|
||||
action: searchForm.getAttribute( 'action' ),
|
||||
searchAccessKey: search.getAttribute( 'accessKey' ),
|
||||
searchPageTitle: searchPageTitle,
|
||||
searchTitle: search.getAttribute( 'title' ),
|
||||
searchPlaceholder: search.getAttribute( 'placeholder' ),
|
||||
searchQuery: search.value
|
||||
},
|
||||
// Pass additional config from server.
|
||||
config
|
||||
)
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {NodeList} searchForms
|
||||
* @return {void}
|
||||
*/
|
||||
function initApp( searchForm, secondarySearchElements, search, searchPageTitle ) {
|
||||
/**
|
||||
*
|
||||
* @ignore
|
||||
* @param {Function} createElement
|
||||
* @param {string} id
|
||||
* @return {Vue.VNode}
|
||||
*/
|
||||
var renderFn = function ( createElement, id ) {
|
||||
return createElement( App, {
|
||||
props: $.extend( {
|
||||
id: id,
|
||||
autofocusInput: search === document.activeElement,
|
||||
action: searchForm.getAttribute( 'action' ),
|
||||
searchAccessKey: search.getAttribute( 'accessKey' ),
|
||||
searchPageTitle: searchPageTitle,
|
||||
searchTitle: search.getAttribute( 'title' ),
|
||||
searchPlaceholder: search.getAttribute( 'placeholder' ),
|
||||
searchQuery: search.value
|
||||
},
|
||||
// Pass additional config from server.
|
||||
config
|
||||
)
|
||||
} );
|
||||
};
|
||||
// eslint-disable-next-line no-new
|
||||
new Vue( {
|
||||
el: searchForm,
|
||||
render: function ( createElement ) {
|
||||
return renderFn( createElement, 'searchform' );
|
||||
}
|
||||
} );
|
||||
|
||||
// Initialize secondary search elements like the search in the sticky header.
|
||||
Array.prototype.forEach.call( secondarySearchElements, function ( secondarySearchElement ) {
|
||||
function initApp( searchForms ) {
|
||||
searchForms.forEach( function ( searchForm ) {
|
||||
// eslint-disable-next-line no-new
|
||||
new Vue( {
|
||||
el: secondarySearchElement,
|
||||
el: /** @type {Element} */ ( searchForm ),
|
||||
render: function ( createElement ) {
|
||||
return renderFn( createElement, secondarySearchElement.id );
|
||||
return renderFn( createElement, /** @type {Element} */ ( searchForm ) );
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
@ -62,16 +61,9 @@ function initApp( searchForm, secondarySearchElements, search, searchPageTitle )
|
|||
*/
|
||||
function main( document ) {
|
||||
var
|
||||
searchForm = /** @type {HTMLElement} */ ( document.querySelector( '#searchform' ) ),
|
||||
titleInput = /** @type {HTMLInputElement|null} */ (
|
||||
searchForm.querySelector( 'input[name=title]' )
|
||||
),
|
||||
search = /** @type {HTMLInputElement|null} */ ( document.getElementById( 'searchInput' ) ),
|
||||
// Since App.vue requires a unique id prop, only query elements with an id attribute.
|
||||
secondarySearchElements = document.querySelectorAll( '.vector-secondary-search[id]' );
|
||||
// FIXME: Use .vector-search-box-form instead when cache allows.
|
||||
searchForms = document.querySelectorAll( '.vector-search-box form' );
|
||||
|
||||
if ( search && searchForm ) {
|
||||
initApp( searchForm, secondarySearchElements, search, titleInput && titleInput.value );
|
||||
}
|
||||
initApp( searchForms );
|
||||
}
|
||||
main( document );
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
@import 'mediawiki.mixins.less';
|
||||
|
||||
// Search portlet.
|
||||
#p-search h3 {
|
||||
.vector-search-box h3 {
|
||||
.mixin-screen-reader-text();
|
||||
}
|
||||
|
|
|
@ -128,8 +128,10 @@ body {
|
|||
// Defined as `div`.
|
||||
// Provide extra element for gadgets due to `form` already carrying an `id`.
|
||||
// FIXME: This selector requires knowledge of the internals of the search component
|
||||
// FIXME: #simpleSearch selector can be removed when cache has cleared.
|
||||
// and should not be used here.
|
||||
#simpleSearch {
|
||||
#simpleSearch,
|
||||
.vector-search-box-inner {
|
||||
min-width: 5em;
|
||||
// Support: IE 8, Firefox 18-, Chrome 19-, Safari 5.1-, Opera 19-, Android 4.4.4-.
|
||||
width: 13.2em;
|
||||
|
@ -180,7 +182,9 @@ body {
|
|||
padding-left: 0.5em;
|
||||
}
|
||||
|
||||
#p-search {
|
||||
// FIXME: p-search is for cached HTML only. Can be removed in 1 week.
|
||||
#p-search,
|
||||
.vector-search-box {
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,9 @@
|
|||
min-width: @min-width-search-desktop;
|
||||
flex-basis: @min-width-search;
|
||||
|
||||
> div > #searchform,
|
||||
// FIXME: Modify to use .vector-search-box-form when cache allows.
|
||||
// When changing check the specificity is strong enough so that is still applies.
|
||||
> div > form,
|
||||
.wvui-typeahead-search {
|
||||
max-width: @max-width-search;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
// https://gerrit.wikimedia.org/r/plugins/gitiles/wvui/+/e32b54f3b8d1118b6a25cdc46b5638d6d048533e/src/themes/wikimedia-ui.less#27
|
||||
@padding-vertical-typeahead-suggestion: 8px;
|
||||
|
||||
#simpleSearch.search-form__loader:after {
|
||||
.search-form__loader:after {
|
||||
// Set the i18n message.
|
||||
content: attr( data-loading-msg );
|
||||
//
|
||||
|
|
|
@ -27,25 +27,33 @@
|
|||
}
|
||||
|
||||
// Typeahead search elements
|
||||
// FIXME: remove ID selectors when cache has cleared.
|
||||
#searchInput,
|
||||
#searchButton,
|
||||
#mw-searchButton {
|
||||
#mw-searchButton,
|
||||
.vector-search-box-vue .vector-search-box-input,
|
||||
.vector-search-box-vue .searchButton {
|
||||
// Overrides #mw-searchButton in resources/skins.vector.styles/SearchBox.less
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
// FIXME: remove #searchInput selector when cache has cleared.
|
||||
.vector-search-box-vue .vector-search-box-input,
|
||||
#searchInput {
|
||||
height: @size-base;
|
||||
}
|
||||
|
||||
#searchButton,
|
||||
#mw-searchButton {
|
||||
// FIXME: Remove searchButton when cache has cleared.
|
||||
.vector-search-box-vue .searchButton,
|
||||
#searchButton {
|
||||
background-size: @background-size-x-search-button auto;
|
||||
}
|
||||
|
||||
// Only apply the following WVUI-related rules to clients who have js enabled.
|
||||
// TODO: .skin-vector-search-vue class can be removed when $wgVectorUseWvuiSearch is no longer supported.
|
||||
.client-js .skin-vector-search-vue {
|
||||
// TODO: .skin-vector-search-vue class can be removed when $wgVectorUseWvuiSearch is no longer supported
|
||||
// OR .vector-search-box-vue is in cached HTML.
|
||||
.client-js .skin-vector-search-vue,
|
||||
.client-js .vector-search-box-vue {
|
||||
// Derived from @size-search-figure in WVUI
|
||||
// https://gerrit.wikimedia.org/r/plugins/gitiles/wvui/+/e32b54f3b8d1118b6a25cdc46b5638d6d048533e/src/themes/wikimedia-ui.less#21
|
||||
@size-search-figure: unit( 36px / @font-size-browser / @font-size-base, em );
|
||||
|
@ -56,11 +64,13 @@
|
|||
text-decoration: none;
|
||||
}
|
||||
|
||||
#searchform-suggestions li {
|
||||
.wvui-typeahead-search__suggestions li {
|
||||
// Remove margin-bottom on li elements that is applied by mediawiki.skinning/elements.css.
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
// FIXME: Remove #searchInput selector when cache has cleared.
|
||||
.vector-search-box-input,
|
||||
#searchInput {
|
||||
padding-left: @size-search-figure;
|
||||
// Derived from @padding-input-text in WVUI's Input component.
|
||||
|
@ -68,8 +78,7 @@
|
|||
}
|
||||
|
||||
// Move & resize search icon to match WVUI.
|
||||
#searchButton,
|
||||
#mw-searchButton {
|
||||
.searchButton {
|
||||
// T270202: Act like a an inert element instead of a submit button before
|
||||
// WVUI loads to discourage people clicking on it since it is a submit
|
||||
// button styled to look like WVUI's inert start icon. Note, ideally these
|
||||
|
@ -97,6 +106,8 @@
|
|||
.p-search--show-thumbnail,
|
||||
.vector-search-box-show-thumbnail {
|
||||
// Recreate WVUI expanding input.
|
||||
// FIXME: Remove #searchInput selector when cache has cleared.
|
||||
.vector-search-box-input:focus,
|
||||
#searchInput:focus {
|
||||
position: relative;
|
||||
// Use ~ and fixed values to disable the LESS transformation in ResourceLoader LESS implementation.
|
||||
|
@ -106,6 +117,8 @@
|
|||
}
|
||||
|
||||
// Reposition search icon for expanded input.
|
||||
// FIXME: Remove #searchInput selectors when cache has cleared.
|
||||
.vector-search-box-input:focus ~ .searchButton,
|
||||
#searchInput:focus ~ #searchButton,
|
||||
#searchInput:focus ~ #mw-searchButton {
|
||||
// Derived from
|
||||
|
@ -116,7 +129,9 @@
|
|||
}
|
||||
|
||||
// Update search loader to match width and position of WVUI expanding input.
|
||||
#simpleSearch.search-form__loader:after {
|
||||
// FIXME: Remove #simpleSearch selector when cache has cleared.
|
||||
#simpleSearch.search-form__loader:after,
|
||||
.vector-search-box-inner.search-form__loader:after {
|
||||
width: ~'calc( 100% + @{size-search-expand} )';
|
||||
left: ~'calc( -1 * @{size-search-expand} )';
|
||||
padding-left: @size-search-expand;
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
"license-name": "GPL-2.0-or-later",
|
||||
"type": "skin",
|
||||
"requires": {
|
||||
"MediaWiki": ">= 1.37.0"
|
||||
"MediaWiki": ">= 1.38.0"
|
||||
},
|
||||
"ValidSkinNames": {
|
||||
"vector": {
|
||||
|
@ -51,6 +51,8 @@
|
|||
"vector-jumptosearch",
|
||||
"vector-jumptocontent",
|
||||
"search",
|
||||
"searchbutton",
|
||||
"searcharticle",
|
||||
"sitesubtitle",
|
||||
"sitetitle",
|
||||
"tagline"
|
||||
|
|
|
@ -6,18 +6,29 @@ import searchBoxTemplate from '!!raw-loader!../includes/templates/SearchBox.must
|
|||
import Button from '!!raw-loader!../includes/templates/Button.mustache';
|
||||
import { htmlUserLanguageAttributes } from './utils';
|
||||
|
||||
const INPUT_ATTRIBUTES = 'type="search" name="search" placeholder="Search Wikipedia" title="Search Wikipedia [⌃⌥f]" accesskey="f" id="searchInput" autocomplete="off"';
|
||||
const FULL_TEXT_ATTRIBUTES = 'name="fulltext" title="Search pages for this text" id="mw-searchButton" class="searchButton mw-fallbackSearchButton"';
|
||||
const GO_ATTRIBUTES = 'name="go" title="Go to a page with this exact name if it exists" id="searchButton" class="searchButton"';
|
||||
|
||||
/**
|
||||
* @type {SearchData}
|
||||
*/
|
||||
const searchBoxData = {
|
||||
'form-action': '/w/index.php',
|
||||
class: 'vector-search-box vector-search-show-thumbnail',
|
||||
'form-id': 'searchform',
|
||||
'is-primary': false,
|
||||
class: 'vector-search-show-thumbnail',
|
||||
'html-user-language-attributes': htmlUserLanguageAttributes,
|
||||
'msg-search': 'Search',
|
||||
'html-input': '<input type="search" name="search" placeholder="Search Wikipedia" title="Search Wikipedia [⌃⌥f]" accesskey="f" id="searchInput" autocomplete="off">',
|
||||
'html-input': `<input ${INPUT_ATTRIBUTES}>`,
|
||||
'page-title': 'Special:Search',
|
||||
'html-button-search-fallback': '<input type="submit" name="fulltext" value="Search" title="Search pages for this text" id="mw-searchButton" class="searchButton mw-fallbackSearchButton"/>',
|
||||
'html-button-search': '<input type="submit" name="go" value="Go" title="Go to a page with this exact name if it exists" id="searchButton" class="searchButton">'
|
||||
'html-input-attributes': INPUT_ATTRIBUTES,
|
||||
'html-button-fulltext-attributes': FULL_TEXT_ATTRIBUTES,
|
||||
'msg-searchbutton': 'Search',
|
||||
'msg-searcharticle': 'Go',
|
||||
'html-button-go-attributes': GO_ATTRIBUTES,
|
||||
'html-button-search-fallback': `<input type="submit" ${FULL_TEXT_ATTRIBUTES} value="Search" />`,
|
||||
'html-button-search': `<input type="submit" ${GO_ATTRIBUTES} value="Go">`
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import mustache from 'mustache';
|
||||
import '../resources/skins.vector.styles/SearchBox.less';
|
||||
import '../resources/skins.vector.styles/layouts/screen.less';
|
||||
|
||||
import { searchBoxData, searchBoxDataWithCollapsing, searchBoxTemplate,
|
||||
SEARCH_TEMPLATE_PARTIALS
|
||||
} from './SearchBox.stories.data';
|
||||
|
|
|
@ -39,11 +39,18 @@
|
|||
/**
|
||||
* @typedef {Object} SearchData
|
||||
* @property {string|null} msg-search
|
||||
* @property {string|null} msg-searchbutton
|
||||
* @property {string|null} msg-searcharticle
|
||||
* @property {string} [html-user-language-attributes]
|
||||
* @property {boolean} is-primary is this the primary method of search?
|
||||
* @property {string} form-action URL
|
||||
* @property {string} form-id
|
||||
* @property {string|null} html-input
|
||||
* @property {string|null} [class] of the menu
|
||||
* @property {string|null} page-title the title of the search page
|
||||
* @property {string} html-input-attributes
|
||||
* @property {string} html-button-fulltext-attributes
|
||||
* @property {string} html-button-go-attributes
|
||||
* @property {string|null} html-button-search-fallback
|
||||
* @property {string|null} html-button-search
|
||||
* @property {string} [input-location] An identifier corresponding the position of the search
|
||||
|
|
Loading…
Reference in a new issue