mirror of
https://gerrit.wikimedia.org/r/mediawiki/skins/Vector.git
synced 2024-11-23 23:33:54 +00:00
Use Codex for typeahead search styles
- Update SearchBox.mustache markup to use codex styles - Scope old SearchBox markup to LegacySearchBox.mustache - Add handling for thumbnail and autoexpand search variants - Adds a 'Search' button to SearchBox.mustache matching the initial non vue search box with the Codex design - Refactor SearchBox CSS so styles are scoped better Visual changes: A "Search" button now appears on page load when it previously only appeared after loading in Vue Bug: T337966 Change-Id: Ibcffe00292ab4f9f5f9919982d578793cf8594de
This commit is contained in:
parent
68239ae344
commit
b2705c55f1
|
@ -26,6 +26,7 @@ class VectorComponentSearchBox implements VectorComponent {
|
|||
private $location;
|
||||
/** @var Config */
|
||||
private $config;
|
||||
private const SEARCH_COLLAPSIBLE_CLASS = 'vector-search-box-collapses';
|
||||
private const SEARCH_SHOW_THUMBNAIL_CLASS = 'vector-search-box-show-thumbnail';
|
||||
private const SEARCH_AUTO_EXPAND_WIDTH_CLASS = 'vector-search-box-auto-expand-width';
|
||||
|
||||
|
@ -56,61 +57,6 @@ class VectorComponentSearchBox implements VectorComponent {
|
|||
return $this->location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Annotates search box with Vector-specific information
|
||||
*
|
||||
* @param array $searchBoxData
|
||||
* @param bool $isCollapsible
|
||||
* @param bool $isPrimary
|
||||
* @param string $formId
|
||||
* @param bool $autoExpandWidth
|
||||
* @return array modified version of $searchBoxData
|
||||
*/
|
||||
private function getSearchData(
|
||||
array $searchBoxData,
|
||||
bool $isCollapsible,
|
||||
bool $isPrimary,
|
||||
string $formId,
|
||||
bool $autoExpandWidth
|
||||
) {
|
||||
$searchClass = 'vector-search-box-vue ';
|
||||
|
||||
if ( $isCollapsible ) {
|
||||
$searchClass .= ' vector-search-box-collapses ';
|
||||
}
|
||||
|
||||
if ( $this->doesSearchHaveThumbnails() ) {
|
||||
$searchClass .= ' ' . self::SEARCH_SHOW_THUMBNAIL_CLASS .
|
||||
( $autoExpandWidth ? ' ' . self::SEARCH_AUTO_EXPAND_WIDTH_CLASS : '' );
|
||||
}
|
||||
|
||||
// Annotate search box with a component class.
|
||||
$searchBoxData['class'] = trim( $searchClass );
|
||||
$searchBoxData['is-collapsible'] = $isCollapsible;
|
||||
$searchBoxData['is-primary'] = $isPrimary;
|
||||
$searchBoxData['form-id'] = $formId;
|
||||
$searchBoxData['input-location'] = $this->getSearchBoxInputLocation();
|
||||
|
||||
// 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).
|
||||
// We'll wire this up in a later task T284242.
|
||||
$collapseIconAttrs = Linker::tooltipAndAccesskeyAttribs( 'search' );
|
||||
$searchButton = new VectorComponentButton(
|
||||
$this->localizer->msg( 'search' ),
|
||||
'search',
|
||||
'',
|
||||
'search-toggle',
|
||||
$collapseIconAttrs,
|
||||
'quiet',
|
||||
'default',
|
||||
true,
|
||||
Title::newFromText( $searchBoxData['page-title'] )->getLocalURL()
|
||||
);
|
||||
$searchBoxData['data-collapsed-search-button'] = $searchButton->getTemplateData();
|
||||
|
||||
return $searchBoxData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $searchBoxData
|
||||
* @param bool $isCollapsible
|
||||
|
@ -145,12 +91,44 @@ class VectorComponentSearchBox implements VectorComponent {
|
|||
* @inheritDoc
|
||||
*/
|
||||
public function getTemplateData(): array {
|
||||
return $this->getSearchData(
|
||||
$this->searchBoxData,
|
||||
$this->isCollapsible,
|
||||
$this->isPrimary,
|
||||
$this->formId,
|
||||
$this->autoExpandWidth
|
||||
$searchBoxData = $this->searchBoxData;
|
||||
$isCollapsible = $this->isCollapsible;
|
||||
$isThumbnail = $this->doesSearchHaveThumbnails();
|
||||
$isAutoExpand = $isThumbnail && $this->autoExpandWidth;
|
||||
$isPrimary = $this->isPrimary;
|
||||
$formId = $this->formId;
|
||||
|
||||
$searchClass = 'vector-search-box-vue ';
|
||||
$searchClass .= $isCollapsible ? ' ' . self::SEARCH_COLLAPSIBLE_CLASS : '';
|
||||
$searchClass .= $isThumbnail ? ' ' . self::SEARCH_SHOW_THUMBNAIL_CLASS : '';
|
||||
$searchClass .= $isAutoExpand ? ' ' . self::SEARCH_AUTO_EXPAND_WIDTH_CLASS : '';
|
||||
|
||||
// Annotate search box with a component class.
|
||||
$searchBoxData['class'] = trim( $searchClass );
|
||||
$searchBoxData['is-collapsible'] = $isCollapsible;
|
||||
$searchBoxData['is-thumbnail'] = $isThumbnail;
|
||||
$searchBoxData['is-auto-expand'] = $isAutoExpand;
|
||||
$searchBoxData['is-primary'] = $isPrimary;
|
||||
$searchBoxData['form-id'] = $formId;
|
||||
$searchBoxData['input-location'] = $this->getSearchBoxInputLocation();
|
||||
|
||||
// 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).
|
||||
// We'll wire this up in a later task T284242.
|
||||
$collapseIconAttrs = Linker::tooltipAndAccesskeyAttribs( 'search' );
|
||||
$searchButton = new VectorComponentButton(
|
||||
$this->localizer->msg( 'search' ),
|
||||
'search',
|
||||
'',
|
||||
'search-toggle',
|
||||
$collapseIconAttrs,
|
||||
'quiet',
|
||||
'default',
|
||||
true,
|
||||
Title::newFromText( $searchBoxData['page-title'] )->getLocalURL()
|
||||
);
|
||||
$searchBoxData['data-collapsed-search-button'] = $searchButton->getTemplateData();
|
||||
|
||||
return $searchBoxData;
|
||||
}
|
||||
}
|
||||
|
|
32
includes/templates/LegacySearchBox.mustache
Normal file
32
includes/templates/LegacySearchBox.mustache
Normal file
|
@ -0,0 +1,32 @@
|
|||
{{!
|
||||
See @typedef SearchData
|
||||
}}
|
||||
<div{{#is-primary}} id="p-search"{{/is-primary}} role="search" class="{{class}} vector-search-box">
|
||||
<h3 {{{html-user-language-attributes}}}>{{msg-search}}</h3>
|
||||
<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"
|
||||
{{#is-primary}}{{{html-input-attributes}}} id="searchInput"{{/is-primary}}
|
||||
{{^is-primary}}
|
||||
type="search" name="search"
|
||||
placeholder="{{msg-searchsuggest-search}}"
|
||||
{{/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).
|
||||
* Browsers will use the 'fulltext' one by default (as it's the first in tree-order),
|
||||
which is desirable when they are unable to show search suggestions (either due to being
|
||||
broken or having JavaScript turned off).
|
||||
* 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. !}}
|
||||
<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>
|
|
@ -7,38 +7,22 @@
|
|||
{{>Button}}
|
||||
{{/data-collapsed-search-button}}
|
||||
{{/is-collapsible}}
|
||||
<div>
|
||||
{{#has-label}}
|
||||
<h3 {{{html-user-language-attributes}}}>
|
||||
<label{{#is-primary}} for="searchInput"{{/is-primary}}>{{msg-search}}</label>
|
||||
</h3>
|
||||
{{/has-label}}
|
||||
<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"
|
||||
{{#is-primary}}{{{html-input-attributes}}} id="searchInput"{{/is-primary}}
|
||||
{{^is-primary}}
|
||||
type="search" name="search"
|
||||
placeholder="{{msg-searchsuggest-search}}"
|
||||
{{/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).
|
||||
* Browsers will use the 'fulltext' one by default (as it's the first in tree-order),
|
||||
which is desirable when they are unable to show search suggestions (either due to being
|
||||
broken or having JavaScript turned off).
|
||||
* 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. !}}
|
||||
<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 class="vector-typeahead-search-container">
|
||||
<div class="cdx-typeahead-search{{#is-thumbnail}} cdx-typeahead-search--show-thumbnail{{/is-thumbnail}}{{#is-auto-expand}} cdx-typeahead-search--auto-expand-width{{/is-auto-expand}}">
|
||||
<form action="{{form-action}}" id="{{form-id}}" class="cdx-search-input cdx-search-input--has-end-button">
|
||||
<div {{#is-primary}}id="simpleSearch"{{/is-primary}} class="cdx-search-input__input-wrapper" {{#input-location}} data-search-loc="{{.}}"{{/input-location}}>
|
||||
<div class="cdx-text-input cdx-text-input--has-start-icon">
|
||||
<input
|
||||
class="cdx-text-input__input"
|
||||
{{#is-primary}}{{{html-input-attributes}}} id="searchInput"{{/is-primary}}
|
||||
{{^is-primary}}type="search" name="search" placeholder="{{msg-searchsuggest-search}}"{{/is-primary}}
|
||||
>
|
||||
<span class="cdx-text-input__icon cdx-text-input__start-icon"></span>
|
||||
</div>
|
||||
<input type="hidden" name="title" value="{{page-title}}">
|
||||
</div>
|
||||
<button class="cdx-button cdx-search-input__end-button">{{msg-searchbutton}}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
<div id="right-navigation">
|
||||
{{#data-views}}{{>LegacyMenu}}{{/data-views}}
|
||||
{{#data-actions}}{{>LegacyMenu}}{{/data-actions}}
|
||||
{{#data-search-box}}{{>SearchBox}}{{/data-search-box}}
|
||||
{{#data-search-box}}{{>LegacySearchBox}}{{/data-search-box}}
|
||||
</div>
|
||||
{{/data-portlets}}
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
@import 'mediawiki.mixins.less';
|
||||
@import '../../common/variables.less';
|
||||
|
||||
// FIXME: Move entire file into the legacy SearchBox.less after caching
|
||||
|
||||
// Defined as `div`.
|
||||
// Provide extra element for gadgets due to `form` already carrying an `id`.
|
||||
.vector-search-box-inner {
|
||||
|
@ -26,7 +28,6 @@
|
|||
box-shadow: @box-shadow-inset-small @box-shadow-color-transparent;
|
||||
// Match Codex.
|
||||
font-family: inherit;
|
||||
font-size: @font-size-search-input;
|
||||
direction: ltr;
|
||||
transition-property: border-color, box-shadow;
|
||||
transition-duration: @transition-duration-medium;
|
||||
|
@ -93,24 +94,3 @@
|
|||
background: no-repeat center/unit( 16 / @font-size-browser / @font-size-search-input, em ) url( images/search.svg );
|
||||
opacity: 0.67;
|
||||
}
|
||||
|
||||
.search-toggle {
|
||||
// At lower resolutions the search input is hidden and a toggle is shown
|
||||
display: inline-flex;
|
||||
float: right;
|
||||
// Ensures the button has a font size of 16px
|
||||
font-size: unit( 16 / @font-size-browser, rem );
|
||||
|
||||
@media ( min-width: @min-width-desktop ) {
|
||||
// Override .cdx-button styles
|
||||
display: none !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
}
|
||||
|
||||
.vector-search-box-collapses > div {
|
||||
display: none;
|
||||
|
||||
@media ( min-width: @min-width-desktop ) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,12 +10,14 @@ const
|
|||
* @return {void}
|
||||
*/
|
||||
function initApp( searchBox ) {
|
||||
const searchForm = searchBox.querySelector( '.vector-search-box-form' ),
|
||||
// FIXME: Remove searchForm.parentNode after caching
|
||||
const searchForm = searchBox.querySelector( '.vector-search-box-form, .cdx-search-input' ),
|
||||
titleInput = /** @type {HTMLInputElement|null} */ (
|
||||
searchBox.querySelector( 'input[name=title]' )
|
||||
),
|
||||
search = /** @type {HTMLInputElement|null} */ ( searchBox.querySelector( 'input[name=search]' ) ),
|
||||
searchPageTitle = titleInput && titleInput.value;
|
||||
searchPageTitle = titleInput && titleInput.value,
|
||||
searchContainer = searchBox.querySelector( '.vector-typeahead-search-container' );
|
||||
|
||||
if ( !searchForm || !search || !titleInput ) {
|
||||
throw new Error( 'Attempted to create Vue search element from an incompatible element.' );
|
||||
|
@ -37,7 +39,8 @@ function initApp( searchBox ) {
|
|||
// Pass additional config from server.
|
||||
}, config )
|
||||
)
|
||||
.mount( searchForm.parentNode );
|
||||
// FIXME: Remove searchForm.parentNode after caching
|
||||
.mount( searchContainer || searchForm.parentNode );
|
||||
}
|
||||
/**
|
||||
* @param {Document} document
|
||||
|
|
|
@ -21,16 +21,26 @@
|
|||
flex-grow: 1;
|
||||
}
|
||||
|
||||
@media ( min-width: @min-width-desktop-wide ) {
|
||||
.client-js & .vector-search-box.vector-search-box-auto-expand-width {
|
||||
// Ensure search box is aligned with content when it autoexpands (i.e. search thumbnails)
|
||||
margin-left: -@size-search-expand;
|
||||
.search-toggle {
|
||||
// At lower resolutions the search input is hidden and a toggle is shown
|
||||
display: inline-flex;
|
||||
float: right;
|
||||
// Ensures the button has a font size of 16px
|
||||
font-size: unit( 16 / @font-size-browser, rem );
|
||||
|
||||
@media ( min-width: @min-width-desktop ) {
|
||||
// Override .cdx-button styles
|
||||
display: none !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
}
|
||||
|
||||
@media ( min-width: @min-width-desktop ) {
|
||||
.vector-search-box {
|
||||
margin-right: @margin-end-search;
|
||||
// FIXME: Remove .vector-search-box-collapses > div after caching
|
||||
.vector-search-box-collapses > div,
|
||||
.vector-typeahead-search-container {
|
||||
display: none;
|
||||
|
||||
@media ( min-width: @min-width-desktop ) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,22 +54,21 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
.vector-search-box-collapses > div {
|
||||
// FIXME: Remove .vector-search-box-collapses > div after caching
|
||||
.vector-search-box-collapses > div,
|
||||
.vector-typeahead-search-container {
|
||||
display: block;
|
||||
// Override default max width at lower resolutions
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.vector-search-box {
|
||||
// T284242#7206507: Widen the suggestion results to the edge of the search
|
||||
// button at small resolutions.
|
||||
position: relative;
|
||||
margin-right: @margin-end-search;
|
||||
|
||||
// Override default max width at lower resolutions
|
||||
> div {
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Remove selector after caching
|
||||
// Make the menu below the search input wider, to match the width of the input+button
|
||||
// rather than just the width of the input
|
||||
.cdx-search-input__input-wrapper {
|
||||
|
|
27
resources/skins.vector.styles/components/SearchBox.less
Normal file
27
resources/skins.vector.styles/components/SearchBox.less
Normal file
|
@ -0,0 +1,27 @@
|
|||
@import '../../common/variables.less';
|
||||
|
||||
// Search container
|
||||
// We have to put those styles outside `.skin-vector-search-vue`,
|
||||
// as we can't address no-JS modern and Vue enhanced otherwise.
|
||||
.vector-search-box {
|
||||
// Use Vector's base font-size, as this is a component outside of `.vector-body`.
|
||||
// Support IE 9-11, Trident cuts values 2 digits after decimal point.
|
||||
// `calc` enables to set correct calculation in place again. See T102364.
|
||||
font-size: @font-size-base--trident-hack;
|
||||
// If this is a flex item, make sure it grows into available space.
|
||||
flex-grow: 1;
|
||||
|
||||
// FIXME: Remove > div selector after caching
|
||||
> div,
|
||||
.vector-typeahead-search-container {
|
||||
max-width: @max-width-search;
|
||||
margin-right: @margin-end-search;
|
||||
}
|
||||
}
|
||||
|
||||
@media ( min-width: @min-width-desktop-wide ) {
|
||||
.client-js & .vector-search-box.vector-search-box-auto-expand-width {
|
||||
// Ensure search box is aligned with content when it autoexpands (i.e. search thumbnails)
|
||||
margin-left: -@size-search-expand;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
@import '../../common/variables.less';
|
||||
|
||||
// FIXME: Remove entire file after caching
|
||||
|
||||
/**
|
||||
* Minimal styling for initial no-JS server-rendered
|
||||
* search form, which gets replaced by Codex on focus.
|
||||
|
@ -8,29 +10,6 @@
|
|||
* ResourceLoader LESS transformation of `calc`.
|
||||
*/
|
||||
|
||||
// Derived from @size-base design token in Codex
|
||||
@size-base: 32px;
|
||||
|
||||
@min-size-search-button: 30px;
|
||||
@background-size-x-search-button: 20px;
|
||||
|
||||
// Search container
|
||||
// We have to put those styles outside `.skin-vector-search-vue`,
|
||||
// as we can't address no-JS modern and Vue enhanced otherwise.
|
||||
.vector-search-box {
|
||||
// Use Vector's base font-size, as this is a component outside of `.vector-body`.
|
||||
font-size: @font-size-base;
|
||||
// Support IE 9-11, Trident cuts values 2 digits after decimal point.
|
||||
// `calc` enables to set correct calculation in place again. See T102364.
|
||||
font-size: @font-size-base--trident-hack;
|
||||
// If this is a flex item, make sure it grows into available space.
|
||||
flex-grow: 1;
|
||||
|
||||
> div {
|
||||
max-width: @max-width-search;
|
||||
}
|
||||
}
|
||||
|
||||
// Typeahead search elements
|
||||
.vector-search-box-vue .vector-search-box-input,
|
||||
.vector-search-box-vue .searchButton {
|
||||
|
@ -41,18 +20,18 @@
|
|||
.vector-search-box-vue .vector-search-box-input {
|
||||
height: auto;
|
||||
// The following is copied directly from Codex.
|
||||
min-height: @size-base;
|
||||
min-height: 32px;
|
||||
line-height: 1.4285714;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.vector-search-box-vue .searchButton {
|
||||
background-size: @background-size-x-search-button auto;
|
||||
background-size: 20px auto;
|
||||
}
|
||||
|
||||
// Only apply the following Codex-related rules to clients who have js enabled.
|
||||
.client-js .vector-search-box-vue {
|
||||
.client-js .vector-search-box-vue > div:not( .vector-typeahead-search-container ) {
|
||||
// Derived from @size-search-figure in Codex.
|
||||
// https://gerrit.wikimedia.org/r/plugins/gitiles/design/codex/+/refs/tags/v0.1.0-alpha.8/packages/codex/src/components/typeahead-search/TypeaheadSearch.vue#676
|
||||
@size-search-figure: 40px;
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
@media screen {
|
||||
// Components
|
||||
@import './components/Button.less';
|
||||
@import './components/SearchBoxLoader.less';
|
||||
@import './components/VueEnhancedSearchBox.less';
|
||||
@import './components/LanguageDropdown.less';
|
||||
@import './components/UserLinks.less';
|
||||
|
@ -41,6 +40,8 @@
|
|||
@import './components/PageTitlebar.less';
|
||||
@import './components/PageToolbar.less';
|
||||
@import './components/PopupNotification.less';
|
||||
@import './components/SearchBox.less';
|
||||
@import './components/SearchBoxLoader.less';
|
||||
@import './components/Watchstar.less';
|
||||
@import './components/LimitedWidthToggle.less';
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue