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
+5 -5
View File
@@ -1,10 +1,10 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>OSM Filter</title>
</head>
<body>
<div id="app"></div>
+70
View File
@@ -10,7 +10,9 @@
"dependencies": {
"@vuestic/tailwind": "^0.1.5",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"vue": "^3.4.21",
"vue-i18n": "^9.13.1",
"vue-router": "^4.3.0",
"vuestic-ui": "^1.9.9"
},
@@ -615,6 +617,47 @@
"integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
"dev": true
},
"node_modules/@intlify/core-base": {
"version": "9.13.1",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.13.1.tgz",
"integrity": "sha512-+bcQRkJO9pcX8d0gel9ZNfrzU22sZFSA0WVhfXrf5jdJOS24a+Bp8pozuS9sBI9Hk/tGz83pgKfmqcn/Ci7/8w==",
"dependencies": {
"@intlify/message-compiler": "9.13.1",
"@intlify/shared": "9.13.1"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@intlify/message-compiler": {
"version": "9.13.1",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.13.1.tgz",
"integrity": "sha512-SKsVa4ajYGBVm7sHMXd5qX70O2XXjm55zdZB3VeMFCvQyvLew/dLvq3MqnaIsTMF1VkkOb9Ttr6tHcMlyPDL9w==",
"dependencies": {
"@intlify/shared": "9.13.1",
"source-map-js": "^1.0.2"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@intlify/shared": {
"version": "9.13.1",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.13.1.tgz",
"integrity": "sha512-u3b6BKGhE6j/JeRU6C/RL2FgyJfy6LakbtfeVF8fJXURpZZTzfh3e05J0bu0XPw447Q6/WUp3C4ajv4TMS4YsQ==",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -3126,6 +3169,14 @@
}
}
},
"node_modules/pinia-plugin-persistedstate": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-3.2.1.tgz",
"integrity": "sha512-MK++8LRUsGF7r45PjBFES82ISnPzyO6IZx3CH5vyPseFLZCk1g2kgx6l/nW8pEBKxxd4do0P6bJw+mUSZIEZUQ==",
"peerDependencies": {
"pinia": "^2.0.0"
}
},
"node_modules/pinia/node_modules/vue-demi": {
"version": "0.14.7",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz",
@@ -4104,6 +4155,25 @@
"eslint": ">=6.0.0"
}
},
"node_modules/vue-i18n": {
"version": "9.13.1",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.13.1.tgz",
"integrity": "sha512-mh0GIxx0wPtPlcB1q4k277y0iKgo25xmDPWioVVYanjPufDBpvu5ySTjP5wOrSvlYQ2m1xI+CFhGdauv/61uQg==",
"dependencies": {
"@intlify/core-base": "9.13.1",
"@intlify/shared": "9.13.1",
"@vue/devtools-api": "^6.5.0"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
},
"peerDependencies": {
"vue": "^3.0.0"
}
},
"node_modules/vue-router": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.3.2.tgz",
+2
View File
@@ -16,7 +16,9 @@
"dependencies": {
"@vuestic/tailwind": "^0.1.5",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"vue": "^3.4.21",
"vue-i18n": "^9.13.1",
"vue-router": "^4.3.0",
"vuestic-ui": "^1.9.9"
},
+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));
}
+1
View File
@@ -1,5 +1,6 @@
/** @type {import('tailwindcss').Config} */
export default {
darkMode: 'class',
content: ['./index.html', './src/**/*.{vue,js,ts}'],
theme: {
extend: {