xtablo-source/go-backend/internal/web/ui/catalog/examples.go
2026-05-09 20:18:24 +02:00

326 lines
8.8 KiB
Go

package catalog
import (
"context"
"io"
"github.com/a-h/templ"
"xtablo-backend/internal/web/ui"
)
type anyComponent = templ.Component
func buttonExamples() []Example {
return []Example{
{
Title: "Primary action",
Description: "Used for the main action in a page section or modal footer.",
Preview: ui.Button(ui.ButtonProps{
Label: "Nouveau projet",
Variant: ui.ButtonVariantPrimary,
Size: ui.SizeMD,
Type: "button",
}),
Snippet: `@ui.Button(ui.ButtonProps{
Label: "Nouveau projet",
Variant: ui.ButtonVariantPrimary,
Size: ui.SizeMD,
Type: "button",
})`,
},
{
Title: "Danger action",
Description: "Used for irreversible actions after explicit confirmation.",
Preview: ui.Button(ui.ButtonProps{
Label: "Supprimer",
Variant: ui.ButtonVariantDanger,
Size: ui.SizeLG,
Type: "submit",
}),
Snippet: `@ui.Button(ui.ButtonProps{
Label: "Supprimer",
Variant: ui.ButtonVariantDanger,
Size: ui.SizeLG,
Type: "submit",
})`,
},
}
}
func tokenExamples() []Example {
return []Example{
{
Title: "Status tones",
Description: "Shared semantic badges for info, warning, success, and danger states.",
Preview: componentFunc(func(ctx context.Context, w io.Writer) error {
for _, component := range []templ.Component{
ui.Badge(ui.BadgeProps{Label: "À faire", Variant: ui.BadgeVariantInfo}),
ui.Badge(ui.BadgeProps{Label: "En cours", Variant: ui.BadgeVariantWarning}),
ui.Badge(ui.BadgeProps{Label: "Terminé", Variant: ui.BadgeVariantSuccess}),
ui.Badge(ui.BadgeProps{Label: "Erreur", Variant: ui.BadgeVariantDanger}),
} {
if _, err := io.WriteString(w, `<div class="catalog-inline">`); err != nil {
return err
}
if err := component.Render(ctx, w); err != nil {
return err
}
if _, err := io.WriteString(w, `</div>`); err != nil {
return err
}
}
return nil
}),
Snippet: `@ui.Badge(ui.BadgeProps{Label: "En cours", Variant: ui.BadgeVariantWarning})`,
},
}
}
func badgeExamples() []Example {
return []Example{
{
Title: "Status set",
Description: "The four semantic badge tones used across the app.",
Preview: componentFunc(func(ctx context.Context, w io.Writer) error {
return renderInlineComponents(ctx, w,
ui.Badge(ui.BadgeProps{Label: "À faire", Variant: ui.BadgeVariantInfo}),
ui.Badge(ui.BadgeProps{Label: "En cours", Variant: ui.BadgeVariantWarning}),
ui.Badge(ui.BadgeProps{Label: "Terminé", Variant: ui.BadgeVariantSuccess}),
ui.Badge(ui.BadgeProps{Label: "Erreur", Variant: ui.BadgeVariantDanger}),
)
}),
Snippet: `@ui.Badge(ui.BadgeProps{Label: "En cours", Variant: ui.BadgeVariantWarning})`,
},
}
}
func iconButtonExamples() []Example {
return []Example{
{
Title: "Borderless destructive action",
Description: "Used for delete controls inside project cards and list rows.",
Preview: ui.IconButton(ui.IconButtonProps{
Label: "Supprimer le projet",
Icon: "trash",
Variant: ui.IconButtonVariantDangerGhost,
Type: "button",
}),
Snippet: `@ui.IconButton(ui.IconButtonProps{
Label: "Supprimer le projet",
Icon: "trash",
Variant: ui.IconButtonVariantDangerGhost,
Type: "button",
})`,
},
}
}
func inputExamples() []Example {
return []Example{
{
Title: "Text input",
Description: "Single-line input for names, titles, and short labels.",
Preview: ui.Input(ui.InputProps{
Name: "name",
Value: "Projet Atlas",
Placeholder: "Nom du projet",
Type: "text",
}),
Snippet: `@ui.Input(ui.InputProps{
Name: "name",
Value: "Projet Atlas",
Placeholder: "Nom du projet",
Type: "text",
})`,
},
{
Title: "Textarea",
Description: "Multiline field for longer project notes and descriptions.",
Preview: ui.Textarea(ui.TextareaProps{
Name: "description",
Value: "Une description de projet plus détaillée.",
Placeholder: "Description",
Rows: 4,
}),
Snippet: `@ui.Textarea(ui.TextareaProps{
Name: "description",
Value: "Une description de projet plus détaillée.",
Placeholder: "Description",
Rows: 4,
})`,
},
}
}
func formFieldExamples() []Example {
return []Example{
{
Title: "Field with validation",
Description: "Wraps a control with label and inline error feedback.",
Preview: ui.FormField(ui.FormFieldProps{
Label: "Nom",
For: "catalog-name",
Field: ui.Input(ui.InputProps{
ID: "catalog-name",
Name: "name",
Placeholder: "Nom du projet",
Type: "text",
}),
Error: "Le nom est requis",
}),
Snippet: `@ui.FormField(ui.FormFieldProps{
Label: "Nom",
For: "catalog-name",
Field: ui.Input(ui.InputProps{
ID: "catalog-name",
Name: "name",
Placeholder: "Nom du projet",
Type: "text",
}),
Error: "Le nom est requis",
})`,
},
}
}
func modalExamples() []Example {
return []Example{
{
Title: "Create modal",
Description: "Shared modal shell with a form body and action footer.",
Preview: ui.Modal(ui.ModalProps{
Title: "Créer un projet",
Body: ui.FormField(ui.FormFieldProps{
Label: "Nom du projet",
For: "modal-name",
Field: ui.Input(ui.InputProps{
ID: "modal-name",
Name: "name",
Placeholder: "Nom du projet",
Type: "text",
}),
}),
Actions: componentFunc(func(ctx context.Context, w io.Writer) error {
return renderComponents(ctx, w,
ui.Button(ui.ButtonProps{
Label: "Annuler",
Variant: ui.ButtonVariantSecondary,
Size: ui.SizeMD,
Type: "button",
}),
ui.Button(ui.ButtonProps{
Label: "Créer le projet",
Variant: ui.ButtonVariantPrimary,
Size: ui.SizeMD,
Type: "submit",
}),
)
}),
}),
Snippet: `@ui.Modal(ui.ModalProps{
Title: "Créer un projet",
Body: ui.FormField(...),
Actions: ui.Button(...),
})`,
},
}
}
func tableExamples() []Example {
return []Example{
{
Title: "List shell",
Description: "Shared wrapper for server-rendered resource tables.",
Preview: ui.Table(ui.TableProps{
Head: textComponent(`<tr><th class="px-6 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">Projet</th><th class="px-6 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">Statut</th></tr>`),
Body: textComponent(`<tr><td class="px-6 py-4">Table View</td><td class="px-6 py-4">En cours</td></tr>`),
}),
Snippet: `@ui.Table(ui.TableProps{
Head: TabloListHead(),
Body: TabloListBody(tablos),
})`,
},
}
}
func emptyStateExamples() []Example {
return []Example{
{
Title: "Centered empty state",
Description: "Used when a list has no rows yet and the next action should stay obvious.",
Preview: ui.EmptyState(ui.EmptyStateProps{
Title: "Aucun projet trouvé",
Description: "Créez votre premier projet",
Icon: ui.UIIcon("grid3x3"),
Action: ui.Button(ui.ButtonProps{
Label: "Nouveau projet",
Variant: ui.ButtonVariantPrimary,
Size: ui.SizeMD,
Type: "button",
Icon: "plus",
}),
}),
Snippet: `@ui.EmptyState(ui.EmptyStateProps{
Title: "Aucun projet trouvé",
Description: "Créez votre premier projet",
Icon: ui.UIIcon("grid3x3"),
Action: ui.Button(...),
})`,
},
}
}
func cardExamples() []Example {
return []Example{
{
Title: "Surface card",
Description: "Generic elevated surface with optional header and footer.",
Preview: ui.Card(ui.CardProps{
Header: textComponent("Header"),
Body: textComponent("Body"),
Footer: textComponent("Footer"),
}),
Snippet: `@ui.Card(ui.CardProps{
Header: textComponent("Header"),
Body: textComponent("Body"),
Footer: textComponent("Footer"),
})`,
},
}
}
func componentFunc(fn func(context.Context, io.Writer) error) templ.Component {
return templ.ComponentFunc(fn)
}
func textComponent(text string) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
_, err := io.WriteString(w, text)
return err
})
}
func renderInlineComponents(ctx context.Context, w io.Writer, components ...templ.Component) error {
for _, component := range components {
if _, err := io.WriteString(w, `<div class="catalog-inline">`); err != nil {
return err
}
if err := component.Render(ctx, w); err != nil {
return err
}
if _, err := io.WriteString(w, `</div>`); err != nil {
return err
}
}
return nil
}
func renderComponents(ctx context.Context, w io.Writer, components ...templ.Component) error {
for _, component := range components {
if err := component.Render(ctx, w); err != nil {
return err
}
}
return nil
}