Replace search loader indicator with Codex pending search message

Bug: T321106
Change-Id: Ic27ecf16277725f6a08038a5501d0903a2494b5f
This commit is contained in:
bwang 2023-06-20 13:21:07 -05:00
parent ecc3c4f5a6
commit 8cc436ac4b
6 changed files with 11 additions and 178 deletions

View file

@ -9,11 +9,11 @@
},
{
"resourceModule": "skins.vector.styles",
"maxSize": "11.4 kB"
"maxSize": "11.2 kB"
},
{
"resourceModule": "skins.vector.js",
"maxSize": "14.8 kB"
"maxSize": "14.3 kB"
},
{
"resourceModule": "skins.vector.legacy.js",

View file

@ -19,8 +19,7 @@ const /** @type {VectorResourceLoaderVirtualConfig} */
performance.getEntriesByName ),
LOAD_START_MARK = 'mwVectorVueSearchLoadStart',
LOAD_END_MARK = 'mwVectorVueSearchLoadEnd',
LOAD_MEASURE = 'mwVectorVueSearchLoadStartToLoadEnd',
SEARCH_LOADING_CLASS = 'search-form__loader';
LOAD_MEASURE = 'mwVectorVueSearchLoadStartToLoadEnd';
/**
* Loads the search module via `mw.loader.using` on the element's
@ -55,64 +54,6 @@ function loadSearchModule( element, moduleName, startMarker, afterLoadFn ) {
}
}
/**
* Event callback that shows or hides the loading indicator based on the event type.
* The loading indicator states are:
* 1. Show on input event (while user is typing)
* 2. Hide on focusout event (when user removes focus from the input )
* 3. Show when input is focused, if it contains a query. (in case user re-focuses on input)
*
* @param {Event} event
*/
function renderSearchLoadingIndicator( event ) {
const form = /** @type {HTMLElement} */ ( event.currentTarget ),
input = /** @type {HTMLInputElement} */ ( event.target );
if (
!( event.currentTarget instanceof HTMLElement ) ||
!( event.target instanceof HTMLInputElement )
) {
return;
}
if ( !form.dataset.loadingMsg ) {
form.dataset.loadingMsg = mw.msg( 'vector-search-loader' );
}
if ( event.type === 'input' ) {
form.classList.add( SEARCH_LOADING_CLASS );
} else if ( event.type === 'focusout' ) {
form.classList.remove( SEARCH_LOADING_CLASS );
} else if ( event.type === 'focusin' && input.value.trim() ) {
form.classList.add( SEARCH_LOADING_CLASS );
}
}
/**
* Attaches or detaches the event listeners responsible for activating
* the loading indicator.
*
* @param {Element} element
* @param {boolean} attach
* @param {function(Event): void} eventCallback
*/
function setLoadingIndicatorListeners( element, attach, eventCallback ) {
/** @type { "addEventListener" | "removeEventListener" } */
const addOrRemoveListener = ( attach ? 'addEventListener' : 'removeEventListener' );
[ 'input', 'focusin', 'focusout' ].forEach( function ( eventType ) {
element[ addOrRemoveListener ]( eventType, eventCallback );
} );
if ( !attach ) {
element.classList.remove( SEARCH_LOADING_CLASS );
}
}
/**
* Marks when the lazy load has completed.
*
@ -158,13 +99,6 @@ function initSearchLoader( document ) {
Array.prototype.forEach.call( searchBoxes, function ( searchBox ) {
const searchInner = searchBox.querySelector( 'form > div' ),
searchInput = searchBox.querySelector( 'input[name="search"]' ),
clearLoadingIndicators = function () {
setLoadingIndicatorListeners(
searchInner,
false,
renderSearchLoadingIndicator
);
},
isPrimarySearch = searchInput && searchInput.getAttribute( 'id' ) === 'searchInput';
if ( !searchInput || !searchInner ) {
@ -172,17 +106,16 @@ function initSearchLoader( document ) {
}
// Remove tooltips while Vue search is still loading
searchInput.setAttribute( 'autocomplete', 'off' );
setLoadingIndicatorListeners( searchInner, true, renderSearchLoadingIndicator );
loadSearchModule(
searchInput,
'skins.vector.search',
isPrimarySearch ? LOAD_START_MARK : null,
// Make sure we clearLoadingIndicators so that event listeners are removed.
// Note, loading Vue.js will remove the element from the DOM.
isPrimarySearch ? function () {
markLoadEnd( LOAD_START_MARK, LOAD_END_MARK, LOAD_MEASURE );
clearLoadingIndicators();
} : clearLoadingIndicators
function () {
if ( isPrimarySearch ) {
markLoadEnd( LOAD_START_MARK, LOAD_END_MARK, LOAD_MEASURE );
}
}
);
} );
}

View file

@ -38,6 +38,9 @@
:value="wprov"
>
</template>
<template #search-results-pending>
{{ $i18n( 'vector-search-loader' ).text() }}
</template>
<!-- eslint-disable-next-line vue/no-template-shadow -->
<template #search-footer-text="{ searchQuery }">
<span v-i18n-html:vector-searchsuggest-containing="[ searchQuery ]"></span>

View file

@ -1,101 +0,0 @@
/**
* Loading indicator for search widget
*
* By adding a class on the parent search form
* <div id="simpleSearch" class="search-form__loader"></div>
* A pseudo element is added via ':after' that mimics the appearance
* of the search suggestion and contains the text
* "Loading search suggestions" (message key: vector-search-loader).
*
* After appearing for more than a second, a progress-bar animation appears
* above the loading indicator.
*
**/
@import '../../common/variables.less';
// 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;
// Derived from text input start icon padding in Codex.
// https://gerrit.wikimedia.org/r/plugins/gitiles/design/codex/+/refs/tags/v0.1.0-alpha.8/packages/codex/src/components/text-input/TextInput.vue#257
@padding-left-start-icon: 36px;
// Derived from @padding-vertical-menu-item: 8px 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#678
@padding-vertical-menu-item: 8px;
.search-form__loader::after {
// Set the i18n message.
content: attr( data-loading-msg );
//
// Position loader below the input.
display: flex;
flex-direction: column;
justify-content: center;
position: absolute;
top: 100%;
width: 100%;
// Compute the height of a Codex menu item: the height of the thumbnail
// + the padding around the thumbnail. Then also add our own border-bottom-width, because we're
// using box-sizing: border-box;
height: ~'calc( @{size-search-figure} + 2*@{padding-vertical-menu-item} + @{border-width-base} )';
//
// Ensure the 100% width doesn't extend beyond the input.
box-sizing: border-box;
//
// Align loader style with input.
border: @border-base;
border-top-width: 0; // Hide the top border so it doesn't interfere with focus state.
border-radius: 0 0 @border-radius-base @border-radius-base;
box-shadow: @box-shadow-inset-small @box-shadow-color-transparent;
padding-left: @padding-left-start-icon;
//
// Hide text in case it extends beyond the input.
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
//
// Prevent Vector tabs from overlapping T254695#6461044
z-index: @z-index-search-loader;
//
// Add a progress-bar to the loading indicator,
// but only show it animating after 1 second of loading.
background: /*image*/ linear-gradient( 90deg, @color-progressive 0%, @color-progressive 100% )
/* position / size*/ -10% 0 ~'/' 0 2px
/* repeat */ no-repeat,/*second bg, just color*/#fff;
//
// Animates the progress-bar.
animation: /* name */ search-loader-progress-bar
/* duration */ 1200ms
/* timing function */ linear
/* delay */ 500ms
/* iteration count */ infinite
/* fill direction */ alternate;
.vector-search-box-show-thumbnail.vector-search-box-auto-expand-width & {
margin-left: @size-search-expand;
width: ~'calc( 100% - @{size-search-expand} )';
}
}
@keyframes search-loader-progress-bar {
0% {
background-size: 0 2px;
background-position: -10% 0;
}
30% {
background-size: 30% 2px;
background-position: -10% 0;
}
70% {
background-size: 30% 2px;
background-position: 110% 0;
}
100% {
background-size: 0 2px;
background-position: 110% 0;
}
}

View file

@ -40,7 +40,6 @@
@import './components/PageToolbar.less';
@import './components/PopupNotification.less';
@import './components/SearchBox.less';
@import './components/SearchBoxLoader.less';
@import './components/Watchstar.less';
@import './components/LimitedWidthToggle.less';
}

View file

@ -144,7 +144,6 @@
// overlay variants and more menu
@z-index-header: @z-index-fixed;
@z-index-search-button: @z-index-stacking-1;
@z-index-search-loader: @z-index-stacking-1;
// Ensure that this is displayed on top of .vector-body and clickable.
@z-index-indicators: @z-index-stacking-1;
// Menus must overlap indicators (@z-index-indicators) and VisualEditor toolbar (z-index: 2).