117 lines
3.5 KiB
Go
117 lines
3.5 KiB
Go
// templui component icon - version: v0.84.0 installed by templui v0.84.0
|
|
package icon
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"sync"
|
|
|
|
"github.com/a-h/templ"
|
|
)
|
|
|
|
// iconContents caches the fully generated SVG strings for icons that have been used,
|
|
// keyed by a composite key of name and props to handle different stylings.
|
|
var (
|
|
iconContents = make(map[string]string)
|
|
iconMutex sync.RWMutex
|
|
)
|
|
|
|
// Props defines the properties that can be set for an icon.
|
|
type Props struct {
|
|
Size int
|
|
Color string
|
|
Fill string
|
|
Stroke string
|
|
StrokeWidth string // Stroke Width of Icon, Usage: "2.5"
|
|
Class string
|
|
}
|
|
|
|
// Icon returns a function that generates a templ.Component for the specified icon name.
|
|
func Icon(name string) func(...Props) templ.Component {
|
|
return func(props ...Props) templ.Component {
|
|
var p Props
|
|
if len(props) > 0 {
|
|
p = props[0]
|
|
}
|
|
|
|
// Create a unique key for the cache based on icon name and all relevant props.
|
|
// This ensures different stylings of the same icon are cached separately.
|
|
cacheKey := fmt.Sprintf("%s|s:%d|c:%s|f:%s|sk:%s|sw:%s|cl:%s",
|
|
name, p.Size, p.Color, p.Fill, p.Stroke, p.StrokeWidth, p.Class)
|
|
|
|
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
|
|
iconMutex.RLock()
|
|
svg, cached := iconContents[cacheKey]
|
|
iconMutex.RUnlock()
|
|
|
|
if cached {
|
|
_, err = w.Write([]byte(svg))
|
|
return err
|
|
}
|
|
|
|
// Not cached, generate it
|
|
// The actual generation now happens once and is cached.
|
|
generatedSvg, err := generateSVG(name, p) // p (Props) is passed to generateSVG
|
|
if err != nil {
|
|
// Provide more context in the error message
|
|
return fmt.Errorf("failed to generate svg for icon '%s' with props %+v: %w", name, p, err)
|
|
}
|
|
|
|
iconMutex.Lock()
|
|
iconContents[cacheKey] = generatedSvg
|
|
iconMutex.Unlock()
|
|
|
|
_, err = w.Write([]byte(generatedSvg))
|
|
return err
|
|
})
|
|
}
|
|
}
|
|
|
|
// generateSVG creates an SVG string for the specified icon with the given properties.
|
|
// This function is called when an icon-prop combination is not yet in the cache.
|
|
func generateSVG(name string, props Props) (string, error) {
|
|
// Get the raw, inner SVG content for the icon name from our internal data map.
|
|
content, err := getIconContent(name) // This now reads from internalSvgData
|
|
if err != nil {
|
|
return "", err // Error from getIconContent already includes icon name
|
|
}
|
|
|
|
size := props.Size
|
|
if size <= 0 {
|
|
size = 24 // Default size
|
|
}
|
|
|
|
fill := props.Fill
|
|
if fill == "" {
|
|
fill = "none" // Default fill
|
|
}
|
|
|
|
stroke := props.Stroke
|
|
if stroke == "" {
|
|
stroke = props.Color // Fallback to Color if Stroke is not set
|
|
}
|
|
if stroke == "" {
|
|
stroke = "currentColor" // Default stroke color
|
|
}
|
|
|
|
strokeWidth := props.StrokeWidth
|
|
if strokeWidth == "" {
|
|
strokeWidth = "2" // Default stroke width
|
|
}
|
|
|
|
// Construct the final SVG string.
|
|
// The data-lucide attribute helps identify these as Lucide icons if needed.
|
|
return fmt.Sprintf("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"%d\" height=\"%d\" viewBox=\"0 0 24 24\" fill=\"%s\" stroke=\"%s\" stroke-width=\"%s\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"%s\" data-lucide=\"icon\">%s</svg>",
|
|
size, size, fill, stroke, strokeWidth, props.Class, content), nil
|
|
}
|
|
|
|
// getIconContent retrieves the raw inner SVG content for a given icon name.
|
|
// It reads from the pre-generated internalSvgData map from icon_data.go.
|
|
func getIconContent(name string) (string, error) {
|
|
content, exists := internalSvgData[name]
|
|
if !exists {
|
|
return "", fmt.Errorf("icon '%s' not found in internalSvgData map", name)
|
|
}
|
|
return content, nil
|
|
}
|