Skip to content

Commit 25c0a6f

Browse files
authored
Rewrite Layout Page using Vue3 syntax (#802)
Bug: T353886
1 parent a4b91a8 commit 25c0a6f

File tree

1 file changed

+84
-100
lines changed

1 file changed

+84
-100
lines changed

resources/js/Pages/Layout.vue

Lines changed: 84 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -64,122 +64,106 @@
6464
</div>
6565
</template>
6666

67-
<script lang="ts">
68-
import { PropType } from 'vue';
67+
<script setup lang="ts">
6968
import { Link as InertiaLink } from '@inertiajs/inertia-vue3';
7069
import { CdxButton as LanguageSelectorButton, CdxIcon } from "@wikimedia/codex";
7170
import { cdxIconLanguage } from '@wikimedia/codex-icons';
7271
import AuthWidget from '../Components/AuthWidget.vue';
7372
import LanguageSelector from '../Components/LanguageSelector.vue';
7473
import WikidataToolFooter from '../Components/WikidataToolFooter.vue';
7574
import { DirectiveBinding, ComponentPublicInstance } from 'vue';
76-
import { defineComponent } from 'vue';
75+
import { ref, computed, nextTick, onMounted, onBeforeUnmount } from 'vue';
76+
import type { Ref } from 'vue';
7777
import User from '../types/User';
7878
import languagedata from '@wikimedia/language-data';
7979
8080
let handleOutsideClick: (event: MouseEvent | TouchEvent) => void;
8181
82-
export default defineComponent({
83-
components: {
84-
AuthWidget,
85-
LanguageSelectorButton,
86-
CdxIcon,
87-
InertiaLink,
88-
LanguageSelector,
89-
WikidataToolFooter
90-
},
91-
setup() {
92-
return {
93-
cdxIconLanguage
94-
};
95-
},
96-
data() {
97-
return {
98-
showLanguageSelector: false,
99-
resizeObserver: null as unknown as ResizeObserver,
100-
};
101-
},
102-
directives: {
103-
detectClickOutside: {
104-
mounted(element: HTMLElement, binding: DirectiveBinding): void {
105-
handleOutsideClick = (event: MouseEvent | TouchEvent): void => {
106-
const callback = binding.value;
107-
if (!element.contains(event.target as Node)) {
108-
callback();
109-
}
110-
};
82+
const showLanguageSelector = ref(false);
83+
const resizeObserver: Ref<ResizeObserver> = ref(null);
84+
const languageSelector: Ref<ComponentPublicInstance> = ref(null);
85+
const header: Ref<HTMLElement> = ref(null);
86+
const userSection: Ref<HTMLElement> = ref(null);
87+
const contentWrap: Ref<Element> = ref(null);
11188
112-
document.addEventListener('click', handleOutsideClick);
113-
document.addEventListener('touchstart', handleOutsideClick);
114-
},
115-
unmounted(): void {
116-
document.removeEventListener('click', handleOutsideClick);
117-
document.removeEventListener('touchstart', handleOutsideClick);
118-
},
119-
},
120-
},
121-
mounted() {
122-
this.resizeObserver = new ResizeObserver(this.onWindowResize);
123-
this.resizeObserver.observe(this.$refs.contentWrap as Element);
124-
},
125-
computed: {
126-
currentLanguageAutonym(): string {
127-
return languagedata.getAutonym(document.documentElement.lang);
128-
},
129-
},
130-
props: {
131-
user: Object as PropType<User>,
132-
},
133-
methods: {
134-
onChangeLanguage(newLanguage: string): void {
135-
/**
136-
* Manipulate the url to maintain it as the single source of truth
137-
* and avoid having either to load all language files upfront or
138-
* request language file reactively.
139-
*/
140-
const url = new URL(document.URL);
141-
url.searchParams.set('uselang', newLanguage);
142-
document.location.assign(url.toString());
143-
},
144-
onCloseLanguageSelector(): void {
145-
this.showLanguageSelector = false;
146-
},
147-
onToggleLanguageSelector(): void {
148-
this.showLanguageSelector = !this.showLanguageSelector;
149-
if (this.showLanguageSelector === true) {
150-
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
151-
const languageSelectorRefs = this.$refs.languageSelector as any;
152-
this.$nextTick(() => {
153-
languageSelectorRefs.focus();
154-
this.changeLanguageSelectorMenuDirection();
155-
});
156-
}
157-
},
158-
onClickOutsideLanguageSelector(): void {
159-
this.showLanguageSelector = false;
160-
},
161-
changeLanguageSelectorMenuDirection(): void {
162-
const headerTop = (this.$refs.header as HTMLElement).getBoundingClientRect().top;
163-
const userSectionTop = (this.$refs.userSection as HTMLElement).getBoundingClientRect().top;
164-
if( userSectionTop > headerTop ){
165-
((this.$refs.languageSelector as ComponentPublicInstance)
166-
.$el as HTMLElement).style.insetInlineEnd = 'unset';
167-
((this.$refs.languageSelector as ComponentPublicInstance)
168-
.$el as HTMLElement).style.insetInlineStart = '0';
169-
} else {
170-
((this.$refs.languageSelector as ComponentPublicInstance)
171-
.$el as HTMLElement).style.insetInlineEnd = '0';
172-
((this.$refs.languageSelector as ComponentPublicInstance)
173-
.$el as HTMLElement).style.insetInlineStart = 'unset';
89+
defineProps<{user: User}>();
90+
91+
const currentLanguageAutonym = computed<string>(() => {
92+
return languagedata.getAutonym(document.documentElement.lang);
93+
});
94+
95+
const vDetectClickOutside = {
96+
mounted: (element: HTMLElement, binding: DirectiveBinding): void => {
97+
handleOutsideClick = (event: MouseEvent | TouchEvent): void => {
98+
const callback = binding.value;
99+
if (!element.contains(event.target as Node)) {
100+
callback();
174101
}
175-
},
176-
onWindowResize(): void {
177-
this.changeLanguageSelectorMenuDirection();
178-
},
102+
};
103+
104+
document.addEventListener('click', handleOutsideClick);
105+
document.addEventListener('touchstart', handleOutsideClick);
179106
},
180-
beforeUnmount () {
181-
this.resizeObserver.unobserve(this.$refs.contentWrap as Element)
107+
unmounted(): void {
108+
document.removeEventListener('click', handleOutsideClick);
109+
document.removeEventListener('touchstart', handleOutsideClick);
182110
}
111+
};
112+
113+
function onChangeLanguage(newLanguage: string): void {
114+
/**
115+
* Manipulate the url to maintain it as the single source of truth
116+
* and avoid having either to load all language files upfront or
117+
* request language file reactively.
118+
*/
119+
const url = new URL(document.URL);
120+
url.searchParams.set('uselang', newLanguage);
121+
document.location.assign(url.toString());
122+
}
123+
124+
function onCloseLanguageSelector(): void {
125+
showLanguageSelector.value = false;
126+
}
127+
128+
async function onToggleLanguageSelector(): Promise<void> {
129+
showLanguageSelector.value = !showLanguageSelector.value;
130+
if (showLanguageSelector.value === true) {
131+
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
132+
const languageSelectorRefs = languageSelector.value as any;
133+
await nextTick(() => {
134+
languageSelectorRefs.focus();
135+
changeLanguageSelectorMenuDirection();
136+
});
137+
}
138+
}
139+
140+
function onClickOutsideLanguageSelector(): void {
141+
showLanguageSelector.value = false;
142+
}
143+
144+
function changeLanguageSelectorMenuDirection(): void {
145+
const headerTop = header.value.getBoundingClientRect().top;
146+
const userSectionTop = (userSection).value.getBoundingClientRect().top;
147+
if( userSectionTop > headerTop ){
148+
(languageSelector.value.$el as HTMLElement).style.insetInlineEnd = 'unset';
149+
languageSelector.value.$el.style.insetInlineStart = '0';
150+
} else {
151+
languageSelector.value.$el.style.insetInlineEnd = '0';
152+
languageSelector.value.$el.style.insetInlineStart = 'unset';
153+
}
154+
}
155+
156+
function onWindowResize(): void {
157+
changeLanguageSelectorMenuDirection();
158+
}
159+
160+
onMounted(() => {
161+
resizeObserver.value = new ResizeObserver(onWindowResize);
162+
resizeObserver.value.observe(contentWrap.value);
163+
});
164+
165+
onBeforeUnmount(() => {
166+
resizeObserver.value.unobserve(contentWrap.value)
183167
});
184168
</script>
185169

0 commit comments

Comments
 (0)