|
1 | 1 | <template> |
2 | 2 | <div class="mismatchfinder__language-selector"> |
3 | 3 | <div class="languageSelector__mobile-header"> |
4 | | - <span>{{ $i18n( 'language-selector-mobile-header' ) }}</span> |
| 4 | + <span>{{ $i18n('language-selector-mobile-header') }}</span> |
5 | 5 | <button @click="onCloseMenu"> |
6 | 6 | <img :src="closeUrl" :alt="$i18n( 'language-selector-close-button-label' )"> |
7 | 7 | </button> |
|
25 | 25 | @select="onSelect" |
26 | 26 | > |
27 | 27 | <template #no-results> |
28 | | - <slot name="no-results" /> |
| 28 | + <slot name="no-results"/> |
29 | 29 | </template> |
30 | 30 | </LanguageSelectorOptionsMenu> |
31 | 31 | </div> |
32 | 32 | </template> |
33 | 33 |
|
34 | | -<script lang="ts"> |
35 | | -import LanguageSelectorInput from '../Components/LanguageSelectorInput.vue'; |
36 | | -import LanguageSelectorOptionsMenu from '../Components/LanguageSelectorOptionsMenu.vue'; |
| 34 | +<script setup lang="ts"> |
| 35 | +import LanguageSelectorOptionsMenu from "./LanguageSelectorOptionsMenu.vue"; |
| 36 | +import LanguageSelectorInput from "./LanguageSelectorInput.vue"; |
37 | 37 | import Language from '../types/Language'; |
38 | | -import { defineComponent } from 'vue'; |
39 | | -import languagedata from '@wikimedia/language-data'; |
40 | | -import closeUrl from '../../img/close.svg'; |
41 | | -
|
42 | | -export default defineComponent( { |
43 | | - name: 'LanguageSelector', |
44 | | - components: { |
45 | | - LanguageSelectorInput, |
46 | | - LanguageSelectorOptionsMenu, |
47 | | - }, |
48 | | - data: () => ( { |
49 | | - searchInput: '', |
50 | | - highlightedIndex: -1, |
51 | | - closeUrl, |
52 | | - } ), |
53 | | - computed: { |
54 | | - languages(): Language[] { |
55 | | - const autonyms = languagedata.getAutonyms(); |
56 | | - const languageCodes = Object.keys( autonyms ); |
57 | | - languageCodes.sort( languagedata.sortByAutonym ); |
58 | | - return languageCodes.map( ( code ) => ( { |
59 | | - code, |
60 | | - autonym: autonyms[ code ], |
61 | | - } ) ); |
62 | | - }, |
63 | | - shownLanguages(): Language[] { |
64 | | - return this.languages.filter( ( language ) => |
65 | | - language.code.startsWith( this.searchInput.toLowerCase() ) || |
66 | | - language.autonym.toLowerCase().includes( this.searchInput.toLowerCase() ), |
67 | | - ); |
68 | | - }, |
69 | | - }, |
70 | | - methods: { |
71 | | - onInput( searchedLanguage: string ): void { |
72 | | - this.searchInput = searchedLanguage; |
73 | | - this.highlightedIndex = 0; |
74 | | - }, |
75 | | - onSelect( languageCode: string ): void { |
76 | | - this.$emit( 'select', languageCode ); |
77 | | - }, |
78 | | - onClearInputValue(): void { |
79 | | - this.searchInput = ''; |
80 | | - }, |
81 | | - onCloseMenu(): void { |
82 | | - this.$emit( 'close' ); |
83 | | - }, |
84 | | - // eslint-disable-next-line vue/no-unused-properties -- exported method |
85 | | - focus(): void { |
86 | | - const inputRef = this.$refs.input as InstanceType<typeof HTMLInputElement> |
87 | | - inputRef.focus(); |
88 | | - }, |
89 | | - onArrowDown(): void { |
90 | | - this.highlightedIndex = ( this.highlightedIndex + 1 ) % this.shownLanguages.length; |
91 | | - }, |
92 | | - onArrowUp(): void { |
93 | | - const length = this.shownLanguages.length; |
94 | | - this.highlightedIndex = ( this.highlightedIndex + length - 1 ) % length; |
95 | | - }, |
96 | | - onEnter(): void { |
97 | | - this.onSelect( this.shownLanguages[ this.highlightedIndex ].code ); |
98 | | - }, |
99 | | - }, |
100 | | -} ); |
| 38 | +import closeUrlSvg from '../../img/close.svg'; |
| 39 | +
|
| 40 | +import {ref, computed} from "vue"; |
| 41 | +import type {Ref} from 'vue'; |
| 42 | +import languageData from "@wikimedia/language-data"; |
| 43 | +
|
| 44 | +const searchInput: Ref<string> = ref(''); |
| 45 | +const highlightedIndex: Ref<number> = ref(-1); |
| 46 | +const closeUrl = ref(closeUrlSvg); |
| 47 | +
|
| 48 | +const input = ref<InstanceType<typeof LanguageSelectorInput> | null>(null); |
| 49 | +
|
| 50 | +const emit = defineEmits(['select', 'close']); |
| 51 | +
|
| 52 | +const languages = computed<Language[]>(() => { |
| 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 | + })); |
| 60 | +}); |
| 61 | +
|
| 62 | +const shownLanguages = computed<Language[]>(() => { |
| 63 | + return languages.value.filter((language) => |
| 64 | + language.code.startsWith(searchInput.value.toLowerCase()) || |
| 65 | + language.autonym.toLowerCase().includes(searchInput.value.toLowerCase()), |
| 66 | + ); |
| 67 | +}); |
| 68 | +
|
| 69 | +function onInput(searchedLanguage: string): void { |
| 70 | + searchInput.value = searchedLanguage; |
| 71 | + highlightedIndex.value = 0; |
| 72 | +} |
| 73 | +
|
| 74 | +function onSelect(languageCode: string): void { |
| 75 | + emit('select', languageCode); |
| 76 | +} |
| 77 | +
|
| 78 | +function onClearInputValue(): void { |
| 79 | + searchInput.value = ''; |
| 80 | +} |
| 81 | +
|
| 82 | +function onCloseMenu(): void { |
| 83 | + emit('close'); |
| 84 | +} |
| 85 | +
|
| 86 | +// eslint-disable-next-line @typescript-eslint/no-unused-vars |
| 87 | +function focus(): void { |
| 88 | + input.value?.focus(); |
| 89 | +} |
| 90 | +
|
| 91 | +function onArrowDown(): void { |
| 92 | + highlightedIndex.value = (highlightedIndex.value + 1) % shownLanguages.value.length; |
| 93 | +} |
| 94 | +
|
| 95 | +function onArrowUp(): void { |
| 96 | + const length = shownLanguages.value.length; |
| 97 | + highlightedIndex.value = (highlightedIndex.value + length - 1) % length; |
| 98 | +} |
| 99 | +
|
| 100 | +function onEnter(): void { |
| 101 | + onSelect(shownLanguages.value[highlightedIndex.value].code) |
| 102 | +} |
| 103 | +
|
| 104 | +defineExpose({focus}) |
| 105 | +
|
101 | 106 | </script> |
102 | 107 |
|
103 | 108 | <style lang="scss"> |
|
0 commit comments