"use client" import { useMemo, useState, type ReactNode } from "react" import { Icon } from "@iconify/react" import { AlignCenter, AlignJustify, AlignLeft, AlignRight, Bold, Check, Italic, List, ListOrdered, Strikethrough, Underline, } from "lucide-react" import { MenubarContent, MenubarItem, MenubarMenu, MenubarSeparator, MenubarShortcut, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, } from "@/components/ui/menubar" import { DocsExclusiveMenuSub, DocsExclusiveMenuSubRoot, } from "@/components/drive/richtext/docs-exclusive-menu-sub" import { DocsFormatBulletPresets, DocsFormatChecklistPresets, DocsFormatColumnPresets, DocsFormatNumberedPresets, } from "@/components/drive/richtext/docs-format-menu-presets" import { DocsCreateStyleDialog } from "@/components/drive/richtext/docs-paragraph-style-ui" import { DocsLineSpacingDialog } from "@/components/drive/richtext/docs-line-spacing-dialog" import { DocsListStartDialog } from "@/components/drive/richtext/docs-list-start-dialog" import { DOCS_MENUBAR_CONTENT_PROPS } from "@/components/drive/richtext/docs-menubar-props" import { DOCS_LINE_HEIGHT_PRESETS } from "@/lib/drive/docs-line-spacing" import { buildParagraphStyleMenuEntries } from "@/lib/drive/docs-paragraph-styles" import { useDocsParagraphStylesContext } from "@/lib/drive/docs-paragraph-styles-context" import { docsModKeyLabel, isMacPlatform } from "@/lib/drive/docs-keyboard-shortcut" import { DOCS_TABLE_BORDER_COLOR_PRESETS } from "@/lib/drive/docs-table-types" import { cn } from "@/lib/utils" export type DocsFormatMenuState = { isBold: boolean isItalic: boolean isUnderline: boolean isStrike: boolean alignLeft: boolean alignCenter: boolean alignRight: boolean alignJustify: boolean isBulletList: boolean isOrderedList: boolean styleId: string tableSelected: boolean tableCanMerge: boolean tableCanSplit: boolean graphicSelected: boolean imageSelected: boolean lineHeightPresetId: string | "mixed" | null hasSpaceBefore: boolean | "mixed" hasSpaceAfter: boolean | "mixed" keepWithNext: boolean | "mixed" keepLinesTogether: boolean | "mixed" preventWidowOrphan: boolean | "mixed" pageBreakBefore: boolean | "mixed" customSpacingDraft: import("@/lib/drive/docs-line-spacing").DocsParagraphSpacingAttrs isTaskList: boolean bulletStyleId: import("@/lib/drive/docs-list-styles").DocsBulletStyleId | null | "mixed" orderedStyleId: import("@/lib/drive/docs-list-styles").DocsOrderedStyleId | null | "mixed" checklistStyleId: import("@/lib/drive/docs-list-styles").DocsChecklistStyleId | null | "mixed" orderedListStart: number | "mixed" } export type DocsFormatMenuActions = { onBold: () => void onItalic: () => void onUnderline: () => void onStrike: () => void onAlignLeft: () => void onAlignCenter: () => void onAlignRight: () => void onAlignJustify: () => void onIncreaseIndent: () => void onDecreaseIndent: () => void onToggleBulletList: () => void onToggleOrderedList: () => void onApplyStyle: (styleId: string) => void onClearFormatting: () => void onPageSetup?: () => void onTableAddRowBefore: () => void onTableAddRowAfter: () => void onTableAddColumnBefore: () => void onTableAddColumnAfter: () => void onTableDeleteRow: () => void onTableDeleteColumn: () => void onTableDelete: () => void onTableMergeCells: () => void onTableSplitCell: () => void onTableToggleHeaderRow: () => void onTableToggleHeaderColumn: () => void onTableSetCellBackground: (color: string | null) => void onTableAlignLeft: () => void onTableAlignCenter: () => void onTableAlignRight: () => void onTableFixStructure: () => void onTableSetCellBorders: (color: string) => void onTableClearCellBorders: () => void onTableSetRowHeight: (height: string | null) => void onSetLineHeight: (lineHeight: number) => void onToggleSpaceBefore: () => void onToggleSpaceAfter: () => void onApplyCustomSpacing: (input: { lineHeight: number | null spaceBeforePt: number spaceAfterPt: number }) => void onToggleKeepWithNext: () => void onToggleKeepLinesTogether: () => void onTogglePreventWidowOrphan: () => void onTogglePageBreakBefore: () => void onApplyBulletStyle: (styleId: import("@/lib/drive/docs-list-styles").DocsBulletStyleId) => void onApplyOrderedStyle: (styleId: import("@/lib/drive/docs-list-styles").DocsOrderedStyleId) => void onApplyChecklistStyle: (styleId: import("@/lib/drive/docs-list-styles").DocsChecklistStyleId) => void onRestartOrderedList: () => void onContinueOrderedList: () => void onSetOrderedListStart: (start: number) => void } const LINE_SPACING_OPTIONS = DOCS_LINE_HEIGHT_PRESETS function MenuIcon({ children }: { children: ReactNode }) { return {children} } function FormatShortcut({ children }: { children: ReactNode }) { return {children} } function modShortcut(key: string, options?: { shift?: boolean; alt?: boolean }) { const mod = docsModKeyLabel() if (isMacPlatform()) { const parts: string[] = [] if (options?.shift) parts.push("Maj") if (options?.alt) parts.push("⌥") if (parts.length > 0) return `${mod}+${parts.join("+")}+${key}` return `${mod}${key}` } const parts: string[] = [] if (options?.shift) parts.push("Shift") if (options?.alt) parts.push("Alt") parts.push(mod, key) return parts.join("+") } function CheckMenuOption({ checked, onSelect, children, disabled, className, }: { checked: boolean onSelect: () => void children: ReactNode disabled?: boolean className?: string }) { return ( {checked ? : null} {children} ) } function ParagraphStyleSubmenu({ styleId, label, disabled, onApply, onUpdate, menuDisabled, }: { styleId: string label: string disabled?: boolean onApply: (styleId: string) => void onUpdate: (styleId: string) => void menuDisabled?: boolean }) { return ( {label} onApply(styleId)} > Appliquer le style {label} onUpdate(styleId)} > Mettre à jour "{label}" en fonction de la sélection/l'emplacement du signe d'insertion ) } export function DocsFormatMenu({ actions, state, disabled, }: { actions: DocsFormatMenuActions state: DocsFormatMenuState disabled?: boolean }) { const paragraphStylesCtx = useDocsParagraphStylesContext() const [createStyleOpen, setCreateStyleOpen] = useState(false) const [customSpacingOpen, setCustomSpacingOpen] = useState(false) const [listStartOpen, setListStartOpen] = useState(false) const styleEntries = useMemo(() => { if (!paragraphStylesCtx) return [] return buildParagraphStyleMenuEntries( paragraphStylesCtx.state.documentStyles, paragraphStylesCtx.state.userStyles ) }, [paragraphStylesCtx]) const objectFormattingDisabled = disabled || (!state.tableSelected && !state.graphicSelected && !state.imageSelected) return ( Format Texte Gras {modShortcut("B")} Italique {modShortcut("I")} Souligner {modShortcut("U")} Barrer {modShortcut("X", { shift: true })} Sc Petites majuscules {isMacPlatform() ? "⌥+Maj+K" : "Alt+Shift+K"} X2 Exposant {modShortcut(".")} X2 Indice {modShortcut(",")} Taille Bientôt disponible Minuscules/majuscules Bientôt disponible Styles de paragraphe Bordures et ombrage {styleEntries .filter((entry) => entry.section === "document") .map(({ definition }) => ( undefined) } /> ))} {styleEntries.some((entry) => entry.section === "user") ? ( <>
Mes styles
{styleEntries .filter((entry) => entry.section === "user") .map(({ definition }) => ( undefined) } /> ))} ) : null} Options setCreateStyleOpen(true)} > Créer un style
Aligner et mettre en retrait À gauche {modShortcut("L", { shift: true })} Centrer {modShortcut("E", { shift: true })} À droite {modShortcut("R", { shift: true })} Justifié {modShortcut("J", { shift: true })} Augmenter le retrait {modShortcut("]")} Diminuer le retrait {modShortcut("[")} Options de mise en retrait Interligne et espace entre paragraphes {LINE_SPACING_OPTIONS.map((option) => ( actions.onSetLineHeight(option.value)} > {option.label} ))} Insérer un espacement avant le paragraphe Insérer un espacement après le paragraphe setCustomSpacingOpen(true)} > Espacement personnalisé Rattacher au paragraphe suivant Ne pas laisser de ligne isolée Empêcher les lignes isolées Ajouter un saut de page avant Colonnes Autres options Puces et numéros Options de liste Recommencer la numérotation Continuer la numérotation précédente setListStartOpen(true)} > Modifier le numéro de départ… Liste numérotée Liste à puces Menu de type checklist
En-têtes et pieds de page Numéros de page Orientation de la page Passer au format Sans pages Tableau Insérer une ligne au-dessus Insérer une ligne en dessous Insérer une colonne à gauche Insérer une colonne à droite Fusionner les cellules Scinder la cellule Ligne d'en-tête Colonne d'en-tête Couleur de cellule actions.onTableSetCellBackground(null)} > Aucune {[ { label: "Gris clair", color: "#f3f3f3" }, { label: "Bleu clair", color: "#c9daf8" }, { label: "Vert clair", color: "#d9ead3" }, { label: "Jaune clair", color: "#fff2cc" }, ].map((preset) => ( actions.onTableSetCellBackground(preset.color)} > {preset.label} ))} Aligner le tableau à gauche Centrer le tableau Aligner le tableau à droite Supprimer la ligne Supprimer la colonne Supprimer le tableau Réparer la structure Image Bientôt disponible Bordures et lignes actions.onTableSetCellBorders("#000000")} > Bordures sur toutes les cellules Supprimer les bordures Couleur des bordures {DOCS_TABLE_BORDER_COLOR_PRESETS.map((color) => ( actions.onTableSetCellBorders(color)} > {color} ))} actions.onTableSetRowHeight("32px")} > Hauteur de ligne compacte actions.onTableSetRowHeight("48px")} > Hauteur de ligne normale actions.onTableSetRowHeight(null)} > Hauteur de ligne automatique Supprimer la mise en forme {modShortcut("\\")}
paragraphStylesCtx?.createUserStyle(input)} />
) }