diff --git a/src/app/(frontend)/(dev)/dev/common/page.tsx b/src/app/(frontend)/(dev)/dev/common/page.tsx index aeeda2b..cb6a8c1 100644 --- a/src/app/(frontend)/(dev)/dev/common/page.tsx +++ b/src/app/(frontend)/(dev)/dev/common/page.tsx @@ -30,6 +30,7 @@ import { DevSection } from "../_components/DevSection"; import ButtonMain from "@/components/ui/ButtonMain"; import SectionTitle from "@/components/ui/SectionTitle"; import { sampleNewsItems } from "../_data/sampleNews"; +import EventFrame from "@/components/ui/EventFrame"; const previewSlides = ["Slide 1", "Slide 2", "Slide 3"]; const noImportantNewsMessage = "現在、重要なお知らせはありません。"; @@ -147,6 +148,25 @@ export default function DevCommonComponentsPage() { + +
+ + + +
+
); diff --git a/src/components/aria/Tooltip.tsx b/src/components/aria/Tooltip.tsx index 3679a94..efca91d 100644 --- a/src/components/aria/Tooltip.tsx +++ b/src/components/aria/Tooltip.tsx @@ -13,7 +13,7 @@ export interface TooltipProps extends Omit { } const styles = tv({ - base: "group bg-neutral-700 dark:bg-neutral-600 border border-neutral-800 dark:border-white/10 font-sans text-xs text-white rounded-lg drop-shadow-lg will-change-transform px-3 py-1.5 box-border", + base: "group bg-white border border-neutral-200 font-sans text-xs text-neutral-900 rounded-lg drop-shadow-lg will-change-transform px-3 py-1.5 box-border", variants: { isEntering: { true: "animate-in fade-in placement-bottom:slide-in-from-top-0.5 placement-top:slide-in-from-bottom-0.5 placement-left:slide-in-from-right-0.5 placement-right:slide-in-from-left-0.5 ease-out duration-200", @@ -24,11 +24,11 @@ const styles = tv({ }, }); -export function Tooltip({ children, ...props }: TooltipProps) { +export function Tooltip({ children, offset = 10, ...props }: TooltipProps) { return ( styles({ ...renderProps, className }), )} @@ -38,7 +38,7 @@ export function Tooltip({ children, ...props }: TooltipProps) { width={8} height={8} viewBox="0 0 8 8" - className="group-placement-left:-rotate-90 group-placement-right:rotate-90 group-placement-bottom:rotate-180 block fill-neutral-700 stroke-neutral-800 dark:fill-neutral-600 dark:stroke-white/10 forced-colors:fill-[Canvas] forced-colors:stroke-[ButtonBorder]" + className="group-placement-left:-rotate-90 group-placement-right:rotate-90 group-placement-bottom:rotate-180 block fill-white stroke-neutral-200 forced-colors:fill-[Canvas] forced-colors:stroke-[ButtonBorder]" > diff --git a/src/components/ui/EventFrame.tsx b/src/components/ui/EventFrame.tsx new file mode 100644 index 0000000..185a0d9 --- /dev/null +++ b/src/components/ui/EventFrame.tsx @@ -0,0 +1,76 @@ +"use client"; + +import { useState } from "react"; +import Link from "next/link"; +import Image from "next/image"; +import { TooltipTrigger, Focusable } from "react-aria-components"; +import { Tooltip } from "@/components/aria/Tooltip"; + +type EventFrameProps = { + name: string; + href: string; + imageUrl: string; +}; + +const DISPLAY_NAME_MAX_LENGTH = 24; +const LARGE_TEXT_MAX_LENGTH = 14; +const TOOLTIP_OFFSET = -120; +const FALLBACK_LOGO = "/favicon/45th-LogoBlue.svg"; + +export default function EventFrame(props: EventFrameProps) { + const { name, href, imageUrl } = props; + + const [hasImageError, setHasImageError] = useState(false); + + const handleImageError = () => { + setHasImageError(true); + }; + + const isTruncated = name.length > DISPLAY_NAME_MAX_LENGTH; + + const displayName = isTruncated ? name.slice(0, DISPLAY_NAME_MAX_LENGTH - 1) + "…" : name; + + const nameClassName = + name.length <= LARGE_TEXT_MAX_LENGTH ? "text-textb" : "text-[14px] leading-[20px]"; + + const card = ( + +
+ {hasImageError ? ( + + ) : ( + + )} +
+ +
+
{displayName}
+
+ + ); + + if (!isTruncated) { + return card; + } + + return ( + + {card} + + {name} + + + ); +}