presentation/components/link.component.ts
presentation/components/link.component.ts is a file in Bane's Lab Site. 87 lines of code and 14 definitions.
import type { ElementChild, ElementSpec } from "#types/element.types";
import { HOME_PATH, pageFromPath, pagePath } from "#core/assets/link.assets";
import { ANCHOR_PREFIX } from "#configuration/constants/document.constants";
import { HOME_PAGE } from "#core/ids/page.ids";
import { ROUTE_REQUESTED } from "#core/ids/route.ids";
import { contextOf } from "#presentation/renderers/element.renderer";
import { createElement } from "#core/factories/element.factory";
import { emitEvent } from "#core/buses/base.bus";
import { getPage } from "#domain/registries/page.registry";
import { isGenericLabel } from "#core/predicates/link.predicate";
import { scrollToId } from "#presentation/components/tab.component";
const specOf = function specOf(
className: string,
children: readonly ElementChild[],
attributes: Readonly<Record<string, string>>,
): ElementSpec {
return className.length === 0 ? { attributes, children } : { attributes, children, className };
};
const jumpTo = function jumpTo(href: string, targetId: string): void {
window.history.replaceState({}, "", href);
scrollToId(targetId);
};
const anchorOf = function anchorOf(href: string): { readonly path: string; readonly target: string | null } {
const cut = href.indexOf(ANCHOR_PREFIX);
return cut === -1
? { path: href, target: null }
: { path: href.slice(0, cut), target: href.slice(cut + ANCHOR_PREFIX.length) };
};
const withContext = function withContext(page: string, children: readonly ElementChild[]): readonly ElementChild[] {
const [only] = children;
const title = getPage(page)?.title;
if (children.length !== 1 || typeof only !== "string" || title === undefined || !isGenericLabel(only)) {
return children;
}
return [only, contextOf(title)];
};
export const createPageLink = function createPageLink(
page: string,
className: string,
children: readonly ElementChild[],
path = pagePath(page),
): HTMLAnchorElement {
const link = createElement("a", specOf(className, withContext(page, children), { href: path }));
const anchor = anchorOf(path);
link.addEventListener("click", (event) => {
event.preventDefault();
if (anchor.target !== null && anchor.path === window.location.pathname) {
jumpTo(path, anchor.target);
return;
}
emitEvent({ name: ROUTE_REQUESTED, page, path });
});
return link;
};
const createAnchorLink = function createAnchorLink(
targetId: string,
className: string,
children: readonly ElementChild[],
): HTMLAnchorElement {
const href = ANCHOR_PREFIX + targetId;
const link = createElement("a", specOf(className, children, { href }));
link.addEventListener("click", (event) => {
event.preventDefault();
jumpTo(href, targetId);
});
return link;
};
export const createExternalLink = function createExternalLink(
href: string,
className: string,
children: readonly ElementChild[],
): HTMLAnchorElement {
return createElement("a", specOf(className, children, { href, rel: "noopener noreferrer", target: "_blank" }));
};
export const createLink = function createLink(
href: string,
className: string,
children: readonly ElementChild[],
): HTMLAnchorElement {
if (href.startsWith(ANCHOR_PREFIX)) {
return createAnchorLink(href.slice(ANCHOR_PREFIX.length), className, children);
}
if (!href.startsWith(HOME_PATH)) {
return createExternalLink(href, className, children);
}
return createPageLink(pageFromPath(href, HOME_PAGE), className, children, href);
};