Skip to content
File

Blob: src/shared/editor/presentation/callout.tsx

typescript59 lines
1import { AlertTriangle, Info, Lightbulb, type LucideIcon } from "lucide-react";
2import type { ReactNode } from "react";
3import { DEFAULT_CALLOUT_KIND, normalizeCalloutKind, type CalloutKind } from "@/shared/editor/schema/callout-model";
4import { bidAttribute, type BlockIdentityProps } from "./attrs";
5 
6interface CalloutKindMeta {
7 label: string;
8 Icon: LucideIcon;
9}
10 
11export const CALLOUT_KIND_META: Record<CalloutKind, CalloutKindMeta> = {
12 info: { label: "Info", Icon: Info },
13 tip: { label: "Tip", Icon: Lightbulb },
14 warning: { label: "Warning", Icon: AlertTriangle },
15};
16 
17export interface CalloutKindContentProps {
18 kind: unknown;
19}
20 
21export function getCalloutKindMeta(kind: unknown): CalloutKindMeta {
22 return CALLOUT_KIND_META[normalizeCalloutKind(kind)] ?? CALLOUT_KIND_META[DEFAULT_CALLOUT_KIND];
23}
24 
25export function CalloutKindContent({ kind }: CalloutKindContentProps) {
26 const { Icon, label } = getCalloutKindMeta(kind);
27 return (
28 <>
29 <Icon size={14} aria-hidden />
30 <span>{label}</span>
31 </>
32 );
33}
34 
35export interface CalloutPresentationProps extends BlockIdentityProps {
36 kind: unknown;
37 controls?: ReactNode;
38 readOnly?: boolean;
39 children?: ReactNode;
40}
41 
42export function CalloutPresentation({ bid, kind, controls, readOnly = false, children }: CalloutPresentationProps) {
43 const normalizedKind = normalizeCalloutKind(kind);
44 return (
45 <div className="tiptap-callout" data-callout="" data-callout-kind={normalizedKind} {...bidAttribute(bid)}>
46 {controls ?? (
47 <div
48 className="tiptap-callout-kind-btn"
49 data-read-only={readOnly ? "" : undefined}
50 aria-label={`Callout: ${getCalloutKindMeta(normalizedKind).label}`}
51 >
52 <CalloutKindContent kind={normalizedKind} />
53 </div>
54 )}
55 <div className="tiptap-callout-body">{children}</div>
56 </div>
57 );
58}