Skip to content

Commit 0dab984

Browse files
committed
switch to dynamic components
1 parent 3d3a521 commit 0dab984

File tree

4 files changed

+161
-81
lines changed

4 files changed

+161
-81
lines changed
Lines changed: 20 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,18 @@
11
<script setup lang="ts">
2-
import { Button } from '@/components/ui/button';
3-
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
4-
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu';
5-
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
62
import { useLocale } from '@/composables/useLocale';
73
import { router, usePage } from '@inertiajs/vue3';
8-
import { Globe } from 'lucide-vue-next';
9-
import { computed, watch } from 'vue';
4+
import { computed, defineAsyncComponent, watch } from 'vue';
105
import { useI18n } from 'vue-i18n';
116
127
interface Props {
138
display?: 'dropdown' | 'select' | 'cards';
149
}
1510
16-
withDefaults(defineProps<Props>(), {
17-
display: 'dropdown',
18-
});
19-
20-
const { t, locale } = useI18n();
11+
const { locale } = useI18n();
2112
const page = usePage();
2213
const { initializeLocale } = useLocale();
2314
24-
const availableLocales = page.props.locale.available;
15+
const availableLocales = (page.props.locale as any).available;
2516
2617
const capitalize = (s: string) => (s ? s.charAt(0).toUpperCase() + s.slice(1) : s);
2718
@@ -31,7 +22,7 @@ const languages = availableLocales.map((code: string) => {
3122
});
3223
3324
const currentLanguage = computed(() => {
34-
return languages.find((lang) => lang.code === locale.value) || languages[0];
25+
return languages.find((lang: any) => lang.code === locale.value) || languages[0];
3526
});
3627
3728
watch(locale, async (newLocale, oldLocale) => {
@@ -65,75 +56,23 @@ const syncLocaleWithBackend = async (langCode: string) => {
6556
const switchLanguage = async (langCode: string) => {
6657
locale.value = langCode;
6758
};
68-
</script>
6959
70-
<template>
71-
<!-- Dropdown Display (Default) -->
72-
<DropdownMenu v-if="display === 'dropdown'">
73-
<DropdownMenuTrigger as-child>
74-
<Button variant="ghost" size="icon" class="h-8 w-8 p-0">
75-
<Globe class="h-4 w-4 text-foreground" />
76-
<span class="sr-only">{{ t('common.switchLanguage') }}</span>
77-
</Button>
78-
</DropdownMenuTrigger>
79-
<DropdownMenuContent align="end">
80-
<DropdownMenuItem
81-
v-for="language in languages"
82-
:key="language.code"
83-
@click="switchLanguage(language.code)"
84-
:class="{ 'bg-accent': language.code === currentLanguage.code }"
85-
>
86-
{{ language.name }}
87-
</DropdownMenuItem>
88-
</DropdownMenuContent>
89-
</DropdownMenu>
60+
// Dynamic component mapping
61+
const componentMap = {
62+
dropdown: defineAsyncComponent(() => import('./LanguageSwitcherDropdown.vue')),
63+
select: defineAsyncComponent(() => import('./LanguageSwitcherSelect.vue')),
64+
cards: defineAsyncComponent(() => import('./LanguageSwitcherCards.vue')),
65+
};
9066
91-
<!-- Select Display -->
92-
<Select v-else-if="display === 'select'" v-model="locale">
93-
<SelectTrigger class="w-[180px]">
94-
<SelectValue class="text-foreground">
95-
<span class="flex items-center gap-2">
96-
<span class="text-foreground">{{ currentLanguage.name }}</span>
97-
</span>
98-
</SelectValue>
99-
</SelectTrigger>
100-
<SelectContent>
101-
<SelectItem v-for="language in languages" :key="language.code" :value="language.code">
102-
<span class="flex items-center gap-2">
103-
<span>{{ language.name }}</span>
104-
</span>
105-
</SelectItem>
106-
</SelectContent>
107-
</Select>
67+
const props = withDefaults(defineProps<Props>(), {
68+
display: 'dropdown',
69+
});
70+
71+
const currentComponent = computed(() => {
72+
return componentMap[props.display as keyof typeof componentMap] || componentMap.dropdown;
73+
});
74+
</script>
10875

109-
<!-- Card Display -->
110-
<Card v-else-if="display === 'cards'">
111-
<CardHeader>
112-
<CardTitle class="flex items-center gap-2">
113-
<Globe class="h-5 w-5" />
114-
{{ t('settings.language.title') }}
115-
</CardTitle>
116-
<CardDescription>
117-
{{ t('settings.language.description') }}
118-
</CardDescription>
119-
</CardHeader>
120-
<CardContent>
121-
<div class="grid gap-3">
122-
<Button
123-
v-for="language in languages"
124-
:key="language.code"
125-
@click="switchLanguage(language.code)"
126-
variant="outline"
127-
:class="['justify-start', language.code === currentLanguage.code ? 'border-primary bg-primary/5' : 'hover:bg-muted']"
128-
>
129-
<div class="flex w-full items-center justify-between">
130-
<span class="font-medium">{{ language.name }}</span>
131-
<span v-if="language.code === currentLanguage.code" class="text-xs text-muted-foreground">
132-
{{ t('settings.language.currentLanguage') }}
133-
</span>
134-
</div>
135-
</Button>
136-
</div>
137-
</CardContent>
138-
</Card>
76+
<template>
77+
<component :is="currentComponent" :languages="languages" :current-language="currentLanguage" :locale="locale" @switch-language="switchLanguage" />
13978
</template>
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<script setup lang="ts">
2+
import { Button } from '@/components/ui/button';
3+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
4+
import { Globe } from 'lucide-vue-next';
5+
import { useI18n } from 'vue-i18n';
6+
7+
interface Props {
8+
languages: Array<{ code: string; name: string }>;
9+
currentLanguage: { code: string; name: string };
10+
locale: string;
11+
}
12+
13+
defineProps<Props>();
14+
15+
const emit = defineEmits<{
16+
switchLanguage: [langCode: string];
17+
}>();
18+
19+
const { t } = useI18n();
20+
21+
const handleLanguageSwitch = (langCode: string) => {
22+
emit('switchLanguage', langCode);
23+
};
24+
</script>
25+
26+
<template>
27+
<Card>
28+
<CardHeader>
29+
<CardTitle class="flex items-center gap-2">
30+
<Globe class="h-5 w-5" />
31+
{{ t('settings.language.title') }}
32+
</CardTitle>
33+
<CardDescription>
34+
{{ t('settings.language.description') }}
35+
</CardDescription>
36+
</CardHeader>
37+
<CardContent>
38+
<div class="grid gap-3">
39+
<Button
40+
v-for="language in languages"
41+
:key="language.code"
42+
@click="handleLanguageSwitch(language.code)"
43+
variant="outline"
44+
:class="['justify-start', language.code === currentLanguage.code ? 'border-primary bg-primary/5' : 'hover:bg-muted']"
45+
>
46+
<div class="flex w-full items-center justify-between">
47+
<span class="font-medium">{{ language.name }}</span>
48+
<span v-if="language.code === currentLanguage.code" class="text-xs text-muted-foreground">
49+
{{ t('settings.language.currentLanguage') }}
50+
</span>
51+
</div>
52+
</Button>
53+
</div>
54+
</CardContent>
55+
</Card>
56+
</template>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<script setup lang="ts">
2+
import { Button } from '@/components/ui/button';
3+
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu';
4+
import { Globe } from 'lucide-vue-next';
5+
import { useI18n } from 'vue-i18n';
6+
7+
interface Props {
8+
languages: Array<{ code: string; name: string }>;
9+
currentLanguage: { code: string; name: string };
10+
locale: string;
11+
}
12+
13+
defineProps<Props>();
14+
15+
const emit = defineEmits<{
16+
switchLanguage: [langCode: string];
17+
}>();
18+
19+
const { t } = useI18n();
20+
21+
const handleLanguageSwitch = (langCode: string) => {
22+
emit('switchLanguage', langCode);
23+
};
24+
</script>
25+
26+
<template>
27+
<DropdownMenu>
28+
<DropdownMenuTrigger as-child>
29+
<Button variant="ghost" size="icon" class="h-8 w-8 p-0">
30+
<Globe class="h-4 w-4 text-foreground" />
31+
<span class="sr-only">{{ t('common.switchLanguage') }}</span>
32+
</Button>
33+
</DropdownMenuTrigger>
34+
<DropdownMenuContent align="end">
35+
<DropdownMenuItem
36+
v-for="language in languages"
37+
:key="language.code"
38+
@click="handleLanguageSwitch(language.code)"
39+
:class="{ 'bg-accent': language.code === currentLanguage.code }"
40+
>
41+
{{ language.name }}
42+
</DropdownMenuItem>
43+
</DropdownMenuContent>
44+
</DropdownMenu>
45+
</template>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<script setup lang="ts">
2+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
3+
4+
interface Props {
5+
languages: Array<{ code: string; name: string }>;
6+
currentLanguage: { code: string; name: string };
7+
locale: string;
8+
}
9+
10+
defineProps<Props>();
11+
12+
const emit = defineEmits<{
13+
'update:locale': [value: string];
14+
switchLanguage: [langCode: string];
15+
}>();
16+
17+
const handleLanguageChange = (langCode: string) => {
18+
emit('update:locale', langCode);
19+
emit('switchLanguage', langCode);
20+
};
21+
</script>
22+
23+
<template>
24+
<Select :model-value="locale" @update:model-value="handleLanguageChange">
25+
<SelectTrigger class="w-[180px]">
26+
<SelectValue class="text-foreground">
27+
<span class="flex items-center gap-2">
28+
<span class="text-foreground">{{ currentLanguage.name }}</span>
29+
</span>
30+
</SelectValue>
31+
</SelectTrigger>
32+
<SelectContent>
33+
<SelectItem v-for="language in languages" :key="language.code" :value="language.code">
34+
<span class="flex items-center gap-2">
35+
<span>{{ language.name }}</span>
36+
</span>
37+
</SelectItem>
38+
</SelectContent>
39+
</Select>
40+
</template>

0 commit comments

Comments
 (0)