// 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("%s", 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 }