xtablo-source/backend/internal/web/ui/catalog/examples.go

408 lines
10 KiB
Go
Raw Normal View History

package catalog
import (
"context"
"io"
"github.com/a-h/templ"
"backend/internal/web/ui"
)
// Example is a single rendered component variant with a templ call annotation.
type Example struct {
Title string
Preview templ.Component
Snippet string
}
type anyComponent = templ.Component
func badgeExamples() []Example {
return []Example{
{
Title: "Info",
Preview: ui.Badge(ui.BadgeProps{
Label: "Info",
Variant: ui.BadgeVariantInfo,
}),
Snippet: `@ui.Badge(ui.BadgeProps{Label: "Info", Variant: ui.BadgeVariantInfo})`,
},
{
Title: "Warning",
Preview: ui.Badge(ui.BadgeProps{
Label: "Warning",
Variant: ui.BadgeVariantWarning,
}),
Snippet: `@ui.Badge(ui.BadgeProps{Label: "Warning", Variant: ui.BadgeVariantWarning})`,
},
{
Title: "Success",
Preview: ui.Badge(ui.BadgeProps{
Label: "Success",
Variant: ui.BadgeVariantSuccess,
}),
Snippet: `@ui.Badge(ui.BadgeProps{Label: "Success", Variant: ui.BadgeVariantSuccess})`,
},
{
Title: "Danger",
Preview: ui.Badge(ui.BadgeProps{
Label: "Danger",
Variant: ui.BadgeVariantDanger,
}),
Snippet: `@ui.Badge(ui.BadgeProps{Label: "Danger", Variant: ui.BadgeVariantDanger})`,
},
{
Title: "Primary",
Preview: ui.Badge(ui.BadgeProps{
Label: "Primary",
Variant: ui.BadgeVariantPrimary,
}),
Snippet: `@ui.Badge(ui.BadgeProps{Label: "Primary", Variant: ui.BadgeVariantPrimary})`,
},
}
}
func buttonExamples() []Example {
return []Example{
{
Title: "Solid / Default",
Preview: ui.Button(ui.ButtonProps{
Label: "Create project",
Variant: ui.ButtonVariantDefault,
Tone: ui.ButtonToneSolid,
Size: ui.SizeMD,
Type: "button",
}),
Snippet: `@ui.Button(ui.ButtonProps{
Label: "Create project",
Variant: ui.ButtonVariantDefault,
Tone: ui.ButtonToneSolid,
Size: ui.SizeMD,
Type: "button",
})`,
},
{
Title: "Solid / Danger",
Preview: ui.Button(ui.ButtonProps{
Label: "Delete",
Variant: ui.ButtonVariantDanger,
Tone: ui.ButtonToneSolid,
Size: ui.SizeMD,
Type: "button",
}),
Snippet: `@ui.Button(ui.ButtonProps{
Label: "Delete",
Variant: ui.ButtonVariantDanger,
Tone: ui.ButtonToneSolid,
Size: ui.SizeMD,
Type: "button",
})`,
},
{
Title: "Soft / Neutral",
Preview: ui.Button(ui.ButtonProps{
Label: "Cancel",
Variant: ui.ButtonVariantNeutral,
Tone: ui.ButtonToneSoft,
Size: ui.SizeMD,
Type: "button",
}),
Snippet: `@ui.Button(ui.ButtonProps{
Label: "Cancel",
Variant: ui.ButtonVariantNeutral,
Tone: ui.ButtonToneSoft,
Size: ui.SizeMD,
Type: "button",
})`,
},
{
Title: "Ghost",
Preview: ui.Button(ui.ButtonProps{
Label: "Learn more",
Variant: ui.ButtonVariantGhost,
Size: ui.SizeMD,
Type: "button",
}),
Snippet: `@ui.Button(ui.ButtonProps{
Label: "Learn more",
Variant: ui.ButtonVariantGhost,
Size: ui.SizeMD,
Type: "button",
})`,
},
}
}
func cardExamples() []Example {
return []Example{
{
Title: "Card with header, body, and footer",
Preview: ui.Card(ui.CardProps{
Header: textBody("Card Header"),
Body: textBody("Card body content goes here."),
Footer: textBody("Card Footer"),
}),
Snippet: `@ui.Card(ui.CardProps{
Header: headerComponent,
Body: bodyComponent,
Footer: footerComponent,
})`,
},
}
}
func emptyStateExamples() []Example {
return []Example{
{
Title: "Empty list",
Preview: ui.EmptyState(ui.EmptyStateProps{
Title: "Nothing here yet",
Description: "Add your first item to get started.",
Icon: ui.UIIcon("grid3x3"),
Action: ui.Button(ui.ButtonProps{
Label: "Add item",
Variant: ui.ButtonVariantDefault,
Tone: ui.ButtonToneSolid,
Size: ui.SizeMD,
Type: "button",
Icon: "plus",
}),
}),
Snippet: `@ui.EmptyState(ui.EmptyStateProps{
Title: "Nothing here yet",
Description: "Add your first item to get started.",
Icon: ui.UIIcon("grid3x3"),
Action: ui.Button(...),
})`,
},
}
}
func formFieldExamples() []Example {
return []Example{
{
Title: "Field with label, hint, and error",
Preview: ui.FormField(ui.FormFieldProps{
Label: "Project name",
For: "catalog-name",
Field: ui.Input(ui.InputProps{
ID: "catalog-name",
Name: "name",
Placeholder: "Enter a name",
Type: "text",
}),
Hint: "Enter a value between 1 and 100.",
Error: "This field is required.",
}),
Snippet: `@ui.FormField(ui.FormFieldProps{
Label: "Project name",
For: "catalog-name",
Field: ui.Input(ui.InputProps{...}),
Hint: "Enter a value between 1 and 100.",
Error: "This field is required.",
})`,
},
}
}
func iconButtonExamples() []Example {
return []Example{
{
Title: "Ghost / Neutral (plus icon)",
Preview: ui.IconButton(ui.IconButtonProps{
Label: "Add item",
Icon: "plus",
Variant: ui.IconButtonVariantNeutral,
Tone: ui.IconButtonToneGhost,
Type: "button",
}),
Snippet: `@ui.IconButton(ui.IconButtonProps{
Label: "Add item",
Icon: "plus",
Variant: ui.IconButtonVariantNeutral,
Tone: ui.IconButtonToneGhost,
Type: "button",
})`,
},
{
Title: "Solid / Danger (trash icon)",
Preview: ui.IconButton(ui.IconButtonProps{
Label: "Delete item",
Icon: "trash",
Variant: ui.IconButtonVariantDanger,
Tone: ui.IconButtonToneSolid,
Type: "button",
}),
Snippet: `@ui.IconButton(ui.IconButtonProps{
Label: "Delete item",
Icon: "trash",
Variant: ui.IconButtonVariantDanger,
Tone: ui.IconButtonToneSolid,
Type: "button",
})`,
},
}
}
func inputExamples() []Example {
return []Example{
{
Title: "Text input",
Preview: ui.Input(ui.InputProps{
Name: "name",
Placeholder: "Enter text here",
Type: "text",
}),
Snippet: `@ui.Input(ui.InputProps{
Name: "name",
Placeholder: "Enter text here",
Type: "text",
})`,
},
{
Title: "Email input",
Preview: ui.Input(ui.InputProps{
Name: "email",
Placeholder: "you@example.com",
Type: "email",
}),
Snippet: `@ui.Input(ui.InputProps{
Name: "email",
Placeholder: "you@example.com",
Type: "email",
})`,
},
}
}
func modalExamples() []Example {
return []Example{
{
Title: "Modal panel (no backdrop)",
// Render only the panel div — not the full Modal component with backdrop.
// Pitfall 7: ui-modal-backdrop is position:fixed and would overlay the catalog page.
Preview: componentFunc(func(ctx context.Context, w io.Writer) error {
_, err := io.WriteString(w, `<div class="ui-modal-panel" style="position:relative;max-width:32rem;margin:0 auto;">`)
if err != nil {
return err
}
if err := renderComponents(ctx, w,
textBody(`<div class="ui-modal-header"><h2>Confirm action</h2></div>`),
textBody(`<div class="ui-modal-body"><p>Are you sure you want to proceed?</p></div>`),
textBody(`<div class="ui-modal-actions">`),
); err != nil {
return err
}
if err := ui.Button(ui.ButtonProps{
Label: "Cancel",
Variant: ui.ButtonVariantNeutral,
Tone: ui.ButtonToneSoft,
Size: ui.SizeMD,
Type: "button",
}).Render(ctx, w); err != nil {
return err
}
if err := ui.Button(ui.ButtonProps{
Label: "Confirm",
Variant: ui.ButtonVariantDefault,
Tone: ui.ButtonToneSolid,
Size: ui.SizeMD,
Type: "button",
}).Render(ctx, w); err != nil {
return err
}
_, err = io.WriteString(w, `</div></div>`)
return err
}),
Snippet: `@ui.Modal(ui.ModalProps{
Title: "Confirm action",
Body: bodyContent,
Actions: actionButtons,
})`,
},
}
}
func selectExamples() []Example {
return []Example{
{
Title: "Single select",
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"},
},
})`,
},
}
}
func tableExamples() []Example {
return []Example{
{
Title: "Data table",
Preview: ui.Table(ui.TableProps{
Head: textBody(`<tr><th style="padding:0.75rem 1rem;text-align:left;font-size:0.75rem;font-weight:600;text-transform:uppercase;letter-spacing:0.05em;">Name</th><th style="padding:0.75rem 1rem;text-align:left;font-size:0.75rem;font-weight:600;text-transform:uppercase;letter-spacing:0.05em;">Status</th></tr>`),
Body: textBody(`<tr><td style="padding:0.75rem 1rem;">Example Project</td><td style="padding:0.75rem 1rem;">In progress</td></tr><tr><td style="padding:0.75rem 1rem;">Another Project</td><td style="padding:0.75rem 1rem;">Done</td></tr>`),
}),
Snippet: `@ui.Table(ui.TableProps{
Head: tableHead,
Body: tableBody,
})`,
},
}
}
func textareaExamples() []Example {
return []Example{
{
Title: "Textarea with placeholder",
Preview: ui.Textarea(ui.TextareaProps{
Name: "description",
Placeholder: "Enter a description...",
Rows: 4,
}),
Snippet: `@ui.Textarea(ui.TextareaProps{
Name: "description",
Placeholder: "Enter a description...",
Rows: 4,
})`,
},
}
}
func componentFunc(fn func(context.Context, io.Writer) error) templ.Component {
return templ.ComponentFunc(fn)
}
func textBody(text string) templ.Component {
return templ.ComponentFunc(func(_ context.Context, w io.Writer) error {
_, err := io.WriteString(w, text)
return err
})
}
func renderComponents(ctx context.Context, w io.Writer, components ...templ.Component) error {
for _, c := range components {
if err := c.Render(ctx, w); err != nil {
return err
}
}
return nil
}