diff --git a/src/timelines/RegularToot.tsx b/src/timelines/RegularToot.tsx index ffcae6a..f72e4fc 100644 --- a/src/timelines/RegularToot.tsx +++ b/src/timelines/RegularToot.tsx @@ -5,13 +5,11 @@ import { type JSX, Show, createRenderEffect, - createEffect, - createMemo, } from "solid-js"; import tootStyle from "./toot.module.css"; import { formatRelative } from "date-fns"; import Img from "../material/Img.js"; -import { Body1, Body2, Title } from "../material/typography.js"; +import { Body2 } from "../material/typography.js"; import { css } from "solid-styled"; import { BookmarkAddOutlined, @@ -30,13 +28,12 @@ import { Divider } from "@suid/material"; import cardStyle from "../material/cards.module.css"; import Button from "../material/Button.js"; import MediaAttachmentGrid from "./MediaAttachmentGrid.js"; -import Color from "colorjs.io"; import { useDateFnLocale } from "../platform/i18n"; import { canShare, share } from "../platform/share"; import { makeAcctText, useDefaultSession } from "../masto/clients"; import TootContent from "./toot-components/TootContent"; import BoostIcon from "./toot-components/BoostIcon"; -import { averageColorHex } from "../platform/blurhash"; +import PreviewCard from "./toot-components/PreviewCard"; type TootActionGroupProps = { onRetoot?: (value: T) => void; @@ -186,95 +183,6 @@ function TootAuthorGroup( ); } -export function TootPreviewCard(props: { - src: mastodon.v1.PreviewCard; - alwaysCompact?: boolean; -}) { - let root: HTMLAnchorElement; - - createEffect(() => { - if (props.alwaysCompact) { - root.classList.add(tootStyle.compact); - return; - } - 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 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 ( - - - - - {props.src.title} - {props.src.description} - - ); -} - /** * find bottom-to-top the element with `data-action`. */ @@ -420,7 +328,7 @@ const RegularToot: Component = (props) => { class={tootStyle.tootContent} /> - + 0}> diff --git a/src/timelines/toot-components/PreviewCard.css b/src/timelines/toot-components/PreviewCard.css new file mode 100644 index 0000000..17d9f2b --- /dev/null +++ b/src/timelines/toot-components/PreviewCard.css @@ -0,0 +1,67 @@ +.PreviewCard { + 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; + overflow: hidden; + z-index: 1; + position: relative; + + >img { + background-color: #eeeeee; + max-width: 100%; + height: auto; + } + + &: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); + max-height: calc(4 * var(--title-line-height) * var(--title-size)); + } + + >p { + max-height: calc(8 * var(--body-line-height) * var(--body-size)); + } + + >h1, + >p { + margin-left: 16px; + margin-right: 16px; + overflow: hidden; + text-overflow: ellipsis; + } + + &.compact { + display: grid; + grid-template-columns: minmax(10%, 30%) 1fr; + padding-left: 16px; + padding-right: 16px; + padding-top: 8px; + + >img:first-child { + grid-row: 1 / 3; + object-fit: contain; + } + + >h1, + >p { + margin-right: 0; + } + } +} diff --git a/src/timelines/toot-components/PreviewCard.tsx b/src/timelines/toot-components/PreviewCard.tsx new file mode 100644 index 0000000..ed20eb8 --- /dev/null +++ b/src/timelines/toot-components/PreviewCard.tsx @@ -0,0 +1,97 @@ +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"; + +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 ( + + + + + {props.src.title} + {props.src.description} + + ); +} + +export default PreviewCard;