1
0
mirror of https://github.com/axzilla/templui.git synced 2025-02-21 01:13:05 +00:00

feat: add icon component

This commit is contained in:
“axzilla” 2024-10-08 17:51:45 +02:00
parent 4fdf078dcc
commit 9693741db0
13 changed files with 6391 additions and 267 deletions

3
.gitignore vendored
View File

@ -1,3 +1,6 @@
# lucide icons
cmd/icongen/icons
.DS_Store
bin
*_templ.go

View File

@ -26,3 +26,7 @@ dev:
# Start development server in debug mode - It's needed to start launch.json in VSCode
debug:
make -j2 templ tailwind
# Generate Lucid icons
generate-icons:
go run cmd/icongen/main.go

View File

@ -628,18 +628,6 @@ body {
}
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
.static {
position: static;
}
@ -656,10 +644,6 @@ body {
position: relative;
}
.sticky {
position: sticky;
}
.inset-0 {
inset: 0px;
}
@ -783,18 +767,6 @@ body {
margin-top: 1rem;
}
.mt-auto {
margin-top: auto;
}
.mt-\[100vh\] {
margin-top: 100vh;
}
.mb-5 {
margin-bottom: 1.25rem;
}
.block {
display: block;
}
@ -811,6 +783,10 @@ body {
display: inline-flex;
}
.table {
display: table;
}
.grid {
display: grid;
}
@ -835,6 +811,14 @@ body {
height: 2.75rem;
}
.h-12 {
height: 3rem;
}
.h-16 {
height: 4rem;
}
.h-24 {
height: 6rem;
}
@ -843,6 +827,10 @@ body {
height: 1rem;
}
.h-5 {
height: 1.25rem;
}
.h-6 {
height: 1.5rem;
}
@ -871,26 +859,6 @@ body {
height: 100vh;
}
.h-\[calc\(100vh-4rem\)\] {
height: calc(100vh - 4rem);
}
.h-5 {
height: 1.25rem;
}
.h-12 {
height: 3rem;
}
.h-16 {
height: 4rem;
}
.h-3 {
height: 0.75rem;
}
.\!max-h-\[501px\] {
max-height: 501px !important;
}
@ -899,10 +867,6 @@ body {
min-height: 100vh;
}
.min-h-full {
min-height: 100%;
}
.w-1\/3 {
width: 33.333333%;
}
@ -911,6 +875,14 @@ body {
width: 2.5rem;
}
.w-12 {
width: 3rem;
}
.w-16 {
width: 4rem;
}
.w-24 {
width: 6rem;
}
@ -923,6 +895,10 @@ body {
width: 1rem;
}
.w-5 {
width: 1.25rem;
}
.w-6 {
width: 1.5rem;
}
@ -935,6 +911,10 @@ body {
width: 1.75rem;
}
.w-8 {
width: 2rem;
}
.w-\[17rem\] {
width: 17rem;
}
@ -947,34 +927,6 @@ body {
width: 100%;
}
.w-5 {
width: 1.25rem;
}
.w-12 {
width: 3rem;
}
.w-16 {
width: 4rem;
}
.w-8 {
width: 2rem;
}
.w-auto {
width: auto;
}
.w-3 {
width: 0.75rem;
}
.w-60 {
width: 15rem;
}
.max-w-3xl {
max-width: 48rem;
}
@ -1007,12 +959,12 @@ body {
flex-shrink: 0;
}
.shrink-0 {
flex-shrink: 0;
.shrink {
flex-shrink: 1;
}
.flex-grow {
flex-grow: 1;
.shrink-0 {
flex-shrink: 0;
}
.-translate-x-1\/4 {
@ -1077,6 +1029,21 @@ body {
list-style-type: disc;
}
.columns-2 {
-moz-columns: 2;
columns: 2;
}
.columns-3 {
-moz-columns: 3;
columns: 3;
}
.columns-4 {
-moz-columns: 4;
columns: 4;
}
.grid-cols-7 {
grid-template-columns: repeat(7, minmax(0, 1fr));
}
@ -1093,6 +1060,10 @@ body {
align-items: flex-start;
}
.items-end {
align-items: flex-end;
}
.items-center {
align-items: center;
}
@ -1157,12 +1128,6 @@ body {
margin-bottom: calc(1.5rem * var(--tw-space-y-reverse));
}
.space-y-8 > :not([hidden]) ~ :not([hidden]) {
--tw-space-y-reverse: 0;
margin-top: calc(2rem * calc(1 - var(--tw-space-y-reverse)));
margin-bottom: calc(2rem * var(--tw-space-y-reverse));
}
.divide-y > :not([hidden]) ~ :not([hidden]) {
--tw-divide-y-reverse: 0;
border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
@ -1174,14 +1139,6 @@ body {
border-color: hsl(var(--border) / var(--tw-divide-opacity));
}
.divide-gray-200\/60 > :not([hidden]) ~ :not([hidden]) {
border-color: rgb(229 231 235 / 0.6);
}
.overflow-auto {
overflow: auto;
}
.\!overflow-hidden {
overflow: hidden !important;
}
@ -1190,10 +1147,6 @@ body {
overflow: hidden;
}
.overflow-scroll {
overflow: scroll;
}
.\!overflow-y-auto {
overflow-y: auto !important;
}
@ -1246,6 +1199,11 @@ body {
border-top-width: 1px;
}
.border-blue-500 {
--tw-border-opacity: 1;
border-color: rgb(59 130 246 / var(--tw-border-opacity));
}
.border-border {
--tw-border-opacity: 1;
border-color: hsl(var(--border) / var(--tw-border-opacity));
@ -1256,9 +1214,9 @@ body {
border-color: hsl(var(--destructive) / var(--tw-border-opacity));
}
.border-gray-300 {
.border-foreground {
--tw-border-opacity: 1;
border-color: rgb(209 213 219 / var(--tw-border-opacity));
border-color: hsl(var(--foreground) / var(--tw-border-opacity));
}
.border-input {
@ -1274,21 +1232,6 @@ body {
border-color: transparent;
}
.border-blue-500 {
--tw-border-opacity: 1;
border-color: rgb(59 130 246 / var(--tw-border-opacity));
}
.border-gray-800 {
--tw-border-opacity: 1;
border-color: rgb(31 41 55 / var(--tw-border-opacity));
}
.border-foreground {
--tw-border-opacity: 1;
border-color: hsl(var(--foreground) / var(--tw-border-opacity));
}
.bg-background {
--tw-bg-opacity: 1;
background-color: hsl(var(--background) / var(--tw-bg-opacity));
@ -1323,6 +1266,10 @@ body {
background-color: hsl(var(--muted) / var(--tw-bg-opacity));
}
.bg-muted\/50 {
background-color: hsl(var(--muted) / 0.5);
}
.bg-neutral-200 {
--tw-bg-opacity: 1;
background-color: rgb(229 229 229 / var(--tw-bg-opacity));
@ -1348,10 +1295,6 @@ body {
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
}
.bg-muted\/50 {
background-color: hsl(var(--muted) / 0.5);
}
.bg-opacity-50 {
--tw-bg-opacity: 0.5;
}
@ -1360,10 +1303,6 @@ body {
--tw-bg-opacity: 0.75;
}
.bg-\[url\(\'\/assets\/img\/grid\.svg\'\)\] {
background-image: url('/assets/img/grid.svg');
}
.bg-gradient-to-br {
background-image: linear-gradient(to bottom right, var(--tw-gradient-stops));
}
@ -1378,10 +1317,6 @@ body {
--tw-gradient-to: #fff var(--tw-gradient-to-position);
}
.bg-center {
background-position: center;
}
.fill-current {
fill: currentColor;
}
@ -1412,10 +1347,6 @@ body {
padding: 1.5rem;
}
.p-3 {
padding: 0.75rem;
}
.px-0\.5 {
padding-left: 0.125rem;
padding-right: 0.125rem;
@ -1490,10 +1421,6 @@ body {
padding-bottom: 1rem;
}
.pb-8 {
padding-bottom: 2rem;
}
.pl-6 {
padding-left: 1.5rem;
}
@ -1506,30 +1433,6 @@ body {
padding-top: 1.25rem;
}
.pb-12 {
padding-bottom: 3rem;
}
.pb-32 {
padding-bottom: 8rem;
}
.pt-6 {
padding-top: 1.5rem;
}
.pt-8 {
padding-top: 2rem;
}
.pb-10 {
padding-bottom: 2.5rem;
}
.pt-10 {
padding-top: 2.5rem;
}
.text-left {
text-align: left;
}
@ -1598,8 +1501,8 @@ body {
font-weight: 600;
}
.uppercase {
text-transform: uppercase;
.italic {
font-style: italic;
}
.leading-6 {
@ -1619,6 +1522,11 @@ body {
color: rgb(0 0 0 / var(--tw-text-opacity));
}
.text-blue-500 {
--tw-text-opacity: 1;
color: rgb(59 130 246 / var(--tw-text-opacity));
}
.text-card-foreground {
--tw-text-opacity: 1;
color: hsl(var(--card-foreground) / var(--tw-text-opacity));
@ -1664,6 +1572,11 @@ body {
color: rgb(17 24 39 / var(--tw-text-opacity));
}
.text-green-500 {
--tw-text-opacity: 1;
color: rgb(34 197 94 / var(--tw-text-opacity));
}
.text-muted-foreground {
--tw-text-opacity: 1;
color: hsl(var(--muted-foreground) / var(--tw-text-opacity));
@ -1694,11 +1607,6 @@ body {
color: rgb(255 255 255 / var(--tw-text-opacity));
}
.text-gray-500 {
--tw-text-opacity: 1;
color: rgb(107 114 128 / var(--tw-text-opacity));
}
.underline {
text-decoration-line: underline;
}
@ -1720,14 +1628,6 @@ body {
opacity: 1;
}
.opacity-90 {
opacity: 0.9;
}
.opacity-70 {
opacity: 0.7;
}
.opacity-80 {
opacity: 0.8;
}
@ -1764,6 +1664,10 @@ body {
--tw-ring-offset-color: hsl(var(--background) / 1);
}
.filter {
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
}
.backdrop-blur-sm {
--tw-backdrop-blur: blur(4px);
-webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);
@ -1806,6 +1710,10 @@ body {
transition-duration: 100ms;
}
.duration-150 {
transition-duration: 150ms;
}
.duration-200 {
transition-duration: 200ms;
}
@ -1814,10 +1722,6 @@ body {
transition-duration: 300ms;
}
.duration-150 {
transition-duration: 150ms;
}
.ease-in {
transition-timing-function: cubic-bezier(0.4, 0, 1, 1);
}
@ -1830,9 +1734,8 @@ body {
transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
}
.\[mask-image\:linear-gradient\(180deg\2c white\2c rgba\(255\2c 255\2c 255\2c 0\)\)\] {
-webkit-mask-image: linear-gradient(180deg,white,rgba(255,255,255,0));
mask-image: linear-gradient(180deg,white,rgba(255,255,255,0));
.\[contentStart\:end\] {
content-start: end;
}
.file\:border-0::file-selector-button {
@ -1886,6 +1789,11 @@ body {
background-color: rgb(75 85 99 / var(--tw-bg-opacity));
}
.hover\:bg-muted:hover {
--tw-bg-opacity: 1;
background-color: hsl(var(--muted) / var(--tw-bg-opacity));
}
.hover\:bg-neutral-200:hover {
--tw-bg-opacity: 1;
background-color: rgb(229 229 229 / var(--tw-bg-opacity));
@ -1899,20 +1807,6 @@ body {
background-color: hsl(var(--secondary) / 0.8);
}
.hover\:bg-gray-50:hover {
--tw-bg-opacity: 1;
background-color: rgb(249 250 251 / var(--tw-bg-opacity));
}
.hover\:bg-accent\/50:hover {
background-color: hsl(var(--accent) / 0.5);
}
.hover\:bg-muted:hover {
--tw-bg-opacity: 1;
background-color: hsl(var(--muted) / var(--tw-bg-opacity));
}
.hover\:bg-opacity-75:hover {
--tw-bg-opacity: 0.75;
}
@ -1922,6 +1816,11 @@ body {
color: hsl(var(--accent-foreground) / var(--tw-text-opacity));
}
.hover\:text-blue-700:hover {
--tw-text-opacity: 1;
color: rgb(29 78 216 / var(--tw-text-opacity));
}
.hover\:text-foreground:hover {
--tw-text-opacity: 1;
color: hsl(var(--foreground) / var(--tw-text-opacity));
@ -1932,6 +1831,11 @@ body {
color: rgb(107 114 128 / var(--tw-text-opacity));
}
.hover\:text-green-700:hover {
--tw-text-opacity: 1;
color: rgb(21 128 61 / var(--tw-text-opacity));
}
.hover\:text-neutral-500:hover {
--tw-text-opacity: 1;
color: rgb(115 115 115 / var(--tw-text-opacity));
@ -1949,17 +1853,6 @@ body {
opacity: 1;
}
.hover\:shadow-md:hover {
--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.focus\:border-primary:focus {
--tw-border-opacity: 1;
border-color: hsl(var(--primary) / var(--tw-border-opacity));
}
.focus\:outline-none:focus {
outline: 2px solid transparent;
outline-offset: 2px;
@ -1980,11 +1873,6 @@ body {
--tw-ring-color: rgb(99 102 241 / var(--tw-ring-opacity));
}
.focus\:ring-primary:focus {
--tw-ring-opacity: 1;
--tw-ring-color: hsl(var(--primary) / var(--tw-ring-opacity));
}
.focus\:ring-ring:focus {
--tw-ring-opacity: 1;
--tw-ring-color: hsl(var(--ring) / var(--tw-ring-opacity));
@ -2026,39 +1914,14 @@ body {
opacity: 0.5;
}
.group:hover .group-hover\:translate-x-1 {
--tw-translate-x: 0.25rem;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.group:hover .group-hover\:translate-x-0 {
--tw-translate-x: 0px;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.group:hover .group-hover\:text-primary {
--tw-text-opacity: 1;
color: hsl(var(--primary) / var(--tw-text-opacity));
}
.group:hover .group-hover\:text-gray-700 {
--tw-text-opacity: 1;
color: rgb(55 65 81 / var(--tw-text-opacity));
}
.group:hover .group-hover\:text-gray-800 {
--tw-text-opacity: 1;
color: rgb(31 41 55 / var(--tw-text-opacity));
}
.group:hover .group-hover\:text-gray-500 {
--tw-text-opacity: 1;
color: rgb(107 114 128 / var(--tw-text-opacity));
}
.group:hover .group-hover\:text-accent-foreground {
--tw-text-opacity: 1;
color: hsl(var(--accent-foreground) / var(--tw-text-opacity));
.group:hover .group-hover\:translate-x-1 {
--tw-translate-x: 0.25rem;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.group:hover .group-hover\:text-foreground {
@ -2066,10 +1929,6 @@ body {
color: hsl(var(--foreground) / var(--tw-text-opacity));
}
.group:hover .group-hover\:underline {
text-decoration-line: underline;
}
.group:hover .group-hover\:opacity-100 {
opacity: 1;
}
@ -2112,11 +1971,6 @@ body {
color: rgb(255 255 255 / var(--tw-text-opacity));
}
.dark\:text-neutral-400:is(.dark *) {
--tw-text-opacity: 1;
color: rgb(163 163 163 / var(--tw-text-opacity));
}
.dark\:file\:text-foreground:is(.dark *)::file-selector-button {
--tw-text-opacity: 1;
color: hsl(var(--foreground) / var(--tw-text-opacity));
@ -2199,18 +2053,10 @@ body {
}
@media (min-width: 768px) {
.md\:block {
display: block;
}
.md\:flex {
display: flex;
}
.md\:hidden {
display: none;
}
.md\:h-40 {
height: 10rem;
}

75
cmd/icongen/main.go Normal file
View File

@ -0,0 +1,75 @@
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
)
type Icon struct {
Name string `json:"name"`
Content string `json:"content"`
}
func main() {
svgDir := "./cmd/icongen/icons"
icons := []Icon{}
err := filepath.Walk(svgDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if filepath.Ext(path) == ".svg" {
content, err := os.ReadFile(path)
if err != nil {
return err
}
name := strings.TrimSuffix(filepath.Base(path), ".svg")
svgContent := extractSVGContent(string(content))
if svgContent != "" {
icons = append(icons, Icon{Name: name, Content: svgContent})
} else {
fmt.Printf("Warning: Empty content for icon %s\n", name)
}
}
return nil
})
if err != nil {
fmt.Printf("Error walking the path %v: %v\n", svgDir, err)
return
}
generateGoCode(icons)
}
func extractSVGContent(svgContent string) string {
start := strings.Index(svgContent, "<svg")
if start == -1 {
return ""
}
contentStart := strings.Index(svgContent[start:], ">") + start + 1
end := strings.LastIndex(svgContent, "</svg>")
if end == -1 || contentStart >= end {
return ""
}
return strings.TrimSpace(svgContent[contentStart:end])
}
func generateGoCode(icons []Icon) {
file, err := os.Create("./internals/ui/components/icon_contents.go")
if err != nil {
fmt.Println("Error creating file:", err)
return
}
defer file.Close()
fmt.Fprintln(file, "package components")
fmt.Fprintln(file, "\nvar iconContents = map[string]string{")
for _, icon := range icons {
if icon.Content != "" {
fmt.Fprintf(file, "\t\"%s\": `%s`,\n", icon.Name, icon.Content)
}
}
fmt.Fprintln(file, "}")
}

View File

@ -28,6 +28,7 @@ func main() {
mux.Handle("GET /docs/components/button", templ.Handler(pages.Button()))
mux.Handle("GET /docs/components/card", templ.Handler(pages.Card()))
mux.Handle("GET /docs/components/datepicker", templ.Handler(pages.Datepicker()))
mux.Handle("GET /docs/components/icon", templ.Handler(pages.Icon()))
mux.Handle("GET /docs/components/input", templ.Handler(pages.Input()))
mux.Handle("GET /docs/components/modal", templ.Handler(pages.Modal()))
mux.Handle("GET /docs/components/sheet", templ.Handler(pages.Sheet()))

View File

@ -53,6 +53,10 @@ var Sections = []Section{
Text: "Datepicker",
Href: "/docs/components/datepicker",
},
{
Text: "Icon",
Href: "/docs/components/icon",
},
{
Text: "Input",
Href: "/docs/components/input",

View File

@ -15,10 +15,10 @@ const (
Ghost ButtonVariant = "ghost"
Link ButtonVariant = "link"
Md ButtonSize = "md"
Sm ButtonSize = "sm"
Lg ButtonSize = "lg"
Icon ButtonSize = "icon"
Md ButtonSize = "md"
Sm ButtonSize = "sm"
Lg ButtonSize = "lg"
ButtonIcon ButtonSize = "icon"
)
// ButtonProps defines the properties for the Button component.
@ -90,7 +90,7 @@ func getSizeClasses(size ButtonSize) string {
return "h-9 px-3 rounded-md"
case Lg:
return "h-11 px-8 rounded-md"
case Icon:
case ButtonIcon:
return "h-10 w-10"
default:
return "h-10 px-4 py-2 rounded-md"

View File

@ -2,5 +2,5 @@ package components
import "embed"
//go:embed *.templ
//go:embed *.templ icon_contents.go
var TemplFiles embed.FS

View File

@ -0,0 +1,99 @@
package components
import (
"fmt"
"strings"
)
// IconProps defines the properties for the Icon component.
type IconProps struct {
Name string // Name of the icon
Size string // Size of the icon (default: "24")
Color string // Color of the icon (default: "currentColor")
Fill string // Fill color of the icon (default: "none")
Stroke string // Stroke color of the icon (overrides Color if set)
Class string // Additional CSS classes
}
// getSize returns the size of the icon, defaulting to "24" if not specified.
func getSize(size string) string {
if size == "" {
return "24"
}
return size
}
// getColor returns the color of the icon, defaulting to "currentColor" if not specified.
func getColor(color string) string {
if color == "" {
return "currentColor"
}
return color
}
// getFill returns the fill color of the icon, defaulting to "none" if not specified.
func getFill(fill string) string {
if fill == "" {
return "none"
}
return fill
}
// getStroke returns the stroke color, using the specified stroke or falling back to the color.
func getStroke(stroke, color string) string {
if stroke != "" {
return stroke
}
return getColor(color)
}
// getClasses generates the class string for the icon.
func getClasses(name, class string) string {
classes := []string{"lucide", "lucide-" + strings.ToLower(name)}
if class != "" {
classes = append(classes, class)
}
return strings.Join(classes, " ")
}
// Icon renders a Lucide icon as an SVG element.
//
// Usage:
//
// @components.Icon(components.IconProps{
// Name: "user",
// Size: "24",
// Color: "blue",
// Fill: "none",
// Stroke: "currentColor",
// Class: "my-icon-class",
// })
templ Icon(props IconProps) {
if content, ok := iconContents[props.Name]; ok && content != "" {
@templ.Raw(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">%s</svg>`,
getSize(props.Size),
getSize(props.Size),
getFill(props.Fill),
getStroke(props.Stroke, props.Color),
getClasses(props.Name, props.Class),
content))
} else {
<svg
xmlns="http://www.w3.org/2000/svg"
width={ getSize(props.Size) }
height={ getSize(props.Size) }
viewBox="0 0 24 24"
fill={ getFill(props.Fill) }
stroke={ getStroke(props.Stroke, props.Color) }
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class={ getClasses(props.Name, props.Class) }
>
<!-- Fallback for missing icons -->
<rect x="0" y="0" width="24" height="24" fill="none" stroke="currentColor"></rect>
<line x1="0" y1="0" x2="24" y2="24" stroke="currentColor"></line>
<line x1="24" y1="0" x2="0" y2="24" stroke="currentColor"></line>
</svg>
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,46 @@
package pages
import (
"github.com/axzilla/goilerplate/internals/ui/components"
"github.com/axzilla/goilerplate/internals/ui/layouts"
"github.com/axzilla/goilerplate/internals/ui/showcase"
)
templ Icon() {
@layouts.DocsLayout() {
<div>
<div class="mb-16">
<h1 class="text-3xl font-bold mb-2">Icon</h1>
<p class="mb-4 text-muted-foreground">
A wrapper for Lucide Icons with optional settings for size, color, fill, stroke, and custom classes. To use this component, you'll also need the code or component from the "Icon" tab!
</p>
</div>
@components.Tabs(components.TabsProps{
Tabs: []components.Tab{
{
ID: "preview",
Title: "Preview",
Content: showcase.IconShowcase(),
},
{
ID: "code",
Title: "Code",
Content: CodeSnippetFromEmbedded("icon.templ", "go", showcase.TemplFiles),
},
{
ID: "component",
Title: "Component",
Content: CodeSnippetFromEmbedded("icon.templ", "go", components.TemplFiles),
},
{
ID: "icons",
Title: "Icons",
Content: CodeSnippetFromEmbedded("icon_contents.go", "go", components.TemplFiles),
},
},
TabsContainerClass: "md:w-1/2",
ContentContainerClass: "w-full",
})
</div>
}
}

View File

@ -22,7 +22,7 @@ templ ButtonShowcase() {
@components.Button(components.ButtonProps{Text: "Default"})
@components.Button(components.ButtonProps{Text: "Small", Size: components.Sm})
@components.Button(components.ButtonProps{Text: "Large", Size: components.Lg})
@components.Button(components.ButtonProps{Size: components.Icon}) {
@components.Button(components.ButtonProps{Size: components.ButtonIcon}) {
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"

View File

@ -0,0 +1,50 @@
package showcase
import "github.com/axzilla/goilerplate/internals/ui/components"
templ IconShowcase() {
<div class="flex justify-center items-center border rounded-md py-16 px-4">
<div>
<div class="mb-8">
<h2 class="font-semibold mb-2">Basic Icons</h2>
<div class="flex flex-wrap gap-2">
@components.Icon(components.IconProps{Name: "user", Size: "24"})
@components.Icon(components.IconProps{Name: "house", Size: "24"})
@components.Icon(components.IconProps{Name: "settings", Size: "24"})
</div>
</div>
<div class="mb-8">
<h2 class="font-semibold mb-2">Colored Icons</h2>
<div class="flex flex-wrap gap-2">
@components.Icon(components.IconProps{Name: "heart", Size: "24", Color: "red"})
@components.Icon(components.IconProps{Name: "star", Size: "24", Color: "gold"})
@components.Icon(components.IconProps{Name: "check", Size: "24", Color: "green"})
</div>
</div>
<div class="mb-8">
<h2 class="font-semibold mb-2">Filled Icons</h2>
<div class="flex flex-wrap gap-2">
@components.Icon(components.IconProps{Name: "circle", Size: "24", Fill: "blue", Stroke: "blue"})
@components.Icon(components.IconProps{Name: "square", Size: "24", Fill: "purple", Stroke: "purple"})
@components.Icon(components.IconProps{Name: "triangle", Size: "24", Fill: "orange", Stroke: "orange"})
</div>
</div>
<div class="mb-8">
<h2 class="font-semibold mb-2">Different Sizes</h2>
<div class="flex flex-wrap gap-2">
@components.Icon(components.IconProps{Name: "house", Size: "16"})
@components.Icon(components.IconProps{Name: "house", Size: "24"})
@components.Icon(components.IconProps{Name: "house", Size: "32"})
@components.Icon(components.IconProps{Name: "house", Size: "48"})
</div>
</div>
<div class="mb-8">
<h2 class="font-semibold mb-2">Custom Classes</h2>
<div class="flex flex-wrap gap-2">
@components.Icon(components.IconProps{Name: "arrow-right", Size: "24", Class: "text-blue-500 hover:text-blue-700"})
@components.Icon(components.IconProps{Name: "arrow-left", Size: "24", Class: "text-green-500 hover:text-green-700"})
</div>
</div>
</div>
</div>
}