feat: setup unplugin-vue-router, fixed eslnt config, SmartLink for type-safe internal/external links

This commit is contained in:
Benjamin Singleton 2026-02-12 01:18:14 -06:00
parent 3c0e46c9ab
commit 60ce6d75f7
15 changed files with 435 additions and 311 deletions

View file

@ -4,6 +4,8 @@ import { ref, type Ref } from "vue";
import LocalePicker from "./components/organisms/LocalePicker.vue";
import { vOnClickOutside } from "@vueuse/components";
import { useLocale } from "./i18n";
import { RouterLink, useRouter } from "vue-router";
import SmartLink from "./components/organisms/SmartLink.vue";
const burgerOpen: Ref<boolean> = ref<boolean>(false);
@ -16,6 +18,11 @@ const closeBurger = (): void => {
};
const locale = useLocale();
const router = useRouter();
router.beforeEach(() => {
closeBurger();
});
</script>
<template>
@ -44,21 +51,15 @@ const locale = useLocale();
<div :class="`navbar-menu ${burgerOpen ? 'is-active' : ''}`">
<div class="navbar-start">
<RouterLink
class="navbar-item"
to="/"
@click="closeBurger()"
>{{ locale.navbar.whatIsViossa }}</RouterLink
>
<RouterLink
class="navbar-item"
to="/resources"
@click="closeBurger()"
>{{ locale.navbar.resources }}</RouterLink
>
<RouterLink class="navbar-item" to="/kotoba">
<SmartLink class="navbar-item" internal to="/">{{
locale.navbar.whatIsViossa
}}</SmartLink>
<SmartLink class="navbar-item" internal to="/resources">{{
locale.navbar.resources
}}</SmartLink>
<SmartLink class="navbar-item" internal to="/kotoba">
{{ locale.navbar.kotoba }}
</RouterLink>
</SmartLink>
<LocalePicker class="navbar-item" />
</div>
</div>

View file

@ -1,53 +1,54 @@
<template>
<div class="box columns is-vcentered is-gap-4">
<div class="column is-one-quarter" v-if="image">
<figure class="image">
<img :src="image" :alt="alt" :title="alt" />
</figure>
</div>
<div class="column">
<h4 class="title">{{ title }}</h4>
<p class="subtitle">{{ subtitle }}</p>
<p class="content">{{ desc }}</p>
<div class="box columns is-vcentered is-gap-4">
<div class="column is-one-quarter" v-if="image">
<figure class="image">
<img :src="image" :alt="alt" :title="alt" />
</figure>
</div>
<div class="column">
<h4 class="title">{{ title }}</h4>
<p class="subtitle">{{ subtitle }}</p>
<p class="content">{{ desc }}</p>
<div class="level">
<a :href="link"
target="_blank"
rel="noopener noreferrer nofollow"
class="button is-primary is-medium"
:text="joinText">
</a>
<div class="level">
<a
:href="link"
target="_blank"
rel="noopener noreferrer nofollow"
class="button is-primary is-medium"
>{{ joinText }}</a
>
<a :href="``"
target="_blank"
rel="noopener noreferrer nofollow"
class="button is-info is-outlined is-medium"
:text="rulesText">
</a>
<a
:href="``"
target="_blank"
rel="noopener noreferrer nofollow"
class="button is-info is-outlined is-medium"
>{{ rulesText }}</a
>
<a :href="rulesLink"
target="_blank"
rel="noopener noreferrer nofollow"
class="button is-warning is-outlined is-medium"
:text="rulesText">
</a>
</div>
</div>
</div>
<a
:href="rulesLink"
target="_blank"
rel="noopener noreferrer nofollow"
class="button is-warning is-outlined is-medium"
>{{ rulesText }}</a
>
</div>
</div>
</div>
</template>
<script setup lang="ts">
defineProps({
title: String,
subtitle: String,
desc: String,
link: String,
rulesLink: String,
joinText: String,
rulesText: String,
image: { type: String, required: false },
alt: { type: String, required: false },
})
</script>
title: String,
subtitle: String,
desc: String,
link: String,
rulesLink: String,
joinText: String,
rulesText: String,
image: { type: String, required: false },
alt: { type: String, required: false },
});
</script>

View file

@ -0,0 +1,18 @@
<script setup lang="ts">
import type { RouterLinkProps } from "vue-router";
import type { RouteNamedMap } from "vue-router/auto-routes";
defineProps<
Omit<RouterLinkProps, "to">
& (
| { internal: true; to: keyof RouteNamedMap }
| { external: true; to: `https://${string}` | `http://${string}` }
)
>();
</script>
<template>
<RouterLink v-bind="$props">
<slot />
</RouterLink>
</template>

View file

@ -1,5 +1,5 @@
import { createApp } from "vue";
import App from "./App.vue";
import router from "./routes";
import router from "./router";
createApp(App).use(router).mount("#app");

View file

@ -1,10 +1,9 @@
<script setup lang="ts">
import { useLocale } from '@/i18n';
import { useLocale } from "@/i18n";
const locale = useLocale();
</script>
<template>
<div>
<section class="section">
@ -17,14 +16,10 @@ const locale = useLocale();
</div>
<div class="block is-flex is-flex-direction-row is-gap-2">
<input class="input" type="text"></input>
</div>
<div class="container">
<input class="input" type="text" />
</div>
<div class="container"></div>
</section>
</div>
</template>

View file

@ -0,0 +1,10 @@
import { createRouter, createWebHistory } from "vue-router";
import { routes, handleHotUpdate } from "vue-router/auto-routes";
const router = createRouter({ history: createWebHistory(), routes });
if (import.meta.hot) {
handleHotUpdate(router);
}
export default router;

View file

@ -1,21 +0,0 @@
import { createRouter, createWebHistory } from "vue-router";
import type { RouteRecordRaw } from "vue-router";
import HomePage from "@/components/pages/HomePage.vue";
import ResourcesPage from "@/components/pages/ResourcesPage.vue";
import KotobaPage from "@/components/pages/KotobaPage.vue";
const routes: RouteRecordRaw[] = [
{ path: "/", name: "Home", component: HomePage },
{ path: "/resources", name: "Resources", component: ResourcesPage },
{ path: "/kotoba", name: "Kotoba", component: KotobaPage },
// {
// path: '/:pathMatch(.*)*', // Vue Router 4 catch-all for 404s
// name: 'NotFound',
// component: NotFoundPage,
// },
];
const router = createRouter({ history: createWebHistory(), routes });
export default router;

View file

@ -18,5 +18,8 @@ declare module 'vue-router/auto-routes' {
* Route name map generated by unplugin-vue-router
*/
export interface RouteNamedMap {
'/': RouteRecordInfo<'/', '/', Record<never, never>, Record<never, never>>,
'/kotoba': RouteRecordInfo<'/kotoba', '/kotoba', Record<never, never>, Record<never, never>>,
'/resources': RouteRecordInfo<'/resources', '/resources', Record<never, never>, Record<never, never>>,
}
}