ultisuite-client/components/drive/richtext/docs-line-spacing-dialog.tsx
R3D347HR4Y 303b2b1074
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
wow
2026-06-11 01:22:40 +02:00

172 lines
5.7 KiB
TypeScript

"use client"
import { useEffect, useState } from "react"
import { Button } from "@/components/ui/button"
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import {
DOCS_DEFAULT_LINE_HEIGHT,
DOCS_LINE_HEIGHT_PRESETS,
} from "@/lib/drive/docs-line-spacing"
import type { DocsParagraphSpacingAttrs } from "@/lib/drive/docs-line-spacing"
import {
DRIVE_BTN_GHOST,
DRIVE_BTN_PRIMARY,
DRIVE_DIALOG_BODY,
DRIVE_DIALOG_CONTENT,
DRIVE_DIALOG_FOOTER,
DRIVE_DIALOG_HEADER,
DRIVE_FIELD_CLASS,
DRIVE_LABEL_CLASS,
DRIVE_TEXT_TITLE,
} from "@/lib/drive/drive-dialog-styles"
import { cn } from "@/lib/utils"
export function DocsLineSpacingDialog({
open,
onOpenChange,
initial,
onApply,
}: {
open: boolean
onOpenChange: (open: boolean) => void
initial: DocsParagraphSpacingAttrs
onApply: (input: Pick<DocsParagraphSpacingAttrs, "lineHeight" | "spaceBeforePt" | "spaceAfterPt">) => void
}) {
const [lineHeightPreset, setLineHeightPreset] = useState<string>("1.15")
const [customLineHeight, setCustomLineHeight] = useState(String(DOCS_DEFAULT_LINE_HEIGHT))
const [spaceBefore, setSpaceBefore] = useState("0")
const [spaceAfter, setSpaceAfter] = useState("0")
useEffect(() => {
if (!open) return
const height = initial.lineHeight ?? DOCS_DEFAULT_LINE_HEIGHT
const preset = DOCS_LINE_HEIGHT_PRESETS.find((item) => Math.abs(item.value - height) < 0.001)
setLineHeightPreset(preset?.id ?? "custom")
setCustomLineHeight(String(height))
setSpaceBefore(String(initial.spaceBeforePt))
setSpaceAfter(String(initial.spaceAfterPt))
}, [initial, open])
const resolvedLineHeight = (): number | null => {
if (lineHeightPreset === "custom") {
const value = Number.parseFloat(customLineHeight)
return Number.isFinite(value) && value > 0 ? value : DOCS_DEFAULT_LINE_HEIGHT
}
const preset = DOCS_LINE_HEIGHT_PRESETS.find((item) => item.id === lineHeightPreset)
return preset?.value ?? DOCS_DEFAULT_LINE_HEIGHT
}
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className={cn(DRIVE_DIALOG_CONTENT, "max-w-md gap-0 p-0")}>
<DialogHeader className={DRIVE_DIALOG_HEADER}>
<DialogTitle className={cn("text-base font-medium", DRIVE_TEXT_TITLE)}>
Espacement personnalisé
</DialogTitle>
</DialogHeader>
<div className={cn(DRIVE_DIALOG_BODY, "space-y-4")}>
<div className="space-y-2">
<Label className={DRIVE_LABEL_CLASS} htmlFor="docs-line-height-preset">
Interligne
</Label>
<Select value={lineHeightPreset} onValueChange={setLineHeightPreset}>
<SelectTrigger id="docs-line-height-preset" className={DRIVE_FIELD_CLASS}>
<SelectValue />
</SelectTrigger>
<SelectContent>
{DOCS_LINE_HEIGHT_PRESETS.map((preset) => (
<SelectItem key={preset.id} value={preset.id}>
{preset.label}
</SelectItem>
))}
<SelectItem value="custom">Personnalisé</SelectItem>
</SelectContent>
</Select>
{lineHeightPreset === "custom" ? (
<Input
type="number"
min={0.5}
max={5}
step={0.05}
value={customLineHeight}
onChange={(event) => setCustomLineHeight(event.target.value)}
className={DRIVE_FIELD_CLASS}
aria-label="Interligne personnalisé"
/>
) : null}
</div>
<div className="grid grid-cols-2 gap-3">
<div className="space-y-2">
<Label className={DRIVE_LABEL_CLASS} htmlFor="docs-space-before">
Avant (pt)
</Label>
<Input
id="docs-space-before"
type="number"
min={0}
max={108}
step={1}
value={spaceBefore}
onChange={(event) => setSpaceBefore(event.target.value)}
className={DRIVE_FIELD_CLASS}
/>
</div>
<div className="space-y-2">
<Label className={DRIVE_LABEL_CLASS} htmlFor="docs-space-after">
Après (pt)
</Label>
<Input
id="docs-space-after"
type="number"
min={0}
max={108}
step={1}
value={spaceAfter}
onChange={(event) => setSpaceAfter(event.target.value)}
className={DRIVE_FIELD_CLASS}
/>
</div>
</div>
</div>
<DialogFooter className={DRIVE_DIALOG_FOOTER}>
<Button type="button" variant="ghost" className={DRIVE_BTN_GHOST} onClick={() => onOpenChange(false)}>
Annuler
</Button>
<Button
type="button"
className={DRIVE_BTN_PRIMARY}
onClick={() => {
onApply({
lineHeight: resolvedLineHeight(),
spaceBeforePt: Math.max(0, Number.parseFloat(spaceBefore) || 0),
spaceAfterPt: Math.max(0, Number.parseFloat(spaceAfter) || 0),
})
onOpenChange(false)
}}
>
Appliquer
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}