/**
* 登录态查价表单视觉 chrome(MotherShip / Flock 共用)
* 主色仅局部 #1890FF,不改全局 primary token
*/
"use client";
import { useEffect, useId, useRef, useState, type ReactNode } from "react";
import { CaretDown, Info, Warning } from "@phosphor-icons/react";
/** 查价表单局部主色 */
export const QUOTE_ACCENT = "#1890FF";
export const QUOTE_ACCENT_HOVER = "#1580E0";
export const QUOTE_TITLE = "#1F2937";
export const QUOTE_SECTION = "#4B5563";
export const QUOTE_REQUIRED = "#EF4444";
export const quoteInputCls =
"h-10 w-full rounded-md border border-border bg-surface px-3 text-sm text-text-primary transition-colors focus:border-[#1890FF] focus:outline-none focus:ring-2 focus:ring-[#1890FF]/20 disabled:opacity-50";
export const quoteInputErrCls =
"h-10 w-full rounded-md border border-[#EF4444] bg-surface px-3 text-sm text-text-primary focus:border-[#EF4444] focus:outline-none focus:ring-2 focus:ring-[#EF4444]/20 disabled:opacity-50";
export const quoteCtaCls =
"inline-flex h-10 min-w-[48px] items-center justify-center gap-2 rounded-md bg-[#1890FF] px-5 text-sm font-semibold text-white transition-colors hover:bg-[#1580E0] focus:outline-none focus:ring-2 focus:ring-[#1890FF]/30 disabled:cursor-not-allowed disabled:opacity-50";
export const quoteAddRowCls =
"inline-flex h-10 items-center justify-center gap-1.5 rounded-md border border-border bg-surface px-3 text-sm font-medium text-text-primary transition-transform hover:-translate-y-0.5 hover:border-[#1890FF]/40 hover:text-[#1890FF] disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:translate-y-0";
export function QuoteRequiredMark() {
return (
*
);
}
/** ● 步骤1 → ○ 步骤2 → ○ 步骤3 */
export function QuoteStepProgress({ current = 1 }: { current?: 1 | 2 | 3 }) {
const steps = [
{ n: 1 as const, label: "步骤1:填写基础信息" },
{ n: 2 as const, label: "步骤2:确认价格" },
{ n: 3 as const, label: "步骤3:提交" },
];
return (
);
}
export function QuotePageTitle({ children }: { children: ReactNode }) {
return (
{children}
);
}
export function QuoteSectionTitle({ children }: { children: ReactNode }) {
return (
{children}
);
}
/** ⓘ 悬停/点击 Tooltip(全程 span,可放在 label/行内;tip 勿传 div/p) */
export function QuoteFieldTooltip({
tip,
label = "说明",
wide = false,
position = "above",
}: {
tip: ReactNode;
label?: string;
/** 长文案加宽 */
wide?: boolean;
position?: "above" | "below";
}) {
const tipId = useId();
const [pinned, setPinned] = useState(false);
const rootRef = useRef(null);
const posCls =
position === "below"
? "top-full left-0 mt-1.5 translate-x-0"
: "bottom-full left-1/2 mb-1.5 -translate-x-1/2";
const widthCls = wide
? "w-[min(22rem,calc(100vw-2rem))]"
: "w-56";
useEffect(() => {
if (!pinned) return;
const onDoc = (ev: MouseEvent) => {
const el = rootRef.current;
if (!el) return;
if (ev.target instanceof Node && !el.contains(ev.target)) {
setPinned(false);
}
};
const onKey = (ev: KeyboardEvent) => {
if (ev.key === "Escape") setPinned(false);
};
document.addEventListener("mousedown", onDoc);
document.addEventListener("keydown", onKey);
return () => {
document.removeEventListener("mousedown", onDoc);
document.removeEventListener("keydown", onKey);
};
}, [pinned]);
return (
{
e.preventDefault();
e.stopPropagation();
setPinned((v) => !v);
}}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
e.stopPropagation();
setPinned((v) => !v);
}
}}
>
{tip}
);
}
/** Tooltip 内:加粗标题 + 说明列表(仅用 span,避免嵌套 p/div 破坏外层 p) */
export function QuoteTipDefs({
intro,
items,
}: {
intro?: string;
items: ReadonlyArray<{ title: string; body: string }>;
}) {
return (
{intro ? (
{intro}
) : null}
{items.map((it) => (
{it.title}
{it.body}
))}
);
}
/** 可展开「注意事项」 */
export function QuoteNoticeCollapse({
title = "注意事项",
children,
defaultOpen = false,
}: {
title?: string;
children: ReactNode;
defaultOpen?: boolean;
}) {
const [open, setOpen] = useState(defaultOpen);
return (
{open ? (
{children}
) : null}
);
}
/** 校验失败浮动提示 */
export function QuoteFloatHint({ message }: { message: string | null }) {
if (!message) return null;
return (
{message}
);
}