175 lines
3.8 KiB
TypeScript
175 lines
3.8 KiB
TypeScript
import type {
|
|
DataCollectionItemBase,
|
|
ProjectsCollectionItem,
|
|
} from "@nuxt/content";
|
|
import gsap from "gsap";
|
|
|
|
type ProjectItem = Omit<
|
|
ProjectsCollectionItem,
|
|
keyof DataCollectionItemBase
|
|
> & {
|
|
id: string;
|
|
};
|
|
export type Project = Omit<ProjectItem, "en" | "fr"> & {
|
|
description: string;
|
|
summary: string;
|
|
tasks: string[];
|
|
};
|
|
|
|
export const useProjectsStore = defineStore("projects", {
|
|
state: () => ({
|
|
projects: [] as Project[],
|
|
currentProject: 0,
|
|
loading: true,
|
|
offsetX: 0,
|
|
|
|
intro: {
|
|
fadeOpacity: 1,
|
|
},
|
|
|
|
outro: {
|
|
fadeOpacity: 0,
|
|
},
|
|
|
|
isIntro: true,
|
|
isOutro: false,
|
|
showConfirmationPopup: false,
|
|
}),
|
|
|
|
actions: {
|
|
async loadProjects() {
|
|
const { locale } = useI18n();
|
|
this.loading = true;
|
|
|
|
const items = await queryCollection("projects")
|
|
.order("order", "ASC")
|
|
.all();
|
|
|
|
if (!items) throw "Cannot load projects";
|
|
|
|
this.projects = items.map((item) => {
|
|
const slug = item.id.split("/")[2]!;
|
|
const localeData = item[locale.value as "en"] ?? item.en;
|
|
if (!localeData) {
|
|
console.warn(`Missing '${locale.value}' for ${slug}`);
|
|
}
|
|
|
|
const { en: _en, fr: _fr, ...meta } = item;
|
|
|
|
return {
|
|
...meta,
|
|
id: slug.replace(/-([a-z])/g, (_: string, letter: string) =>
|
|
letter.toUpperCase(),
|
|
),
|
|
description: localeData.description ?? "",
|
|
summary: localeData.summary ?? "",
|
|
tasks: localeData.tasks ?? [],
|
|
};
|
|
});
|
|
|
|
this.loading = false;
|
|
},
|
|
|
|
visitProject() {
|
|
const link = this.projects[this.currentProject]?.link;
|
|
if (link) {
|
|
navigateTo(link, { open: { target: "_blank" } });
|
|
const achievements = useAchievementsStore();
|
|
achievements.unlock("projects_open_link");
|
|
}
|
|
},
|
|
|
|
scrollProjects(direction: "left" | "right") {
|
|
let offset = 0;
|
|
|
|
if (
|
|
direction === "right" &&
|
|
this.currentProject < this.projects.length - 1
|
|
) {
|
|
this.currentProject += 1;
|
|
offset = 104;
|
|
} else if (direction === "left" && this.currentProject > 0) {
|
|
this.currentProject -= 1;
|
|
offset = -104;
|
|
}
|
|
|
|
if (offset !== 0) {
|
|
gsap.fromTo(
|
|
this,
|
|
{ offsetX: offset },
|
|
{
|
|
offsetX: 0,
|
|
duration: 0.1,
|
|
ease: "none",
|
|
},
|
|
);
|
|
}
|
|
|
|
if (this.currentProject >= 4) {
|
|
setTimeout(() => {
|
|
const achievements = useAchievementsStore();
|
|
achievements.unlock("projects_view_5");
|
|
}, 500);
|
|
}
|
|
},
|
|
|
|
// TODO: not used anymore
|
|
scrollToProject(index: number) {
|
|
if (index === this.currentProject) return;
|
|
|
|
const offset = (index - this.currentProject) * 69;
|
|
this.currentProject = index;
|
|
|
|
gsap.fromTo(
|
|
this,
|
|
{ offsetX: offset },
|
|
{
|
|
offsetX: 0,
|
|
duration: 0.1,
|
|
ease: "none",
|
|
},
|
|
);
|
|
},
|
|
|
|
animateIntro() {
|
|
this.isIntro = true;
|
|
|
|
gsap.fromTo(
|
|
this.intro,
|
|
{ fadeOpacity: 1 },
|
|
{
|
|
delay: 3,
|
|
fadeOpacity: 0,
|
|
duration: 0.35,
|
|
ease: "none",
|
|
onComplete: () => {
|
|
this.isIntro = false;
|
|
},
|
|
},
|
|
);
|
|
},
|
|
|
|
animateOutro() {
|
|
this.isOutro = true;
|
|
|
|
gsap.fromTo(
|
|
this.outro,
|
|
{ fadeOpacity: 0 },
|
|
{
|
|
delay: 0.5,
|
|
fadeOpacity: 1,
|
|
duration: 0.35,
|
|
ease: "none",
|
|
onComplete: () => {
|
|
setTimeout(() => {
|
|
this.isOutro = false;
|
|
const app = useAppStore();
|
|
app.navigateTo("home");
|
|
}, 3000);
|
|
},
|
|
},
|
|
);
|
|
},
|
|
},
|
|
});
|