xtablo-source/deprecated/internal/web/ui/catalog/examples.go
Arthur Belleville 5d0c201e86
Some checks failed
backend-ci / Backend tests (pull_request) Failing after 53s
backend-ci / Backend tests (push) Failing after 1s
Some work
2026-05-23 17:26:01 +02:00

482 lines
13 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: "Default solid action",
Description: "Used for the main action in a page section or modal footer.",
Preview: ui.Button(ui.ButtonProps{
Label: "Nouveau projet",
Variant: ui.ButtonVariantDefault,
Size: ui.SizeMD,
Type: "button",
}),
Snippet: `@ui.Button(ui.ButtonProps{
Label: "Nouveau projet",
Variant: ui.ButtonVariantDefault,
Size: ui.SizeMD,
Type: "button",
})`,
},
{
Title: "Soft warning action",
Description: "Used for inline actions that need emphasis without the weight of a solid button.",
Preview: ui.Button(ui.ButtonProps{
Label: "Relancer",
Variant: ui.ButtonVariantWarning,
Tone: ui.ButtonToneSoft,
Size: ui.SizeMD,
Type: "button",
}),
Snippet: `@ui.Button(ui.ButtonProps{
Label: "Relancer",
Variant: ui.ButtonVariantWarning,
Tone: ui.ButtonToneSoft,
Size: ui.SizeMD,
Type: "button",
})`,
},
{
Title: "Soft danger action",
Description: "Used for irreversible actions after explicit confirmation.",
Preview: ui.Button(ui.ButtonProps{
Label: "Supprimer",
Variant: ui.ButtonVariantDanger,
Tone: ui.ButtonToneSoft,
Size: ui.SizeLG,
Type: "submit",
}),
Snippet: `@ui.Button(ui.ButtonProps{
Label: "Supprimer",
Variant: ui.ButtonVariantDanger,
Tone: ui.ButtonToneSoft,
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.IconButtonVariantDanger,
Tone: ui.IconButtonToneGhost,
Type: "button",
}),
Snippet: `@ui.IconButton(ui.IconButtonProps{
Label: "Supprimer le projet",
Icon: "trash",
Variant: ui.IconButtonVariantDanger,
Tone: ui.IconButtonToneGhost,
Type: "button",
})`,
},
{
Title: "Borderless neutral action",
Description: "Used for lightweight edit or details actions inside cards and list rows.",
Preview: ui.IconButton(ui.IconButtonProps{
Label: "Modifier le projet",
Icon: "pencil",
Variant: ui.IconButtonVariantNeutral,
Tone: ui.IconButtonToneGhost,
Type: "button",
}),
Snippet: `@ui.IconButton(ui.IconButtonProps{
Label: "Modifier le projet",
Icon: "pencil",
Variant: ui.IconButtonVariantNeutral,
Tone: ui.IconButtonToneGhost,
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 selectExamples() []Example {
return []Example{
{
Title: "Single select",
Description: "Single-choice dropdown with the shared input shell and custom chevron.",
Preview: ui.Select(ui.SelectProps{
Name: "status",
Placeholder: "Select a status",
Value: "in-progress",
Options: []ui.SelectOption{
{Value: "todo", Label: "To do"},
{Value: "in-progress", Label: "In progress"},
{Value: "done", Label: "Done"},
},
}),
Snippet: `@ui.Select(ui.SelectProps{
Name: "status",
Placeholder: "Select a status",
Value: "in-progress",
Options: []ui.SelectOption{
{Value: "todo", Label: "To do"},
{Value: "in-progress", Label: "In progress"},
{Value: "done", Label: "Done"},
},
})`,
},
{
Title: "Multiple select",
Description: "Multi-value selection with inline pills that stay form-compatible.",
Preview: ui.Select(ui.SelectProps{
Name: "assignee_ids",
Placeholder: "Select multiple values",
Multiple: true,
Values: []string{"alice", "bob"},
Options: []ui.SelectOption{
{Value: "alice", Label: "Alice"},
{Value: "bob", Label: "Bob"},
{Value: "charlie", Label: "Charlie"},
},
}),
Snippet: `@ui.Select(ui.SelectProps{
Name: "assignee_ids",
Placeholder: "Select multiple values",
Multiple: true,
Values: []string{"alice", "bob"},
Options: []ui.SelectOption{
{Value: "alice", Label: "Alice"},
{Value: "bob", Label: "Bob"},
{Value: "charlie", Label: "Charlie"},
},
})`,
},
}
}
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.ButtonVariantNeutral,
Size: ui.SizeMD,
Type: "button",
}),
ui.Button(ui.ButtonProps{
Label: "Créer le projet",
Variant: ui.ButtonVariantDefault,
Size: ui.SizeMD,
Type: "submit",
}),
)
}),
}),
Snippet: `@ui.Modal(ui.ModalProps{
Title: "Créer un projet",
Body: ui.FormField(...),
Actions: ui.Button(...),
})`,
},
}
}
func spacingExamples() []Example {
return []Example{
{
Title: "Horizontal spacing",
Description: "Use SpaceX to insert fixed horizontal gaps between inline or row-aligned components.",
Preview: componentFunc(func(ctx context.Context, w io.Writer) error {
if _, err := io.WriteString(w, `<div class="catalog-spacing-row">`); err != nil {
return err
}
for _, component := range []templ.Component{
ui.Button(ui.ButtonProps{
Label: "Précédent",
Variant: ui.ButtonVariantNeutral,
Size: ui.SizeMD,
Type: "button",
}),
ui.SpaceX(ui.SpaceProps{Size: ui.SpacingStepLG}),
ui.Button(ui.ButtonProps{
Label: "Suivant",
Variant: ui.ButtonVariantDefault,
Size: ui.SizeMD,
Type: "button",
}),
} {
if err := component.Render(ctx, w); err != nil {
return err
}
}
_, err := io.WriteString(w, `</div>`)
return err
}),
Snippet: `@ui.SpaceX(ui.SpaceProps{Size: ui.SpacingStepLG})`,
},
{
Title: "Vertical spacing",
Description: "Use SpaceY to insert fixed vertical gaps between stacked blocks.",
Preview: componentFunc(func(ctx context.Context, w io.Writer) error {
if _, err := io.WriteString(w, `<div class="catalog-spacing-column">`); err != nil {
return err
}
for _, component := range []templ.Component{
ui.Card(ui.CardProps{
Body: textComponent("Bloc 1"),
}),
ui.SpaceY(ui.SpaceProps{Size: ui.SpacingStepMD}),
ui.Card(ui.CardProps{
Body: textComponent("Bloc 2"),
}),
} {
if err := component.Render(ctx, w); err != nil {
return err
}
}
_, err := io.WriteString(w, `</div>`)
return err
}),
Snippet: `@ui.SpaceY(ui.SpaceProps{Size: ui.SpacingStepMD})`,
},
}
}
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.ButtonVariantDefault,
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
}