107 lines
2.8 KiB
TypeScript
107 lines
2.8 KiB
TypeScript
import Color from "colorjs.io";
|
|
import type { mastodon } from "masto";
|
|
import { createEffect, createMemo, Show } from "solid-js";
|
|
import { Title, Body1 } from "~material/typography";
|
|
import { averageColorHex } from "~platform/blurhash";
|
|
import "./PreviewCard.css";
|
|
|
|
function onResetImg(event: Event & { currentTarget: HTMLImageElement }) {
|
|
event.currentTarget.classList.remove("loaded");
|
|
}
|
|
|
|
function onImgLoaded(event: Event & { currentTarget: HTMLImageElement }) {
|
|
event.currentTarget.classList.add("loaded");
|
|
}
|
|
|
|
export function PreviewCard(props: {
|
|
src: mastodon.v1.PreviewCard;
|
|
alwaysCompact?: boolean;
|
|
}) {
|
|
let root: HTMLAnchorElement;
|
|
|
|
createEffect(() => {
|
|
if (props.alwaysCompact) {
|
|
root.classList.add("compact");
|
|
return;
|
|
}
|
|
if (!props.src.width) return;
|
|
const width = root.getBoundingClientRect().width;
|
|
if (width > props.src.width) {
|
|
root.classList.add("compact");
|
|
} else {
|
|
root.classList.remove("compact");
|
|
}
|
|
});
|
|
|
|
const imgAverageColor = createMemo(() => {
|
|
if (!props.src.image) return;
|
|
return new Color(averageColorHex(props.src.blurhash));
|
|
});
|
|
|
|
const prefersWhiteText = createMemo(() => {
|
|
const oc = imgAverageColor();
|
|
if (!oc) return;
|
|
const colorWhite = new Color("white");
|
|
|
|
return colorWhite.luminance / oc.luminance > 3.5;
|
|
});
|
|
|
|
const focusSurfaceColor = createMemo(() => {
|
|
const oc = imgAverageColor();
|
|
if (!oc) return;
|
|
if (prefersWhiteText()) {
|
|
return new Color(oc).darken(0.2);
|
|
} else {
|
|
return new Color(oc).lighten(0.2);
|
|
}
|
|
});
|
|
|
|
const textColorName = createMemo(() => {
|
|
const useWhiteText = prefersWhiteText();
|
|
if (typeof useWhiteText === "undefined") {
|
|
return;
|
|
}
|
|
return useWhiteText ? "white" : "black";
|
|
});
|
|
|
|
const secondaryTextColor = createMemo(() => {
|
|
const tcn = textColorName();
|
|
if (!tcn) return;
|
|
const tc = new Color(tcn);
|
|
tc.alpha = 0.75;
|
|
return tc;
|
|
});
|
|
|
|
return (
|
|
<a
|
|
ref={root!}
|
|
class={"PreviewCard"}
|
|
href={props.src.url}
|
|
target="_blank"
|
|
referrerPolicy="unsafe-url"
|
|
style={{
|
|
"--tutu-color-surface": imgAverageColor()?.toString(),
|
|
"--tutu-color-surface-d": focusSurfaceColor()?.toString(),
|
|
"--tutu-color-on-surface": textColorName(),
|
|
"--tutu-color-secondary-text-on-surface":
|
|
secondaryTextColor()?.toString(),
|
|
}}
|
|
>
|
|
<Show when={props.src.image}>
|
|
<img
|
|
onLoadStart={onResetImg}
|
|
onLoad={onImgLoaded}
|
|
crossOrigin="anonymous"
|
|
src={props.src.image!}
|
|
width={props.src.width || undefined}
|
|
height={props.src.height || undefined}
|
|
loading="lazy"
|
|
/>
|
|
</Show>
|
|
<Title component="h1">{props.src.title}</Title>
|
|
<Body1 component="p">{props.src.description}</Body1>
|
|
</a>
|
|
);
|
|
}
|
|
|
|
export default PreviewCard;
|