Setup basic structure

This commit is contained in:
2024-05-26 19:15:17 +02:00
parent 6479807680
commit 7b344d4fa4
13 changed files with 364 additions and 28 deletions
+112 -22
View File
@@ -1,29 +1,119 @@
<script setup lang="ts">
import { RouterLink, RouterView } from 'vue-router';
import HelloWorld from './components/HelloWorld.vue';
import { RouterView, useRouter } from 'vue-router';
import { computed, ref } from 'vue';
import { useThemeStore } from '@/stores/theme';
import { useMenuStore } from '@/stores/menu';
import { SUPPORT_LOCALES } from '@/i18n';
const theme = useThemeStore();
const router = useRouter();
const activeRoute = computed({
get() {
return router.currentRoute.value.path;
},
async set(value) {
await router.push(value);
}
});
const menuStore = useMenuStore();
const locales = ref(SUPPORT_LOCALES);
</script>
<template>
<header>
<img
alt="Vue logo"
class="logo"
src="@/assets/logo.svg"
width="125"
height="125"
/>
<div class="wrapper">
<HelloWorld msg="You did it!" />
<nav>
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
</nav>
</div>
</header>
<RouterView />
<VaLayout
:top="{ fixed: true, order: 3 }"
:bottom="{ fixed: true, order: 4 }"
>
<template #top>
<VaNavbar color="primary" class="py-2">
<template #right>
<VaMenu :close-on-content-click="false">
<template #anchor>
<VaButton icon="menu" />
</template>
<VaMenuItem
v-if="menuStore.items.length > 0"
:icon="menuStore.items[0].icon"
:rightIcon="menuStore.items[0].rightIcon"
@selected="() => menuStore.click(0)"
>{{ menuStore.items[0].text }}</VaMenuItem
>
<VaMenuItem
v-if="menuStore.items.length > 1"
:icon="menuStore.items[1].icon"
:rightIcon="menuStore.items[1].rightIcon"
@selected="() => menuStore.click(1)"
>{{ menuStore.items[1].text }}</VaMenuItem
>
<VaMenuItem
v-if="menuStore.items.length > 2"
:icon="menuStore.items[2].icon"
:rightIcon="menuStore.items[2].rightIcon"
@selected="() => menuStore.click(2)"
>{{ menuStore.items[2].text }}</VaMenuItem
>
<VaMenuItem
v-if="menuStore.items.length > 3"
:icon="menuStore.items[3].icon"
:rightIcon="menuStore.items[3].rightIcon"
@selected="() => menuStore.click(3)"
>{{ menuStore.items[3].text }}</VaMenuItem
>
<VaMenuItem
v-if="menuStore.items.length > 4"
:icon="menuStore.items[4].icon"
:rightIcon="menuStore.items[4].rightIcon"
@selected="() => menuStore.click(4)"
>{{ menuStore.items[4].text }}</VaMenuItem
>
<VaDivider />
<VaMenuItem>
<VaSwitch
v-model="theme.mode"
true-value="dark"
false-value="light"
label="Toggle dark mode"
size="small"
/></VaMenuItem>
<VaMenuItem
><VaSelect
class="w-full"
v-model="theme.lang"
label="Language"
:options="locales"
value-by="code"
track-by="code"
:text-by="(l) => $t(`locales.${(l as any)?.code || 'en'}`)"
placeholder="Select an option"
/></VaMenuItem>
</VaMenu>
</template>
<template #left>
<VaNavbarItem class="text-lg font-bold"> LOGO </VaNavbarItem>
</template>
</VaNavbar>
</template>
<template #bottom>
<VaTabs
grow
class="bg-gray-200 dark:bg-gray-700"
hidePagination
style="--va-tabs-container-height: 3rem"
v-model="activeRoute"
>
<template #tabs>
<VaTab name="/"><VaIcon name="house" />Home</VaTab>
<VaTab name="/map"><VaIcon name="house" />Home</VaTab>
<VaTab name="/filter"><VaIcon name="house" />filter</VaTab>
<VaTab name="/about"><VaIcon name="house" />Home</VaTab>
</template>
</VaTabs>
</template>
<template #content>
<RouterView />
</template>
</VaLayout>
</template>
<style scoped></style>
+5
View File
@@ -4,3 +4,8 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
background-color: var(--va-background-primary);
color: var(--va-text-primary);
}
+39
View File
@@ -5,6 +5,21 @@ import ToolingIcon from './icons/IconTooling.vue';
import EcosystemIcon from './icons/IconEcosystem.vue';
import CommunityIcon from './icons/IconCommunity.vue';
import SupportIcon from './icons/IconSupport.vue';
import { useAdditionalMenu } from '@/stores/menu';
import { computed, ref } from 'vue';
const toggle = ref(false);
const menu = computed(() => {
return [
{
text: toggle.value ? 'option 1 select' : 'option 1 unselect',
onClick: () => (toggle.value = !toggle.value)
},
{ text: 'option 2' }
];
});
useAdditionalMenu(menu);
</script>
<template>
@@ -118,4 +133,28 @@ import SupportIcon from './icons/IconSupport.vue';
>becoming a sponsor</a
>.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<SupportIcon />
</template>
<template #heading>Support Vue</template>
As an independent project, Vue relies on community backing for its
sustainability. You can help us by
<a href="https://vuejs.org/sponsor/" target="_blank" rel="noopener"
>becoming a sponsor</a
>.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<SupportIcon />
</template>
<template #heading>Support Vue</template>
As an independent project, Vue relies on community backing for its
sustainability. You can help us by
<a href="https://vuejs.org/sponsor/" target="_blank" rel="noopener"
>becoming a sponsor</a
>.
</WelcomeItem>
</template>
+16
View File
@@ -0,0 +1,16 @@
import { createI18n } from 'vue-i18n';
import en from './locales/en.json';
import de from './locales/de.json';
export const SUPPORT_LOCALES = [{ code: 'en' }, { code: 'de' }];
export const i18n = createI18n({
legacy: false,
locale: 'en',
fallbackLocale: 'en',
messages: {
en,
de
},
availableLocales: SUPPORT_LOCALES.map((l) => l.code)
});
+7
View File
@@ -0,0 +1,7 @@
{
"text": "Hallo",
"locales": {
"en": "Englisch",
"de": "Deutsch"
}
}
+7
View File
@@ -0,0 +1,7 @@
{
"text": "Hello",
"locales": {
"en": "English",
"de": "German"
}
}
+8 -1
View File
@@ -2,11 +2,18 @@ import './assets/main.css';
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
import App from './App.vue';
import router from './router';
import { createVuestic } from 'vuestic-ui';
import { i18n } from '@/i18n';
const app = createApp(App);
app.use(createPinia()).use(router).use(createVuestic()).mount('#app');
app
.use(i18n)
.use(createPinia().use(piniaPluginPersistedstate))
.use(router)
.use(createVuestic())
.mount('#app');
+47
View File
@@ -0,0 +1,47 @@
import { acceptHMRUpdate, defineStore } from 'pinia';
import { onMounted, onUnmounted, type Ref, ref, toRef, watch } from 'vue';
type MenuOption = {
icon?: string;
rightIcon?: string;
text: string;
onClick?: () => void;
};
export const useMenuStore = defineStore(
'menu',
() => {
const items = ref<Array<MenuOption>>([]);
function click(index: number) {
if (items.value.length <= index) {
return;
}
console.log('menu', `selected ${index}`, items.value[index]);
items.value[index].onClick?.();
}
return { items, click };
},
{ persist: false }
);
export function useAdditionalMenu(
items: Ref<Array<MenuOption>> | Array<MenuOption>
) {
const store = useMenuStore();
onMounted(() => {
store.items.splice(0);
store.items.push(...toRef(items).value);
});
onUnmounted(() => {
store.items.splice(0);
});
watch(toRef(items), (newItems) => {
store.items.splice(0);
store.items.push(...newItems);
});
}
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useMenuStore, import.meta.hot));
}
+45
View File
@@ -0,0 +1,45 @@
import { ref, watch } from 'vue';
import { acceptHMRUpdate, defineStore } from 'pinia';
import { useColors } from 'vuestic-ui';
import { i18n, SUPPORT_LOCALES } from '@/i18n';
export const useThemeStore = defineStore(
'theme',
() => {
const { applyPreset, currentPresetName } = useColors();
const mode = ref(currentPresetName.value);
const h = document.querySelector('html');
watch(mode, (newMode, oldMode) => {
applyPreset(newMode);
console.log(newMode);
if (h?.classList.replace(oldMode, newMode) === false) {
h?.classList.add(newMode);
}
});
console.log(navigator.language);
const lang = ref(
SUPPORT_LOCALES.find((l) =>
navigator.language.toLowerCase().startsWith(l.code)
)?.code || SUPPORT_LOCALES[0].code
);
watch(lang, (l) => {
console.log(l);
i18n.global.locale.value = l as any;
document.querySelector('html')?.setAttribute('lang', l);
});
return { mode, lang };
},
{
persist: {
afterRestore: (ctx) => {
i18n.global.locale.value = ctx.store.lang as any;
document.querySelector('html')?.setAttribute('lang', ctx.store.lang);
}
}
}
);
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useThemeStore, import.meta.hot));
}