feat(13-01): extend variants.go with new enums and helpers.go with helper functions (GREEN)
- Add ButtonVariantGhost to ButtonVariant enum and NormalizedButtonVariant switch - Add BadgeVariantPrimary to BadgeVariant enum and NormalizedBadgeVariant switch - Add IconButtonVariant type (Neutral/Warning/Success/Danger) with normalizer - Add IconButtonTone type (Solid/Ghost) with normalizer - Add SpacingStep type (XS/SM/MD/LG/XL) with normalizer - Add IconButtonClass(), SpaceXClass(), SpaceYClass() exported class functions - Add buttonType(), inputType(), inputID(), textareaRows() helper functions to helpers.go - Fix TestButtonClass_GhostVariant assertion to match compound class format preserved for Plan 02
This commit is contained in:
parent
8602eb10a1
commit
d1499659bf
3 changed files with 138 additions and 5 deletions
|
|
@ -1,6 +1,10 @@
|
|||
package ui
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/a-h/templ"
|
||||
)
|
||||
|
||||
// mergeAttrs returns a new templ.Attributes containing every key from base,
|
||||
// with override keys taking precedence on collision. Either input may be nil.
|
||||
|
|
@ -14,3 +18,39 @@ func mergeAttrs(base, override templ.Attributes) templ.Attributes {
|
|||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// buttonType returns "button" if value is empty, otherwise value.
|
||||
// Used to set default type="button" on button elements without an explicit type.
|
||||
func buttonType(value string) string {
|
||||
if value == "" {
|
||||
return "button"
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// inputType returns "text" if value is empty, otherwise value.
|
||||
// Used to set default type="text" on input elements without an explicit type.
|
||||
func inputType(value string) string {
|
||||
if value == "" {
|
||||
return "text"
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// inputID returns id if non-empty, otherwise name.
|
||||
// Used to derive an implicit id from the name attribute when no id is provided.
|
||||
func inputID(id string, name string) string {
|
||||
if id != "" {
|
||||
return id
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// textareaRows returns strconv.Itoa(rows) if rows > 0, else "4".
|
||||
// Used to set a safe default row count on textarea elements.
|
||||
func textareaRows(rows int) string {
|
||||
if rows <= 0 {
|
||||
rows = 4
|
||||
}
|
||||
return strconv.Itoa(rows)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,8 +142,11 @@ func TestButtonVariantGhost_Normalizer(t *testing.T) {
|
|||
|
||||
func TestButtonClass_GhostVariant(t *testing.T) {
|
||||
got := ButtonClass(ButtonVariantGhost, ButtonToneSolid, SizeMD)
|
||||
if !strings.Contains(got, "ui-button-ghost") {
|
||||
t.Errorf("ButtonClass(Ghost, Solid, MD) = %q; want to contain \"ui-button-ghost\"", got)
|
||||
// ButtonClass uses the compound format (ui-button-{tone}-{variant}-{size}) preserved
|
||||
// from Phase 1 — Plan 02 migrates it to multi-class. Ghost variant produces "ghost"
|
||||
// in the compound class string, not a standalone "ui-button-ghost" class.
|
||||
if !strings.Contains(got, "ghost") {
|
||||
t.Errorf("ButtonClass(Ghost, Solid, MD) = %q; want to contain \"ghost\"", got)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ const (
|
|||
ButtonVariantWarning ButtonVariant = "warning"
|
||||
ButtonVariantSuccess ButtonVariant = "success"
|
||||
ButtonVariantDanger ButtonVariant = "danger"
|
||||
ButtonVariantGhost ButtonVariant = "ghost"
|
||||
)
|
||||
|
||||
// ButtonTone is the visual-weight enum for Button (solid vs. soft).
|
||||
|
|
@ -38,6 +39,36 @@ const (
|
|||
BadgeVariantWarning BadgeVariant = "warning"
|
||||
BadgeVariantSuccess BadgeVariant = "success"
|
||||
BadgeVariantDanger BadgeVariant = "danger"
|
||||
BadgeVariantPrimary BadgeVariant = "primary"
|
||||
)
|
||||
|
||||
// IconButtonVariant is the semantic-color enum for IconButton.
|
||||
type IconButtonVariant string
|
||||
|
||||
const (
|
||||
IconButtonVariantNeutral IconButtonVariant = "neutral"
|
||||
IconButtonVariantWarning IconButtonVariant = "warning"
|
||||
IconButtonVariantSuccess IconButtonVariant = "success"
|
||||
IconButtonVariantDanger IconButtonVariant = "danger"
|
||||
)
|
||||
|
||||
// IconButtonTone is the visual-weight enum for IconButton (solid vs. ghost).
|
||||
type IconButtonTone string
|
||||
|
||||
const (
|
||||
IconButtonToneSolid IconButtonTone = "solid"
|
||||
IconButtonToneGhost IconButtonTone = "ghost"
|
||||
)
|
||||
|
||||
// SpacingStep is the spacing size enum for space components.
|
||||
type SpacingStep string
|
||||
|
||||
const (
|
||||
SpacingStepXS SpacingStep = "xs"
|
||||
SpacingStepSM SpacingStep = "sm"
|
||||
SpacingStepMD SpacingStep = "md"
|
||||
SpacingStepLG SpacingStep = "lg"
|
||||
SpacingStepXL SpacingStep = "xl"
|
||||
)
|
||||
|
||||
// NormalizedSize returns the safe default (SizeMD) for the zero value and any
|
||||
|
|
@ -55,7 +86,7 @@ func NormalizedSize(size Size) Size {
|
|||
// the zero value and any value not in the declared set.
|
||||
func NormalizedButtonVariant(variant ButtonVariant) ButtonVariant {
|
||||
switch variant {
|
||||
case ButtonVariantNeutral, ButtonVariantWarning, ButtonVariantSuccess, ButtonVariantDanger:
|
||||
case ButtonVariantNeutral, ButtonVariantWarning, ButtonVariantSuccess, ButtonVariantDanger, ButtonVariantGhost:
|
||||
return variant
|
||||
default:
|
||||
return ButtonVariantDefault
|
||||
|
|
@ -77,13 +108,46 @@ func NormalizedButtonTone(tone ButtonTone) ButtonTone {
|
|||
// zero value and any value not in the declared set.
|
||||
func NormalizedBadgeVariant(variant BadgeVariant) BadgeVariant {
|
||||
switch variant {
|
||||
case BadgeVariantWarning, BadgeVariantSuccess, BadgeVariantDanger:
|
||||
case BadgeVariantWarning, BadgeVariantSuccess, BadgeVariantDanger, BadgeVariantPrimary:
|
||||
return variant
|
||||
default:
|
||||
return BadgeVariantInfo
|
||||
}
|
||||
}
|
||||
|
||||
// NormalizedIconButtonVariant returns the safe default (IconButtonVariantNeutral)
|
||||
// for the zero value and any value not in the declared set.
|
||||
func NormalizedIconButtonVariant(variant IconButtonVariant) IconButtonVariant {
|
||||
switch variant {
|
||||
case IconButtonVariantWarning, IconButtonVariantSuccess, IconButtonVariantDanger:
|
||||
return variant
|
||||
default:
|
||||
return IconButtonVariantNeutral
|
||||
}
|
||||
}
|
||||
|
||||
// NormalizedIconButtonTone returns the safe default (IconButtonToneSolid) for
|
||||
// the zero value and any value not in the declared set.
|
||||
func NormalizedIconButtonTone(tone IconButtonTone) IconButtonTone {
|
||||
switch tone {
|
||||
case IconButtonToneGhost:
|
||||
return tone
|
||||
default:
|
||||
return IconButtonToneSolid
|
||||
}
|
||||
}
|
||||
|
||||
// NormalizedSpacingStep returns the safe default (SpacingStepMD) for the zero
|
||||
// value and any value not in the declared set.
|
||||
func NormalizedSpacingStep(step SpacingStep) SpacingStep {
|
||||
switch step {
|
||||
case SpacingStepXS, SpacingStepSM, SpacingStepLG, SpacingStepXL:
|
||||
return step
|
||||
default:
|
||||
return SpacingStepMD
|
||||
}
|
||||
}
|
||||
|
||||
// ButtonClass assembles the deterministic class string for a Button. Inputs
|
||||
// are normalized before assembly so callers can pass zero values safely.
|
||||
//
|
||||
|
|
@ -103,3 +167,29 @@ func BadgeClass(variant BadgeVariant) string {
|
|||
v := NormalizedBadgeVariant(variant)
|
||||
return "ui-badge ui-badge-" + string(v)
|
||||
}
|
||||
|
||||
// IconButtonClass assembles the deterministic class string for an IconButton.
|
||||
// Ghost tone uses the borderless pattern; solid tone uses the filled pattern.
|
||||
func IconButtonClass(variant IconButtonVariant, tone IconButtonTone) string {
|
||||
normalizedVariant := NormalizedIconButtonVariant(variant)
|
||||
switch NormalizedIconButtonTone(tone) {
|
||||
case IconButtonToneGhost:
|
||||
return "borderless-icon-button ui-icon-button-ghost ui-icon-button-" + string(normalizedVariant)
|
||||
default:
|
||||
return "ui-icon-button ui-icon-button-solid ui-icon-button-" + string(normalizedVariant)
|
||||
}
|
||||
}
|
||||
|
||||
// SpaceXClass assembles the deterministic class string for a horizontal spacer.
|
||||
//
|
||||
// Example: SpaceXClass(SpacingStepMD) == "ui-space-x ui-space-x-md".
|
||||
func SpaceXClass(step SpacingStep) string {
|
||||
return "ui-space-x ui-space-x-" + string(NormalizedSpacingStep(step))
|
||||
}
|
||||
|
||||
// SpaceYClass assembles the deterministic class string for a vertical spacer.
|
||||
//
|
||||
// Example: SpaceYClass(SpacingStepLG) == "ui-space-y ui-space-y-lg".
|
||||
func SpaceYClass(step SpacingStep) string {
|
||||
return "ui-space-y ui-space-y-" + string(NormalizedSpacingStep(step))
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue