184 lines
5.2 KiB
TypeScript
184 lines
5.2 KiB
TypeScript
"use client"
|
|
|
|
import type { DocsGraphicPositionMode, DocsGraphicWrap } from "@/lib/drive/docs-graphic-types"
|
|
import { cn } from "@/lib/utils"
|
|
|
|
function PreviewFrame({ children, className }: { children: React.ReactNode; className?: string }) {
|
|
return (
|
|
<div
|
|
className={cn(
|
|
"docs-graphic-layout-preview relative overflow-hidden rounded border border-muted-foreground/25 bg-background",
|
|
className
|
|
)}
|
|
aria-hidden
|
|
>
|
|
{children}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function TextLines({
|
|
count = 3,
|
|
className,
|
|
inset,
|
|
}: {
|
|
count?: number
|
|
className?: string
|
|
inset?: string
|
|
}) {
|
|
return (
|
|
<div className={cn("flex flex-col gap-[3px]", inset, className)}>
|
|
{Array.from({ length: count }).map((_, i) => (
|
|
<span
|
|
key={i}
|
|
className="block h-[3px] rounded-full bg-muted-foreground/35"
|
|
style={{ width: i === count - 1 ? "72%" : "100%" }}
|
|
/>
|
|
))}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function ImageBlock({ className }: { className?: string }) {
|
|
return (
|
|
<span
|
|
className={cn(
|
|
"block shrink-0 rounded-[2px] border border-primary/50 bg-primary/25",
|
|
className
|
|
)}
|
|
/>
|
|
)
|
|
}
|
|
|
|
export function DocsGraphicWrapPreview({ wrap }: { wrap: DocsGraphicWrap }) {
|
|
switch (wrap) {
|
|
case "inline":
|
|
return (
|
|
<PreviewFrame className="flex h-11 items-center gap-1 px-1.5">
|
|
<TextLines count={1} className="w-[22%]" />
|
|
<ImageBlock className="size-4" />
|
|
<TextLines count={1} className="flex-1" />
|
|
</PreviewFrame>
|
|
)
|
|
case "square":
|
|
return (
|
|
<PreviewFrame className="flex h-11 gap-1.5 p-1.5">
|
|
<ImageBlock className="h-full w-[34%]" />
|
|
<TextLines count={3} className="flex-1 pt-0.5" />
|
|
</PreviewFrame>
|
|
)
|
|
case "tight":
|
|
return (
|
|
<PreviewFrame className="flex h-11 gap-0.5 p-1">
|
|
<ImageBlock className="h-full w-[30%]" />
|
|
<TextLines count={4} className="flex-1 gap-[2px] pt-0" />
|
|
</PreviewFrame>
|
|
)
|
|
case "through":
|
|
return (
|
|
<PreviewFrame className="h-11 p-1.5">
|
|
<ImageBlock className="absolute inset-1.5 opacity-45" />
|
|
<TextLines count={3} className="relative z-10 h-full justify-center" />
|
|
</PreviewFrame>
|
|
)
|
|
case "top-bottom":
|
|
return (
|
|
<PreviewFrame className="flex h-11 flex-col gap-1 p-1.5">
|
|
<ImageBlock className="h-[42%] w-full" />
|
|
<TextLines count={2} className="flex-1" />
|
|
</PreviewFrame>
|
|
)
|
|
case "behind":
|
|
return (
|
|
<PreviewFrame className="h-11 p-1.5">
|
|
<ImageBlock className="absolute inset-1.5 opacity-35" />
|
|
<TextLines count={3} className="relative z-10 h-full justify-center" />
|
|
</PreviewFrame>
|
|
)
|
|
case "in-front":
|
|
return (
|
|
<PreviewFrame className="h-11 p-1.5">
|
|
<TextLines count={3} className="absolute inset-x-1.5 top-2" />
|
|
<ImageBlock className="absolute bottom-1.5 left-1/2 z-10 h-[52%] w-[46%] -translate-x-1/2 opacity-90" />
|
|
</PreviewFrame>
|
|
)
|
|
default:
|
|
return <PreviewFrame className="h-11" />
|
|
}
|
|
}
|
|
|
|
export function DocsGraphicPositionModePreview({
|
|
mode,
|
|
}: {
|
|
mode: DocsGraphicPositionMode
|
|
}) {
|
|
if (mode === "move-with-text") {
|
|
return (
|
|
<PreviewFrame className="flex h-11 flex-col justify-center gap-1 p-1.5">
|
|
<div className="flex items-center gap-1">
|
|
<TextLines count={1} className="w-[28%]" />
|
|
<ImageBlock className="size-4" />
|
|
<TextLines count={1} className="flex-1" />
|
|
</div>
|
|
<TextLines count={1} className="w-full" />
|
|
<TextLines count={1} className="w-[80%]" />
|
|
</PreviewFrame>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<PreviewFrame className="h-11 p-1">
|
|
<span className="absolute inset-1 rounded border border-dashed border-muted-foreground/30" />
|
|
<ImageBlock className="absolute right-2 top-2 h-[38%] w-[32%]" />
|
|
<TextLines count={2} className="absolute bottom-2 left-2 right-2" />
|
|
</PreviewFrame>
|
|
)
|
|
}
|
|
|
|
export function DocsGraphicMarginPreview({ mm }: { mm: number }) {
|
|
const gap = mm === 0 ? 1 : mm <= 3 ? 3 : mm <= 6 ? 5 : 8
|
|
return (
|
|
<PreviewFrame className="flex h-11 items-start gap-0 p-1.5">
|
|
<div className="flex h-full items-start" style={{ gap: `${gap}px` }}>
|
|
<ImageBlock className="h-[70%] w-[38%]" />
|
|
<TextLines count={3} className="w-[52%] pt-0.5" />
|
|
</div>
|
|
</PreviewFrame>
|
|
)
|
|
}
|
|
|
|
export function DocsGraphicPageAnchorPreview({
|
|
h,
|
|
v,
|
|
active,
|
|
}: {
|
|
h: 0 | 0.5 | 1
|
|
v: 0 | 0.5 | 1
|
|
active?: boolean
|
|
}) {
|
|
return (
|
|
<span
|
|
className={cn(
|
|
"flex size-9 rounded-md border p-1",
|
|
active
|
|
? "border-primary bg-primary/5"
|
|
: "border-border bg-background hover:border-muted-foreground/50"
|
|
)}
|
|
>
|
|
<span
|
|
className={cn(
|
|
"relative flex size-full rounded-[2px] border border-muted-foreground/30",
|
|
h === 0 && "justify-start",
|
|
h === 0.5 && "justify-center",
|
|
h === 1 && "justify-end",
|
|
v === 0 && "items-start",
|
|
v === 0.5 && "items-center",
|
|
v === 1 && "items-end"
|
|
)}
|
|
>
|
|
<span className="size-2 rounded-[1px] bg-primary/70" />
|
|
</span>
|
|
</span>
|
|
)
|
|
}
|