feat(vorlagen): Editor, Liste und Versionsgeschichte (#24)
This commit was merged in pull request #24.
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { KlappKarte, LoeschKnopf, TextKnopf, Textfeld } from "@/components/vorlagen/editor-felder";
|
||||
import { SortierBereich, SortierZeile, VerschiebeKnoepfe } from "@/components/vorlagen/editor-sortieren";
|
||||
import { SchrittKarte } from "@/components/vorlagen/editor-schritt";
|
||||
import { abschnittNummer, type EditorAbschnitt, type EditorSchritt } from "@/lib/editor-aufbau";
|
||||
import { GRENZEN } from "@/lib/vorlagen-format";
|
||||
|
||||
/**
|
||||
* Ein Abschnitt mit seinen Schritten (#010).
|
||||
*
|
||||
* Die Nummer links steht fuer die Position und wird nie gespeichert — wer den
|
||||
* Abschnitt verschiebt, bekommt sofort die neue Nummer zu sehen.
|
||||
*
|
||||
* Aufgeklappt als Standard, anders als Schritte und Variablen: man arbeitet in
|
||||
* aller Regel in einem Abschnitt und will seine Schritte sehen. Zugeklappt steht
|
||||
* die Zahl der Schritte daneben, damit beim Umsortieren klar ist, was da
|
||||
* gerade wandert.
|
||||
*
|
||||
* Schritte werden innerhalb ihres Abschnitts sortiert; jeder Abschnitt hat dazu
|
||||
* seinen eigenen Sortierbereich. Ein Schritt laesst sich damit nicht in einen
|
||||
* anderen Abschnitt ziehen — das braucht die Vorlagenpflege nicht, und ein
|
||||
* Bereich ueber alle Abschnitte hinweg haette am Handy Ziehwege von mehreren
|
||||
* Bildschirmhoehen.
|
||||
*/
|
||||
export function AbschnittKarte({
|
||||
abschnitt,
|
||||
index,
|
||||
anzahl,
|
||||
lesend,
|
||||
zuletztAngelegt,
|
||||
anfasser,
|
||||
aendern,
|
||||
loeschen,
|
||||
aufSortiert,
|
||||
schrittAendern,
|
||||
schrittHinzufuegen,
|
||||
schrittLoeschen,
|
||||
schritteUmsortieren,
|
||||
}: {
|
||||
abschnitt: EditorAbschnitt;
|
||||
index: number;
|
||||
anzahl: number;
|
||||
lesend: boolean;
|
||||
/** Kennung der zuletzt angelegten Zeile — die wird aufgeklappt gezeigt. */
|
||||
zuletztAngelegt: string | null;
|
||||
anfasser: React.ReactNode;
|
||||
aendern: (teil: Partial<EditorAbschnitt>) => void;
|
||||
loeschen: () => void;
|
||||
aufSortiert: (von: number, nach: number) => void;
|
||||
schrittAendern: (kennung: string, teil: Partial<EditorSchritt>) => void;
|
||||
schrittHinzufuegen: () => void;
|
||||
schrittLoeschen: (kennung: string) => void;
|
||||
schritteUmsortieren: (von: number, nach: number) => void;
|
||||
}) {
|
||||
const [offen, setzeOffen] = useState(true);
|
||||
const voll = abschnitt.schritte.length >= GRENZEN.schritteJeAbschnitt;
|
||||
const anzahlSchritte = abschnitt.schritte.length;
|
||||
|
||||
return (
|
||||
<KlappKarte
|
||||
offen={offen}
|
||||
umschalten={() => setzeOffen((vorher) => !vorher)}
|
||||
flaeche="flaeche"
|
||||
ueberschrift
|
||||
anfasser={anfasser}
|
||||
kopf={
|
||||
<>
|
||||
<span className="zahl shrink-0 text-lg text-akzent">{abschnittNummer(index)}</span>
|
||||
<span className="shrink-0 text-nebentext">·</span>
|
||||
<span className={`truncate font-semibold ${abschnitt.titel.trim() === "" ? "text-nebentext" : ""}`}>
|
||||
{abschnitt.titel.trim() === "" ? "ohne Titel" : abschnitt.titel}
|
||||
</span>
|
||||
<span className="shrink-0 text-xs font-normal text-nebentext">
|
||||
{anzahlSchritte} {anzahlSchritte === 1 ? "Schritt" : "Schritte"}
|
||||
</span>
|
||||
</>
|
||||
}
|
||||
knoepfe={
|
||||
lesend ? null : (
|
||||
<>
|
||||
<VerschiebeKnoepfe art="Abschnitt" index={index} anzahl={anzahl} aufSortiert={aufSortiert} />
|
||||
<LoeschKnopf
|
||||
beschriftung="Abschnitt löschen"
|
||||
{...(anzahlSchritte > 0
|
||||
? {
|
||||
frage: `Abschnitt mit ${anzahlSchritte} ${
|
||||
anzahlSchritte === 1 ? "Schritt" : "Schritten"
|
||||
} löschen?`,
|
||||
}
|
||||
: {})}
|
||||
aufLoeschen={loeschen}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
>
|
||||
{lesend ? null : (
|
||||
<Textfeld
|
||||
beschriftung="Titel des Abschnitts"
|
||||
wert={abschnitt.titel}
|
||||
setzen={(titel) => aendern({ titel })}
|
||||
lesend={lesend}
|
||||
vorschlag="Vorbereitung"
|
||||
/>
|
||||
)}
|
||||
|
||||
{anzahlSchritte === 0 ? (
|
||||
<p className="text-sm text-nebentext">Noch kein Schritt in diesem Abschnitt.</p>
|
||||
) : (
|
||||
<SortierBereich
|
||||
kennungen={abschnitt.schritte.map((schritt) => schritt.kennung)}
|
||||
art="Schritt"
|
||||
lesend={lesend}
|
||||
aufSortiert={schritteUmsortieren}
|
||||
>
|
||||
<ul className="flex flex-col gap-3">
|
||||
{abschnitt.schritte.map((schritt, stelle) => (
|
||||
<SortierZeile key={schritt.kennung} kennung={schritt.kennung} lesend={lesend}>
|
||||
{(schrittAnfasser) => (
|
||||
<SchrittKarte
|
||||
schritt={schritt}
|
||||
index={stelle}
|
||||
anzahl={anzahlSchritte}
|
||||
lesend={lesend}
|
||||
neu={schritt.kennung === zuletztAngelegt}
|
||||
anfasser={schrittAnfasser}
|
||||
aendern={(teil) => schrittAendern(schritt.kennung, teil)}
|
||||
loeschen={() => schrittLoeschen(schritt.kennung)}
|
||||
aufSortiert={schritteUmsortieren}
|
||||
/>
|
||||
)}
|
||||
</SortierZeile>
|
||||
))}
|
||||
</ul>
|
||||
</SortierBereich>
|
||||
)}
|
||||
|
||||
{lesend ? null : (
|
||||
<TextKnopf
|
||||
beschriftung={voll ? `Höchstens ${GRENZEN.schritteJeAbschnitt} Schritte` : "Schritt hinzufügen"}
|
||||
aus={voll}
|
||||
aufKlick={schrittHinzufuegen}
|
||||
/>
|
||||
)}
|
||||
</KlappKarte>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user