Unify the logic of Buttons and Windows in Main
dir
I struggled to add the `Webrings` button earlier So that warranted a much-needed revisit of the logic
This commit is contained in:
parent
b1f0300a11
commit
b8f8af8782
15 changed files with 262 additions and 356 deletions
|
@ -1,6 +1,6 @@
|
|||
import React from "react";
|
||||
|
||||
export default function TabButton({
|
||||
export default function WindowButton({
|
||||
colors,
|
||||
onClick,
|
||||
content,
|
78
src/Main/Header/index.tsx
Normal file
78
src/Main/Header/index.tsx
Normal file
|
@ -0,0 +1,78 @@
|
|||
import React from "react";
|
||||
import AnimateHeight from "react-animate-height";
|
||||
|
||||
import Translatable from "#parts/Translatable.tsx";
|
||||
|
||||
import {type WindowDetails, LanguageContext, WindowContext} from "#contexts";
|
||||
import type { OpenableWindow } from "#Main/index.tsx";
|
||||
import WindowButton from "./WindowButton.tsx";
|
||||
|
||||
export default function Header({
|
||||
openableWindows,
|
||||
setLang,
|
||||
setWindows,
|
||||
}: {
|
||||
openableWindows: OpenableWindow[]
|
||||
setLang: React.Dispatch<React.SetStateAction<string>>;
|
||||
setWindows: React.Dispatch<React.SetStateAction<WindowDetails[]>>;
|
||||
}) {
|
||||
const lang = React.useContext(LanguageContext);
|
||||
const windows = React.useContext(WindowContext);
|
||||
|
||||
const isWindowOpened = (window_id: string) => windows.map((w) => w.id).includes(window_id);
|
||||
const toggleWindow = (window_id: string) => {
|
||||
if (isWindowOpened(window_id)) {
|
||||
setWindows(windows.filter((t) => t.id !== window_id));
|
||||
} else {
|
||||
if (window.innerWidth >= 1024) {
|
||||
setWindows([...windows.map((w) => {
|
||||
const newPriority = w.priority === "z-40" ? "z-30" :
|
||||
w.priority === "z-30" ? "z-20" : "z-10";
|
||||
return {id: w.id, priority: newPriority};
|
||||
}), {id: window_id, priority: "z-40"}]);
|
||||
} else {
|
||||
setWindows([{id: window_id, priority: "z-40"}]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const buttons = openableWindows.map((w) => {
|
||||
return <WindowButton
|
||||
key={`button-${w.id}`}
|
||||
colors={`${w.colors} ${isWindowOpened(w.id) ? "brightness-75" : ""}`}
|
||||
onClick={() => toggleWindow(w.id)}
|
||||
content={w.title.long}
|
||||
/>;
|
||||
});
|
||||
|
||||
return (
|
||||
<header className="bg-blue-600 text-white lg:border-solid lg:border-white lg:border-8 lg:rounded-xl lg:mb-8">
|
||||
<AnimateHeight
|
||||
id="intro"
|
||||
duration={300}
|
||||
height={windows.length ? 0 : "auto"}
|
||||
>
|
||||
<div className="bg-white text-blue-600 relative justify-center items-center pb-4 pt-2 lg:rounded-b-xl lg:pt-0 lg:px-4 hover:brightness-110 active:brightness-110">
|
||||
<Translatable
|
||||
en={<h1 className="text-6xl md:text-8xl">Hi, I'm Taevas!</h1>}
|
||||
fr={<h1 className="text-6xl md:text-8xl">Bonjour, je m'appelle <span className="text-nowrap">Taevas !</span></h1>}
|
||||
/>
|
||||
<h2 className="text-3xl pt-4 md:pt-2">
|
||||
<Translatable
|
||||
en={"If you're here, you're probably interested by who I am and what I do"}
|
||||
fr={"Si vous êtes ici, vous êtes alors probablement intéressé·e par qui je suis et ce que je fais"}
|
||||
/>
|
||||
</h2>
|
||||
</div>
|
||||
</AnimateHeight>
|
||||
<nav className="p-4">
|
||||
<WindowButton
|
||||
colors={"from-slate-500 to-slate-600 hover:from-slate-700 hover:to-slate-600"}
|
||||
onClick={() => setLang(lang !== "en" ? "en" : "fr")}
|
||||
content={lang === "fr" ? "🇬🇧" : "🇫🇷"}
|
||||
/>
|
||||
{...buttons}
|
||||
</nav>
|
||||
</header>
|
||||
);
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
import React from "react";
|
||||
import TabButton from "./TabButton.tsx";
|
||||
import Translatable from "#parts/Translatable.tsx";
|
||||
import {type TabDetails, LanguageContext, TabContext} from "#contexts";
|
||||
|
||||
export default function TabButtons({
|
||||
setLang,
|
||||
setTabs,
|
||||
}: {
|
||||
setLang: React.Dispatch<React.SetStateAction<string>>;
|
||||
setTabs: React.Dispatch<React.SetStateAction<TabDetails[]>>;
|
||||
}) {
|
||||
const lang = React.useContext(LanguageContext);
|
||||
const tabs = React.useContext(TabContext);
|
||||
const isTabActive = (id: string) => tabs.map((t) => t.id).includes(id);
|
||||
|
||||
const toggleTab = (tab: string) => {
|
||||
if (isTabActive(tab)) {
|
||||
setTabs(tabs.filter((t) => t.id !== tab));
|
||||
} else {
|
||||
if (window.innerWidth >= 1024) {
|
||||
setTabs([...tabs.map((tab) => {
|
||||
const newPriority = tab.priority === "z-40" ? "z-30" :
|
||||
tab.priority === "z-30" ? "z-20" : "z-10";
|
||||
return {id: tab.id, priority: newPriority};
|
||||
}), {id: tab, priority: "z-40"}]);
|
||||
} else {
|
||||
setTabs([{id: tab, priority: "z-40"}]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<nav className="relative justify-center items-center">
|
||||
<TabButton
|
||||
colors={"from-slate-500 to-slate-600 hover:from-slate-700 hover:to-slate-600"}
|
||||
onClick={() => {
|
||||
setLang(lang !== "en" ? "en" : "fr");
|
||||
}}
|
||||
content={lang === "fr" ? "🇬🇧" : "🇫🇷"}
|
||||
/>
|
||||
<TabButton
|
||||
colors={`from-purple-500 to-purple-600 hover:from-purple-700 hover:to-purple-600 ${isTabActive("about") ? "brightness-75" : ""}`}
|
||||
onClick={() => {
|
||||
toggleTab("about");
|
||||
}}
|
||||
content={
|
||||
<Translatable
|
||||
en="About me"
|
||||
fr="À propos de moi"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<TabButton
|
||||
colors={`from-emerald-500 to-emerald-600 hover:from-emerald-700 hover:to-emerald-600 ${isTabActive("projects") ? "brightness-75" : ""}`}
|
||||
onClick={() => {
|
||||
toggleTab("projects");
|
||||
}}
|
||||
content={
|
||||
<Translatable
|
||||
en="My projects"
|
||||
fr="Mes projets"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<TabButton
|
||||
colors={`from-blue-500 to-blue-600 hover:from-blue-700 hover:to-blue-600 ${isTabActive("contact") ? "brightness-75" : ""}`}
|
||||
onClick={() => {
|
||||
toggleTab("contact");
|
||||
}}
|
||||
content={
|
||||
<Translatable
|
||||
en="Contact me"
|
||||
fr="Me contacter"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<TabButton
|
||||
colors={`from-rose-500 to-rose-600 hover:from-rose-700 hover:to-rose-600 ${isTabActive("support") ? "brightness-75" : ""}`}
|
||||
onClick={() => {
|
||||
toggleTab("support");
|
||||
}}
|
||||
content={
|
||||
<Translatable
|
||||
en="Support me"
|
||||
fr="Me soutenir"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<TabButton
|
||||
colors={`from-cyan-500 to-cyan-600 hover:from-cyan-700 hover:to-cyan-600 ${isTabActive("webrings") ? "brightness-75" : ""}`}
|
||||
onClick={() => {
|
||||
toggleTab("webrings");
|
||||
}}
|
||||
content="Webrings"
|
||||
/>
|
||||
</nav>
|
||||
);
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
import React from "react";
|
||||
import AnimateHeight from "react-animate-height";
|
||||
|
||||
import TabButtons from "./TabButtons/index.tsx";
|
||||
import Translatable from "#parts/Translatable.tsx";
|
||||
|
||||
import {type TabDetails, TabContext} from "#contexts";
|
||||
|
||||
export default function MainWindow({
|
||||
setLang,
|
||||
setTabs,
|
||||
}: {
|
||||
setLang: React.Dispatch<React.SetStateAction<string>>;
|
||||
setTabs: React.Dispatch<React.SetStateAction<TabDetails[]>>;
|
||||
}) {
|
||||
const tabs = React.useContext(TabContext);
|
||||
return (
|
||||
<div className="bg-blue-600 text-white lg:border-solid lg:border-white lg:border-8 lg:rounded-xl lg:mb-8">
|
||||
<AnimateHeight
|
||||
id="intro"
|
||||
duration={300}
|
||||
height={tabs.length ? 0 : "auto"}
|
||||
>
|
||||
<div className="bg-white text-blue-600 relative justify-center items-center pb-4 pt-2 lg:rounded-b-xl lg:pt-0 lg:px-4 hover:brightness-110 active:brightness-110">
|
||||
<Translatable
|
||||
en={<h1 className="text-6xl md:text-8xl">Hi, I'm Taevas!</h1>}
|
||||
fr={<h1 className="text-6xl md:text-8xl">Bonjour, je m'appelle <span className="text-nowrap">Taevas !</span></h1>}
|
||||
/>
|
||||
<h2 className="text-3xl pt-4 md:pt-2">
|
||||
<Translatable
|
||||
en={"If you're here, you're probably interested by who I am and what I do"}
|
||||
fr={"Si vous êtes ici, vous êtes alors probablement intéressé·e par qui je suis et ce que je fais"}
|
||||
/>
|
||||
</h2>
|
||||
</div>
|
||||
</AnimateHeight>
|
||||
<div className="p-4">
|
||||
<TabButtons
|
||||
setLang={setLang}
|
||||
setTabs={setTabs}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
import React from "react";
|
||||
import Tab from "../Tab.tsx";
|
||||
import Translatable from "#parts/Translatable.tsx";
|
||||
import {UserMultiple} from "@carbon/icons-react";
|
||||
import {type TabDetails} from "#contexts";
|
||||
import Link from "#parts/Link.tsx";
|
||||
|
||||
export default function Webrings({
|
||||
setTabs,
|
||||
}: {
|
||||
setTabs: React.Dispatch<React.SetStateAction<TabDetails[]>>;
|
||||
}) {
|
||||
const elements = [
|
||||
<div className="pb-4" key="webrings-list">
|
||||
<iframe id="bucket-webring" className="w-full h-[3rem]" src="https://webring.bucketfish.me/embed.html?name=Taevas&lightmode=true"/>
|
||||
</div>,
|
||||
<div className="text-left" key="webrings-intro">
|
||||
<p className="pb-2">
|
||||
<Translatable
|
||||
en={<><Link link="https://en.wikipedia.org/wiki/Webring" text="Webrings"/> are kinda thought of as <b>something from the past,</b> mainly because search engines had become good at their job in the 2000s, and also partly because of how much everything has become centralized on a few websites.</>}
|
||||
fr={<>Les <Link link="https://fr.wikipedia.org/wiki/Webring" text="Webrings"/> sont un peu perçus comme <b>une chose du passé</b>, surtout parce que les moteurs de recherche sont devenus bons à ce qu'ils sont censés faire dans les années 2000, mais aussi un peu parce que tout est devenu bien plus centralisé sur certains sites.</>}
|
||||
/>
|
||||
</p>
|
||||
<p className="pb-2">
|
||||
<Translatable
|
||||
en={<>However, <b>they've kinda been making a comeback</b> thanks to fans of the <Link link="https://en.wikipedia.org/wiki/Web_2.0#Web_1.0" text="Web 1.0"/> such as the people that are on <Link link="https://neocities.org/" text="Neocities"/>. Furthermore, people have been reporting that search engines have been becoming worse over the past few years!</>}
|
||||
fr={<>Néanmoins, <b>ils sont un peu revenus</b> grâce aux fans du Web 1.0, tel que les gens qui sont sur <Link link="https://neocities.org/" text="Neocities"/>. Et aussi, il est parfois dit que les moteurs de recherche sont devenus pires durant ces dernières années !</>}
|
||||
/>
|
||||
</p>
|
||||
<p className="pb-2">
|
||||
<Translatable
|
||||
en={<>Finally, don't you think <b>it's hard to discover personal websites?</b> Like, if I asked you to find the website of someone you don't know, your best bet would likely be to go on the nerdiest Fediverse instance you know of and to click on a few profiles there in the hope of finding a link, I think.</>}
|
||||
fr={<>Enfin, n'êves-vous pas d'accord pour dire <b>qu'il est difficile de trouver des sites web personnels ?</b> Genre, si je vous demandais de trouver le site de quelqu'un que vous ne connaissez pas, la meilleure façon serait probablement d'aller sur l'instance Fediverse la plus nerd que vous connaissez et de cliquer sur quelques profils dans l'espoir de trouver un lien, en tout cas je pense.</>}
|
||||
/>
|
||||
</p>
|
||||
<p className="pb-2">
|
||||
<Translatable
|
||||
en={<>The Webrings above can be thought of as <b>curated lists of like-minded people, so feel free to surf through those once you're done here!</b></>}
|
||||
fr={<>Les Webrings ci-dessus peuvent être considérés comme des <b>listes de personnes comme moi, alors allez y surfer une fois que vous avez fini ici !</b></>}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
];
|
||||
|
||||
return (
|
||||
<Tab
|
||||
setTabs={setTabs}
|
||||
id="webrings"
|
||||
name={
|
||||
<Translatable
|
||||
en="Webrings"
|
||||
fr="Webrings"
|
||||
/>
|
||||
}
|
||||
elements={elements}
|
||||
logo={<UserMultiple size={48} fill=""/>}
|
||||
position="lg:left-[700px] lg:top-[400px]"
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
import React from "react";
|
||||
import {type TabDetails} from "#contexts";
|
||||
import About from "./About/index.tsx";
|
||||
import Contact from "./Contact/index.tsx";
|
||||
import Projects from "./Projects/index.tsx";
|
||||
import Support from "./Support/index.tsx";
|
||||
import Webrings from "./Webrings/index.tsx";
|
||||
|
||||
export default function Tabs({
|
||||
setTabs,
|
||||
}: {
|
||||
setTabs: React.Dispatch<React.SetStateAction<TabDetails[]>>;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<About setTabs={setTabs} />
|
||||
<Projects setTabs={setTabs} />
|
||||
<Contact setTabs={setTabs} />
|
||||
<Support setTabs={setTabs} />
|
||||
<Webrings setTabs={setTabs} />
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1,16 +1,9 @@
|
|||
import React from "react";
|
||||
import Tab from "../Tab.tsx";
|
||||
import Translatable from "#parts/Translatable.tsx";
|
||||
import {UserProfile} from "@carbon/icons-react";
|
||||
import {type TabDetails} from "#contexts";
|
||||
import Link from "#parts/Link.tsx";
|
||||
|
||||
export default function About({
|
||||
setTabs,
|
||||
}: {
|
||||
setTabs: React.Dispatch<React.SetStateAction<TabDetails[]>>;
|
||||
}) {
|
||||
const elements = [(
|
||||
export default function About() {
|
||||
return (
|
||||
<div className="order-1" key="about">
|
||||
<div className="ml-auto max-w-3xl text-left">
|
||||
<img className="m-4 float-right h-24" src="/assets/brittany.jpg" alt="Flag of Brittany" title="Flag of Brittany"/>
|
||||
|
@ -49,21 +42,5 @@ export default function About({
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
)];
|
||||
|
||||
return (
|
||||
<Tab
|
||||
setTabs={setTabs}
|
||||
id="about"
|
||||
name={
|
||||
<Translatable
|
||||
en="About"
|
||||
fr="À propos"
|
||||
/>
|
||||
}
|
||||
elements={elements}
|
||||
logo={<UserProfile size={48} fill=""/>}
|
||||
position="lg:left-[100px] lg:top-[200px]"
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -1,18 +1,11 @@
|
|||
import React from "react";
|
||||
import Tab from "../Tab.tsx";
|
||||
import {MailAll} from "@carbon/icons-react";
|
||||
import CopyField from "#parts/CopyField.tsx";
|
||||
import ButtonLink from "#parts/ButtonLink.tsx";
|
||||
import Translatable from "#parts/Translatable.tsx";
|
||||
import Link from "#parts/Link.tsx";
|
||||
import {type TabDetails} from "#contexts";
|
||||
|
||||
export default function Contact({
|
||||
setTabs,
|
||||
}: {
|
||||
setTabs: React.Dispatch<React.SetStateAction<TabDetails[]>>;
|
||||
}) {
|
||||
const elements = [(
|
||||
export default function Contact() {
|
||||
return (
|
||||
<div className="text-left" key="contact">
|
||||
<Translatable
|
||||
en={<p>It is my belief that it's actually difficult to communicate with the people you want on the internet, I find emails to be bad for real-time communication, some other platforms enshittify themselves too much, while others do not quite offer the best user experience in my honest opinion.</p>}
|
||||
|
@ -34,21 +27,5 @@ export default function Contact({
|
|||
fr={<ButtonLink link="https://matrix.to/#/@taevas:matrix.org" text="lien matrix.to" />}
|
||||
/>
|
||||
</div>
|
||||
)];
|
||||
|
||||
return (
|
||||
<Tab
|
||||
setTabs={setTabs}
|
||||
id="contact"
|
||||
name={
|
||||
<Translatable
|
||||
en="Contact"
|
||||
fr="Contacter"
|
||||
/>
|
||||
}
|
||||
elements={elements}
|
||||
logo={<MailAll size={48} fill=""/>}
|
||||
position="lg:left-[400px] lg:top-[300px]"
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -1,16 +1,9 @@
|
|||
import React from "react";
|
||||
import Tab from "../Tab.tsx";
|
||||
import Translatable from "#parts/Translatable.tsx";
|
||||
import {Devices} from "@carbon/icons-react";
|
||||
import {type TabDetails} from "#contexts";
|
||||
import Link from "#parts/Link.tsx";
|
||||
|
||||
export default function Projects({
|
||||
setTabs,
|
||||
}: {
|
||||
setTabs: React.Dispatch<React.SetStateAction<TabDetails[]>>;
|
||||
}) {
|
||||
const elements = [(
|
||||
export default function Projects() {
|
||||
return (
|
||||
<div className="inline-block text-left" key="projects">
|
||||
<div className="border-b-4 pb-4">
|
||||
<a href="https://tttaevas.itch.io/swordventure" target="_blank" rel="noreferrer"><img className="m-4 float-right w-40" src="/assets/swordventure.png" alt="SwordVenture thumbnail"/></a>
|
||||
|
@ -106,21 +99,5 @@ export default function Projects({
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
)];
|
||||
|
||||
return (
|
||||
<Tab
|
||||
setTabs={setTabs}
|
||||
id="projects"
|
||||
name={
|
||||
<Translatable
|
||||
en="Projects"
|
||||
fr="Projets"
|
||||
/>
|
||||
}
|
||||
elements={elements}
|
||||
logo={<Devices size={48} fill=""/>}
|
||||
position="lg:left-[250px] lg:top-[250px]"
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -1,16 +1,9 @@
|
|||
import React from "react";
|
||||
import Tab from "../Tab.tsx";
|
||||
import Translatable from "#parts/Translatable.tsx";
|
||||
import {UserFavorite} from "@carbon/icons-react";
|
||||
import {type TabDetails} from "#contexts";
|
||||
import ButtonLink from "#parts/ButtonLink.tsx";
|
||||
|
||||
export default function Support({
|
||||
setTabs,
|
||||
}: {
|
||||
setTabs: React.Dispatch<React.SetStateAction<TabDetails[]>>;
|
||||
}) {
|
||||
const elements = [(
|
||||
export default function Support() {
|
||||
return (
|
||||
<div className="pb-2" key="support">
|
||||
<p>
|
||||
<b>
|
||||
|
@ -37,21 +30,5 @@ export default function Support({
|
|||
fr={<ButtonLink link="https://ko-fi.com/taevas" text="Soutenez-moi sur Ko-fi !"/>}
|
||||
/>
|
||||
</div>
|
||||
)];
|
||||
|
||||
return (
|
||||
<Tab
|
||||
setTabs={setTabs}
|
||||
id="support"
|
||||
name={
|
||||
<Translatable
|
||||
en="Support"
|
||||
fr="Soutenir"
|
||||
/>
|
||||
}
|
||||
elements={elements}
|
||||
logo={<UserFavorite size={48} fill=""/>}
|
||||
position="lg:left-[550px] lg:top-[350px]"
|
||||
/>
|
||||
);
|
||||
}
|
39
src/Main/Windows/Content/Webrings.tsx
Normal file
39
src/Main/Windows/Content/Webrings.tsx
Normal file
|
@ -0,0 +1,39 @@
|
|||
import React from "react";
|
||||
import Translatable from "#parts/Translatable.tsx";
|
||||
import Link from "#parts/Link.tsx";
|
||||
|
||||
export default function Webrings() {
|
||||
return (
|
||||
<div key="webrings">
|
||||
<div className="pb-4" key="webrings-list">
|
||||
<iframe id="bucket-webring" className="w-full h-[3rem]" src="https://webring.bucketfish.me/embed.html?name=Taevas&lightmode=true"/>
|
||||
</div>,
|
||||
<div className="text-left" key="webrings-intro">
|
||||
<p className="pb-2">
|
||||
<Translatable
|
||||
en={<><Link link="https://en.wikipedia.org/wiki/Webring" text="Webrings"/> are kinda thought of as <b>something from the past,</b> mainly because search engines had become good at their job in the 2000s, and also partly because of how much everything has become centralized on a few websites.</>}
|
||||
fr={<>Les <Link link="https://fr.wikipedia.org/wiki/Webring" text="Webrings"/> sont un peu perçus comme <b>une chose du passé</b>, surtout parce que les moteurs de recherche sont devenus bons à ce qu'ils sont censés faire dans les années 2000, mais aussi un peu parce que tout est devenu bien plus centralisé sur certains sites.</>}
|
||||
/>
|
||||
</p>
|
||||
<p className="pb-2">
|
||||
<Translatable
|
||||
en={<>However, <b>they've kinda been making a comeback</b> thanks to fans of the <Link link="https://en.wikipedia.org/wiki/Web_2.0#Web_1.0" text="Web 1.0"/> such as the people that are on <Link link="https://neocities.org/" text="Neocities"/>. Furthermore, people have been reporting that search engines have been becoming worse over the past few years!</>}
|
||||
fr={<>Néanmoins, <b>ils sont un peu revenus</b> grâce aux fans du Web 1.0, tel que les gens qui sont sur <Link link="https://neocities.org/" text="Neocities"/>. Et aussi, il est parfois dit que les moteurs de recherche sont devenus pires durant ces dernières années !</>}
|
||||
/>
|
||||
</p>
|
||||
<p className="pb-2">
|
||||
<Translatable
|
||||
en={<>Finally, don't you think <b>it's hard to discover personal websites?</b> Like, if I asked you to find the website of someone you don't know, your best bet would likely be to go on the nerdiest Fediverse instance you know of and to click on a few profiles there in the hope of finding a link, I think.</>}
|
||||
fr={<>Enfin, n'êves-vous pas d'accord pour dire <b>qu'il est difficile de trouver des sites web personnels ?</b> Genre, si je vous demandais de trouver le site de quelqu'un que vous ne connaissez pas, la meilleure façon serait probablement d'aller sur l'instance Fediverse la plus nerd que vous connaissez et de cliquer sur quelques profils dans l'espoir de trouver un lien, en tout cas je pense.</>}
|
||||
/>
|
||||
</p>
|
||||
<p className="pb-2">
|
||||
<Translatable
|
||||
en={<>The Webrings above can be thought of as <b>curated lists of like-minded people, so feel free to surf through those once you're done here!</b></>}
|
||||
fr={<>Les Webrings ci-dessus peuvent être considérés comme des <b>listes de personnes comme moi, alors allez y surfer une fois que vous avez fini ici !</b></>}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,39 +1,39 @@
|
|||
import React, {Component} from "react";
|
||||
import AnimateHeight from "react-animate-height";
|
||||
import type Translatable from "#parts/Translatable.tsx";
|
||||
import {type TabDetails, TabContext} from "#contexts";
|
||||
import {type WindowDetails, WindowContext} from "#contexts";
|
||||
|
||||
export default class Tab extends Component<{
|
||||
setTabs: React.Dispatch<React.SetStateAction<TabDetails[]>>;
|
||||
export default class Window extends Component<{
|
||||
setWindows: React.Dispatch<React.SetStateAction<WindowDetails[]>>;
|
||||
id: string;
|
||||
name: ReturnType<typeof Translatable>;
|
||||
elements: React.JSX.Element[];
|
||||
logo?: React.JSX.Element;
|
||||
position?: string;
|
||||
}> {
|
||||
static contextType = TabContext;
|
||||
context!: React.ContextType<typeof TabContext>;
|
||||
static contextType = WindowContext;
|
||||
context!: React.ContextType<typeof WindowContext>;
|
||||
private readonly div = React.createRef<HTMLDivElement>();
|
||||
private readonly header = React.createRef<HTMLDivElement>();
|
||||
|
||||
render() {
|
||||
return (
|
||||
<TabContext.Consumer>
|
||||
{tabs => (
|
||||
<WindowContext.Consumer>
|
||||
{windows => (
|
||||
<AnimateHeight
|
||||
className={`absolute w-full lg:w-[625px] lg:rounded-xl ${this.props.position}
|
||||
bg-blue-800/75 hover:bg-blue-800/80 active:bg-blue-800/70 backdrop-brightness-75 backdrop-contrast-150 backdrop-blur
|
||||
shadow-[12px_12px_0_0] shadow-blue-950/75
|
||||
${tabs.find((t) => t.id === this.props.id)?.priority ?? "z-50"}`}
|
||||
${windows.find((w) => w.id === this.props.id)?.priority ?? "z-50"}`}
|
||||
ref={this.div}
|
||||
duration={250}
|
||||
height={tabs.map((t) => t.id).includes(this.props.id) ? "auto" : 0}
|
||||
height={windows.map((w) => w.id).includes(this.props.id) ? "auto" : 0}
|
||||
>
|
||||
<div ref={this.header} className="relative bg-white lg:rounded-xl h-12 hover:brightness-110 lg:hover:cursor-grab lg:active:cursor-move">
|
||||
{this.props.logo ? <div className="absolute start-0 h-0 ml-2 invisible lg:visible fill-gray-600">{this.props.logo}</div> : <></>}
|
||||
<div className="absolute end-0 w-0 sm:w-10 mr-1 mt-1 invisible lg:visible cursor-pointer
|
||||
rounded-full fill-red-500 hover:fill-black hover:bg-red-500 active:brightness-50" onClick={() => {
|
||||
this.props.setTabs(tabs.filter((t) => t.id !== this.props.id));
|
||||
this.props.setWindows(windows.filter((w) => w.id !== this.props.id));
|
||||
}}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<path d="M24 9.4L22.6 8L16 14.6L9.4 8L8 9.4l6.6 6.6L8 22.6L9.4 24l6.6-6.6l6.6 6.6l1.4-1.4l-6.6-6.6L24 9.4z"/>
|
||||
|
@ -49,7 +49,7 @@ export default class Tab extends Component<{
|
|||
</div>
|
||||
</AnimateHeight>
|
||||
)}
|
||||
</TabContext.Consumer>
|
||||
</WindowContext.Consumer>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -76,14 +76,14 @@ export default class Tab extends Component<{
|
|||
});
|
||||
|
||||
this.div.current?.addEventListener("pointerdown", () => {
|
||||
const tabs = this.context;
|
||||
this.props.setTabs(tabs.map((tab) => {
|
||||
if (tab.id === this.props.id) {
|
||||
return {id: tab.id, priority: "z-40"};
|
||||
const windows = this.context;
|
||||
this.props.setWindows(windows.map((w) => {
|
||||
if (w.id === this.props.id) {
|
||||
return {id: w.id, priority: "z-40"};
|
||||
} else {
|
||||
const newPriority = tab.priority === "z-40" ? "z-30" :
|
||||
tab.priority === "z-30" ? "z-20" : "z-10";
|
||||
return {id: tab.id, priority: newPriority};
|
||||
const newPriority = w.priority === "z-40" ? "z-30" :
|
||||
w.priority === "z-30" ? "z-20" : "z-10";
|
||||
return {id: w.id, priority: newPriority};
|
||||
}
|
||||
}));
|
||||
});
|
37
src/Main/Windows/index.tsx
Normal file
37
src/Main/Windows/index.tsx
Normal file
|
@ -0,0 +1,37 @@
|
|||
import React from "react";
|
||||
import {type WindowDetails} from "#contexts";
|
||||
import type { OpenableWindow } from "#Main/index.tsx";
|
||||
import Window from "./Window";
|
||||
|
||||
export default function Windows({
|
||||
openableWindows,
|
||||
setWindows
|
||||
}: {
|
||||
openableWindows: OpenableWindow[];
|
||||
setWindows: React.Dispatch<React.SetStateAction<WindowDetails[]>>;
|
||||
}) {
|
||||
const lefts = [
|
||||
"lg:left-[50px]",
|
||||
"lg:left-[100px]",
|
||||
"lg:left-[150px]",
|
||||
"lg:left-[200px]",
|
||||
"lg:left-[250px]",
|
||||
"lg:left-[300px]",
|
||||
"lg:left-[350px]",
|
||||
"lg:left-[400px]",
|
||||
];
|
||||
|
||||
const windows = openableWindows.map((w, i) => {
|
||||
return <Window
|
||||
key={`window-${w.id}`}
|
||||
setWindows={setWindows}
|
||||
id={w.id}
|
||||
name={w.title.short}
|
||||
elements={[w.content]}
|
||||
logo={w.icon}
|
||||
position={lefts[i]}
|
||||
/>;
|
||||
});
|
||||
|
||||
return <>{...windows}</>;
|
||||
}
|
|
@ -1,11 +1,82 @@
|
|||
import React, {useEffect, useState} from "react";
|
||||
import MainWindow from "./MainWindow/index.tsx";
|
||||
import Tabs from "./Tabs/index.tsx";
|
||||
import {type TabDetails, LanguageContext, TabContext} from "#contexts";
|
||||
import Header from "./Header/index.tsx";
|
||||
import Windows from "./Windows/index.tsx";
|
||||
import {type WindowDetails, LanguageContext, WindowContext} from "#contexts";
|
||||
import Translatable from "#parts/Translatable.tsx";
|
||||
import { Devices, MailAll, UserFavorite, UserMultiple, UserProfile } from "@carbon/icons-react";
|
||||
import About from "./Windows/Content/About.tsx";
|
||||
import Projects from "./Windows/Content/Projects.tsx";
|
||||
import Contact from "./Windows/Content/Contact.tsx";
|
||||
import Support from "./Windows/Content/Support.tsx";
|
||||
import Webrings from "./Windows/Content/Webrings.tsx";
|
||||
|
||||
export interface OpenableWindow {
|
||||
/** Used for React keys and tracking what's opened and what's not */
|
||||
id: string
|
||||
title: {
|
||||
/** Used for places where width is unimportant, like buttons */
|
||||
long: React.JSX.Element
|
||||
/** Used for places where **width IS important**, like the top of a window */
|
||||
short: React.JSX.Element
|
||||
}
|
||||
/** Classes, intended for buttons */
|
||||
colors: string
|
||||
/** An icon from Carbon, intended to make certain things feel less empty */
|
||||
icon: React.JSX.Element
|
||||
/** The content of the window */
|
||||
content: React.JSX.Element
|
||||
}
|
||||
|
||||
export default function MainContent() {
|
||||
const [lang, setLang] = useState<string>(localStorage.getItem("lang") ?? "en");
|
||||
const [tabs, setTabs] = useState<TabDetails[]>([]);
|
||||
const [windows, setWindows] = useState<WindowDetails[]>([]);
|
||||
|
||||
const [openableWindows] = useState<OpenableWindow[]>([{
|
||||
id: "about",
|
||||
title: {
|
||||
long: <Translatable en="About me" fr="À propos de moi"/>,
|
||||
short: <Translatable en="About" fr="À propos"/>
|
||||
},
|
||||
colors: "from-purple-500 to-purple-600 hover:from-purple-700 hover:to-purple-600",
|
||||
icon: <UserProfile size={48} fill=""/>,
|
||||
content: <About/>
|
||||
}, {
|
||||
id: "projects",
|
||||
title: {
|
||||
long: <Translatable en="My projects" fr="Mes projets"/>,
|
||||
short: <Translatable en="Projects" fr="Projets"/>
|
||||
},
|
||||
colors: "from-emerald-500 to-emerald-600 hover:from-emerald-700 hover:to-emerald-600",
|
||||
icon: <MailAll size={48} fill=""/>,
|
||||
content: <Projects/>
|
||||
}, {
|
||||
id: "contact",
|
||||
title: {
|
||||
long: <Translatable en="Contact me" fr="Me contacter"/>,
|
||||
short: <Translatable en="Contact" fr="Contacter"/>
|
||||
},
|
||||
colors: "from-blue-500 to-blue-600 hover:from-blue-700 hover:to-blue-600",
|
||||
icon: <Devices size={48} fill=""/>,
|
||||
content: <Contact/>
|
||||
}, {
|
||||
id: "support",
|
||||
title: {
|
||||
long: <Translatable en="Support me" fr="Me soutenir"/>,
|
||||
short: <Translatable en="Support" fr="Soutenir"/>
|
||||
},
|
||||
colors: "from-rose-500 to-rose-600 hover:from-rose-700 hover:to-rose-600",
|
||||
icon: <UserFavorite size={48} fill=""/>,
|
||||
content: <Support/>
|
||||
}, {
|
||||
id: "webrings",
|
||||
title: {
|
||||
long: <Translatable en="Webrings"/>,
|
||||
short: <Translatable en="Webrings"/>
|
||||
},
|
||||
colors: "from-cyan-500 to-cyan-600 hover:from-cyan-700 hover:to-cyan-600",
|
||||
icon: <UserMultiple size={48} fill=""/>,
|
||||
content: <Webrings/>
|
||||
}]);
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem("lang", lang);
|
||||
|
@ -14,10 +85,10 @@ export default function MainContent() {
|
|||
return (
|
||||
<main className="text-lg/6 h-screen w-screen max-w-[1632px] m-auto lg:pl-[50px] lg:pr-[413px] lg:py-12">
|
||||
<LanguageContext.Provider value={lang}>
|
||||
<TabContext.Provider value={tabs}>
|
||||
<MainWindow setLang={setLang} setTabs={setTabs}/>
|
||||
<Tabs setTabs={setTabs}/>
|
||||
</TabContext.Provider>
|
||||
<WindowContext.Provider value={windows}>
|
||||
<Header openableWindows={openableWindows} setLang={setLang} setWindows={setWindows}/>
|
||||
<Windows openableWindows={openableWindows} setWindows={setWindows}/>
|
||||
</WindowContext.Provider>
|
||||
</LanguageContext.Provider>
|
||||
</main>
|
||||
);
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import React from "react";
|
||||
|
||||
export interface TabDetails {
|
||||
export interface WindowDetails {
|
||||
id: string;
|
||||
priority: string;
|
||||
}
|
||||
|
||||
export const LanguageContext = React.createContext<string>("en");
|
||||
export const TabContext = React.createContext<TabDetails[]>([]);
|
||||
export const WindowContext = React.createContext<WindowDetails[]>([]);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue