Setup basic structure
This commit is contained in:
+5
-5
@@ -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>
|
||||
|
||||
Generated
+70
@@ -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",
|
||||
|
||||
@@ -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
@@ -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));
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
darkMode: 'class',
|
||||
content: ['./index.html', './src/**/*.{vue,js,ts}'],
|
||||
theme: {
|
||||
extend: {
|
||||
|
||||
Reference in New Issue
Block a user