presentation/views/profile.view.ts

presentation/views/profile.view.ts is a file in Bane's Lab Site. 137 lines of code and 25 definitions.

import { ABOUT_DESCRIPTION, ABOUT_TITLE } from "#configuration/strings/page.strings";
import {
    ABOUT_META,
    ABOUT_SECTIONS,
    EMAIL_ACTION,
    GITHUB_ACTION,
    LINKEDIN_ACTION,
    PORTRAIT_ALT,
    PROFILE_EYEBROW,
    PROFILE_FACTS,
    PROFILE_LEAD,
    PROFILE_THESIS,
} from "#configuration/strings/profile.strings";
import { AUTHOR_GITHUB, AUTHOR_MAIL, AUTHOR_PROFILE } from "#core/assets/link.assets";
import { FADE_IN, FADE_IN_UP, FADE_IN_UP_LARGE } from "#configuration/constants/element.constants";
import { ABOUT_PAGE } from "#core/ids/page.ids";
import type { PageDefinition } from "#types/page.types";
import type { ProfileFact } from "#types/profile.types";
import { SITE_PORTRAIT } from "#core/assets/image.assets";
import { SPACE } from "#configuration/constants/document.constants";
import type { Section } from "#types/document.types";
import { animated } from "#presentation/renderers/element.renderer";
import { createElement } from "#core/factories/element.factory";
import { createExternalLink } from "#presentation/components/link.component";
import { fillMarkup } from "#presentation/renderers/text.renderer";

const DELAY_HEADLINE = 90;
const DELAY_PROFILE_LEAD = 240;
const DELAY_CONTACTS = 300;
const DELAY_PORTRAIT = 120;
const DELAY_FACTS = 60;
const DELAY_TOPICS = 80;
const HEADLINE_WORD_STEP = 60;
const PROFILE_STEP = 60;

const profileHeadline = function profileHeadline(): HTMLElement {
    const words = PROFILE_THESIS.split(SPACE).flatMap((word, index) => [
        animated("span", { className: "word", text: word }, FADE_IN_UP, DELAY_HEADLINE + index * HEADLINE_WORD_STEP),
        SPACE,
    ]);
    return createElement("h1", { children: words, className: "home-thesis" });
};

const profileContacts = function profileContacts(): HTMLElement {
    return animated(
        "div",
        {
            children: [
                createExternalLink(AUTHOR_MAIL, "btn", [EMAIL_ACTION]),
                createExternalLink(AUTHOR_GITHUB, "btn ghost", [GITHUB_ACTION]),
                createExternalLink(AUTHOR_PROFILE, "go", [LINKEDIN_ACTION]),
            ],
            className: "home-actions",
        },
        FADE_IN,
        DELAY_CONTACTS,
    );
};

const profilePortrait = function profilePortrait(): HTMLElement {
    const image = createElement("img", {
        attributes: { alt: PORTRAIT_ALT, src: SITE_PORTRAIT },
        className: "profile-image",
    });
    return animated("div", { children: [image], className: "profile-portrait" }, FADE_IN_UP_LARGE, DELAY_PORTRAIT);
};

const profileHero = function profileHero(): HTMLElement {
    const column = createElement("div", {
        children: [
            animated("p", { className: "home-eyebrow", text: PROFILE_EYEBROW }, FADE_IN, 0),
            profileHeadline(),
            animated("p", { className: "home-lead", text: PROFILE_LEAD }, FADE_IN, DELAY_PROFILE_LEAD),
            profileContacts(),
        ],
        className: "home-intro",
    });
    const glow = createElement("div", { className: "home-glow" });
    return createElement("section", {
        children: [glow, column, profilePortrait()],
        className: "home-hero profile-hero",
    });
};

const profileFactCell = function profileFactCell(entry: ProfileFact, delay: number): HTMLElement {
    return animated(
        "div",
        {
            children: [
                createElement("dt", { text: entry.label }),
                createElement("dd", { text: entry.value }),
            ],
            className: "profile-fact",
        },
        FADE_IN_UP,
        delay,
    );
};

const profileFactList = function profileFactList(): HTMLElement {
    return createElement("dl", {
        children: PROFILE_FACTS.map((entry, index) => profileFactCell(entry, DELAY_FACTS + index * PROFILE_STEP)),
        className: "profile-facts",
    });
};

const profileTopic = function profileTopic(section: Section, delay: number): HTMLElement {
    const [lead] = section.subsections;
    const icon = createElement("span", {
        attributes: { "aria-hidden": "true" },
        children: [createElement("i", { className: section.icon })],
        className: "page-icon",
    });
    const heading = createElement("h2", {
        children: [section.title, createElement("small", { text: lead?.title ?? "" })],
    });
    return animated(
        "article",
        {
            attributes: { id: section.id },
            children: [icon, heading, fillMarkup(createElement("p"), lead?.content ?? "")],
            className: "home-row profile-topic",
        },
        FADE_IN_UP,
        delay,
    );
};

const renderAbout = function renderAbout(): Element {
    const topics = createElement("div", {
        children: ABOUT_SECTIONS.map((section, index) => profileTopic(section, DELAY_TOPICS + index * PROFILE_STEP)),
        className: "home-rows profile-topics",
    });
    return createElement("div", {
        children: [profileHero(), profileFactList(), topics],
        className: "home-content profile-content",
    });
};

export const PAGE: PageDefinition = {
    content: { kind: "document", meta: ABOUT_META, sections: ABOUT_SECTIONS },
    description: ABOUT_DESCRIPTION,
    id: ABOUT_PAGE,
    profile: true,
    render: renderAbout,
    title: ABOUT_TITLE,
};