feat: integrate animate.css for animations, enhance layout, and improve localization files

This commit is contained in:
yuanhau 2025-05-06 19:31:53 +08:00
parent 95661e908a
commit 2f20d49091
21 changed files with 252 additions and 172 deletions

View file

@ -1,11 +1,11 @@
<script setup lang="ts">
import "bootstrap-icons/font/bootstrap-icons.css";
import "animate.css";
const { t } = useI18n();
</script>
<template>
<!--A yhw_tw project-->
<NuxtLayout>
<NuxtPage/>
<NuxtPage />
</NuxtLayout>
</template>

View file

@ -12,6 +12,7 @@
"@nuxtjs/tailwindcss": "6.14.0",
"@tailwindcss/vite": "^4.1.5",
"@uploadthing/nuxt": "^7.1.7",
"animate.css": "^4.1.1",
"bootstrap-icons": "^1.12.1",
"gsap": "^3.13.0",
"nuxt": "^3.17.2",
@ -605,6 +606,8 @@
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
"animate.css": ["animate.css@4.1.1", "", {}, "sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ=="],
"ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],

View file

@ -1,3 +1,9 @@
<template>
<div
class="flex flex-row justify-center align-center text-center absolute inset-x-0"
>
<span class="text-xs m-4"
>{{ new Date().getFullYear() }} &copy; 吳元皓</span
>
</div>
</template>

View file

@ -1,62 +1,91 @@
<script setup lang="ts">
import { gsap } from 'gsap';
import { ScrambleTextPlugin } from 'gsap/dist/ScrambleTextPlugin';
gsap.registerPlugin(ScrambleTextPlugin)
import { gsap } from "gsap";
import { ScrambleTextPlugin } from "gsap/dist/ScrambleTextPlugin";
gsap.registerPlugin(ScrambleTextPlugin);
const { t, locale, locales } = useI18n();
const switchLocalePath = useSwitchLocalePath()
const switchLocalePath = useSwitchLocalePath();
const dropdownOpen = ref(false);
const title = ref(null);
const availableLocales = computed(() => {
return locales.value.filter(i => i.code !== locale.value)
})
return locales.value.filter((i) => i.code !== locale.value);
});
const toggleDropdown = () => {
dropdownOpen.value = !dropdownOpen.value;
}
};
const runAnimation = () => {
gsap.to(title.value, {
duration: 1,
scrambleText: "BlindSpec",
});
}
});
};
</script>
<template>
<!--Spent too much time trying to set a Navbar....-->
<div class="fixed top-0 inset-x-0 bg-[#81611a]/70 backdrop-blur-sm h-[55px] flex align-center flex-row text-white pl-4 pt-2 gap-x-5 justify-between z-50 rounded-3xl m-2">
<div
class="fixed top-0 inset-x-0 bg-[#81611a]/70 backdrop-blur-sm h-[55px] flex align-center flex-row text-white pl-4 pt-2 gap-x-5 justify-between z-50 rounded-3xl m-2"
>
<div class="text-3xl text-bold">
<!--Use mouseenter instead of hover-->
<a href="/" @mouseenter="runAnimation" ref="title">
BlindSpec
</a>
<a href="/" @mouseenter="runAnimation" ref="title"> BlindSpec </a>
</div>
<div class="text-[0.9em] text-center align-center">
<a href="/" class="hover:text-blue-500 cursor-pointer transiton-all duration-100">{{ t("nav.home") }}</a>
<a
href="/"
class="hover:text-blue-500 cursor-pointer transiton-all duration-100"
>{{ t("nav.home") }}</a
>
&nbsp;
<a href="/dailybriefing" class="hover:text-blue-500 cursor-pointer transiton-all duration-100">{{ t("nav.dailybriefing") }}</a>
<a
href="/dailybriefing"
class="hover:text-blue-500 cursor-pointer transiton-all duration-100"
>{{ t("nav.dailybriefing") }}</a
>
</div>
<div class="relative">
<button @click="toggleDropdown"
class="flex items-center space-x-2 px-4 py-2 rounded hover:bg-gray-900 transition-all duration-100">
<button
@click="toggleDropdown"
class="flex items-center space-x-2 px-4 py-2 rounded hover:bg-gray-900 transition-all duration-100"
>
<span>{{ locale }}</span>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 9l-7 7-7-7"/>
<svg
class="w-4 h-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 9l-7 7-7-7"
/>
</svg>
</button>
<div v-if="dropdownOpen"
class="absolute top-full left-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1">
<a v-for="loc in availableLocales"
<Transition
enter-active-class="animate__animated animate__fadeInDown animate__faster"
leave-active-class="animate__animated animate__fadeOutUp animate__faster"
>
<div
v-if="dropdownOpen"
class="absolute top-full right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1"
>
<a
v-for="loc in availableLocales"
:key="loc.code"
:href="switchLocalePath(loc.code)"
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
v-on:click="dropdownOpen = false"
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 transition-all duration-100"
>
{{ loc.name || loc.code }}
</a>
</div>
</Transition>
</div>
</div>
</template>

