RegularToot: added preview cards
This commit is contained in:
parent
bd1a515ef8
commit
df19e54d9b
5 changed files with 144 additions and 11 deletions
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
|
@ -36,7 +36,9 @@
|
|||
"@suid/icons-material": "^0.7.0",
|
||||
"@suid/material": "^0.16.0",
|
||||
"blurhash": "^2.0.5",
|
||||
"colorjs.io": "^0.5.2",
|
||||
"date-fns": "^3.6.0",
|
||||
"fast-average-color": "^9.4.0",
|
||||
"hammerjs": "^2.0.8",
|
||||
"masto": "^6.8.0",
|
||||
"nanostores": "^0.9.5",
|
||||
|
|
|
@ -18,7 +18,7 @@ const CompactToot: Component<CompactTootProps> = (props) => {
|
|||
const toot = () => props.status;
|
||||
return (
|
||||
<section
|
||||
class={[tootStyle.compact, props.class || ""].join(" ")}
|
||||
class={[tootStyle.toot, tootStyle.compact, props.class || ""].join(" ")}
|
||||
lang={toot().language || undefined}
|
||||
>
|
||||
<Img
|
||||
|
|
|
@ -5,11 +5,19 @@ import {
|
|||
type JSX,
|
||||
Show,
|
||||
createRenderEffect,
|
||||
createSignal,
|
||||
createEffect,
|
||||
} from "solid-js";
|
||||
import tootStyle from "./toot.module.css";
|
||||
import { formatRelative } from "date-fns";
|
||||
import Img from "../material/Img.js";
|
||||
import { Body2 } from "../material/typography.js";
|
||||
import {
|
||||
Body1,
|
||||
Body2,
|
||||
Caption,
|
||||
Subheading,
|
||||
Title,
|
||||
} from "../material/typography.js";
|
||||
import { css } from "solid-styled";
|
||||
import {
|
||||
BookmarkAddOutlined,
|
||||
|
@ -26,6 +34,8 @@ import { Divider } from "@suid/material";
|
|||
import cardStyle from "../material/cards.module.css";
|
||||
import Button from "../material/Button.js";
|
||||
import MediaAttachmentGrid from "./MediaAttachmentGrid.js";
|
||||
import { FastAverageColor } from "fast-average-color";
|
||||
import Color from "colorjs.io";
|
||||
|
||||
type TootContentViewProps = {
|
||||
source?: string;
|
||||
|
@ -187,6 +197,67 @@ function TootAuthorGroup(props: { status: mastodon.v1.Status; now: Date }) {
|
|||
);
|
||||
}
|
||||
|
||||
function TootPreviewCard(props: { src: mastodon.v1.PreviewCard }) {
|
||||
let root: HTMLAnchorElement;
|
||||
|
||||
createEffect(() => {
|
||||
if (!props.src.width) return;
|
||||
const width = root.getBoundingClientRect().width;
|
||||
if (width > props.src.width) {
|
||||
root.classList.add(tootStyle.compact);
|
||||
} else {
|
||||
root.classList.remove(tootStyle.compact);
|
||||
}
|
||||
});
|
||||
|
||||
const onImgLoad = (event: Event & { currentTarget: HTMLImageElement }) => {
|
||||
// TODO: better extraction algorithm
|
||||
// I'd like to use a pattern panel and match one in the panel from the extracted color
|
||||
const fac = new FastAverageColor();
|
||||
const result = fac.getColor(event.currentTarget);
|
||||
if (result.error) {
|
||||
console.error(result.error);
|
||||
fac.destroy();
|
||||
return;
|
||||
}
|
||||
root.style.setProperty("--tutu-color-surface", result.hex);
|
||||
const focusSurface = result.isDark
|
||||
? new Color(result.hex).darken(0.2)
|
||||
: new Color(result.hex).lighten(0.2);
|
||||
root.style.setProperty("--tutu-color-surface-d", focusSurface.toString());
|
||||
const textColor = result.isDark ? "white" : "black";
|
||||
const secondaryTextColor = new Color(textColor);
|
||||
secondaryTextColor.alpha = 0.75;
|
||||
root.style.setProperty("--tutu-color-on-surface", textColor);
|
||||
root.style.setProperty(
|
||||
"--tutu-color-secondary-text-on-surface",
|
||||
secondaryTextColor.toString(),
|
||||
);
|
||||
fac.destroy();
|
||||
};
|
||||
|
||||
return (
|
||||
<a
|
||||
ref={root!}
|
||||
class={tootStyle.previewCard}
|
||||
href={props.src.url}
|
||||
target="_blank"
|
||||
referrerPolicy="unsafe-url"
|
||||
>
|
||||
<Show when={props.src.image}>
|
||||
<img
|
||||
crossOrigin="anonymous"
|
||||
src={props.src.image!}
|
||||
onLoad={onImgLoad}
|
||||
loading="lazy"
|
||||
/>
|
||||
</Show>
|
||||
<Title component="h1">{props.src.title}</Title>
|
||||
<Body1 component="p">{props.src.description}</Body1>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
const RegularToot: Component<TootCardProps> = (props) => {
|
||||
let rootRef: HTMLElement;
|
||||
const [managed, managedActionGroup, rest] = splitProps(
|
||||
|
@ -241,6 +312,9 @@ const RegularToot: Component<TootCardProps> = (props) => {
|
|||
emojis={toot().emojis}
|
||||
class={tootStyle.tootContent}
|
||||
/>
|
||||
<Show when={toot().card}>
|
||||
<TootPreviewCard src={toot().card!} />
|
||||
</Show>
|
||||
<Show when={toot().mediaAttachments.length > 0}>
|
||||
<MediaAttachmentGrid attachments={toot().mediaAttachments} />
|
||||
</Show>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
border-radius: 0;
|
||||
}
|
||||
|
||||
& > .toot {
|
||||
&>.toot {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@
|
|||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
|
||||
> * {
|
||||
>* {
|
||||
color: var(--tutu-color-secondary-text-on-surface);
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@
|
|||
grid-column: 1 /3;
|
||||
}
|
||||
|
||||
> time {
|
||||
>time {
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
|
@ -104,7 +104,64 @@
|
|||
}
|
||||
}
|
||||
|
||||
.compact {
|
||||
.previewCard {
|
||||
composes: cardGutSkip from "../material/cards.module.css";
|
||||
display: block;
|
||||
border: 1px solid #eeeeee;
|
||||
background-color: var(--tutu-color-surface);
|
||||
text-decoration: none;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1.5em;
|
||||
color: var(--tutu-color-secondary-text-on-surface);
|
||||
transition: color 220ms var(--tutu-anim-curve-std), background-color 220ms var(--tutu-anim-curve-std);
|
||||
padding-bottom: 8px;
|
||||
|
||||
>img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
background-color: var(--tutu-color-surface-d);
|
||||
color: var(--tutu-color-on-surface);
|
||||
|
||||
>h1 {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
>h1 {
|
||||
color: var(--tutu-color-on-surface);
|
||||
}
|
||||
|
||||
>h1,
|
||||
>p {
|
||||
margin-left: 16px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
&.compact {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(10%, 30%) 1fr;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
|
||||
>img:first-child {
|
||||
grid-row: 1 / 3;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
>h1,
|
||||
>p {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.toot.compact {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: 8px;
|
||||
|
@ -127,12 +184,12 @@
|
|||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
|
||||
> .compactAuthorUsername {
|
||||
>.compactAuthorUsername {
|
||||
color: var(--tutu-color-secondary-text-on-surface);
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
> time {
|
||||
>time {
|
||||
color: var(--tutu-color-secondary-text-on-surface);
|
||||
}
|
||||
}
|
||||
|
@ -177,11 +234,11 @@
|
|||
flex-flow: row wrap;
|
||||
justify-content: space-evenly;
|
||||
|
||||
> button {
|
||||
>button {
|
||||
color: var(--tutu-color-on-surface);
|
||||
padding: 10px 8px;
|
||||
|
||||
> svg {
|
||||
>svg {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
@ -207,4 +264,4 @@
|
|||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue