204 lines
5.8 KiB
TypeScript
204 lines
5.8 KiB
TypeScript
"use client"
|
||
|
||
import type { ReactNode } from "react"
|
||
import {
|
||
DOCS_BULLET_STYLE_PRESETS,
|
||
DOCS_ORDERED_STYLE_PRESETS,
|
||
type DocsBulletStyleId,
|
||
type DocsChecklistStyleId,
|
||
type DocsOrderedStyleId,
|
||
} from "@/lib/drive/docs-list-styles"
|
||
import { cn } from "@/lib/utils"
|
||
|
||
function PresetButton({
|
||
disabled,
|
||
active,
|
||
onClick,
|
||
children,
|
||
className,
|
||
label,
|
||
}: {
|
||
disabled?: boolean
|
||
active?: boolean
|
||
onClick?: () => void
|
||
children: ReactNode
|
||
className?: string
|
||
label: string
|
||
}) {
|
||
return (
|
||
<button
|
||
type="button"
|
||
disabled={disabled}
|
||
aria-label={label}
|
||
aria-pressed={active}
|
||
onClick={onClick}
|
||
className={cn(
|
||
"rounded border border-[#dadce0] bg-white p-2 text-left text-[10px] leading-tight text-[#3c4043]",
|
||
"transition-colors hover:bg-[#f1f3f4] disabled:cursor-not-allowed disabled:opacity-50",
|
||
"dark:border-border dark:bg-background dark:text-foreground dark:hover:bg-muted",
|
||
active && "border-[#1a73e8] ring-1 ring-[#1a73e8]",
|
||
className
|
||
)}
|
||
>
|
||
{children}
|
||
</button>
|
||
)
|
||
}
|
||
|
||
export function DocsFormatColumnPresets({ disabled }: { disabled?: boolean }) {
|
||
return (
|
||
<div className="grid grid-cols-3 gap-2 px-1">
|
||
{[1, 2, 3].map((count) => (
|
||
<PresetButton
|
||
key={count}
|
||
disabled={disabled}
|
||
active={count === 1}
|
||
label={`${count} colonne${count > 1 ? "s" : ""}`}
|
||
className="flex h-14 items-stretch justify-center gap-0.5 p-1.5"
|
||
>
|
||
{Array.from({ length: count }, (_, index) => (
|
||
<span
|
||
key={index}
|
||
className="flex flex-1 flex-col justify-center gap-0.5 rounded-sm bg-[#e8eaed] px-0.5 py-1 dark:bg-muted"
|
||
>
|
||
<span className="block h-px w-full bg-[#9aa0a6]" />
|
||
<span className="block h-px w-full bg-[#9aa0a6]" />
|
||
<span className="block h-px w-3/4 bg-[#9aa0a6]" />
|
||
</span>
|
||
))}
|
||
</PresetButton>
|
||
))}
|
||
</div>
|
||
)
|
||
}
|
||
|
||
const NUMBERED_PRESET_LINES: Record<DocsOrderedStyleId, readonly string[]> = {
|
||
decimal: ["1.", " a.", " i."],
|
||
"decimal-paren": ["1)", " a)", " i)"],
|
||
outline: ["1.", " 1.1.", " 1.1.1."],
|
||
"upper-alpha": ["A.", " a.", " i.", "B."],
|
||
"upper-roman": ["I.", " A.", " 1.", "II."],
|
||
"zero-padded": ["01.", "02."],
|
||
}
|
||
|
||
export function DocsFormatNumberedPresets({
|
||
disabled,
|
||
activeStyleId,
|
||
onSelect,
|
||
}: {
|
||
disabled?: boolean
|
||
activeStyleId?: DocsOrderedStyleId | null | "mixed"
|
||
onSelect: (styleId: DocsOrderedStyleId) => void
|
||
}) {
|
||
return (
|
||
<div className="grid grid-cols-3 gap-2">
|
||
{DOCS_ORDERED_STYLE_PRESETS.map((preset) => (
|
||
<PresetButton
|
||
key={preset.id}
|
||
disabled={disabled}
|
||
active={activeStyleId !== "mixed" && activeStyleId === preset.id}
|
||
label={`Liste numérotée ${preset.id}`}
|
||
onClick={() => onSelect(preset.id)}
|
||
className="min-h-[72px] font-mono"
|
||
>
|
||
{NUMBERED_PRESET_LINES[preset.id].map((line) => (
|
||
<span key={line} className="block whitespace-pre">
|
||
{line}
|
||
</span>
|
||
))}
|
||
</PresetButton>
|
||
))}
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export function DocsFormatBulletPresets({
|
||
disabled,
|
||
activeStyleId,
|
||
onSelect,
|
||
}: {
|
||
disabled?: boolean
|
||
activeStyleId?: DocsBulletStyleId | null | "mixed"
|
||
onSelect: (styleId: DocsBulletStyleId) => void
|
||
}) {
|
||
const symbols: Record<DocsBulletStyleId, string> = {
|
||
disc: "•",
|
||
circle: "◦",
|
||
square: "▪",
|
||
arrow: "➤",
|
||
check: "✓",
|
||
dash: "–",
|
||
}
|
||
|
||
return (
|
||
<div className="grid grid-cols-3 gap-2">
|
||
{DOCS_BULLET_STYLE_PRESETS.map((preset) => (
|
||
<PresetButton
|
||
key={preset.id}
|
||
disabled={disabled}
|
||
active={activeStyleId !== "mixed" && activeStyleId === preset.id}
|
||
label={`Liste à puces ${preset.id}`}
|
||
onClick={() => onSelect(preset.id)}
|
||
className="min-h-[56px]"
|
||
>
|
||
<span className="block">
|
||
{symbols[preset.id]} Élément
|
||
</span>
|
||
<span className="block">
|
||
{symbols[preset.id]} Élément
|
||
</span>
|
||
</PresetButton>
|
||
))}
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export function DocsFormatChecklistPresets({
|
||
disabled,
|
||
activeStyleId,
|
||
onSelect,
|
||
}: {
|
||
disabled?: boolean
|
||
activeStyleId?: DocsChecklistStyleId | null | "mixed"
|
||
onSelect: (styleId: DocsChecklistStyleId) => void
|
||
}) {
|
||
return (
|
||
<div className="grid grid-cols-2 gap-2">
|
||
<PresetButton
|
||
disabled={disabled}
|
||
active={activeStyleId !== "mixed" && activeStyleId === "strikethrough"}
|
||
label="Checklist avec texte barré"
|
||
className="min-h-[56px]"
|
||
onClick={() => onSelect("strikethrough")}
|
||
>
|
||
<span className="flex items-center gap-1.5">
|
||
<span className="inline-flex size-3 shrink-0 rounded-sm border border-[#5f6368]" />
|
||
Tâche
|
||
</span>
|
||
<span className="flex items-center gap-1.5 line-through opacity-70">
|
||
<span className="inline-flex size-3 shrink-0 items-center justify-center rounded-sm border border-[#1a73e8] bg-[#1a73e8] text-[8px] text-white">
|
||
✓
|
||
</span>
|
||
Terminé
|
||
</span>
|
||
</PresetButton>
|
||
<PresetButton
|
||
disabled={disabled}
|
||
active={activeStyleId !== "mixed" && activeStyleId === "simple"}
|
||
label="Checklist simple"
|
||
className="min-h-[56px]"
|
||
onClick={() => onSelect("simple")}
|
||
>
|
||
<span className="flex items-center gap-1.5">
|
||
<span className="inline-flex size-3 shrink-0 rounded-sm border border-[#5f6368]" />
|
||
Tâche
|
||
</span>
|
||
<span className="flex items-center gap-1.5">
|
||
<span className="inline-flex size-3 shrink-0 rounded-sm border border-[#5f6368]" />
|
||
Tâche
|
||
</span>
|
||
</PresetButton>
|
||
</div>
|
||
)
|
||
}
|