Skip to content

Commit 58a65d2

Browse files
committed
Rewrite Layout Page using Vue3 syntax
Bug: T353886
1 parent 3358e75 commit 58a65d2

File tree

1 file changed

+84
-99
lines changed

1 file changed

+84
-99
lines changed

resources/js/Pages/Layout.vue

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

67-
<script lang="ts">
67+
<script setup lang="ts">
6868
import { PropType } from 'vue';
6969
import { Link as InertiaLink } from '@inertiajs/inertia-vue3';
7070
import { CdxButton as LanguageSelectorButton, CdxIcon } from "@wikimedia/codex";
@@ -73,113 +73,98 @@ import AuthWidget from '../Components/AuthWidget.vue';
7373
import LanguageSelector from '../Components/LanguageSelector.vue';
7474
import WikidataToolFooter from '../Components/WikidataToolFooter.vue';
7575
import { DirectiveBinding, ComponentPublicInstance } from 'vue';
76-
import { defineComponent } from 'vue';
76+
import { ref, computed, nextTick, onMounted, onBeforeUnmount } from 'vue';
77+
import type { Ref } from 'vue';
7778
import User from '../types/User';
7879
import languagedata from '@wikimedia/language-data';
7980
8081
let handleOutsideClick: (event: MouseEvent | TouchEvent) => void;
8182
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-
};
83+
const showLanguageSelector = ref(false);
84+
const resizeObserver: Ref<ResizeObserver> = ref(null);
85+
const languageSelector: Ref<ComponentPublicInstance> = ref(null);
86+
const header: Ref<HTMLElement> = ref(null);
87+
const userSection: Ref<HTMLElement> = ref(null);
88+
const contentWrap: Ref<Element> = ref(null);
11189
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';
90+
const props = defineProps<{user: User}>();
91+
92+
const currentLanguageAutonym = computed<string>(() => {
93+
return languagedata.getAutonym(document.documentElement.lang);
94+
});
95+
96+
const vDetectClickOutside = {
97+
mounted: (element: HTMLElement, binding: DirectiveBinding): void => {
98+
handleOutsideClick = (event: MouseEvent | TouchEvent): void => {
99+
const callback = binding.value;
100+
if (!element.contains(event.target as Node)) {
101+
callback();
174102
}
175-
},
176-
onWindowResize(): void {
177-
this.changeLanguageSelectorMenuDirection();
178-
},
103+
};
104+
105+
document.addEventListener('click', handleOutsideClick);
106+
document.addEventListener('touchstart', handleOutsideClick);
179107
},
180-
beforeUnmount () {
181-
this.resizeObserver.unobserve(this.$refs.contentWrap as Element)
108+
unmounted(): void {
109+
document.removeEventListener('click', handleOutsideClick);
110+
document.removeEventListener('touchstart', handleOutsideClick);
111+
}
112+
};
113+
114+
function onChangeLanguage(newLanguage: string): void {
115+
/**
116+
* Manipulate the url to maintain it as the single source of truth
117+
* and avoid having either to load all language files upfront or
118+
* request language file reactively.
119+
*/
120+
const url = new URL(document.URL);
121+
url.searchParams.set('uselang', newLanguage);
122+
document.location.assign(url.toString());
123+
};
124+
125+
function onCloseLanguageSelector(): void {
126+
showLanguageSelector.value = false;
127+
};
128+
129+
async function onToggleLanguageSelector(): Promise<void> {
130+
showLanguageSelector.value = !showLanguageSelector.value;
131+
if (showLanguageSelector.value === true) {
132+
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
133+
const languageSelectorRefs = languageSelector.value as any;
134+
await nextTick(() => {
135+
languageSelectorRefs.focus();
136+
changeLanguageSelectorMenuDirection();
137+
});
182138
}
139+
};
140+
141+
function onClickOutsideLanguageSelector(): void {
142+
showLanguageSelector.value = false;
143+
};
144+
145+
function changeLanguageSelectorMenuDirection(): void {
146+
const headerTop = header.value.getBoundingClientRect().top;
147+
const userSectionTop = (userSection).value.getBoundingClientRect().top;
148+
if( userSectionTop > headerTop ){
149+
(languageSelector.value.$el as HTMLElement).style.insetInlineEnd = 'unset';
150+
languageSelector.value.$el.style.insetInlineStart = '0';
151+
} else {
152+
languageSelector.value.$el.style.insetInlineEnd = '0';
153+
languageSelector.value.$el.style.insetInlineStart = 'unset';
154+
}
155+
};
156+
157+
function onWindowResize(): void {
158+
changeLanguageSelectorMenuDirection();
159+
};
160+
161+
onMounted(() => {
162+
resizeObserver.value = new ResizeObserver(onWindowResize);
163+
resizeObserver.value.observe(contentWrap.value);
164+
});
165+
166+
onBeforeUnmount(() => {
167+
resizeObserver.value.unobserve(contentWrap.value)
183168
});
184169
</script>
185170

0 commit comments

Comments
 (0)