Setup basic structure
This commit is contained in:
+112
-22
@@ -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>
|
||||
|
||||
@@ -4,3 +4,8 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
body {
|
||||
background-color: var(--va-background-primary);
|
||||
color: var(--va-text-primary);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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)
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"text": "Hallo",
|
||||
"locales": {
|
||||
"en": "Englisch",
|
||||
"de": "Deutsch"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"text": "Hello",
|
||||
"locales": {
|
||||
"en": "English",
|
||||
"de": "German"
|
||||
}
|
||||
}
|
||||
+8
-1
@@ -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');
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
Reference in New Issue
Block a user