Localisation updates from https://translatewiki.net.
[mediawiki.git] / resources / src / mediawiki.special.block / components / PagesField.vue
blob028615899da85fd1e1a548051fac7a09bec3e603
1 <template>
2         <cdx-field>
3                 <template #label>
4                         {{ $i18n( 'ipb-pages-label' ).text() }}
5                 </template>
6                 <cdx-multiselect-lookup
7                         v-model:input-chips="chips"
8                         v-model:selected="selection"
9                         v-model:input-value="inputValue"
10                         class="mw-block-pages"
11                         :menu-items="menuItems"
12                         :menu-config="menuConfig"
13                         :aria-label="$i18n( 'ipb-pages-label' ).text()"
14                         :placeholder="placeholder"
15                         @input="onInput"
16                         @update:input-chips="onUpdateChips"
17                 ></cdx-multiselect-lookup>
18         </cdx-field>
19 </template>
21 <script>
22 const { computed, defineComponent, ref } = require( 'vue' );
23 const { storeToRefs } = require( 'pinia' );
24 const { CdxField, ChipInputItem, CdxMultiselectLookup } = require( '@wikimedia/codex' );
25 const useBlockStore = require( '../stores/block.js' );
26 const api = new mw.Api();
27 const useMultiblocks = mw.config.get( 'blockEnableMultiblocks' );
28 const MAX_CHIPS = useMultiblocks ? 50 : 10;
30 /**
31  * Field component for selecting pages to block.
32  *
33  * @todo Abstract for general use in MediaWiki (T375220)
34  */
35 module.exports = exports = defineComponent( {
36         name: 'PagesField',
37         components: {
38                 CdxField,
39                 CdxMultiselectLookup
40         },
41         setup() {
42                 const { pages } = storeToRefs( useBlockStore() );
43                 if ( pages.value.length > MAX_CHIPS ) {
44                         pages.value = pages.value.slice( 0, MAX_CHIPS );
45                 }
46                 const chips = ref(
47                         pages.value.map( ( page ) => ( { value: page, label: page } ) )
48                 );
49                 const placeholder = computed( () => {
50                         if ( chips.value.length === MAX_CHIPS ) {
51                                 return '';
52                         }
53                         return mw.msg( 'block-pages-placeholder' );
54                 } );
55                 const selection = ref( pages.value );
56                 const inputValue = ref( '' );
57                 const menuItems = ref( [] );
58                 const menuConfig = { visibleItemLimit: 10 };
60                 /**
61                  * Get search results.
62                  *
63                  * @param {string} searchTerm
64                  *
65                  * @return {Promise}
66                  */
67                 function fetchResults( searchTerm ) {
68                         return api.get( {
69                                 action: 'query',
70                                 list: 'prefixsearch',
71                                 pssearch: searchTerm,
72                                 pslimit: 10,
73                                 format: 'json'
74                         } ).then( ( response ) => response.query );
75                 }
77                 /**
78                  * Handle lookup input.
79                  *
80                  * @param {string} value
81                  */
82                 const onInput = mw.util.debounce( ( value ) => {
83                         if ( chips.value.length >= MAX_CHIPS ) {
84                                 menuItems.value = [];
85                                 return;
86                         }
88                         // Clear menu items if the input was cleared.
89                         if ( !value ) {
90                                 menuItems.value = [];
91                                 return;
92                         }
94                         fetchResults( value ).then( ( data ) => {
95                                 // Make sure this data is still relevant first.
96                                 if ( inputValue.value !== value ) {
97                                         return;
98                                 }
100                                 // Reset the menu items if there are no results.
101                                 if ( !data || data.prefixsearch.length === 0 ) {
102                                         menuItems.value = [];
103                                         return;
104                                 }
106                                 // Build and update the array of menu items.
107                                 menuItems.value = data.prefixsearch.map( ( result ) => ( {
108                                         label: result.title,
109                                         value: result.title
110                                 } ) );
111                         } ).catch( () => {
112                                 // On error, set results to empty.
113                                 menuItems.value = [];
114                         } );
115                 }, 300 );
117                 /**
118                  * Update the store with the new chip values.
119                  *
120                  * @param {ChipInputItem[]} newChips
121                  */
122                 function onUpdateChips( newChips ) {
123                         pages.value = newChips.map( ( chip ) => chip.value );
124                 }
126                 return {
127                         chips,
128                         placeholder,
129                         selection,
130                         inputValue,
131                         menuItems,
132                         menuConfig,
133                         onInput,
134                         onUpdateChips
135                 };
136         }
137 } );
138 </script>