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,
};