152 lines
4.5 KiB
TypeScript
152 lines
4.5 KiB
TypeScript
import type { CSSProperties } from "react"
|
|
import {
|
|
type DocsGraphicAttrs,
|
|
parseGraphicAttrs,
|
|
} from "./docs-graphic-types.ts"
|
|
|
|
export type DocsGraphicLayoutStyle = {
|
|
wrapper: CSSProperties
|
|
inner: CSSProperties
|
|
content: CSSProperties
|
|
behindText: boolean
|
|
inFrontText: boolean
|
|
}
|
|
|
|
export function computeGraphicLayoutStyle(
|
|
raw: Record<string, unknown> | DocsGraphicAttrs
|
|
): DocsGraphicLayoutStyle {
|
|
const attrs = "graphicType" in raw && typeof raw.graphicType === "string"
|
|
? (raw as DocsGraphicAttrs)
|
|
: parseGraphicAttrs(raw)
|
|
|
|
const width = attrs.width
|
|
const height = attrs.height
|
|
const rotation = attrs.rotationDeg
|
|
const transformParts = rotation ? [`rotate(${rotation}deg)`] : []
|
|
|
|
const behindText = attrs.wrap === "behind"
|
|
const inFrontText = attrs.wrap === "in-front"
|
|
const isAbsolute = attrs.placement === "absolute" || behindText || inFrontText
|
|
|
|
const wrapper: CSSProperties = {
|
|
width: isAbsolute ? undefined : width,
|
|
height: isAbsolute ? undefined : height,
|
|
maxWidth: "100%",
|
|
}
|
|
|
|
const inner: CSSProperties = {
|
|
width,
|
|
height,
|
|
transform: transformParts.length ? transformParts.join(" ") : undefined,
|
|
transformOrigin: "center center",
|
|
position: isAbsolute ? "absolute" : "relative",
|
|
left: isAbsolute ? attrs.x : undefined,
|
|
top: isAbsolute ? attrs.y : undefined,
|
|
zIndex: behindText ? 0 : inFrontText ? 20 : attrs.zIndex || undefined,
|
|
pointerEvents: behindText ? "none" : "auto",
|
|
opacity: attrs.opacity < 1 ? attrs.opacity : undefined,
|
|
boxShadow: attrs.shadow || undefined,
|
|
}
|
|
|
|
if (attrs.placement === "inline" || attrs.wrap === "inline") {
|
|
inner.display = "inline-block"
|
|
inner.verticalAlign = "baseline"
|
|
} else if (attrs.wrap === "top-bottom") {
|
|
inner.display = "block"
|
|
inner.clear = "both"
|
|
inner.marginBlock = "8px"
|
|
if (attrs.floatSide === "center") {
|
|
inner.marginInline = "auto"
|
|
}
|
|
} else if (attrs.wrap === "square" || attrs.wrap === "tight" || attrs.wrap === "through") {
|
|
inner.display = "block"
|
|
inner.float = attrs.floatSide === "right" ? "right" : "left"
|
|
inner.marginInlineStart = attrs.floatSide === "right" ? "12px" : undefined
|
|
inner.marginInlineEnd = attrs.floatSide === "right" ? undefined : "12px"
|
|
inner.marginBlock = "4px"
|
|
if (attrs.wrap === "tight") {
|
|
inner.shapeOutside = "margin-box"
|
|
inner.marginInlineStart = attrs.floatSide === "right" ? "6px" : undefined
|
|
inner.marginInlineEnd = attrs.floatSide === "right" ? undefined : "6px"
|
|
}
|
|
if (attrs.wrap === "through") {
|
|
inner.opacity = 0.85
|
|
inner.mixBlendMode = "multiply"
|
|
}
|
|
} else if (attrs.placement === "block") {
|
|
inner.display = "block"
|
|
inner.marginBlock = "8px"
|
|
if (attrs.floatSide === "center") {
|
|
inner.marginInline = "auto"
|
|
}
|
|
}
|
|
|
|
const content: CSSProperties = {
|
|
width: "100%",
|
|
height: "100%",
|
|
overflow: "hidden",
|
|
}
|
|
|
|
return { wrapper, inner, content, behindText, inFrontText }
|
|
}
|
|
|
|
export const RESIZE_HANDLES = [
|
|
"nw",
|
|
"n",
|
|
"ne",
|
|
"e",
|
|
"se",
|
|
"s",
|
|
"sw",
|
|
"w",
|
|
] as const
|
|
|
|
export type ResizeHandle = (typeof RESIZE_HANDLES)[number]
|
|
|
|
export function resizeWithHandle(
|
|
handle: ResizeHandle,
|
|
startWidth: number,
|
|
startHeight: number,
|
|
dx: number,
|
|
dy: number,
|
|
minSize = 24,
|
|
lockAspect = false
|
|
): { width: number; height: number; xOffset: number; yOffset: number } {
|
|
let width = startWidth
|
|
let height = startHeight
|
|
let xOffset = 0
|
|
let yOffset = 0
|
|
|
|
if (lockAspect) {
|
|
const aspect = startWidth / Math.max(startHeight, 1)
|
|
if (Math.abs(dx) >= Math.abs(dy)) {
|
|
width = startWidth + (handle.includes("w") ? -dx : handle.includes("e") ? dx : dx)
|
|
height = width / aspect
|
|
} else {
|
|
height = startHeight + (handle.includes("n") ? -dy : handle.includes("s") ? dy : dy)
|
|
width = height * aspect
|
|
}
|
|
if (handle.includes("w")) xOffset = startWidth - width
|
|
if (handle.includes("n")) yOffset = startHeight - height
|
|
} else {
|
|
if (handle.includes("e")) width = startWidth + dx
|
|
if (handle.includes("w")) {
|
|
width = startWidth - dx
|
|
xOffset = dx
|
|
}
|
|
if (handle.includes("s")) height = startHeight + dy
|
|
if (handle.includes("n")) {
|
|
height = startHeight - dy
|
|
yOffset = dy
|
|
}
|
|
}
|
|
|
|
width = Math.max(minSize, width)
|
|
height = Math.max(minSize, height)
|
|
|
|
if (handle.includes("w") && width === minSize) xOffset = startWidth - minSize
|
|
if (handle.includes("n") && height === minSize) yOffset = startHeight - minSize
|
|
|
|
return { width: Math.round(width), height: Math.round(height), xOffset, yOffset }
|
|
}
|