mirror of
https://github.com/NotXia/notxia.github.io.git
synced 2025-12-16 11:31:49 +01:00
Add about me section
This commit is contained in:
BIN
src/assets/images/moon.png
Normal file
BIN
src/assets/images/moon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 108 KiB |
BIN
src/assets/images/profile/picture-bright.png
Normal file
BIN
src/assets/images/profile/picture-bright.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 134 KiB |
BIN
src/assets/images/profile/picture-dark.png
Normal file
BIN
src/assets/images/profile/picture-dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 206 KiB |
BIN
src/assets/images/profile/picture-light.png
Normal file
BIN
src/assets/images/profile/picture-light.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 224 KiB |
BIN
src/assets/images/profile/picture-nolight.png
Normal file
BIN
src/assets/images/profile/picture-nolight.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 165 KiB |
BIN
src/assets/images/sun.png
Normal file
BIN
src/assets/images/sun.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 102 KiB |
@ -48,14 +48,6 @@
|
|||||||
border-radius: 0.2rem;
|
border-radius: 0.2rem;
|
||||||
border: 1px solid #424242;
|
border: 1px solid #424242;
|
||||||
}
|
}
|
||||||
.gr_grid_book_container:first-child {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0.5rem;
|
|
||||||
}
|
|
||||||
.gr_grid_book_container:last-child {
|
|
||||||
margin-left: 0.5rem;
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gr_grid_book_container > * img {
|
.gr_grid_book_container > * img {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
@ -11,6 +11,8 @@
|
|||||||
<CookieEgg v-if="easteregg === 'cookie'" />
|
<CookieEgg v-if="easteregg === 'cookie'" />
|
||||||
<FutureEgg v-if="easteregg === 'future'" />
|
<FutureEgg v-if="easteregg === 'future'" />
|
||||||
<SomethingEgg v-if="easteregg === 'change-something'" />
|
<SomethingEgg v-if="easteregg === 'change-something'" />
|
||||||
|
<PictureBrightEgg v-if="easteregg === 'picture-bright'" />
|
||||||
|
<PictureNoLightEgg v-if="easteregg === 'picture-nolights'" />
|
||||||
|
|
||||||
<div class="mt-1 text-center">
|
<div class="mt-1 text-center">
|
||||||
<p v-if="found_eastereggs != total_eastereggs">{{ found_eastereggs }}/{{ total_eastereggs }} {{ t("easter eggs found") }}</p>
|
<p v-if="found_eastereggs != total_eastereggs">{{ found_eastereggs }}/{{ total_eastereggs }} {{ t("easter eggs found") }}</p>
|
||||||
@ -31,6 +33,8 @@
|
|||||||
import CookieEgg from "./eggs/Cookie.vue";
|
import CookieEgg from "./eggs/Cookie.vue";
|
||||||
import FutureEgg from "./eggs/Future.vue";
|
import FutureEgg from "./eggs/Future.vue";
|
||||||
import SomethingEgg from "./eggs/Something.vue";
|
import SomethingEgg from "./eggs/Something.vue";
|
||||||
|
import PictureBrightEgg from "./eggs/PictureBright.vue";
|
||||||
|
import PictureNoLightEgg from "./eggs/PictureNoLight.vue";
|
||||||
|
|
||||||
const show_banner = ref(false);
|
const show_banner = ref(false);
|
||||||
const easteregg = ref("");
|
const easteregg = ref("");
|
||||||
@ -41,7 +45,7 @@
|
|||||||
const { t } = useI18n({ messages: {
|
const { t } = useI18n({ messages: {
|
||||||
"en": {
|
"en": {
|
||||||
"easter eggs found": "easter eggs found",
|
"easter eggs found": "easter eggs found",
|
||||||
"all easter eggs found": "You found all the easter eggs 🥚"
|
"all easter eggs found": "You found all easter eggs 🥚"
|
||||||
},
|
},
|
||||||
"it": {
|
"it": {
|
||||||
"easter eggs found": "easter egg trovati",
|
"easter eggs found": "easter egg trovati",
|
||||||
|
|||||||
40
src/components/easteregg-banner/eggs/PictureBright.vue
Normal file
40
src/components/easteregg-banner/eggs/PictureBright.vue
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
<div class="flex text-sm">
|
||||||
|
<div class="flex items-center justify-center">
|
||||||
|
<div class="w-10 h-10 flex items-center justify-center overflow-hidden">
|
||||||
|
<img :src="image" alt="" class="h-full w-full" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-1 ml-2">
|
||||||
|
<p class="font-bold text-base">{{ t("title") }}</p>
|
||||||
|
<p>{{ t("description") }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import image from "@/assets/images/sun.png";
|
||||||
|
import { getFoundEasterEggs } from "@/utilities/easteregg_handler";
|
||||||
|
|
||||||
|
let locale = {
|
||||||
|
"en": {
|
||||||
|
"title": "It's bright",
|
||||||
|
"description": "I don't like the light"
|
||||||
|
},
|
||||||
|
"it": {
|
||||||
|
"title": "È luminoso",
|
||||||
|
"description": "Non mi piace la luce"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (getFoundEasterEggs().includes("picture-nolights")) {
|
||||||
|
locale["en"]["description"] = "I don't like the light either"
|
||||||
|
locale["it"]["description"] = "Non mi piace nemmeno la luce"
|
||||||
|
}
|
||||||
|
|
||||||
|
const { t } = useI18n({ messages: locale });
|
||||||
|
</script>
|
||||||
40
src/components/easteregg-banner/eggs/PictureNoLight.vue
Normal file
40
src/components/easteregg-banner/eggs/PictureNoLight.vue
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
<div class="flex text-sm">
|
||||||
|
<div class="flex items-center justify-center">
|
||||||
|
<div class="w-10 h-10 flex items-center justify-center overflow-hidden">
|
||||||
|
<img :src="image" alt="" class="h-full w-full" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-1 ml-2">
|
||||||
|
<p class="font-bold text-base">{{ t("title") }}</p>
|
||||||
|
<p>{{ t("description") }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import image from "@/assets/images/moon.png";
|
||||||
|
import { getFoundEasterEggs } from "@/utilities/easteregg_handler";
|
||||||
|
|
||||||
|
let locale = {
|
||||||
|
"en": {
|
||||||
|
"title": "It's dark",
|
||||||
|
"description": "I don't like the dark"
|
||||||
|
},
|
||||||
|
"it": {
|
||||||
|
"title": "È buio",
|
||||||
|
"description": "Non mi piace il buio"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (getFoundEasterEggs().includes("picture-bright")) {
|
||||||
|
locale["en"]["description"] = "I don't like the dark either"
|
||||||
|
locale["it"]["description"] = "Non mi piace nemmeno il buio"
|
||||||
|
}
|
||||||
|
|
||||||
|
const { t } = useI18n({ messages: locale });
|
||||||
|
</script>
|
||||||
108
src/components/profile-picture/ProfilePicture.vue
Normal file
108
src/components/profile-picture/ProfilePicture.vue
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
<div class="relative">
|
||||||
|
<div class="flex items-center h-60 w-60">
|
||||||
|
<img v-show="picture === 'dark'" :src="picture_dark" alt="" class="max-h-full max-w-full">
|
||||||
|
<img v-show="picture === 'light'" :src="picture_light" alt="" class="max-h-full max-w-full">
|
||||||
|
<img v-show="picture === 'bright'" :src="picture_bright" alt="" class="max-h-full max-w-full">
|
||||||
|
<img v-show="picture === 'no light'" :src="picture_nolight" alt="" class="max-h-full max-w-full">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="message" class="absolute bottom-0 left-0 w-full">
|
||||||
|
<p class="w-fit mx-auto px-2 pt-1 mb-1 bg-gray-200 dark:bg-gray-700">{{ message }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { ref, onMounted, watch } from "vue";
|
||||||
|
import { addFoundEasterEgg } from "@/utilities/easteregg_handler";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
import picture_dark from "@/assets/images/profile/picture-dark.png";
|
||||||
|
import picture_light from "@/assets/images/profile/picture-light.png";
|
||||||
|
import picture_bright from "@/assets/images/profile/picture-bright.png";
|
||||||
|
import picture_nolight from "@/assets/images/profile/picture-nolight.png";
|
||||||
|
|
||||||
|
const { t, locale } = useI18n({ messages: {
|
||||||
|
"en": {
|
||||||
|
"that's bright": "That's bright",
|
||||||
|
"better": "Better",
|
||||||
|
"where lights": "Who turned off the lights?",
|
||||||
|
"here lights": "Here they are"
|
||||||
|
},
|
||||||
|
"it": {
|
||||||
|
"that's bright": "È luminoso",
|
||||||
|
"better": "Meglio",
|
||||||
|
"where lights": "Chi ha spento le luci?",
|
||||||
|
"here lights": "Eccole"
|
||||||
|
}
|
||||||
|
} });
|
||||||
|
|
||||||
|
const picture = ref(picture_dark);
|
||||||
|
const message = ref("picture_dark");
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
|
||||||
|
// Finite-state automata to handle theme changes
|
||||||
|
interface State {
|
||||||
|
image: string; // Image to display
|
||||||
|
message: string; // Message to display (string to parse with i18n)
|
||||||
|
expect: string; // Expected active theme when this state is applied
|
||||||
|
next: string; // Next state
|
||||||
|
}
|
||||||
|
|
||||||
|
let current_state = document.querySelector("html")?.classList.contains("dark") ? "dark1" : "light1";
|
||||||
|
let states:Record<string, State> = {
|
||||||
|
"dark1": { image: "dark", message: "", expect: "dark", next: "bright" },
|
||||||
|
"bright": { image: "bright", message: "that's bright", expect: "light", next: "dark2" },
|
||||||
|
"dark2": { image: "dark", message: "better", expect: "dark", next: "light_final" },
|
||||||
|
|
||||||
|
"light1": { image: "light", message: "", expect: "light", next: "nolights" },
|
||||||
|
"nolights": { image: "no light", message: "where lights", expect: "dark", next: "light2" },
|
||||||
|
"light2": { image: "light", message: "here lights", expect: "light", next: "dark_final" },
|
||||||
|
|
||||||
|
"dark_final": { image: "dark", message: "", expect: "dark", next: "light_final" },
|
||||||
|
"light_final": { image: "light", message: "", expect: "light", next: "dark_final" },
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function applyState() {
|
||||||
|
picture.value = states[current_state]?.image;
|
||||||
|
message.value = t(states[current_state]?.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextState() {
|
||||||
|
current_state = states[current_state]?.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
applyState();
|
||||||
|
|
||||||
|
let observer = new MutationObserver(function(mutations) {
|
||||||
|
if (route.path !== "/about") { observer.disconnect(); return; } // Disconnect observer if on other routes
|
||||||
|
|
||||||
|
const is_dark_theme = document.querySelector("html")?.classList.contains("dark");
|
||||||
|
|
||||||
|
if ( states[states[current_state].next].expect === (is_dark_theme ? "dark" : "light") ) {
|
||||||
|
nextState();
|
||||||
|
applyState();
|
||||||
|
|
||||||
|
if (current_state === "bright") { addFoundEasterEgg("picture-bright"); }
|
||||||
|
else if (current_state === "nolights") { addFoundEasterEgg("picture-nolights"); }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Observes for theme changes
|
||||||
|
observer.observe((document.querySelector("html") as Node), { attributes: true, attributeFilter: ['class'] });
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(locale, () => { // To update the language of the displayed message
|
||||||
|
applyState();
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
@ -1,4 +1,4 @@
|
|||||||
const EASTER_EGGS = ["cookie", "future", "change-something"];
|
const EASTER_EGGS = ["cookie", "future", "change-something", "picture-bright", "picture-nolights"];
|
||||||
|
|
||||||
export function addFoundEasterEgg(name:string):void {
|
export function addFoundEasterEgg(name:string):void {
|
||||||
if (!EASTER_EGGS.includes(name)) { return; }
|
if (!EASTER_EGGS.includes(name)) { return; }
|
||||||
|
|||||||
@ -1,20 +1,124 @@
|
|||||||
<template>
|
<template>
|
||||||
<Navbar />
|
<Navbar />
|
||||||
|
|
||||||
|
<ScreenCenter>
|
||||||
<main>
|
<main>
|
||||||
<div class="container mx-auto">
|
|
||||||
<h1 class="text-5xl font-bold">
|
<div class="flex flex-col md:flex-row md:justify-between items-center h-full">
|
||||||
{{ t("hello") }}
|
<div class="text-xl order-2 md:order-1 [&>*>p]:mb-2">
|
||||||
</h1>
|
<h1 class="text-5xl font-bold mb-5">{{ t("about me") }}</h1>
|
||||||
|
|
||||||
|
<div v-show="current_locale === 'it'">
|
||||||
|
<p>
|
||||||
|
Mi chiamo
|
||||||
|
<span data-popover-target="popover-name" class="underline decoration-dashed decoration-slate-900/70 dark:decoration-slate-50/70">Xia Tian Cheng</span>,
|
||||||
|
spesso conosciuto come Xia (cognome) o Riccardo.
|
||||||
|
Studio informatica all'Università di Bologna e al momento i miei interessi sono orientati verso l'analisi dati e l'intelligenza artificiale.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Oltre a premere tasti sulla tastiera,
|
||||||
|
mi piace leggere,
|
||||||
|
sperimentare in cucina <span class="text-xs">(preferibilmente con un estintore nei paraggi)</span>
|
||||||
|
e giocare a giochi strategici.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Secondo <a class="hover:underline" href="https://www.16personalities.com">16Personalities</a> sono un
|
||||||
|
<a class="font-mono hover:underline" href="https://www.16personalities.com/profiles/6b57f54bf1242">Architetto (INTJ)</a>.
|
||||||
|
Devo dire che la descrizione è decisamente accurata.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-show="current_locale === 'en'">
|
||||||
|
<p>
|
||||||
|
My name is
|
||||||
|
<span data-popover-target="popover-name" class="underline decoration-dashed decoration-slate-900/70 dark:decoration-slate-50/70">Xia Tian Cheng</span>,
|
||||||
|
frequently known as Xia (surname) or Richard.
|
||||||
|
I am a computer science student at the University of Bologna and currently my interests are focused on data analysis and artificial intelligence.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Aside from pressing keys on a keyboard,
|
||||||
|
I like reading,
|
||||||
|
cooking <span class="text-xs">(preferably with a fire extinguisher nearby)</span>
|
||||||
|
and playing strategic games.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
According to <a class="hover:underline" href="https://www.16personalities.com">16Personalities</a> I'm an
|
||||||
|
<a class="font-mono hover:underline" href="https://www.16personalities.com/profiles/6b57f54bf1242">Architect (INTJ)</a>.
|
||||||
|
I must admit that the description is quiet accurate.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="order-1 mb-3 ml-0 md:order-2 md:mb-0 md:ml-10">
|
||||||
|
<ProfilePicture />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-5">
|
||||||
|
<p class="text-center text-xl">{{ t("reading") }}</p>
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<Goodreads class="h-48"/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
</ScreenCenter>
|
||||||
|
|
||||||
|
<div data-popover id="popover-name" role="tooltip" class="absolute z-10 invisible inline-block transition-opacity duration-500 opacity-0">
|
||||||
|
<div class="text-lg px-3 py-2 bg-slate-200 dark:bg-slate-800 rounded">
|
||||||
|
夏天成
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Navbar from "@/components/navbar/Navbar.vue";
|
import Navbar from "@/components/navbar/Navbar.vue";
|
||||||
|
import Goodreads from "@/components/goodreads/Goodreads.vue";
|
||||||
|
import ScreenCenter from "@/components/screen-center/ScreenCenter.vue";
|
||||||
|
import ProfilePicture from "@/components/profile-picture/ProfilePicture.vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import locale from "./locale.json";
|
import { type ComputedRef, computed, onMounted, watch, ref } from "vue";
|
||||||
|
import { initPopovers } from "flowbite";
|
||||||
|
|
||||||
const { t } = useI18n({ messages: locale });
|
const { t, locale } = useI18n({ messages: {
|
||||||
|
"en": {
|
||||||
|
"about me": "About me",
|
||||||
|
"reading": "Currently I'm reading",
|
||||||
|
|
||||||
|
"that's bright": "That's bright",
|
||||||
|
"thanks": "Thanks"
|
||||||
|
},
|
||||||
|
"it": {
|
||||||
|
"about me": "Su di me",
|
||||||
|
"reading": "Attualmente sto leggendo",
|
||||||
|
|
||||||
|
"that's bright": "È luminoso",
|
||||||
|
"thanks": "Grazie"
|
||||||
|
}
|
||||||
|
} });
|
||||||
|
|
||||||
|
const current_locale = ref(locale.value);
|
||||||
|
|
||||||
|
|
||||||
|
const my_age: ComputedRef<number> = computed((): number => {
|
||||||
|
const today = new Date();
|
||||||
|
const birthDate = new Date(2001, 8, 29);
|
||||||
|
const month_diff = today.getMonth() - birthDate.getMonth();
|
||||||
|
let age = today.getFullYear() - birthDate.getFullYear();
|
||||||
|
if (month_diff < 0 ||
|
||||||
|
(month_diff === 0 && today.getDate() < birthDate.getDate())) {
|
||||||
|
age--;
|
||||||
|
}
|
||||||
|
return age;
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
initPopovers();
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(locale, () => {
|
||||||
|
current_locale.value = locale.value;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"en": {
|
|
||||||
"hello": "Hello"
|
|
||||||
},
|
|
||||||
"it": {
|
|
||||||
"hello": "Ciao"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user