Some checks are pending
E2E / Playwright e2e (push) Waiting to run
- Updated select trigger components for paragraph style and font family to include a ghost variant for improved visual consistency. - Adjusted CSS styles to standardize height and padding, enhancing the overall appearance of select elements. - Improved hover and focus states for better user interaction feedback across different themes.
181 lines
5.8 KiB
TypeScript
181 lines
5.8 KiB
TypeScript
"use client"
|
|
|
|
import { useMemo, useState } from "react"
|
|
import {
|
|
buildParagraphStyleMenuEntries,
|
|
type DocParagraphStyleDefinition,
|
|
type DocParagraphStylesCatalog,
|
|
} from "@/lib/drive/docs-paragraph-styles"
|
|
import { paragraphStylePreviewStyle } from "@/lib/drive/docs-paragraph-styles-css"
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectGroup,
|
|
SelectItem,
|
|
SelectLabel,
|
|
SelectSeparator,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from "@/components/ui/select"
|
|
import { cn } from "@/lib/utils"
|
|
|
|
export function DocsParagraphStyleSelect({
|
|
value,
|
|
disabled,
|
|
documentStyles,
|
|
userStyles,
|
|
onValueChange,
|
|
triggerClassName,
|
|
}: {
|
|
value: string
|
|
disabled?: boolean
|
|
documentStyles: DocParagraphStylesCatalog
|
|
userStyles: DocParagraphStylesCatalog
|
|
onValueChange: (styleId: string) => void
|
|
triggerClassName?: string
|
|
}) {
|
|
const entries = useMemo(
|
|
() => buildParagraphStyleMenuEntries(documentStyles, userStyles),
|
|
[documentStyles, userStyles]
|
|
)
|
|
|
|
const documentEntries = entries.filter((entry) => entry.section === "document")
|
|
const userEntries = entries.filter((entry) => entry.section === "user")
|
|
const active = entries.find((entry) => entry.definition.id === value)?.definition
|
|
|
|
return (
|
|
<Select disabled={disabled} value={value} onValueChange={onValueChange}>
|
|
<SelectTrigger
|
|
variant="ghost"
|
|
className={cn(
|
|
"docs-toolbar-select docs-toolbar-select--style h-7 w-[120px] shrink-0 border-0 bg-transparent px-1 py-0 shadow-none",
|
|
triggerClassName
|
|
)}
|
|
>
|
|
<SelectValue placeholder="Style">{active?.name ?? "Texte normal"}</SelectValue>
|
|
</SelectTrigger>
|
|
<SelectContent className="docs-toolbar-select-content docs-toolbar-select-content--style max-h-[min(70vh,480px)]">
|
|
<SelectGroup>
|
|
<SelectLabel>Styles du document</SelectLabel>
|
|
{documentEntries.map(({ definition }) => (
|
|
<SelectItem
|
|
key={definition.id}
|
|
value={definition.id}
|
|
className="docs-toolbar-style-item"
|
|
>
|
|
<span className="docs-toolbar-style-preview" style={paragraphStylePreviewStyle(definition)}>
|
|
{definition.name}
|
|
</span>
|
|
</SelectItem>
|
|
))}
|
|
</SelectGroup>
|
|
{userEntries.length > 0 ? (
|
|
<>
|
|
<SelectSeparator />
|
|
<SelectGroup>
|
|
<SelectLabel>Mes styles</SelectLabel>
|
|
{userEntries.map(({ definition }) => (
|
|
<SelectItem
|
|
key={`user-${definition.id}`}
|
|
value={definition.id}
|
|
className="docs-toolbar-style-item"
|
|
>
|
|
<span
|
|
className="docs-toolbar-style-preview"
|
|
style={paragraphStylePreviewStyle(definition)}
|
|
>
|
|
{definition.name}
|
|
</span>
|
|
</SelectItem>
|
|
))}
|
|
</SelectGroup>
|
|
</>
|
|
) : null}
|
|
</SelectContent>
|
|
</Select>
|
|
)
|
|
}
|
|
|
|
export function DocsCreateStyleDialog({
|
|
open,
|
|
onOpenChange,
|
|
onCreate,
|
|
}: {
|
|
open: boolean
|
|
onOpenChange: (open: boolean) => void
|
|
onCreate: (input: { name: string; basedOn: string }) => void
|
|
}) {
|
|
const [name, setName] = useState("")
|
|
const [basedOn, setBasedOn] = useState("normal")
|
|
|
|
return open ? (
|
|
<div className="fixed inset-0 z-[200] flex items-center justify-center bg-black/30 p-4">
|
|
<div
|
|
className="w-full max-w-md rounded-lg border bg-background p-4 shadow-lg"
|
|
role="dialog"
|
|
aria-modal="true"
|
|
aria-labelledby="docs-create-style-title"
|
|
>
|
|
<h2 id="docs-create-style-title" className="text-base font-medium">
|
|
Créer un style
|
|
</h2>
|
|
<p className="mt-1 text-sm text-muted-foreground">
|
|
Le nouveau style sera enregistré dans vos styles personnels.
|
|
</p>
|
|
<div className="mt-4 space-y-3">
|
|
<label className="block space-y-1">
|
|
<span className="text-sm">Nom</span>
|
|
<input
|
|
className="flex h-9 w-full rounded-md border bg-background px-3 text-sm"
|
|
value={name}
|
|
onChange={(event) => setName(event.target.value)}
|
|
placeholder="Mon style"
|
|
/>
|
|
</label>
|
|
<label className="block space-y-1">
|
|
<span className="text-sm">Basé sur</span>
|
|
<select
|
|
className="flex h-9 w-full rounded-md border bg-background px-3 text-sm"
|
|
value={basedOn}
|
|
onChange={(event) => setBasedOn(event.target.value)}
|
|
>
|
|
<option value="normal">Normal</option>
|
|
<option value="title">Titre</option>
|
|
<option value="subtitle">Sous-titre</option>
|
|
<option value="heading1">Titre 1</option>
|
|
<option value="heading2">Titre 2</option>
|
|
<option value="heading3">Titre 3</option>
|
|
<option value="heading4">Titre 4</option>
|
|
</select>
|
|
</label>
|
|
</div>
|
|
<div className="mt-4 flex justify-end gap-2">
|
|
<button
|
|
type="button"
|
|
className="rounded-md px-3 py-1.5 text-sm hover:bg-muted"
|
|
onClick={() => onOpenChange(false)}
|
|
>
|
|
Annuler
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className="rounded-md bg-primary px-3 py-1.5 text-sm text-primary-foreground"
|
|
onClick={() => {
|
|
onCreate({ name: name.trim() || "Style personnalisé", basedOn })
|
|
setName("")
|
|
setBasedOn("normal")
|
|
onOpenChange(false)
|
|
}}
|
|
>
|
|
Créer
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
) : null
|
|
}
|
|
|
|
export function paragraphStyleSubmenuLabel(definition: DocParagraphStyleDefinition) {
|
|
return definition.name
|
|
}
|