Skip to content

Commit

Permalink
💄 外部リンクへの案内 (ホバー) 追加
Browse files Browse the repository at this point in the history
  • Loading branch information
wappon28dev committed Jun 19, 2024
1 parent 596b7b8 commit f64f94d
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 103 deletions.
4 changes: 3 additions & 1 deletion client/postcss.config.cjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module.exports = {
plugins: [require("@pandacss/dev/postcss")()],
plugins: {
"@pandacss/dev/postcss": {},
},
};
183 changes: 119 additions & 64 deletions client/src/components/VisualMain.astro
Original file line number Diff line number Diff line change
Expand Up @@ -8,41 +8,49 @@ import {
} from "@client/lib/middlewares/rewriters/convert2media";
import { modifySrc } from "@client/lib/services/media";
import { YouTube, Vimeo } from "astro-embed";
import { Icon } from "astro-icon/components";
import { Image, getImage } from "astro:assets";
import { css } from "panda/css";
import { styled as p } from "panda/jsx";
import { HStack, styled as p } from "panda/jsx";
import { inferImageSize } from "src/lib/services/image";
import { AbsCenter } from "src/lib/style";
import type { works } from "src/lib/types/cms-types";
import { match } from "ts-pattern";
import { VideoPlayer } from "./VideoPlayer";
type VisualKey = MediaKey | "youtube" | "vimeo";
type Props = {
url: string;
alt: string;
worksDetails: works;
};
const { url, alt } = Astro.props;
let key: MediaKey | "youtube" | "vimeo" | undefined;
let tmbUrl: string | undefined;
const {
worksDetails: { visualMain: url, title, externalLink },
} = Astro.props;
let tmbUrl: string | undefined;
let imgSize: { width: number; height: number } = { height: 0, width: 0 };
const media = async (): Promise<void> => {
const media = async (): Promise<VisualKey> => {
const ext = url.split(".").at(-1);
if (ext == null) throw new Error(`ext is null: ${url}`);
key = detectMediaKey(ext);
if (key === "img") {
imgSize = inferImageSize(modifySrc(url));
}
return match(detectMediaKey(ext))
.with("img", () => {
imgSize = inferImageSize(modifySrc(url));
return "img" as const;
})
.otherwise((k) => k);
};
const youtube = async (): Promise<void> => {
const youtube = async (): Promise<VisualKey> => {
const id = matcherYoutube(url);
if (id == null) throw new Error(`youtube match is null: ${url}`);
const src = `https://i.ytimg.com/vi/${id}/maxresdefault.jpg`;
tmbUrl = (await getImage({ src, alt, inferSize: true })).src;
key = "youtube";
tmbUrl = (await getImage({ src, title, inferSize: true })).src;
return "youtube";
};
const vimeo = async (): Promise<void> => {
const vimeo = async (): Promise<VisualKey> => {
const id = matcherVimeo(url);
if (id == null) throw new Error(`vimeo match is null: ${url}`);
const res = await fetch(`https://vimeo.com/api/v2/video/${id}.json`);
Expand All @@ -51,68 +59,115 @@ const vimeo = async (): Promise<void> => {
}> = await res.json();
const src = json[0]?.thumbnail_large;
if (src == null) throw new Error(`vimeo thumbnail is null: ${url}`);
tmbUrl = (await getImage({ src, alt, inferSize: true })).src;
key = "vimeo";
tmbUrl = (await getImage({ src, title, inferSize: true })).src;
return "vimeo";
};
if (url.startsWith(INFO.site.assets)) {
await media();
} else if (matcherYoutube(url) != null) {
await youtube();
} else if (matcherVimeo(url) != null) {
await vimeo();
} else {
throw new Error(`unknown url: ${url}`);
}
const key: VisualKey = await match(url)
.when((u) => u.startsWith(INFO.site.assets), media)
.when(matcherYoutube, youtube)
.when(matcherVimeo, vimeo)
.otherwise(() => {
throw new Error(`unknown url: ${url}`);
});
---

<p.div
_hover={{
"& .external-link-card": {
opacity: 1,
},
}}
className={css({
"& > *": {
h: "100%",
maxH: "66vh", // タイトルが見える高さ
aspectRatio: "16 / 9",
margin: "0 auto",
},
})}
position="relative"
>
{
(() => {
switch (key) {
case "img":
return (
<Image
alt={alt}
height={imgSize.height}
src={modifySrc(url)}
style={{
width: "auto",
}}
width={imgSize.width}
/>
);
case "video":
return <VideoPlayer autoPlay loop muted src={modifySrc(url)} />;
case "youtube":
return (
<YouTube
id={url}
params="widget_referrer"
playlabel="作品を再生する"
poster={tmbUrl ?? ""}
/>
);
case "vimeo":
return (
<Vimeo
id={url}
params="portrait&title"
playlabel="作品を再生する"
poster={tmbUrl ?? ""}
/>
);
default:
throw new Error(`unknown key`);
}
})()
externalLink != null && (
<p.a
className="external-link-card"
cursor="pointer"
display="block"
href={externalLink}
opacity="0"
position="absolute"
transition="opacity 0.3s"
>
<p.div display="block" h="100%" position="relative" w="100%">
<AbsCenter
bg="9u-red1"
h="100%"
opacity="0.5"
position="absolute"
w="100%"
/>
<AbsCenter
color="9u-white"
p="3"
pointerEvents="none"
position="absolute"
w="max-content"
>
<HStack gap="1">
<p.div transform="rotate(-45deg)">
{/* @ts-expect-error: size should be number */}
<Icon name="mdi:link" size="1lh" />
</p.div>
<p.p
fontSize="xl"
fontWeight="bold"
lineClamp="1"
textAlign="center"
textOverflow="ellipsis"
>
{new URL(externalLink).hostname} で視聴
</p.p>
</HStack>
</AbsCenter>
</p.div>
</p.a>
)
}
<p.div pointerEvents="none">
{
match(key)
.with("img", () => (
<Image
alt={title}
height={imgSize.height}
src={modifySrc(url)}
style={{
width: "auto",
}}
width={imgSize.width}
/>
))
.with("video", () => (
<VideoPlayer autoPlay loop muted src={modifySrc(url)} />
))
.with("youtube", () => (
<YouTube
id={url}
params="widget_referrer"
playlabel="作品を再生する"
poster={tmbUrl ?? ""}
/>
))
.with("vimeo", () => (
<Vimeo
id={url}
params="portrait&title"
playlabel="作品を再生する"
poster={tmbUrl ?? ""}
/>
))
.exhaustive()
}
</p.div>
</p.div>
2 changes: 2 additions & 0 deletions client/src/components/VisualSub.astro
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ type Props = {
};
type Img = { url: string; width: number; height: number };
const { data } = Astro.props;
if (data == null) throw new Error("data is required");
---

<VStack
Expand Down
6 changes: 3 additions & 3 deletions client/src/components/list/BlogsList.astro
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ const returnToHome = needOmit ? "?returnToHome" : "";
<p.a href={`/blogs/${id}${returnToHome}`} minW="300px" w="300px">
<Image
alt={title}
height={ogpImg.height}
src={ogpImg.url}
width={ogpImg.width}
height={ogpImg?.height ?? 1080}
src={ogpImg?.url ?? "/assets/images/sub-visual.png"}
width={ogpImg?.width ?? 1920}
/>
</p.a>
<p.a
Expand Down
26 changes: 6 additions & 20 deletions client/src/components/list/WorksList.astro
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { getContentList } from "@client/lib/services/microcms";
import { Image } from "astro:assets";
import { css } from "panda/css";
import { styled as p } from "panda/jsx";
import { AbsCenter } from "src/lib/style";
const worksList = await getContentList("works", {
orders: "-releaseDate",
Expand Down Expand Up @@ -46,19 +47,15 @@ const worksList = await getContentList("works", {
src={previewImg?.url ?? "/assets/images/sub-visual.png"}
width={previewImg?.width ?? 1920}
/>
<p.div
<AbsCenter
h="100%"
left="50%"
opacity={{
base: "0",
_hover: "1",
}}
position="absolute"
style={{
pointerEvents: previewVideo != null ? "none" : "auto",
}}
top="50%"
transform="translate(-50%, -50%)"
transition="opacity 0.3s"
w="100%"
>
Expand All @@ -76,25 +73,14 @@ const worksList = await getContentList("works", {
}}
/>
)}
<p.div
<AbsCenter
bg="9u-red1"
h="100%"
left="50%"
opacity="0.5"
position="absolute"
top="50%"
transform="translate(-50%, -50%)"
w="100%"
/>
<p.div
color="9u-white"
left="50%"
p="3"
position="absolute"
top="50%"
transform="translate(-50%, -50%)"
w="100%"
>
<AbsCenter color="9u-white" p="3" position="absolute" w="100%">
<p.p
fontSize="xl"
fontWeight="bold"
Expand All @@ -104,9 +90,9 @@ const worksList = await getContentList("works", {
{title}
</p.p>
<p.p>{formatDate(new Date(releaseDate), "YYYY.MM")}</p.p>
</p.div>
</AbsCenter>
</p.div>
</p.div>
</AbsCenter>
</p.a>
),
)
Expand Down
15 changes: 14 additions & 1 deletion client/src/lib/style.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { css } from "panda/css";
import { css, cva } from "panda/css";

import { styled as p } from "panda/jsx";

export const contentStyle = css({
display: "flex",
Expand Down Expand Up @@ -58,6 +60,7 @@ export const contentStyle = css({
"& > *": {
w: "fit-content",
margin: "0 auto",
maxH: "100vh",
},
"& figcaption": {
pt: "1",
Expand Down Expand Up @@ -103,3 +106,13 @@ export const formStyle = css({
},
},
});

export const absCenterStyle = cva({
base: {
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
},
});
export const AbsCenter = p("div", absCenterStyle);
6 changes: 3 additions & 3 deletions client/src/lib/types/cms-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ type DateType = {
revisedAt: string;
};
type Structure<T, P> = T extends "get"
? { id: string } & DateType & Required<P>
? { id: string } & DateType & P
: T extends "gets"
? GetsType<{ id: string } & DateType & Required<P>>
? GetsType<{ id: string } & DateType & P>
: Partial<DateType> & (T extends "patch" ? Partial<P> : P);

export type works<T = "get"> = Structure<
Expand All @@ -22,7 +22,7 @@ export type works<T = "get"> = Structure<
/**
* メインビジュアル
*/
visualMain?: string;
visualMain: string;
/**
* タイトル
*/
Expand Down
Loading

0 comments on commit f64f94d

Please sign in to comment.