11<template >
2- <div class =" mismatchfinder__language-selector" >
3- <div class =" languageSelector__mobile-header" >
4- <span >{{ $i18n( 'language-selector-mobile-header' ) }}</span >
5- <button @click =" onCloseMenu" >
6- <img :src =" closeUrl" :alt =" $i18n( 'language-selector-close-button-label' )" >
7- </button >
2+ <div class =" mismatchfinder__language-selector" >
3+ <div class =" languageSelector__mobile-header" >
4+ <span >{{ $i18n( 'language-selector-mobile-header' ) }}</span >
5+ <button @click =" onCloseMenu" >
6+ <img :src =" closeUrl" :alt =" $i18n( 'language-selector-close-button-label' )" >
7+ </button >
8+ </div >
9+ <LanguageSelectorInput
10+ ref =" input"
11+ :value =" searchInput"
12+ :placeholder =" $i18n( 'language-selector-input-placeholder' )"
13+ @input =" onInput"
14+ @clear =" onClearInputValue"
15+ @tab =" onCloseMenu"
16+ @arrowDown =" onArrowDown"
17+ @arrowUp =" onArrowUp"
18+ @enter =" onEnter"
19+ @escape =" onCloseMenu"
20+ />
21+ <LanguageSelectorOptionsMenu
22+ tabindex =" -1"
23+ :languages =" shownLanguages"
24+ :highlighted-index =" highlightedIndex"
25+ @select =" onSelect"
26+ >
27+ <template #no-results >
28+ <slot name =" no-results" />
29+ </template >
30+ </LanguageSelectorOptionsMenu >
831 </div >
9- <LanguageSelectorInput
10- ref =" input"
11- :value =" searchInput"
12- :placeholder =" $i18n( 'language-selector-input-placeholder' )"
13- @input =" onInput"
14- @clear =" onClearInputValue"
15- @tab =" onCloseMenu"
16- @arrowDown =" onArrowDown"
17- @arrowUp =" onArrowUp"
18- @enter =" onEnter"
19- @escape =" onCloseMenu"
20- />
21- <LanguageSelectorOptionsMenu
22- tabindex =" -1"
23- :languages =" shownLanguages"
24- :highlighted-index =" highlightedIndex"
25- @select =" onSelect"
26- >
27- <template #no-results >
28- <slot name =" no-results" />
29- </template >
30- </LanguageSelectorOptionsMenu >
31- </div >
3232</template >
33+
3334<script setup lang="ts">
3435import LanguageSelectorOptionsMenu from " ./LanguageSelectorOptionsMenu.vue" ;
3536import LanguageSelectorInput from " ./LanguageSelectorInput.vue" ;
3637import Language from ' ../types/Language' ;
3738import closeUrlSvg from ' ../../img/close.svg' ;
3839
39- import { ref , computed } from " vue" ;
40- import type { Ref } from ' vue' ;
40+ import {ref , computed } from " vue" ;
41+ import type {Ref } from ' vue' ;
4142import languageData from " @wikimedia/language-data" ;
4243
4344const searchInput: Ref <string > = ref (' ' );
@@ -49,95 +50,96 @@ const input = ref<InstanceType<typeof LanguageSelectorInput> | null>(null);
4950const emit = defineEmits ([' select' , ' close' ]);
5051
5152const languages = computed <Language []>(() => {
52- const autonyms = languageData .getAutonyms ();
53- const languageCodes = Object .keys ( autonyms );
54- languageCodes .sort ( languageData .sortByAutonym );
55- return languageCodes .map ( ( code ) => ( {
56- code ,
57- autonym: autonyms [ code ],
58- } ) );
53+ const autonyms = languageData .getAutonyms ();
54+ const languageCodes = Object .keys (autonyms );
55+ languageCodes .sort (languageData .sortByAutonym );
56+ return languageCodes .map (( code ) => ({
57+ code ,
58+ autonym: autonyms [code ],
59+ }) );
5960});
6061
6162const shownLanguages = computed <Language []>(() => {
62- return languages .value .filter ( ( language ) =>
63- language .code .startsWith ( searchInput .value .toLowerCase () ) ||
64- language .autonym .toLowerCase ().includes ( searchInput .value .toLowerCase () ),
65- );
63+ return languages .value .filter (( language ) =>
64+ language .code .startsWith (searchInput .value .toLowerCase ()) ||
65+ language .autonym .toLowerCase ().includes (searchInput .value .toLowerCase ()),
66+ );
6667});
6768
6869function onInput(searchedLanguage : string ): void {
69- searchInput .value = searchedLanguage ;
70- highlightedIndex .value = 0 ;
70+ searchInput .value = searchedLanguage ;
71+ highlightedIndex .value = 0 ;
7172}
7273
7374function onSelect(languageCode : string ): void {
74- emit (' select' , languageCode );
75+ emit (' select' , languageCode );
7576}
7677
7778function onClearInputValue(): void {
78- searchInput .value = ' ' ;
79+ searchInput .value = ' ' ;
7980}
8081
8182function onCloseMenu(): void {
82- emit (' close' );
83+ emit (' close' );
8384}
8485
8586// eslint-disable-next-line @typescript-eslint/no-unused-vars
8687function focus(): void {
87- input .value ?.focus ();
88+ input .value ?.focus ();
8889}
8990
9091function onArrowDown(): void {
91- highlightedIndex .value = ( highlightedIndex .value + 1 ) % shownLanguages .value .length ;
92+ highlightedIndex .value = (highlightedIndex .value + 1 ) % shownLanguages .value .length ;
9293}
9394
9495function onArrowUp(): void {
95- const length = shownLanguages .value .length ;
96- highlightedIndex .value = ( highlightedIndex .value + length - 1 ) % length ;
96+ const length = shownLanguages .value .length ;
97+ highlightedIndex .value = (highlightedIndex .value + length - 1 ) % length ;
9798}
9899
99100function onEnter(): void {
100- onSelect ( shownLanguages .value [ highlightedIndex .value ].code )
101+ onSelect (shownLanguages .value [highlightedIndex .value ].code )
101102}
102103
103104defineExpose ({focus })
104105
105106 </script >
107+
106108<style lang="scss">
107109$tinyViewportWidth : 38em ;
108110
109111.mismatchfinder__language-selector {
110- position : absolute ;
111- inset-inline-end : 0 ;
112- width : 384px ;
113- z-index : 1 ;
114-
115- @media (max-width : $tinyViewportWidth ) {
116- width : 100% ;
117- position : fixed ;
118- top : 0 ;
119- display : flex ;
120- flex-direction : column ;
121- height : 100% ;
122- }
123-
124- .languageSelector__mobile-header {
125- display : none ;
126- padding-block : 12px ;
127- padding-inline : 16px ;
128- justify-content : space-between ;
129- background-color : #fff ;
130-
131- span {
132- color : #202122 ;
133- font-family : -apple-system , BlinkMacSystemFont, ' Segoe UI' , Roboto, Lato, Helvetica , Arial , sans-serif ;
134- font-size : 1em ;
135- font-weight : bold ;
136- }
112+ position : absolute ;
113+ inset-inline-end : 0 ;
114+ width : 384px ;
115+ z-index : 1 ;
137116
138117 @media (max-width : $tinyViewportWidth ) {
139- display : flex ;
118+ width : 100% ;
119+ position : fixed ;
120+ top : 0 ;
121+ display : flex ;
122+ flex-direction : column ;
123+ height : 100% ;
124+ }
125+
126+ .languageSelector__mobile-header {
127+ display : none ;
128+ padding-block : 12px ;
129+ padding-inline : 16px ;
130+ justify-content : space-between ;
131+ background-color : #fff ;
132+
133+ span {
134+ color : #202122 ;
135+ font-family : -apple-system , BlinkMacSystemFont, ' Segoe UI' , Roboto, Lato, Helvetica , Arial , sans-serif ;
136+ font-size : 1em ;
137+ font-weight : bold ;
138+ }
139+
140+ @media (max-width : $tinyViewportWidth ) {
141+ display : flex ;
142+ }
140143 }
141- }
142144}
143145 </style >
0 commit comments