4
i18n/i18n.config.ts Normal file
View file

@ -0,0 +1,4 @@
export default defineI18nConfig(() => ({
legacy: false,
locale: "en",
}));

View file

@ -3,7 +3,8 @@
"sitename": "BlindSpec"
},
"nav": {
"home": "Home"
"home": "Home",
"dailybriefing": "Daily Briefing"
},
"dailybriefing": "Daily Briefing",
"Welcome": "Welcome",

View file

@ -1,4 +0,0 @@
export default defineI18nConfig(() => ({
legacy: false,
locale: 'en',
}))

View file

@ -3,7 +3,8 @@
"sitename": "新聞盲點平台"
},
"nav": {
"home": "首頁"
"home": "首頁",
"dailybriefing": "今日報導"
},
"dailybriefing": "今日報導",
"Welcome": "歡迎",

View file

@ -1,15 +1,15 @@
<script setup lang="ts">
import Navigation from '~/components/navigation.vue';
import Navigation from "~/components/navigation.vue";
import Footer from "~/components/footer.vue";
</script>
<template>
<nav>
<Navigation/>
<Navigation />
</nav>
<main class="pt-[60px]">
<slot/>
<slot />
</main>
<footer>
<Footer/>
<Footer />
</footer>
</template>

View file

@ -1,15 +1,15 @@
<script setup lang="ts">
import Navigation from '~/components/navigation.vue';
import Navigation from "~/components/navigation.vue";
import Footer from "~/components/footer.vue";
</script>
<template>
<nav>
<Navigation/>
<Navigation />
</nav>
<main class="pt-[60px]">
<slot/>
<slot />
</main>
<footer>
<Footer/>
<Footer />
</footer>
</template>

View file

@ -1,19 +1,25 @@
export default defineNuxtConfig({
compatibilityDate: '2025-05-06',
compatibilityDate: "2025-05-06",
devtools: { enabled: true },
routeRules: {
"/": { redirect: "/home"},
"/zh_tw": { redirect: "/zh_tw/home"},
} ,
css: ['~/styles/main.css'],
modules: ['@nuxt/image', '@nuxtjs/robots', '@nuxtjs/seo', '@nuxtjs/i18n', "@nuxtjs/tailwindcss"],
"/": { redirect: "/home" },
"/zh_tw": { redirect: "/zh_tw/home" },
},
css: ["~/styles/main.css"],
modules: [
"@nuxt/image",
"@nuxtjs/robots",
"@nuxtjs/seo",
"@nuxtjs/i18n",
"@nuxtjs/tailwindcss",
],
i18n: {
defaultLocale: 'en',
vueI18n: './i18n.config.ts',
defaultLocale: "en",
vueI18n: "./i18n.config.ts",
locales: [
{ code: 'en', name: 'English', file: 'en.json' },
{ code: 'zh_tw', name: 'Chinese Tradional', file: 'zh-tw.json' },
]
{ code: "en", name: "English", file: "en.json" },
{ code: "zh_tw", name: "Chinese Tradional", file: "zh-tw.json" },
],
},
site: {
url: "https://news.yuanhau.com",
@ -28,14 +34,17 @@ export default defineNuxtConfig({
{ charset: "utf-8" },
{ name: "viewport", content: "width=device-width, initial-scale=1" },
{ name: "og:author", content: "@hpware on GitHub" },
{ name: "og:author:email", content: "public+newscompareauthor@yuanhau.com" },
]
}
{
name: "og:author:email",
content: "public+newscompareauthor@yuanhau.com",
},
],
},
},
postcss: {
plugins: {
tailwindcss: {},
autoprefixer: {},
},// Add your content paths here
}, // Add your content paths here
},
})
});

View file

@ -19,6 +19,7 @@
"@nuxtjs/tailwindcss": "6.14.0",
"@tailwindcss/vite": "^4.1.5",
"@uploadthing/nuxt": "^7.1.7",
"animate.css": "^4.1.1",
"bootstrap-icons": "^1.12.1",
"gsap": "^3.13.0",
"nuxt": "^3.17.2",

View file

@ -1,7 +1,5 @@
<template>
<div>
<h1 class="text-3xl font-bold text-center">
</h1>
<h1 class="text-3xl font-bold text-center"></h1>
</div>
</template>

View file

