- 新增收藏功能:favorite 服务与 handler、FavoriteButton 组件 - 新增关注功能:follow 服务与 handler、FollowButton 组件 - 用户主页适配关注/粉丝与收藏展示 - flash 消息工具、广告与侧边栏组件微调
93 lines
2.3 KiB
TypeScript
93 lines
2.3 KiB
TypeScript
"use client";
|
||
|
||
import { useEffect, useRef, useState, type CSSProperties } from "react";
|
||
import { AD_TIP_DELAY_MS, adHoverTip } from "@/lib/ads";
|
||
|
||
type AdTextProps = {
|
||
title: string;
|
||
textColor?: string;
|
||
bgColor?: string;
|
||
href?: string;
|
||
/** 广告到期时间(ISO 字符串):有值时气泡首行展示,无值时仅显示免责声明 */
|
||
endsAt?: string | null;
|
||
className?: string;
|
||
};
|
||
|
||
/**
|
||
* 文字广告:仅当文字宽度超出容器时才启用跑马灯滚动,
|
||
* 能完整显示时居中静止。结构与样式复用侧栏 .j13-ad-text。
|
||
*/
|
||
export function AdText({
|
||
title,
|
||
textColor,
|
||
bgColor,
|
||
href,
|
||
endsAt,
|
||
className = "",
|
||
}: AdTextProps) {
|
||
const trackRef = useRef<HTMLSpanElement>(null);
|
||
const [overflow, setOverflow] = useState(false);
|
||
|
||
useEffect(() => {
|
||
const track = trackRef.current;
|
||
if (!track) return;
|
||
const measure = () => {
|
||
const run = track.querySelector<HTMLElement>(".j13-ad-text-run");
|
||
const container = track.parentElement;
|
||
if (!run || !container) return;
|
||
setOverflow(run.scrollWidth > container.clientWidth);
|
||
};
|
||
measure();
|
||
window.addEventListener("resize", measure);
|
||
return () => window.removeEventListener("resize", measure);
|
||
}, [title]);
|
||
|
||
const cls = `j13-ad-text${overflow ? " is-overflow" : ""}${className ? ` ${className}` : ""}`;
|
||
const style: CSSProperties = { color: textColor, background: bgColor };
|
||
|
||
// 气泡首行:到期时间;第二行:免责声明
|
||
const tip = adHoverTip(endsAt);
|
||
|
||
const inner = (
|
||
<span
|
||
className={`j13-ad-text-track${overflow ? " is-overflow" : ""}`}
|
||
ref={trackRef}
|
||
>
|
||
<span className="j13-ad-text-run">{title}</span>
|
||
{overflow ? (
|
||
<span className="j13-ad-text-run" aria-hidden>
|
||
{title}
|
||
</span>
|
||
) : null}
|
||
</span>
|
||
);
|
||
|
||
if (href) {
|
||
return (
|
||
<a
|
||
href={href}
|
||
target="_blank"
|
||
rel="noopener noreferrer sponsored"
|
||
className={cls}
|
||
style={style}
|
||
data-tip={tip}
|
||
data-tip-delay={AD_TIP_DELAY_MS}
|
||
>
|
||
{inner}
|
||
</a>
|
||
);
|
||
}
|
||
return (
|
||
<span
|
||
className={cls}
|
||
style={style}
|
||
data-tip={tip}
|
||
data-tip-delay={AD_TIP_DELAY_MS}
|
||
>
|
||
{inner}
|
||
</span>
|
||
);
|
||
}
|
||
|
||
export default AdText;
|