1
0
mirror of https://github.com/axzilla/templui.git synced 2025-02-22 10:13:27 +00:00
templui/pkg/icons/icons.go
“axzilla” b290a2a7ff change: Improve icon implementation and documentation
- Add link to Lucide in documentation
- Implement icon names as constants for autocomplete
- Optimize icon storage for more efficient resource usage

These changes enhance usability through improved documentation
and autocomplete functionality, while also improving memory
efficiency by optimizing icon storage.
2024-10-11 13:43:24 +02:00

125 lines
2.9 KiB
Go

// Package icons provides a set of Lucide icons for use with the templ library.
package icons
import (
"context"
"embed"
"fmt"
"io"
"strings"
"sync"
"github.com/a-h/templ"
)
// LucideVersion represents the version of Lucide icons used in this package.
const LucideVersion = "0.451.0"
var (
iconContents = make(map[string]string)
iconMutex sync.RWMutex
)
//go:embed content/*.svg
var iconFS embed.FS
// IconProps defines the properties that can be set for an icon.
type IconProps struct {
Size string
Color string
Fill string
Stroke string
Class string
}
// Icon returns a function that generates a templ.Component for the specified icon.
func Icon(name string) func(IconProps) templ.Component {
return func(props IconProps) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
svg, err := generateSVG(name, props)
if err != nil {
return err
}
_, err = w.Write([]byte(svg))
return
})
}
}
// generateSVG creates an SVG string for the specified icon with the given properties.
func generateSVG(name string, props IconProps) (string, error) {
content, err := getIconContent(name)
if err != nil {
return "", err
}
size := props.Size
if size == "" {
size = "24"
}
fill := props.Fill
if fill == "" {
fill = "none"
}
stroke := props.Stroke
if stroke == "" {
stroke = props.Color
}
if stroke == "" {
stroke = "currentColor"
}
return fmt.Sprintf(`<svg xmlns="http://www.w3.org/2000/svg" width="%s" height="%s" viewBox="0 0 24 24" fill="%s" stroke="%s" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="%s" data-lucide="icon">%s</svg>`,
size, size, fill, stroke, props.Class, content), nil
}
// getIconContent retrieves the content of an icon, loading it if necessary.
func getIconContent(name string) (string, error) {
iconMutex.RLock()
content, exists := iconContents[name]
iconMutex.RUnlock()
if exists {
return content, nil
}
iconMutex.Lock()
defer iconMutex.Unlock()
// Check again in case another goroutine has loaded the icon
content, exists = iconContents[name]
if exists {
return content, nil
}
// Load the icon content
content, err := loadIconContent(name)
if err != nil {
return "", err
}
iconContents[name] = content
return content, nil
}
// loadIconContent reads the content of an icon from the embedded filesystem.
func loadIconContent(name string) (string, error) {
content, err := iconFS.ReadFile(fmt.Sprintf("content/%s.svg", name))
if err != nil {
return "", fmt.Errorf("icon %s not found: %w", name, err)
}
return extractSVGContent(string(content)), nil
}
// extractSVGContent removes the outer SVG tags from the icon content.
func extractSVGContent(svgContent string) string {
start := strings.Index(svgContent, ">") + 1
end := strings.LastIndex(svgContent, "</svg>")
if start == -1 || end == -1 {
return ""
}
return strings.TrimSpace(svgContent[start:end])
}