All checks were successful
Container-Image bauen und veröffentlichen / build-and-push (push) Successful in 5m15s
93 lines
2.7 KiB
TypeScript
93 lines
2.7 KiB
TypeScript
"use client"
|
||
|
||
import { useEffect, useState } from "react"
|
||
import { Eye, Pencil } from "lucide-react"
|
||
|
||
import { Label } from "@/components/ui/label"
|
||
import { Textarea } from "@/components/ui/textarea"
|
||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||
import { MarkdownRenderer } from "@/components/markdown-renderer"
|
||
import { cn } from "@/lib/utils"
|
||
|
||
interface MarkdownInputProps {
|
||
id: string
|
||
label: string
|
||
value: string
|
||
onChange: (value: string) => void
|
||
placeholder?: string
|
||
required?: boolean
|
||
rows?: number
|
||
className?: string
|
||
invalid?: boolean
|
||
}
|
||
|
||
export function MarkdownInput({
|
||
id,
|
||
label,
|
||
value,
|
||
onChange,
|
||
placeholder,
|
||
required = false,
|
||
rows = 6,
|
||
className,
|
||
invalid = false,
|
||
}: MarkdownInputProps) {
|
||
const [tab, setTab] = useState<"edit" | "preview">("edit")
|
||
useEffect(() => { if (invalid) setTab("edit") }, [invalid])
|
||
|
||
return (
|
||
<Tabs
|
||
value={tab}
|
||
onValueChange={(next) => setTab(next as "edit" | "preview")}
|
||
className={cn("gap-2", className)}
|
||
>
|
||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||
<Label htmlFor={id} className="gap-1">
|
||
{label}
|
||
{required && <span className="text-destructive"> (Pflichtfeld)</span>}
|
||
</Label>
|
||
<TabsList aria-label={label + ": Ansicht"} className="h-8">
|
||
<TabsTrigger value="edit" className="px-2.5">
|
||
<Pencil />
|
||
<span className="sr-only">Bearbeiten</span>
|
||
</TabsTrigger>
|
||
<TabsTrigger value="preview" className="px-2.5">
|
||
<Eye />
|
||
<span className="sr-only">Vorschau</span>
|
||
</TabsTrigger>
|
||
</TabsList>
|
||
</div>
|
||
|
||
<TabsContent value="edit">
|
||
<Textarea
|
||
id={id}
|
||
value={value}
|
||
onChange={(e) => onChange(e.target.value)}
|
||
placeholder={placeholder}
|
||
rows={rows}
|
||
aria-invalid={invalid}
|
||
aria-required={required}
|
||
aria-describedby={invalid ? id + "-error" : undefined}
|
||
className="resize-y font-mono text-sm"
|
||
/>
|
||
{invalid && <p id={id + "-error"} className="text-sm text-destructive">Bitte ergänzen oder korrigieren Sie dieses Feld.</p>}
|
||
</TabsContent>
|
||
|
||
<TabsContent value="preview">
|
||
<div
|
||
className="rounded-md border bg-muted/30 p-4"
|
||
style={{ minHeight: `${rows * 1.5 + 1}rem` }}
|
||
>
|
||
{value ? (
|
||
<MarkdownRenderer content={value} />
|
||
) : (
|
||
<p className="text-sm text-muted-foreground italic">
|
||
Noch kein Inhalt – im Bearbeiten-Tab eingeben.
|
||
</p>
|
||
)}
|
||
</div>
|
||
</TabsContent>
|
||
</Tabs>
|
||
)
|
||
}
|