@ -1,7 +1,7 @@
<script setup lang="ts">
import { gsap } from 'gsap';
import { ScrambleTextPlugin } from 'gsap/dist/ScrambleTextPlugin';
gsap.registerPlugin(ScrambleTextPlugin)
import { gsap } from "gsap";
import { ScrambleTextPlugin } from "gsap/dist/ScrambleTextPlugin";
gsap.registerPlugin(ScrambleTextPlugin);
const loading = ref(true);
const route = useRoute();
const slug = route.params.slug;
@ -11,7 +11,11 @@ definePageMeta({
layout: "newsorg",
});
const { data: fetchNewsOrgInfo, pending, error } = useFetch("/api/getData/fetchNewsOrgInfo", {
const {
data: fetchNewsOrgInfo,
pending,
error,
} = useFetch("/api/getData/fetchNewsOrgInfo", {
method: "POST",
headers: {
"Content-Type": "application/json",
@ -35,7 +39,7 @@ onMounted(() => {
gsap.to(orgNameAnimation.value, {
duration: 1,
scrambleText: fetchNewsOrgInfo.value?.title,
});
});
});
// Import Icons
@ -44,16 +48,32 @@ import { GlobeAltIcon } from "@heroicons/vue/24/outline";
<template>
<div>
<div class="text-center align-center justify-center">
<div class="flex flex-row bg-[#AAACAAFF] rounded-3xl p-3 gap-3 m-3">
<NuxtImg :src="fetchNewsOrgInfo?.logoUrl" class="w-48 h-48 rounded-[10px]"/>
<div
class="flex flex-row bg-[#AAACAAFF] rounded-3xl p-3 gap-3 m-3 scale-5"
>
<NuxtImg
:src="fetchNewsOrgInfo?.logoUrl"
class="w-48 h-48 rounded-[10px]"
/>
<div class="flex flex-col gap-3 text-left">
<h1 class="text-4xl font-bold m-3 text-left">{{ fetchNewsOrgInfo?.title }}</h1>
<span class="text-ms m-1 mt-5 text-left text-wrap">{{ fetchNewsOrgInfo?.description }}</span>
<h1 class="text-4xl font-bold m-3 text-left">
{{ fetchNewsOrgInfo?.title }}
</h1>
<span class="text-ms m-1 mt-5 text-left text-wrap">{{
fetchNewsOrgInfo?.description
}}</span>
</div>
</div>
<div class="gap-[3px] flex flex-row text-center align-center justify-center">
<a :href="fetchNewsOrgInfo?.website" target="_blank" class="text-blue-200 hover:text-blue-300 transiton-all duration-100 flex flex-row"><GlobeAltIcon class="w-6 h-6" />網站</a>
<div
class="gap-[3px] flex flex-row text-center align-center justify-center"
>
<a
:href="fetchNewsOrgInfo?.website"
target="_blank"
class="text-blue-200 hover:text-blue-300 transiton-all duration-100 flex flex-row"
><GlobeAltIcon class="w-6 h-6" />網站</a
>
</div>
</div>
</div>

View file

@ -1,4 +1,4 @@
export default defineEventHandler(async(event) => {
export default defineEventHandler(async (event) => {
const body = await readBody(event);
return {
body: body,
@ -7,8 +7,7 @@ export default defineEventHandler(async(event) => {
website: "https://www.taisounds.com.tw",
description: "wah wah wah wah wah wah I dont fucking care",
facebook: "https://www.facebook.com/taisounds",
logoUrl: "https://cdn.discordapp.com/avatars/918723093646684180/4eecc27ac05ee8a701fa167808610c7a.jpg",
}
})
logoUrl:
"https://cdn.discordapp.com/avatars/918723093646684180/4eecc27ac05ee8a701fa167808610c7a.jpg",
};
});

View file

@ -2,5 +2,9 @@ import s3 from "~/server/components/s3";
export default defineEventHandler(async (event) => {
const slug = getRouterParam(event, "slug");
return sendRedirect(event, `${process.env.S3_ENDPOINT}/${process.env.S3_BUCKETNAME}/${slug}`, 302);
})
return sendRedirect(
event,
`${process.env.S3_ENDPOINT}/${process.env.S3_BUCKETNAME}/${slug}`,
302,
);
});

View file

@ -1,3 +1 @@
export default defineEventHandler(async () => {
});
export default defineEventHandler(async () => {});

View file

@ -9,4 +9,4 @@
body {
@apply bg-black m-0 p-0 min-h-screen text-white;
}
}
}

View file

@ -1,4 +1,15 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
}
content: [],
theme: {
extend: {
backgroundColor: {
page: "#000000",
},
},
},
plugins: [],
corePlugins: {
preflight: true,
},
};