Repository: haatos/goshipit Branch: main Commit: efe198c76672 Files: 144 Total size: 569.6 KB Directory structure: gitextract_6j5602nm/ ├── .gitignore ├── LICENSE ├── Makefile ├── cmd/ │ ├── generate/ │ │ └── main.go │ ├── gsi/ │ │ └── main.go │ └── server/ │ └── main.go ├── go.mod ├── go.sum ├── input.css ├── internal/ │ ├── assets/ │ │ ├── content/ │ │ │ ├── cli.md │ │ │ ├── getting_started.md │ │ │ └── types.md │ │ ├── embed.go │ │ ├── generated/ │ │ │ ├── component_code_map.json │ │ │ ├── component_example_code_map.json │ │ │ └── components.go │ │ └── public/ │ │ └── static/ │ │ ├── css/ │ │ │ ├── chroma.css │ │ │ └── custom.css │ │ └── js/ │ │ ├── alpine.js │ │ └── datastar.js │ ├── components.go │ ├── echo.go │ ├── handler/ │ │ ├── components.go │ │ ├── error.go │ │ ├── htmx.go │ │ ├── input_validation.go │ │ ├── pages.go │ │ └── render.go │ ├── markdown/ │ │ └── markdown.go │ ├── markdown.go │ ├── model/ │ │ └── generation.go │ ├── settings.go │ └── views/ │ ├── components/ │ │ ├── accordion.templ │ │ ├── active_search.templ │ │ ├── alert.templ │ │ ├── anchor.templ │ │ ├── avatar.templ │ │ ├── banner.templ │ │ ├── breadcrumbs.templ │ │ ├── card.templ │ │ ├── carousel.templ │ │ ├── chat.templ │ │ ├── checkbox.templ │ │ ├── collapse.templ │ │ ├── combobox.templ │ │ ├── countdown.templ │ │ ├── date_picker.templ │ │ ├── diff.templ │ │ ├── drawer.templ │ │ ├── dropdown.templ │ │ ├── fab.templ │ │ ├── features.templ │ │ ├── file_input.templ │ │ ├── footer.templ │ │ ├── hero.templ │ │ ├── hover_3d_card.templ │ │ ├── infinite_scroll.templ │ │ ├── input.templ │ │ ├── lazy_load.templ │ │ ├── menu.templ │ │ ├── modal.templ │ │ ├── pagination.templ │ │ ├── pricing.templ │ │ ├── radio.templ │ │ ├── range.templ │ │ ├── rating.templ │ │ ├── select.templ │ │ ├── skeleton.templ │ │ ├── stats.templ │ │ ├── status.templ │ │ ├── steps.templ │ │ ├── swap.templ │ │ ├── table.templ │ │ ├── tabs.templ │ │ ├── testimonial.templ │ │ ├── text_rotate.templ │ │ ├── textarea.templ │ │ ├── time_slot_picker.templ │ │ ├── timeline.templ │ │ ├── toast.templ │ │ ├── toggle.templ │ │ └── tooltip.templ │ ├── custom/ │ │ └── toast.templ │ ├── embed.go │ ├── examples/ │ │ ├── accordion.templ │ │ ├── active_search.templ │ │ ├── alert.templ │ │ ├── anchor.templ │ │ ├── avatar.templ │ │ ├── banner.templ │ │ ├── breadcrumbs.templ │ │ ├── card.templ │ │ ├── carousel.templ │ │ ├── chat.templ │ │ ├── checkbox.templ │ │ ├── collapse.templ │ │ ├── combobox.templ │ │ ├── countdown.templ │ │ ├── date_picker.templ │ │ ├── diff.templ │ │ ├── drawer.templ │ │ ├── dropdown.templ │ │ ├── fab.templ │ │ ├── features.templ │ │ ├── file_input.templ │ │ ├── footer.templ │ │ ├── hero.templ │ │ ├── hover_3d_card.templ │ │ ├── infinite_scroll.templ │ │ ├── input.templ │ │ ├── lazy_load.templ │ │ ├── menu.templ │ │ ├── modal.templ │ │ ├── pagination.templ │ │ ├── pricing.templ │ │ ├── radio.templ │ │ ├── range.templ │ │ ├── rating.templ │ │ ├── select.templ │ │ ├── skeleton.templ │ │ ├── stats.templ │ │ ├── status.templ │ │ ├── steps.templ │ │ ├── swap.templ │ │ ├── table.templ │ │ ├── tabs.templ │ │ ├── testimonial.templ │ │ ├── text_rotate.templ │ │ ├── textarea.templ │ │ ├── time_slot_picker.templ │ │ ├── timeline.templ │ │ ├── toast.templ │ │ ├── toggle.templ │ │ └── tooltip.templ │ ├── pages/ │ │ ├── base.templ │ │ ├── cli.templ │ │ ├── client_error.templ │ │ ├── component.templ │ │ ├── index.templ │ │ └── server_error.templ │ └── scripts/ │ └── copy_button.templ ├── package.json ├── readme.md └── tailwind.config.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .vscode/ .env *_templ.go *_templ.txt bin/ node_modules/ internal/assets/public/static/css/tw.css .DS_Store ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2024 Tomi Haapalainen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Makefile ================================================ .DEFAULT_GOAL := dev GOOS := "linux" GOARCH := "amd64" deploy: npx @tailwindcss/cli -i input.css -o ./internal/assets/public/static/css/tw.css --minify go run cmd/generate/main.go templ generate GOOS=$(GOOS) GOARCH=$(GOARCH) go build -ldflags "-s -w" -o bin/new-main cmd/server/main.go scp 'bin/new-main' $(user)@$(ip):/opt/goshipit/ ssh $(user)@$(ip) "sudo service goshipit stop" ssh $(user)@$(ip) "rm /opt/goshipit/main" ssh $(user)@$(ip) "mv /opt/goshipit/new-main /opt/goshipit/main" ssh $(user)@$(ip) "sudo service goshipit start" gen: go run cmd/generate/main.go tw: @npx @tailwindcss/cli -i input.css -o ./internal/assets/public/static/css/tw.css --watch dev: gen @templ generate -watch -proxyport=7332 -proxy="http://localhost:8080" -open-browser=false -cmd="go run cmd/server/main.go" ================================================ FILE: cmd/generate/main.go ================================================ package main import ( "bufio" "bytes" "encoding/json" "fmt" "go/format" "io/fs" "log" "os" "path/filepath" "strings" "github.com/haatos/goshipit/internal/markdown" "github.com/haatos/goshipit/internal/model" ) const ( componentCodeMapJSONPath = "internal/assets/generated/component_code_map.json" componentExampleCodeMapJSONPath = "internal/assets/generated/component_example_code_map.json" componentsDir = "internal/views/components/" componentsHandlerPath = "internal/handler/components.go" examplesDir = "internal/views/examples/" generatedDir = "internal/assets/generated" generatedComponentsPath = "internal/assets/generated/components.go" ) func main() { generateComponentCodeMap() generateComponentExampleCodeMap() generateComponentMap() } func generateComponentCodeMap() { m := model.ComponentCodeMap{} if err := filepath.Walk(componentsDir, func(path string, info fs.FileInfo, err error) error { if !info.IsDir() && strings.HasSuffix(path, ".templ") { if err := getComponentCode(path, info, m); err != nil { return err } } return nil }); err != nil { log.Fatal(err) } if err := os.RemoveAll(generatedDir); err != nil { log.Fatal(err) } if err := os.Mkdir(generatedDir, 0755); err != nil { log.Fatal(err) } b, err := json.MarshalIndent(m, "", " ") if err != nil { log.Fatal(err) } fg, err := os.Create(componentCodeMapJSONPath) if err != nil { log.Fatal(err) } defer fg.Close() if _, err := fg.Write(b); err != nil { log.Fatal(err) } } func getComponentCode(path string, info fs.FileInfo, fmap model.ComponentCodeMap) error { f, err := os.Open(path) if err != nil { return err } defer f.Close() componentName := strings.TrimSuffix(info.Name(), ".templ") scanner := bufio.NewScanner(f) function := []string{} inFunction := false description := []string{} inDescription := false daisyUIURL := "" var category string for scanner.Scan() { line := scanner.Text() if category == "" { category = strings.TrimPrefix(line, "// ") continue } if strings.HasPrefix(line, "// https://daisyui.com") { daisyUIURL = strings.TrimPrefix(line, "// ") } if strings.HasPrefix(line, "/*") { inDescription = true continue } if strings.HasPrefix(line, "*/") { inDescription = false continue } if inDescription { description = append(description, line) continue } if strings.HasPrefix(line, "type ") || strings.HasPrefix(line, "templ ") { inFunction = true } if inFunction { function = append(function, line) } } if _, ok := fmap[category]; !ok { fmap[category] = []model.ComponentCode{} } fmap[category] = append( fmap[category], model.ComponentCode{ Name: componentName, Code: markdown.CodeSliceToMarkdown(function), Description: strings.Join(description, "\n"), DaisyUIURL: daisyUIURL, }) return nil } func generateComponentExampleCodeMap() { m := model.ComponentExampleCodeMap{} if err := filepath.Walk(examplesDir, func(path string, info fs.FileInfo, err error) error { if !info.IsDir() && strings.HasSuffix(path, ".templ") { if err := addComponentExampleToMap(m, info, path); err != nil { return err } } return nil }); err != nil { log.Fatal(err) } for comName := range m { for i := range m[comName] { if err := addComponentExampleBackendParts(m, comName, i); err != nil { log.Fatal(err) } } } b, err := json.MarshalIndent(m, "", " ") if err != nil { log.Fatal(err) } fg, err := os.Create(componentExampleCodeMapJSONPath) if err != nil { log.Fatal(err) } defer fg.Close() if _, err := fg.Write(b); err != nil { log.Fatal(err) } } func addComponentExampleToMap( m model.ComponentExampleCodeMap, info fs.FileInfo, path string, ) error { f, err := os.Open(path) if err != nil { return err } defer f.Close() functionLines := []string{} var functionName string componentName := strings.TrimSuffix(info.Name(), ".templ") m[componentName] = []model.ComponentCode{} inExample := false description := []string{} title := "" inDescription := false scanner := bufio.NewScanner(f) for scanner.Scan() { line := scanner.Text() if strings.HasPrefix(line, "// example") { if inExample { m[componentName] = append( m[componentName], model.ComponentCode{ Name: functionName, Code: markdown.CodeSliceToMarkdown(functionLines), Title: title, Description: strings.Join(description, "\n"), }) functionName = "" functionLines = []string{} description = []string{} } inExample = true continue } if strings.HasPrefix(line, "// ") && !strings.HasPrefix(line, "// example") { title = strings.TrimPrefix(line, "// ") continue } if strings.HasPrefix(line, "/*") { inDescription = true continue } if strings.HasPrefix(line, "*/") { inDescription = false continue } if inDescription { description = append(description, line) } if strings.HasPrefix(line, "templ ") && functionName == "" { functionName = strings.TrimPrefix(line, "templ ") functionName = functionName[:strings.Index(functionName, "(")] } if inExample && !inDescription { functionLines = append(functionLines, line) } } m[componentName] = append( m[componentName], model.ComponentCode{ Name: functionName, Code: markdown.CodeSliceToMarkdown(functionLines), Title: title, Description: strings.Join(description, "\n"), }, ) return nil } func addComponentExampleBackendParts( m model.ComponentExampleCodeMap, comName string, index int, ) error { f, err := os.Open(componentsHandlerPath) if err != nil { return err } defer f.Close() inExampleHandler := false functionLines := []string{} scanner := bufio.NewScanner(f) for scanner.Scan() { line := scanner.Text() if strings.HasPrefix(line, fmt.Sprintf("// %s", m[comName][index].Name)) { if inExampleHandler { inExampleHandler = false m[comName][index].Handler = markdown.CodeSliceToMarkdown(functionLines) break } else { inExampleHandler = true } continue } if inExampleHandler { functionLines = append(functionLines, line) } } return nil } func generateComponentMap() { functionNames := []string{} if err := filepath.Walk(examplesDir, func(path string, info fs.FileInfo, err error) error { if !info.IsDir() && strings.HasSuffix(path, ".templ") { f, err := os.Open(path) if err != nil { return err } inExample := false scanner := bufio.NewScanner(f) for scanner.Scan() { line := scanner.Text() if strings.HasPrefix(line, "// example") { inExample = true } if inExample && strings.HasPrefix(line, "templ ") { functionName := strings.TrimPrefix(line, "templ ") functionName = functionName[:strings.Index(functionName, "(")] functionNames = append(functionNames, functionName) inExample = false } } } return nil }); err != nil { log.Fatal(err) } writeGeneratedFunctions(functionNames) } func writeGeneratedFunctions(functionNames []string) { // write functions into a buffer src := bytes.NewBuffer(nil) src.WriteString("package generated\n\n") src.WriteString("import (\n") src.WriteString("\t\"github.com/a-h/templ\"\n") src.WriteString("\t\"github.com/haatos/goshipit/internal/views/examples\"\n") src.WriteString(")\n\n") src.WriteString("var ExampleComponents = map[string]templ.Component{\n") for _, name := range functionNames { fmt.Fprintf(src, "\t\"%s\": examples.%s(),\n", name, name) } src.WriteString("}\n") // format the buffer's bytes using gofmt b, err := format.Source(src.Bytes()) if err != nil { log.Fatal(err) } // write the file fg, err := os.Create(generatedComponentsPath) if err != nil { log.Fatal(err) } defer fg.Close() if _, err := fg.Write(b); err != nil { log.Fatal(err) } } ================================================ FILE: cmd/gsi/main.go ================================================ package main import ( "bytes" "errors" "fmt" "io" "log" "os" "path/filepath" "slices" "strings" "github.com/haatos/goshipit/internal/views" ) var componentsDirParts = []string{"internal", "views", "components"} func main() { code := run(os.Stdout, os.Stderr, os.Args) if code != 0 { os.Exit(code) } } const usageText string = `usage: gsi [...] gsi - GoShip.it CLI commands: add Add a component to internal/views/components remove Remove a component from internal/views/components list List all available components ` func run(stdout, stderr io.Writer, args []string) int { if len(args) < 2 { _, _ = fmt.Fprint(stdout, usageText) return 64 } switch args[1] { case "add": if len(args) < 3 { _, _ = fmt.Fprint(stdout, usageText) return 64 } name := strings.Join(args[2:], " ") return runAddCommand(stderr, name) case "remove": if len(args) < 3 { _, _ = fmt.Fprint(stdout, usageText) return 64 } name := strings.Join(args[2:], " ") return runRemoveCommand(stdout, name) case "list": return runListCommand(stdout) } return 1 } func runAddCommand(stderr io.Writer, name string) int { name = strings.ReplaceAll(name, " ", "_") b, err := views.ComponentFS.ReadFile("components/" + name + ".templ") if err != nil { _, _ = fmt.Fprintf(stderr, "component '%s' does not exist\n", name) return 1 } packageIndex := bytes.Index(b, []byte("package")) b = b[packageIndex:] dirPath := filepath.Join(componentsDirParts...) fileName := fmt.Sprintf("%s.templ", name) fullPath := filepath.Join(dirPath, fileName) componentExists := true if _, err := os.Stat(fullPath); errors.Is(err, os.ErrNotExist) { componentExists = false } if componentExists { var ans string fmt.Printf("Component '%s' already exists. Replace [Y/n]: ", name) if _, err := fmt.Scan(&ans); err != nil { log.Fatal(err) } if ans == "n" { return 0 } fmt.Printf("Replacing component '%s'...\n", name) } if err := os.MkdirAll(dirPath, os.ModePerm); err != nil { log.Fatal("err making directories '", dirPath+"':", err) } f, err := os.Create(fullPath) if err != nil { log.Fatal("err creating file '", fullPath+"':", err) } if _, err := f.Write(b); err != nil { log.Fatal("err writing component to '", fullPath+"':", err) } return 0 } func runRemoveCommand(stdout io.Writer, name string) int { name = strings.ReplaceAll(name, " ", "_") dirPath := filepath.Join(componentsDirParts...) fileName := fmt.Sprintf("%s.templ", name) templFileName := fmt.Sprintf("%s_templ.go", name) if err := os.Remove(filepath.Join(dirPath, fileName)); err == nil { _, _ = fmt.Fprintf(stdout, "Removed '%s'\n", filepath.Join(dirPath, fileName)) } if err := os.Remove(filepath.Join(dirPath, templFileName)); err != nil { _, _ = fmt.Fprintf(stdout, "Removed '%s'\n", filepath.Join(dirPath, templFileName)) } return 0 } func runListCommand(stdout io.Writer) int { entries, err := views.ComponentFS.ReadDir("components") if err != nil { log.Fatal(err) } _, _ = fmt.Fprint(stdout, "Run 'gsi add ' to add a component\n\n") m := make(map[string][]string) for _, entry := range entries { b, err := views.ComponentFS.ReadFile(filepath.Join("components", entry.Name())) if err != nil { log.Fatal(err) } split := bytes.Split(b, []byte("\n")) category := split[0] category = bytes.TrimPrefix(category, []byte("// ")) category = bytes.ReplaceAll(category, []byte("_"), []byte(" ")) categoryStr := string(category) m[categoryStr] = append(m[categoryStr], strings.TrimSuffix(entry.Name(), ".templ")) } keys := make([]string, 0, len(m)) for k := range m { keys = append(keys, k) } slices.Sort(keys) for _, key := range keys { _, _ = fmt.Fprint(stdout, strings.ToUpper(key)+"\n") _, _ = fmt.Fprint(stdout, "=======================\n") for _, comp := range m[key] { p := filepath.Join("internal", "views", "components", comp+".templ") _, err := os.Stat(p) exists := err == nil line := strings.ReplaceAll(comp, "_", " ") if exists { line += " ✓" } line += "\n" _, _ = fmt.Fprint(stdout, line) } _, _ = fmt.Fprint(stdout, "\n") } return 0 } ================================================ FILE: cmd/server/main.go ================================================ package main import ( "github.com/haatos/goshipit/internal" "github.com/haatos/goshipit/internal/assets" "github.com/haatos/goshipit/internal/handler" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" _ "github.com/mattn/go-sqlite3" ) func main() { internal.ReadDotenv() internal.Settings = internal.NewSettings() e := echo.New() e.HTTPErrorHandler = handler.ErrorHandler loggerFormat := "${method} ${uri} [${status}] (${latency_human}) | ${short_file}:${line} | ${message}\n" e.Logger.SetHeader(loggerFormat) config := internal.GetRateLimiterConfig() e.Use(middleware.RateLimiterWithConfig(config)) e.Use( middleware.LoggerWithConfig(middleware.LoggerConfig{ Format: loggerFormat, }), middleware.GzipWithConfig(middleware.GzipConfig{ Skipper: middleware.DefaultSkipper, Level: 3, }), ) publicFS := echo.MustSubFS(assets.PublicFS, "public") e.StaticFS("/", publicFS) e.GET("/", handler.GetIndexPage) e.GET("/about", handler.GetAboutPage) e.GET("/get-started", handler.GetGettingStartedPage) e.GET("/types", handler.GetTypesPage) e.GET("/cli", handler.GetCLIPage) e.GET("/component-anchors", handler.GetComponentAnchors) e.GET("/privacy", handler.GetPrivacyPolicyPage) e.GET("/terms-of-service", handler.GetTermsOfServicePage) e.GET("/components/:category/:name", handler.GetComponentPage) e.GET("/components/search", handler.GetComponentSearch) // handlers for component examples e.POST("/validate/string/:name", handler.PostValidateString) e.GET("/infinite-scroll", handler.GetInfiniteScrollExample) e.GET("/infinite-scroll-rows", handler.GetInfiniteScrollExampleRows) e.GET("/active-search", handler.GetActiveSearchExample) e.GET("/lazy-load", handler.GetLazyLoadExample) e.GET("/models", handler.GetCascadingSelectExample) e.GET("/pagination-pages", handler.GetPaginationExamplePage) e.POST("/combobox/:name/:value", handler.PostCombobox) e.POST("/combobox-submit/:name", handler.PostComboboxSubmit) e.DELETE("/modal-confirm", handler.DeleteModalExample) e.POST("/datepicker/select", handler.PostDatePickerSelectDay) e.GET("/datepicker", handler.GetDatePicker) e.GET("/datepicker/monthpicker", handler.GetDatePickerMonthPicker) e.GET("/datepicker/yearpicker", handler.GetDatePickerYearPicker) e.GET("/timeslotpicker", handler.GetTimeSlotPicker) e.POST("/timeslotpicker/reserve", handler.PostTimeSlotPickerReserve) // handlers for component examples internal.GracefulShutdown(e, internal.Settings.Port) } ================================================ FILE: go.mod ================================================ module github.com/haatos/goshipit go 1.24 require ( github.com/PuerkitoBio/goquery v1.10.1 github.com/a-h/templ v0.3.1001 github.com/alecthomas/chroma v0.10.0 github.com/labstack/echo/v4 v4.12.0 github.com/labstack/gommon v0.4.2 github.com/mattn/go-sqlite3 v1.14.24 github.com/russross/blackfriday v1.6.0 golang.org/x/time v0.7.0 ) require ( github.com/andybalholm/cascadia v1.3.3 // indirect github.com/dlclark/regexp2 v1.11.4 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect golang.org/x/crypto v0.40.0 // indirect golang.org/x/net v0.42.0 // indirect golang.org/x/sys v0.34.0 // indirect golang.org/x/text v0.27.0 // indirect ) ================================================ FILE: go.sum ================================================ github.com/PuerkitoBio/goquery v1.10.1 h1:Y8JGYUkXWTGRB6Ars3+j3kN0xg1YqqlwvdTV8WTFQcU= github.com/PuerkitoBio/goquery v1.10.1/go.mod h1:IYiHrOMps66ag56LEH7QYDDupKXyo5A8qrjIx3ZtujY= github.com/a-h/templ v0.3.943 h1:o+mT/4yqhZ33F3ootBiHwaY4HM5EVaOJfIshvd5UNTY= github.com/a-h/templ v0.3.943/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo= github.com/a-h/templ v0.3.960 h1:trshEpGa8clF5cdI39iY4ZrZG8Z/QixyzEyUnA7feTM= github.com/a-h/templ v0.3.960/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo= github.com/a-h/templ v0.3.977 h1:kiKAPXTZE2Iaf8JbtM21r54A8bCNsncrfnokZZSrSDg= github.com/a-h/templ v0.3.977/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo= github.com/a-h/templ v0.3.1001 h1:yHDTgexACdJttyiyamcTHXr2QkIeVF1MukLy44EAhMY= github.com/a-h/templ v0.3.1001/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo= github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek= github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s= github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM= github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= ================================================ FILE: input.css ================================================ @import "tailwindcss"; @plugin "@tailwindcss/typography"; @source "./internal/views/**/*.templ"; @plugin "daisyui" { themes: light --default, night --prefersdark; } ================================================ FILE: internal/assets/content/cli.md ================================================ # CLI ## Installation ``` go install github.com/haatos/cmd/gsi ``` Then run `gsi` in your terminal to see a list of commands: ``` usage: gsi [...] gsi - GoShip.it CLI commands: add Add a component to internal/views/components remove Remove a component from internal/views/components list List all available components ``` Use the CLI at the root of your application. New components will be added to `internal/views/components/`. ================================================ FILE: internal/assets/content/getting_started.md ================================================ # Getting started To get started with goship.it in a new project using _Echo_ router: - Create a new folder for your project - Initialize the module by running - `go mod init github.com/my/package` - replace the package with your own repository! - Get Go modules: - `go get -u github.com/labstack/echo/v4` - `go get -u github.com/a-h/templ` - Install Templ CLI: - `go install github.com/a-h/templ/cmd/templ@latest` - Install TailwindCSS and DaisyUI: - `npm i -D tailwindcss @tailwindcss/cli @tailwindcss/typography daisyui@latest` - Create `input.css` at the root of the project with the following contents: ```input.css @import "tailwindcss" source(none); @plugin "@tailwindcss/typography"; @source "./internal/views/**/*.templ"; @plugin "daisyui" { themes: light --default, dark --prefersdark; } @plugin "daisyui/theme" { name: "light"; default: true; prefersdark: false; color-scheme: "light"; --color-base-100: oklch(92% 0 0); --color-base-200: oklch(87% 0 0); --color-base-300: oklch(70% 0 0); --color-base-content: oklch(0% 0 0); --color-primary: oklch(55% 0.135 66.442); --color-primary-content: oklch(98% 0.018 155.826); --color-secondary: oklch(53% 0.157 131.589); --color-secondary-content: oklch(98% 0.031 120.757); --color-accent: oklch(64% 0.222 41.116); --color-accent-content: oklch(98% 0.016 73.684); --color-neutral: oklch(43% 0 0); --color-neutral-content: oklch(98% 0 0); --color-info: oklch(52% 0.105 223.128); --color-info-content: oklch(98% 0.019 200.873); --color-success: oklch(50% 0.118 165.612); --color-success-content: oklch(98% 0.018 155.826); --color-warning: oklch(55% 0.195 38.402); --color-warning-content: oklch(98% 0.016 73.684); --color-error: oklch(52% 0.223 3.958); --color-error-content: oklch(97% 0.014 343.198); --radius-selector: 0.5rem; --radius-field: 0.5rem; --radius-box: 0.5rem; --size-selector: 0.28125rem; --size-field: 0.28125rem; --border: 1px; --depth: 1; --noise: 0; } @plugin "daisyui/theme" { name: "dark"; default: false; prefersdark: true; color-scheme: "dark"; --color-base-100: oklch(26% 0 0); --color-base-200: oklch(20% 0 0); --color-base-300: oklch(14% 0 0); --color-base-content: oklch(97% 0 0); --color-primary: oklch(79% 0.184 86.047); --color-primary-content: oklch(28% 0.066 53.813); --color-secondary: oklch(64% 0.2 131.684); --color-secondary-content: oklch(98% 0.031 120.757); --color-accent: oklch(64% 0.222 41.116); --color-accent-content: oklch(98% 0.016 73.684); --color-neutral: oklch(14% 0 0); --color-neutral-content: oklch(98% 0 0); --color-info: oklch(71% 0.143 215.221); --color-info-content: oklch(98% 0.019 200.873); --color-success: oklch(72% 0.219 149.579); --color-success-content: oklch(98% 0.018 155.826); --color-warning: oklch(70% 0.213 47.604); --color-warning-content: oklch(98% 0.016 73.684); --color-error: oklch(65% 0.241 354.308); --color-error-content: oklch(97% 0.014 343.198); --radius-selector: 0.5rem; --radius-field: 0.5rem; --radius-box: 0.5rem; --size-selector: 0.28125rem; --size-field: 0.28125rem; --border: 1px; --depth: 1; --noise: 0; } ``` - Create `Makefile` at the base of your project with the following contents: ```make tw: @npx @tailwindcss/cli -i input.css -o ./public/static/css/tw.css --watch dev: @templ generate -watch -proxyport=7332 -proxy="http://localhost:8080" -open-browser=false -cmd="go run main.go" ``` - Place the following rows in `main.go` (remember to update the components package import path to match your project): ```go package main import ( "net/http" "github.com/my/package/internal/views/components" "github.com/a-h/templ" "github.com/labstack/echo/v4" ) func main() { e := echo.New() e.Static("/", "public") e.GET("/", func(c echo.Context) error { accordion := components.AccordionExample() return render(c, accordion) }) e.Start(":8080") } func render(c echo.Context, component templ.Component) error { buf := templ.GetBuffer() defer templ.ReleaseBuffer(buf) if err := component.Render(c.Request().Context(), buf); err != nil { return err } return c.HTML(http.StatusOK, buf.String()) } ``` - Create the accordion component `internal/views/components/accordion.templ`: ```go package components type AccordionRowProps struct { Label string Type string Name string } templ AccordionRow(props AccordionRowProps) {
{ props.Label }
{ children... }
} templ AccordionExample() { Document
@AccordionRow(AccordionRowProps{Label: "Accordion row 1", Type: "checkbox"}) {

This is the first content

} @AccordionRow(AccordionRowProps{Label: "Accordion row 2", Type: "checkbox"}) {

This is the second content

}
} ``` At this point, the filetree of your project should look something like this: ```sh . ├── Makefile ├── go.mod ├── go.sum ├── input.css ├── internal │ └── views │ └── components │ └── accordion.templ ├── main.go ├── node_modules ├── package-lock.json ├── package.json ├── public │ └── static │ └── css │ └── tw.css └── tailwind.config.js ``` If you are using VSCode as your IDE, you should also add a `.vscode/settings.json` with the following contents (or place these settings in some other VSCode configuration file): ```json { "[templ]": { "editor.formatOnSave": true, "editor.defaultFormatter": "a-h.templ" }, "tailwindCSS.includeLanguages": { "templ": "html" }, "emmet.includeLanguages": { "templ": "html" } } ``` These will enable TailwindCSS autocompletions and HTML element autocompletions (emmet), as well as automatically formatting `.templ` files when saving. Finally, you can run the example application by running `make tw` and `make dev` in two separate terminals. The site with the accordion should now be visible at http://localhost:8080. ================================================ FILE: internal/assets/content/types.md ================================================ # Types ## Introduction Most components use a struct as the input argument. This provides a convenient way to pass default values to the components; struct fields initialize to the respective type's default value. We can use this feature to make some of the fields of a struct optional to make initialization less cumbersome. For example, we can pass the `components.AnchorProps` argument to an anchor element, ``: ```go package components type AnchorProps struct { Href string Label string LeftIcon templ.Component RightIcon templ.Component Attrs templ.Attributes Class string } templ Anchor(props AnchorProps) { if props.LeftIcon != nil {
@props.LeftIcon
} { props.Label } if props.RightIcon != nil {
@props.RightIcon
}
} ``` Here we can define the anchor element to optionally have an `href` attribute, or if we choose, a `hx-get` instead: ```go @components.Anchor(components.AnchorProps{Href: "/"}) @components.Anchor(components.AnchorProps{Attrs: templ.Attributes{"hx-get": "/"}}) ``` ## Deprecated types ```go package model import ( "time" "github.com/a-h/templ" ) type Accordion struct { Label string Type string Name string } type ActiveSearchInput struct { ID string URL string Target string Input Input } type Anchor struct { Href string Label string LeftIcon templ.Component RightIcon templ.Component Attrs templ.Attributes Class string } type Avatar struct { AvatarClass string ContainerClass string Source string Placeholder string PlaceholderClass string } type Banner struct { Title templ.Component Description string CallToAction Button SecondaryCallToAction Button } type Button struct { Label string Attrs templ.Attributes } type Card struct { Title string Content string Source string Alt string Class string } type Chat struct { Messages []ChatMessage } type ChatMessage struct { AvatarURL string Sender string Time string Message string Footer string Location string Class string } type Checkbox struct { ID string Before string After string Name string Checked bool Class string Attrs templ.Attributes } type Collapse struct { Class string Title string TitleClass string ContentClass string } type Combobox struct { Label string Name string URL string Options []string Selected []string } type CompanyInfo struct { Icon templ.Component Name string Description string Copyright string } type DatePicker struct { Year int Month int Selected time.Time StartOfWeek time.Weekday } func (dp DatePicker) Days() []time.Time { days := make([]time.Time, 0, 31) now := time.Now().UTC() start := time.Date(dp.Year, time.Month(dp.Month), 1, 0, 0, 0, 0, now.Location()) end := start.AddDate(0, 1, -1) for end.Weekday() != dp.StartOfWeek { end = end.AddDate(0, 0, 1) } end = end.AddDate(0, 0, -1) for start.Weekday() != dp.StartOfWeek { start = start.AddDate(0, 0, -1) } for !start.After(end) { days = append(days, start) start = start.AddDate(0, 0, 1) } return days } func (dp DatePicker) Months() []time.Time { months := make([]time.Time, 12) for i := 1; i <= 12; i++ { dt := time.Date(dp.Year, time.Month(i), 1, 0, 0, 0, 0, time.Now().Location()) months[i-1] = dt } return months } type Dropdown struct { Label string Class string ListClass string Items []DropdownItem } type DropdownItem struct { Label string Attrs templ.Attributes } type Feature struct { Icon templ.Component Title string Description string URL string } type Image struct { Source string Alt string } type Input struct { ID string Type string // defaults to "text" Label string Name string Value string Placeholder string Err string Attrs templ.Attributes Class string Icon templ.Component Disabled bool DisabledMessage string Required bool } type PaginationItem struct { URL string Page int Low int High int MaxPages int } type Price struct { Title string Description string Price string Per string IncludedFeatures []string ExcludedFeatures []string CallToAction Button Footer templ.Component } type Radio struct { Name string Values map[string]string Class string } type Range struct { ID string Label string Name string Value int Min int Max int Step int Class string } type Rating struct { Name string Min int Max int Class string Value int } type Script struct { Source string Defer bool } type Select struct { ID string Label string Name string Options []SelectOption Attrs templ.Attributes Class string } type SelectOption struct { Label string Value string Selected bool Disabled bool } type Stat struct { Title string Value string Description string } type Status struct { Code int Title string Description string ReturnButton Button } type Swap struct { On templ.Component Off templ.Component Class string } type Tabs struct { Name string Class string Tabs []Tab ContentClass string } type Tab struct { Label string Content templ.Component } type Testimonial struct { Avatar templ.Component Name string Rating int Content string } type Textarea struct { ID string Label string Name string Placeholder string Value string Rows int Err string Class string Attrs templ.Attributes } type TimelineItem struct { Start string Middle templ.Component End string } type Toast struct { Name string ToastClass string AlertClass string } type Toggle struct { ID string Before string After string Name string Checked bool Class string Highlight bool Attrs templ.Attributes } type Tooltip struct { Tip string Class string } ``` ================================================ FILE: internal/assets/embed.go ================================================ package assets import "embed" //go:embed public/* var PublicFS embed.FS //go:embed content/* var ContentFS embed.FS //go:embed generated/* var GeneratedFS embed.FS ================================================ FILE: internal/assets/generated/component_code_map.json ================================================ { "actions": [ { "name": "dropdown", "code": "```go\ntype DropdownProps struct {\n\tLabel string\n\tClass string\n\tListClass string\n\tItems []DropdownItem\n}\n\ntype DropdownItem struct {\n\tLabel string\n\tAttrs templ.Attributes\n}\n\ntempl Dropdown(props DropdownProps) {\n\t\u003cdetails class={ \"dropdown\", props.Class }\u003e\n\t\t\u003csummary class=\"btn m-1\"\u003e{ props.Label }\u003c/summary\u003e\n\t\t\u003cul class={ \"menu dropdown-content\", props.ListClass }\u003e\n\t\t\tfor _, di := range props.Items {\n\t\t\t\t\u003cli\u003e\u003ca { di.Attrs... }\u003e{ di.Label }\u003c/a\u003e\u003c/li\u003e\n\t\t\t}\n\t\t\u003c/ul\u003e\n\t\u003c/details\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/dropdown" }, { "name": "fab", "code": "```go\ntype FABProps struct {\n\tClass string\n\tToggle any // string or templ.Component\n\tToggleClass string\n\tClose templ.Component\n\tMainAction templ.Component\n}\n\ntempl FAB(props FABProps) {\n\t\u003cdiv class={ \"fab\", props.Class }\u003e\n\t\t\u003cdiv tabindex=\"0\" role=\"button\" class={ \"btn\", props.ToggleClass }\u003e\n\t\t\tif t, ok := props.Toggle.(string); ok {\n\t\t\t\t{ t }\n\t\t\t} else {\n\t\t\t\t@props.Toggle.(templ.Component)\n\t\t\t}\n\t\t\u003c/div\u003e\n\t\tif props.Close != nil {\n\t\t\t\u003cdiv class=\"fab-close\"\u003e\n\t\t\t\t@props.Close\n\t\t\t\u003c/div\u003e\n\t\t}\n\t\tif props.MainAction != nil {\n\t\t\t\u003cdiv class=\"fab-main-action\"\u003e\n\t\t\t\t@props.MainAction\n\t\t\t\u003c/div\u003e\n\t\t}\n\t\t{ children... }\n\t\u003c/div\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/fab" }, { "name": "modal", "code": "```go\ntype ModalProps struct {\n\tID string\n\tLabel any\n}\n\ntempl Modal(props ModalProps) {\n\t@modalWrapper(\n\t\tprops,\n\t\ttempl.Attributes{\"onclick\": fmt.Sprintf(\"%s.showModal()\", props.ID)},\n\t) {\n\t\t{ children... }\n\t}\n}\n\ntempl modalWrapper(props ModalProps, attrs templ.Attributes) {\n\t// you can use a string or a templ.Component as the 'label'\n\t// of the modal button\n\tif s, ok := props.Label.(string); ok {\n\t\t\u003cdiv class=\"btn\" { attrs... }\u003e\n\t\t\t{ s }\n\t\t\u003c/div\u003e\n\t} else if c, ok := props.Label.(templ.Component); ok {\n\t\t@c\n\t}\n\t\u003cdialog id={ props.ID } class=\"modal\"\u003e\n\t\t\u003cdiv class=\"modal-box\"\u003e\n\t\t\t{ children... }\n\t\t\u003c/div\u003e\n\t\t\u003cform method=\"dialog\" class=\"modal-backdrop\"\u003e\n\t\t\t\u003cbutton\u003eclose\u003c/button\u003e\n\t\t\u003c/form\u003e\n\t\u003c/dialog\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/modal" }, { "name": "swap", "code": "```go\ntype SwapProps struct {\n\tOn templ.Component\n\tOff templ.Component\n\tClass string\n}\n\ntempl Swap(props SwapProps) {\n\t\u003clabel class={ \"swap\", props.Class }\u003e\n\t\t\u003cinput type=\"checkbox\"/\u003e\n\t\t\u003cdiv class=\"swap-on\"\u003e\n\t\t\t@props.On\n\t\t\u003c/div\u003e\n\t\t\u003cdiv class=\"swap-off\"\u003e\n\t\t\t@props.Off\n\t\t\u003c/div\u003e\n\t\u003c/label\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/swap" } ], "data_display": [ { "name": "accordion", "code": "```go\ntype AccordionRowProps struct {\n\tLabel string\n\tType string\n\tName string\n}\n\ntempl AccordionRow(props AccordionRowProps) {\n\t\u003cdiv class=\"collapse collapse-arrow bg-base-300 join-item\"\u003e\n\t\t\u003cinput\n\t\t\tif props.Type == \"\" {\n\t\t\t\ttype=\"checkbox\"\n\t\t\t} else {\n\t\t\t\ttype={ props.Type }\n\t\t\t}\n\t\t\tname={ props.Name }\n\t\t/\u003e\n\t\t\u003cdiv class=\"collapse-title text-xl font-medium\"\u003e{ props.Label }\u003c/div\u003e\n\t\t\u003cdiv class=\"collapse-content bg-base-200\"\u003e\n\t\t\t{ children... }\n\t\t\u003c/div\u003e\n\t\u003c/div\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/accordion" }, { "name": "active_search", "code": "```go\ntype ActiveSearchInputProps struct {\n\tID string\n\tURL string\n\tTarget string\n\tInputProps InputProps\n}\n\ntempl ActiveSearchInput(props ActiveSearchInputProps) {\n\t\u003cdiv class=\"w-full flex space-x-2 items-center\"\u003e\n\t\t\u003cdiv class=\"flex items-center gap-2 w-full max-w-xs\"\u003e\n\t\t\t\u003cform\n\t\t\t\tid={ props.ID }\n\t\t\t\thx-get={ props.URL }\n\t\t\t\thx-target={ props.Target }\n\t\t\t\thx-swap=\"innerHTML\"\n\t\t\t\thx-trigger={ fmt.Sprintf(\"keyup from:input[name=%s] delay:500ms, search\", props.InputProps.Name) }\n\t\t\t\thx-indicator={ fmt.Sprintf(\"#%s\", props.ID) }\n\t\t\t\tclass=\"w-full relative\"\n\t\t\t\u003e\n\t\t\t\t@Input(props.InputProps)\n\t\t\t\t\u003cdiv class=\"absolute -right-8 top-2\"\u003e\n\t\t\t\t\t\u003cspan id={ props.ID } class=\"htmx-indicator loading loading-sm loading-spinner\"\u003e\u003c/span\u003e\n\t\t\t\t\u003c/div\u003e\n\t\t\t\u003c/form\u003e\n\t\t\u003c/div\u003e\n\t\u003c/div\u003e\n}\n```" }, { "name": "avatar", "code": "```go\ntype AvatarProps struct {\n\tAvatarClass string\n\tContainerClass string\n\tSource string\n\tPlaceholder string\n\tPlaceholderClass string\n}\n\ntempl Avatar(props AvatarProps) {\n\t\u003cdiv class={ \"avatar\", props.AvatarClass }\u003e\n\t\t\u003cdiv class={ props.ContainerClass }\u003e\n\t\t\t\u003cimg src={ props.Source }/\u003e\n\t\t\tif props.Placeholder != \"\" {\n\t\t\t\t\u003cspan class={ props.PlaceholderClass }\u003e{ props.Placeholder }\u003c/span\u003e\n\t\t\t}\n\t\t\u003c/div\u003e\n\t\u003c/div\u003e\n}\n\ntempl AvatarGroup(class string) {\n\t\u003cdiv class={ \"avatar-group rtl:space-x-reverse\", class }\u003e\n\t\t{ children... }\n\t\u003c/div\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/avatar" }, { "name": "card", "code": "```go\ntype CardProps struct {\n\tTitle string\n\tContent string\n\tSource string\n\tAlt string\n\tClass string\n}\n\ntempl Card(props CardProps) {\n\t\u003cdiv class={ \"card\", props.Class }\u003e\n\t\tif props.Source != \"\" {\n\t\t\t\u003cfigure\u003e\n\t\t\t\t\u003cimg src={ props.Source } alt={ props.Alt }/\u003e\n\t\t\t\u003c/figure\u003e\n\t\t}\n\t\t\u003cdiv class=\"card-body\"\u003e\n\t\t\t\u003ch2 class=\"card-title\"\u003e{ props.Title }\u003c/h2\u003e\n\t\t\t\u003cp\u003e{ props.Content }\u003c/p\u003e\n\t\t\t\u003cdiv class=\"card-actions justify-end\"\u003e\n\t\t\t\t{ children... }\n\t\t\t\u003c/div\u003e\n\t\t\u003c/div\u003e\n\t\u003c/div\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/card" }, { "name": "carousel", "code": "```go\ntype CarouselProps []CarouselProp\n\ntype CarouselProp struct {\n\tSource string\n\tAlt string\n}\n\ntempl Carousel(props CarouselProps) {\n\t\u003cdiv class=\"carousel carousel-center rounded-box\"\u003e\n\t\tfor _, prop := range props {\n\t\t\t\u003cdiv class=\"carousel-item [\u0026:not(:last-child)]:border-r border-r-base-300\"\u003e\n\t\t\t\t\u003cimg class=\"max-w-xs\" src={ prop.Source } alt={ prop.Alt }/\u003e\n\t\t\t\u003c/div\u003e\n\t\t}\n\t\u003c/div\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/carousel" }, { "name": "chat", "code": "```go\ntype ChatProps []ChatMessageProps\n\ntype ChatMessageProps struct {\n\tAvatarURL string\n\tSender string\n\tTime string\n\tMessage string\n\tFooter string\n\tLocation string\n\tClass string\n}\n\ntempl Chat(props ChatProps) {\n\tfor _, prop := range props {\n\t\t@ChatMessage(prop)\n\t}\n}\n\ntempl ChatMessage(props ChatMessageProps) {\n\t\u003cdiv\n\t\tclass={\n\t\t\t\"chat\",\n\t\t\ttempl.KV(\"chat-start\", props.Location == \"start\"),\n\t\t\ttempl.KV(\"chat-end\", props.Location == \"end\"),\n\t\t}\n\t\u003e\n\t\tif props.AvatarURL != \"\" {\n\t\t\t@Avatar(AvatarProps{ContainerClass: \"chat-image w-10 rounded-full\", Source: props.AvatarURL})\n\t\t}\n\t\t\u003cdiv class=\"chat-header\"\u003e\n\t\t\t{ props.Sender }\n\t\t\t\u003ctime class=\"text-xs opacity-50\"\u003e{ props.Time }\u003c/time\u003e\n\t\t\u003c/div\u003e\n\t\t\u003cdiv class={ \"chat-bubble\", props.Class }\u003e\n\t\t\t{ props.Message }\n\t\t\u003c/div\u003e\n\t\t\u003cdiv class=\"chat-footer\"\u003e\n\t\t\t{ props.Footer }\n\t\t\u003c/div\u003e\n\t\u003c/div\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/chat" }, { "name": "collapse", "code": "```go\ntype CollapseProps struct {\n\tClass string\n\tTitle string\n\tTitleClass string\n\tContentClass string\n}\n\ntempl Collapse(props CollapseProps) {\n\t\u003cdiv\n\t\ttabindex=\"0\"\n\t\tclass={ \"collapse\", props.Class }\n\t\u003e\n\t\t\u003cinput type=\"checkbox\"/\u003e\n\t\t\u003cdiv class={ \"collapse-title\", props.TitleClass }\u003e\n\t\t\t{ props.Title }\n\t\t\u003c/div\u003e\n\t\t\u003cdiv class={ \"collapse-content\", props.ContentClass }\u003e\n\t\t\t{ children... }\n\t\t\u003c/div\u003e\n\t\u003c/div\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/collapse" }, { "name": "countdown", "code": "```go\ntype CountdownProps struct {\n\tExpires time.Time\n\tDays bool\n\tHours bool\n\tMinutes bool\n\tSeconds bool\n}\n\ntempl Countdown(props CountdownProps) {\n\t{{ spanID := time.Now().UnixNano() }}\n\t\u003cdiv class=\"grid auto-cols-max grid-flow-col gap-5 text-center\"\u003e\n\t\t\u003cdiv\n\t\t\tclass={\n\t\t\t\t\"bg-neutral rounded-box text-neutral-content flex flex-col p-2\",\n\t\t\t\ttempl.KV(\"hidden\", !props.Days),\n\t\t\t}\n\t\t\u003e\n\t\t\t\u003cspan class=\"countdown font-mono text-5xl\"\u003e\n\t\t\t\t\u003cspan id={ fmt.Sprintf(\"span-days-%d\", spanID) } style=\"--value:15;\" aria-live=\"polite\" aria-label=\"15\"\u003e15\u003c/span\u003e\n\t\t\t\u003c/span\u003e\n\t\t\tdays\n\t\t\u003c/div\u003e\n\t\t\u003cdiv\n\t\t\tclass={\n\t\t\t\t\"bg-neutral rounded-box text-neutral-content flex flex-col p-2\",\n\t\t\t\ttempl.KV(\"hidden\", !props.Hours),\n\t\t\t}\n\t\t\u003e\n\t\t\t\u003cspan class=\"countdown font-mono text-5xl\"\u003e\n\t\t\t\t\u003cspan id={ fmt.Sprintf(\"span-hours-%d\", spanID) } style=\"--value:10;\" aria-live=\"polite\" aria-label=\"10\"\u003e10\u003c/span\u003e\n\t\t\t\u003c/span\u003e\n\t\t\thours\n\t\t\u003c/div\u003e\n\t\t\u003cdiv\n\t\t\tclass={\n\t\t\t\t\"bg-neutral rounded-box text-neutral-content flex flex-col p-2\",\n\t\t\t\ttempl.KV(\"hidden\", !props.Minutes),\n\t\t\t}\n\t\t\u003e\n\t\t\t\u003cspan class=\"countdown font-mono text-5xl\"\u003e\n\t\t\t\t\u003cspan id={ fmt.Sprintf(\"span-minutes-%d\", spanID) } style=\"--value:24;\" aria-live=\"polite\" aria-label=\"24\"\u003e24\u003c/span\u003e\n\t\t\t\u003c/span\u003e\n\t\t\tmin\n\t\t\u003c/div\u003e\n\t\t\u003cdiv\n\t\t\tclass={\n\t\t\t\t\"bg-neutral rounded-box text-neutral-content flex flex-col p-2\",\n\t\t\t\ttempl.KV(\"hidden\", !props.Seconds),\n\t\t\t}\n\t\t\u003e\n\t\t\t\u003cspan class=\"countdown font-mono text-5xl\"\u003e\n\t\t\t\t\u003cspan id={ fmt.Sprintf(\"span-seconds-%d\", spanID) } style=\"--value:59;\" aria-live=\"polite\" aria-label=\"59\"\u003e59\u003c/span\u003e\n\t\t\t\u003c/span\u003e\n\t\t\tsec\n\t\t\u003c/div\u003e\n\t\t\u003cscript data-expires={ props.Expires.Format(\"2006-01-02T15:04:05Z\") } data-id={ spanID }\u003e\n var intervalId = null;\n\n function updateTimes(expires, days, hours, minutes, seconds) {\n let expiresDate = Date.parse(expires);\n let now = new Date()\n let diffMs = Math.abs(expiresDate - now.getTime());\n let totalSeconds = diffMs / 1000;\n\n if (now.getTime() \u003e= expiresDate) {\n clearInterval(intervalId);\n return\n }\n\n const daysRemaining = Math.floor(diffMs / (1000 * 60 * 60 * 24))\n diffMs -= daysRemaining * (1000 * 60 * 60 *24)\n days.setAttribute(\"style\", `--value:${daysRemaining};`)\n\n const hoursRemaining = Math.floor(diffMs / (1000 * 60 * 60));\n diffMs -= hoursRemaining * (1000 * 60 * 60)\n hours.setAttribute(\"style\", `--value:${hoursRemaining};`)\n\n const minutesRemaining = Math.floor(diffMs / (1000 * 60));\n diffMs -= minutesRemaining * (1000 * 60);\n minutes.setAttribute(\"style\", `--value:${minutesRemaining};`)\n\n const secondsRemaining = Math.floor(diffMs / 1000);\n seconds.setAttribute(\"style\", `--value:${secondsRemaining};`)\n }\n\n function startUpdatingTimes(expires, days, hours, minutes, seconds) {\n updateTimes(expires, days, hours, minutes, seconds);\n intervalId = setInterval(() =\u003e updateTimes(expires, days, hours, minutes, seconds), 1000);\n }\n\n ((expires) =\u003e {\n let days = document.getElementById(`span-days-${document.currentScript.getAttribute(\"data-id\")}`);\n let hours = document.getElementById(`span-hours-${document.currentScript.getAttribute(\"data-id\")}`);\n let minutes = document.getElementById(`span-minutes-${document.currentScript.getAttribute(\"data-id\")}`);\n let seconds = document.getElementById(`span-seconds-${document.currentScript.getAttribute(\"data-id\")}`);\n startUpdatingTimes(expires, days, hours, minutes, seconds);\n })(document.currentScript.getAttribute(\"data-expires\"))\n \u003c/script\u003e\n\t\u003c/div\u003e\n}\n```" }, { "name": "diff", "code": "```go\ntype DiffProps struct {\n\tWidth int\n\tHeight int\n\tImage1 DiffImage\n\tImage2 DiffImage\n}\n\ntype DiffImage struct {\n\tSource string\n\tAlt string\n}\n\ntempl Diff(props DiffProps) {\n\t\u003cdiv class={ \"diff\", fmt.Sprintf(\"aspect-[%d/%d]\", props.Width, props.Height) }\u003e\n\t\t\u003cdiv class=\"diff-item-1\"\u003e\n\t\t\t\u003cimg alt={ props.Image1.Alt } src={ props.Image1.Source }/\u003e\n\t\t\u003c/div\u003e\n\t\t\u003cdiv class=\"diff-item-2\"\u003e\n\t\t\t\u003cimg alt={ props.Image2.Alt } src={ props.Image2.Source }/\u003e\n\t\t\u003c/div\u003e\n\t\t\u003cdiv class=\"diff-resizer\"\u003e\u003c/div\u003e\n\t\u003c/div\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/diff" }, { "name": "features", "code": "```go\ntype FeaturesProps struct {\n\tTitle string\n\tFeatures []FeatureProps\n}\n\ntype FeatureProps struct {\n\tIcon templ.Component\n\tTitle string\n\tDescription string\n\tURL string\n}\n\ntempl Features(props FeaturesProps) {\n\t\u003cdiv class=\"py-12 px-4\"\u003e\n\t\t\u003cdiv class=\"max-w-screen-xl mx-auto text-base-content/80\"\u003e\n\t\t\tif props.Title != \"\" {\n\t\t\t\t\u003ch2 class=\"sm:text-4xl text-2xl font-bold text-center mb-16\"\u003e{ props.Title }\u003c/h2\u003e\n\t\t\t}\n\t\t\t\u003cdiv class=\"grid lg:grid-cols-3 md:grid-cols-2 gap-12 max-md:max-w-lg mx-auto\"\u003e\n\t\t\t\tfor _, feature := range props.Features {\n\t\t\t\t\t@Feature(feature)\n\t\t\t\t}\n\t\t\t\u003c/div\u003e\n\t\t\u003c/div\u003e\n\t\u003c/div\u003e\n}\n\ntempl Feature(feature FeatureProps) {\n\t\u003cdiv\n\t\tclass={\n\t\t\t\"flex flex-col justify-between items-center rounded-box group p-8 text-center\",\n\t\t\t\"hover:bg-base-300 hover:text-base-content hover:shadow-xl transition-all duration-300\",\n\t\t}\n\t\u003e\n\t\t@feature.Icon\n\t\tif feature.Title != \"\" {\n\t\t\t\u003ch3 class=\"text-xl font-semibold mb-3\"\u003e{ feature.Title }\u003c/h3\u003e\n\t\t}\n\t\t\u003cp class=\"text-base-content/70 group-hover:text-base-content text-sm\"\u003e{ feature.Description }\u003c/p\u003e\n\t\tif feature.URL != \"\" {\n\t\t\t\u003ca href={ templ.SafeURL(feature.URL) } class=\"mt-2 link link-primary\"\u003eLearn more\u003c/a\u003e\n\t\t}\n\t\u003c/div\u003e\n}\n```" }, { "name": "hover_3d_card", "code": "```go\ntempl Hover3DCard() {\n\t\u003cdiv class=\"hover-3d\"\u003e\n\t\t{ children... }\n\t\t\u003cdiv\u003e\u003c/div\u003e\n\t\t\u003cdiv\u003e\u003c/div\u003e\n\t\t\u003cdiv\u003e\u003c/div\u003e\n\t\t\u003cdiv\u003e\u003c/div\u003e\n\t\t\u003cdiv\u003e\u003c/div\u003e\n\t\t\u003cdiv\u003e\u003c/div\u003e\n\t\t\u003cdiv\u003e\u003c/div\u003e\n\t\t\u003cdiv\u003e\u003c/div\u003e\n\t\u003c/div\u003e\n}\n```" }, { "name": "infinite_scroll", "code": "```go\ntempl InfiniteScrollTable(rows []templ.Component) {\n\t@Table(\n\t\t[]templ.Component{PlainText(\"Name\"), PlainText(\"Email\")},\n\t\trows,\n\t\tnil,\n\t)\n\t\u003cdiv class=\"flex justify-center\"\u003e\u003cspan id=\"spinner\" class=\"htmx-indicator loading loading-spinner\"\u003e\u003c/span\u003e\u003c/div\u003e\n}\n\ntempl InfiniteScrollRows(rows []templ.Component) {\n\tfor _, r := range rows {\n\t\t@r\n\t}\n}\n\ntempl InfiniteScrollRow(name, email string, page int, hasMore bool) {\n\t\u003ctr\n\t\tif hasMore {\n\t\t\thx-get={ fmt.Sprintf(\"/infinite-scroll-rows?page=%d\", page+1) }\n\t\t\thx-target=\"this\"\n\t\t\thx-trigger=\"intersect once\"\n\t\t\thx-swap=\"afterend\"\n\t\t\thx-indicator=\"#spinner\"\n\t\t}\n\t\u003e\n\t\t\u003ctd\u003e{ name }\u003c/td\u003e\n\t\t\u003ctd\u003e{ email }\u003c/td\u003e\n\t\u003c/tr\u003e\n}\n```" }, { "name": "lazy_load", "code": "```go\ntempl LazyLoad(url string) {\n\t\u003cdiv\n\t\thx-get={ url }\n\t\thx-trigger=\"load\"\n\t\thx-target=\"this\"\n\t\tclass=\"flex justify-center items-center py-8\"\n\t\u003e\n\t\t\u003cspan class=\"loading loading-spinner\"\u003e\u003c/span\u003e\n\t\u003c/div\u003e\n}\n```" }, { "name": "pricing", "code": "```go\ntype PricingProps struct {\n\tChecked bool\n\tPrices []PriceProps\n}\n\ntype PriceProps struct {\n\tTitle string\n\tDescription string\n\tPriceMonthly string\n\tPerMonthly string\n\tPriceAnnually string\n\tPerAnnually string\n\tPerUser bool\n\tPromotion string\n\tIncludedFeatures []string\n\tExcludedFeatures []string\n\tCallToAction PriceButtonProps\n\tFooter templ.Component\n}\n\ntype PriceButtonProps struct {\n\tLabel string\n\tAttrs templ.Attributes\n}\n\n// NOTE: Requires Alpine.js\ntempl Pricing(props PricingProps) {\n\t\u003cscript src=\"/static/js/alpine.js\" defer\u003e\u003c/script\u003e\n\t\u003cdiv x-data=\"{ yearly: true }\" class=\"w-full mx-auto py-8 max-w-screen-xl\"\u003e\n\t\t\u003cdiv class=\"max-w-xs mx-auto my-6\"\u003e\n\t\t\t@Toggle(ToggleProps{\n\t\t\t\tBefore: \"Billed monthly\",\n\t\t\t\tAfter: \"Billed annually\",\n\t\t\t\tName: \"period\",\n\t\t\t\tChecked: props.Checked,\n\t\t\t\tHighlight: true,\n\t\t\t\tAttrs: templ.Attributes{\n\t\t\t\t\t\"x-on:click\": \"yearly = !yearly\",\n\t\t\t\t},\n\t\t\t})\n\t\t\u003c/div\u003e\n\t\t@PriceGrid(props.Prices)\n\t\u003c/div\u003e\n}\n\ntempl PriceGrid(prices []PriceProps) {\n\t\u003cdiv\n\t\tid=\"price-grid\"\n\t\tclass={ \"grid auto-cols-fr grid-flow-row lg:grid-flow-col pt-4 w-full mx-auto gap-4\",\n templ.KV(\"gap-8\", slices.ContainsFunc(prices, func(pp PriceProps) bool {\n return pp.Promotion != \"\"\n })) }\n\t\u003e\n\t\tfor i := range prices {\n\t\t\t@Price(prices[i], nil)\n\t\t}\n\t\u003c/div\u003e\n}\n\ntempl Price(price PriceProps, footer templ.Component) {\n\t\u003cdiv class={ \"relative\", templ.KV(\"scale-110\", price.Promotion != \"\") }\u003e\n\t\t\u003cdiv\n\t\t\tclass={\n\t\t\t\t\"card rounded-box w-full max-w-xs p-6 mx-auto shadow-xl shadow-base-300\",\n\t\t\t\t\"bg-gradient-to-br from-base-200 dark:from-base-300 via-base-100 to-base-200 dark:to-base-300\",\n\t\t\t\ttempl.KV(\"ring-base-content/60\", price.Promotion == \"\"),\n\t\t\t\ttempl.KV(\"ring-1 ring-accent\", price.Promotion != \"\"),\n\t\t\t}\n\t\t\u003e\n\t\t\tif price.Promotion != \"\" {\n\t\t\t\t\u003cspan class=\"absolute top-0 right-0 bg-accent px-2 rounded-tr-box rounded-bl-box\"\u003e\n\t\t\t\t\t{ price.Promotion }\n\t\t\t\t\u003c/span\u003e\n\t\t\t}\n\t\t\t\u003cdiv class=\"card-title text-2xl mt-8\"\u003e\n\t\t\t\t\u003ch2 class=\"mx-auto\"\u003e{ price.Title }\u003c/h2\u003e\n\t\t\t\u003c/div\u003e\n\t\t\t\u003cp x-show=\"yearly\" class=\"text-center text-3xl font-bold mt-8\"\u003e\n\t\t\t\t{ price.PriceAnnually }\n\t\t\t\t\u003cspan class=\"text-xs font-normal\"\u003e\n\t\t\t\t\t{ price.PerAnnually }\n\t\t\t\t\u003c/span\u003e\n\t\t\t\tif price.PerUser {\n\t\t\t\t\t\u003cspan class=\"text-sm font-normal\"\u003e\n\t\t\t\t\t\t{ \" / user\" }\n\t\t\t\t\t\u003c/span\u003e\n\t\t\t\t}\n\t\t\t\u003c/p\u003e\n\t\t\t\u003cp x-show=\"!yearly\" class=\"text-center text-3xl font-bold mt-8\"\u003e\n\t\t\t\t{ price.PriceMonthly } \u003cspan class=\"text-xs font-normal\"\u003e{ price.PerMonthly }\u003c/span\u003e\n\t\t\t\u003c/p\u003e\n\t\t\t\u003cbutton { price.CallToAction.Attrs... }\u003e\n\t\t\t\t{ price.CallToAction.Label }\n\t\t\t\u003c/button\u003e\n\t\t\t\u003cdiv class=\"text-sm mt-8\"\u003e\n\t\t\t\t\u003cul class=\"space-y-4\"\u003e\n\t\t\t\t\tfor i := range price.IncludedFeatures {\n\t\t\t\t\t\t\u003cli class=\"flex items-center space-x-2\"\u003e\n\t\t\t\t\t\t\t\u003csvg class=\"w-4 h-4\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\"\u003e\n\t\t\t\t\t\t\t\t\u003cpath class=\"fill-base-content\" fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M21.5821 5.54289C21.9726 5.93342 21.9726 6.56658 21.5821 6.95711L10.2526 18.2867C9.86452 18.6747 9.23627 18.6775 8.84475 18.293L2.29929 11.8644C1.90527 11.4774 1.89956 10.8443 2.28655 10.4503C2.67354 10.0562 3.30668 10.0505 3.70071 10.4375L9.53911 16.1717L20.1679 5.54289C20.5584 5.15237 21.1916 5.15237 21.5821 5.54289Z\"\u003e\u003c/path\u003e\n\t\t\t\t\t\t\t\u003c/svg\u003e\n\t\t\t\t\t\t\t\u003cspan\u003e{ price.IncludedFeatures[i] }\u003c/span\u003e\n\t\t\t\t\t\t\u003c/li\u003e\n\t\t\t\t\t}\n\t\t\t\t\u003c/ul\u003e\n\t\t\t\tif len(price.ExcludedFeatures) \u003e 0 {\n\t\t\t\t\t\u003cdiv class=\"divider !my-2\"\u003e\u003c/div\u003e\n\t\t\t\t}\n\t\t\t\t\u003cul class=\"space-y-4\"\u003e\n\t\t\t\t\tfor i := range price.ExcludedFeatures {\n\t\t\t\t\t\t\u003cli class=\"flex items-center space-x-2 pl-6\"\u003e\n\t\t\t\t\t\t\t\u003cspan class=\"text-base-content/50\"\u003e{ price.ExcludedFeatures[i] }\u003c/span\u003e\n\t\t\t\t\t\t\u003c/li\u003e\n\t\t\t\t\t}\n\t\t\t\t\u003c/ul\u003e\n\t\t\t\u003c/div\u003e\n\t\t\tif footer != nil {\n\t\t\t\t@footer\n\t\t\t}\n\t\t\u003c/div\u003e\n\t\u003c/div\u003e\n}\n```" }, { "name": "stats", "code": "```go\ntype StatProps struct {\n\tTitle string\n\tValue string\n\tDescription string\n}\n\ntempl Stats() {\n\t\u003cdiv class=\"stats stats-horizontal shadow-sm\"\u003e\n\t\t{ children... }\n\t\u003c/div\u003e\n}\n\ntempl Stat(props StatProps) {\n\t\u003cdiv class=\"stat\"\u003e\n\t\t\u003cdiv class=\"stat-title\"\u003e{ props.Title }\u003c/div\u003e\n\t\t\u003cdiv class=\"stat-value\"\u003e{ props.Value }\u003c/div\u003e\n\t\tif props.Description != \"\" {\n\t\t\t\u003cdiv class=\"stat-desc\"\u003e{ props.Description }\u003c/div\u003e\n\t\t}\n\t\t\u003cdiv class=\"stat-actions\"\u003e\n\t\t\t{ children... }\n\t\t\u003c/div\u003e\n\t\u003c/div\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/stat" }, { "name": "table", "code": "```go\ntempl Table(headers []templ.Component, rows []templ.Component, attrs templ.Attributes) {\n\t\u003cdiv class=\"overflow-x-auto\"\u003e\n\t\t\u003ctable { attrs... } class=\"table\"\u003e\n\t\t\t\u003cthead\u003e\n\t\t\t\t\u003ctr\u003e\n\t\t\t\t\tfor _, header := range headers {\n\t\t\t\t\t\t\u003cth\u003e\n\t\t\t\t\t\t\t@header\n\t\t\t\t\t\t\u003c/th\u003e\n\t\t\t\t\t}\n\t\t\t\t\u003c/tr\u003e\n\t\t\t\u003c/thead\u003e\n\t\t\t\u003ctbody\u003e\n\t\t\t\tfor _, trow := range rows {\n\t\t\t\t\t@trow\n\t\t\t\t}\n\t\t\t\u003c/tbody\u003e\n\t\t\u003c/table\u003e\n\t\u003c/div\u003e\n}\n\n// Component to use as plain text when\n// templ.Component is used as argument\ntempl PlainText(content string) {\n\t{ content }\n}\n```", "daisy_ui_url": "https://daisyui.com/components/table" }, { "name": "testimonial", "code": "```go\ntype TestimonialProps []TestimonialProp\n\ntype TestimonialProp struct {\n\tAvatar templ.Component\n\tName string\n\tRating int\n\tContent string\n}\n\ntempl TestimonialGrid(title string, props TestimonialProps) {\n\t\u003csection\u003e\n\t\t\u003cdiv class=\"mx-auto max-w-screen-xl px-4 py-12 sm:px-6 lg:px-8 lg:py-16\"\u003e\n\t\t\t\u003ch2 class=\"text-center text-4xl font-bold tracking-tight sm:text-5xl\"\u003e\n\t\t\t\t{ title }\n\t\t\t\u003c/h2\u003e\n\t\t\t\u003cdiv class=\"mt-8 [column-fill:_balance] sm:columns-2 sm:gap-6 lg:columns-3 lg:gap-8\"\u003e\n\t\t\t\tfor i := range props {\n\t\t\t\t\t\u003cdiv class=\"mb-8 sm:break-inside-avoid\"\u003e\n\t\t\t\t\t\t\u003cblockquote class=\"rounded-lg bg-base-300 p-6 shadow-sm sm:p-8\"\u003e\n\t\t\t\t\t\t\t\u003cdiv class=\"flex items-center gap-4\"\u003e\n\t\t\t\t\t\t\t\tif props[i].Avatar != nil {\n\t\t\t\t\t\t\t\t\t@props[i].Avatar\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\u003cdiv\u003e\n\t\t\t\t\t\t\t\t\t@RatingDisplay(RatingProps{\n\t\t\t\t\t\t\t\t\t\tName: fmt.Sprintf(\"review-rating-%d\", i),\n\t\t\t\t\t\t\t\t\t\tMin: 1,\n\t\t\t\t\t\t\t\t\t\tMax: 5,\n\t\t\t\t\t\t\t\t\t\tValue: props[i].Rating,\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\u003cp class=\"mt-0.5 text-lg font-medium\"\u003e{ props[i].Name }\u003c/p\u003e\n\t\t\t\t\t\t\t\t\u003c/div\u003e\n\t\t\t\t\t\t\t\u003c/div\u003e\n\t\t\t\t\t\t\t\u003cp class=\"mt-4\"\u003e\n\t\t\t\t\t\t\t\t{ props[i].Content }\n\t\t\t\t\t\t\t\u003c/p\u003e\n\t\t\t\t\t\t\u003c/blockquote\u003e\n\t\t\t\t\t\u003c/div\u003e\n\t\t\t\t}\n\t\t\t\u003c/div\u003e\n\t\t\u003c/div\u003e\n\t\u003c/section\u003e\n}\n```" }, { "name": "text_rotate", "code": "```go\ntype TextRotateProps struct {\n\tClass string\n\tItems []TextRotateItem\n}\n\ntype TextRotateItem struct {\n\tText string\n\tClass string\n}\n\ntempl TextRotate(props TextRotateProps) {\n\t\u003cspan class={ \"text-rotate\", props.Class }\u003e\n\t\t\u003cspan\u003e\n\t\t\tfor _, item := range props.Items {\n\t\t\t\t\u003cspan class={ item.Class }\u003e{ item.Text }\u003c/span\u003e\n\t\t\t}\n\t\t\u003c/span\u003e\n\t\u003c/span\u003e\n}\n```" }, { "name": "timeline", "code": "```go\ntype TimelineProps []TimelineProp\n\ntype TimelineProp struct {\n\tStart string\n\tMiddle templ.Component\n\tEnd string\n}\n\ntempl Timeline(props TimelineProps) {\n\t\u003cul class=\"timeline\"\u003e\n\t\tfor i, prop := range props {\n\t\t\t\u003cli\u003e\n\t\t\t\tif i \u003e 0 {\n\t\t\t\t\t\u003chr/\u003e\n\t\t\t\t}\n\t\t\t\tif prop.Start != \"\" {\n\t\t\t\t\t\u003cdiv class=\"timeline-start\"\u003e{ prop.Start }\u003c/div\u003e\n\t\t\t\t}\n\t\t\t\tif prop.Middle != nil {\n\t\t\t\t\t\u003cdiv class=\"timeline-middle\"\u003e\n\t\t\t\t\t\t@prop.Middle\n\t\t\t\t\t\u003c/div\u003e\n\t\t\t\t}\n\t\t\t\tif prop.End != \"\" {\n\t\t\t\t\t\u003cdiv class=\"timeline-end\"\u003e{ prop.End }\u003c/div\u003e\n\t\t\t\t}\n\t\t\t\tif i \u003c len(props) - 1 {\n\t\t\t\t\t\u003chr/\u003e\n\t\t\t\t}\n\t\t\t\u003c/li\u003e\n\t\t}\n\t\u003c/ul\u003e\n}\n\ntempl TimelineCheckbox(checked bool) {\n\t\u003csvg\n\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\tviewBox=\"0 0 20 20\"\n\t\tfill=\"currentColor\"\n\t\tclass={ \"h-5 w-5\", templ.KV(\"fill-primary\", checked) }\n\t\u003e\n\t\t\u003cpath\n\t\t\tfill-rule=\"evenodd\"\n\t\t\td=\"M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z\"\n\t\t\tclip-rule=\"evenodd\"\n\t\t\u003e\u003c/path\u003e\n\t\u003c/svg\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/timeline" } ], "data_input": [ { "name": "checkbox", "code": "```go\ntype CheckboxProps struct {\n\tID string\n\tBefore string\n\tAfter string\n\tName string\n\tChecked bool\n\tClass string\n\tAttrs templ.Attributes\n\tSize string\n}\n\ntempl Checkbox(props CheckboxProps) {\n\t\u003clabel class=\"label justify-center cursor-pointer\"\u003e\n\t\tif props.Before != \"\" {\n\t\t\t\u003cspan\n\t\t\t\tclass={\n\t\t\t\t\ttempl.KV(\"text-xs\", props.Size == \"xs\"),\n\t\t\t\t\ttempl.KV(\"text-sm\", props.Size == \"sm\"),\n\t\t\t\t\ttempl.KV(\"text-lg\", props.Size == \"lg\"),\n\t\t\t\t\ttempl.KV(\"text-xl\", props.Size == \"xl\"),\n\t\t\t\t\t\"mr-2\",\n\t\t\t\t}\n\t\t\t\u003e\n\t\t\t\t{ props.Before }\n\t\t\t\u003c/span\u003e\n\t\t}\n\t\t\u003cinput\n\t\t\t{ props.Attrs... }\n\t\t\tif props.ID != \"\" {\n\t\t\t\tid={ props.ID }\n\t\t\t}\n\t\t\ttype=\"checkbox\"\n\t\t\tname={ props.Name }\n\t\t\tif props.Checked {\n\t\t\t\tchecked=\"checked\"\n\t\t\t}\n\t\t\tclass={\n\t\t\t\t\"checkbox\", props.Class,\n\t\t\t\ttempl.KV(\"checkbox-xs\", props.Size == \"xs\"),\n\t\t\t\ttempl.KV(\"checkbox-sm\", props.Size == \"sm\"),\n\t\t\t\ttempl.KV(\"checkbox-lg\", props.Size == \"lg\"),\n\t\t\t\ttempl.KV(\"checkbox-xl\", props.Size == \"xl\"),\n\t\t\t}\n\t\t/\u003e\n\t\tif props.After != \"\" {\n\t\t\t\u003cspan class=\"ml-2\"\u003e\n\t\t\t\t{ props.After }\n\t\t\t\u003c/span\u003e\n\t\t}\n\t\u003c/label\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/checkbox" }, { "name": "combobox", "code": "```go\ntype ComboboxProps struct {\n\tLabel string\n\tName string\n\tURL string\n\tOptions []string\n\tSelected []string\n}\n\ntempl Combobox(props ComboboxProps) {\n\t\u003cdetails class=\"dropdown w-full max-w-md min-h-8\"\u003e\n\t\t\u003csummary\n\t\t\tclass={\n\t\t\t\t\"cursor-pointer flex space-x-2 w-full rounded-box\",\n\t\t\t\t\"border border-base-content py-1 px-2\",\n\t\t\t}\n\t\t\u003e\n\t\t\t\u003cspan class=\"text-sm text-nowrap\"\u003e{ props.Label }\u003c/span\u003e\n\t\t\t\u003cdiv class=\"w-full flex items-center space-x-1\"\u003e\n\t\t\t\t\u003cdiv\n\t\t\t\t\tid={ fmt.Sprintf(\"%s_selections\", props.Name) }\n\t\t\t\t\tclass=\"w-full grid-flow-col-dense\"\n\t\t\t\t\u003e\n\t\t\t\t\tfor _, s := range props.Selected {\n\t\t\t\t\t\t@ComboBadge(props.Name, s)\n\t\t\t\t\t}\n\t\t\t\t\u003c/div\u003e\n\t\t\t\u003c/div\u003e\n\t\t\u003c/summary\u003e\n\t\t\u003cul class=\"menu dropdown-content bg-base-200 rounded-box z-50 p-2 shadow-sm\"\u003e\n\t\t\tfor _, opt := range props.Options {\n\t\t\t\t\u003cli\u003e\n\t\t\t\t\t\u003clabel class=\"label cursor-pointer space-x-2\"\u003e\n\t\t\t\t\t\t\u003cspan class=\"label-text\"\u003e\n\t\t\t\t\t\t\t{ opt }\n\t\t\t\t\t\t\u003c/span\u003e\n\t\t\t\t\t\t\u003cinput\n\t\t\t\t\t\t\thx-post={ fmt.Sprintf(props.URL, props.Name, url.PathEscape(opt)) }\n\t\t\t\t\t\t\thx-target={ fmt.Sprintf(\"#%s_selections\", props.Name) }\n\t\t\t\t\t\t\thx-swap=\"beforeend\"\n\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\tname={ props.Name }\n\t\t\t\t\t\t\tclass={ \"checkbox\" }\n\t\t\t\t\t\t/\u003e\n\t\t\t\t\t\u003c/label\u003e\n\t\t\t\t\u003c/li\u003e\n\t\t\t}\n\t\t\u003c/ul\u003e\n\t\t\u003cscript data-checkbox-name={ props.Name } type=\"text/javascript\"\u003e\n\t\t\tvar name = document.currentScript.getAttribute(\"data-checkbox-name\");\n\t\t\t((name) =\u003e {\n\t\t\t\tdocument.addEventListener(\"htmx:configRequest\", (evt) =\u003e {\n\t\t\t\t\tif (evt.target.getAttribute(\"name\") === name \u0026\u0026 !evt.target.checked) {\n\t\t\t\t\t\t// prevent htmx request when checkbox is unchecked\n\t\t\t\t\t\tevt.preventDefault()\n\n\t\t\t\t\t\t// remove from selected elements\n\t\t\t\t\t\tlet label = evt.target.closest(\"label\")\n\t\t\t\t\t\tif (label !== null \u0026\u0026 label !== undefined) {\n\t\t\t\t\t\t\tlet span = label.querySelector(\"span.label-text\")\n\t\t\t\t\t\t\tlet value = span.innerText\n\t\t\t\t\t\t\tlet input = document.querySelector(`input[value=\"${value}\"]`)\n\t\t\t\t\t\t\tif (input.getAttribute(\"name\") === name) {\n\t\t\t\t\t\t\t\tinput.closest(\"div\").remove()\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})(name);\n\t\t\u003c/script\u003e\n\t\u003c/details\u003e\n}\n\ntempl ComboBadge(name, value string) {\n\t\u003cdiv class=\"ml-2 badge badge-neutral p-1 text-nowrap select-none\"\u003e\n\t\t\u003cinput type=\"hidden\" name={ name } value={ value }/\u003e\n\t\t\u003cspan\u003e{ value }\u003c/span\u003e\n\t\t\u003cbutton\n\t\t\tonclick=\"uncheckAndRemoveBadge(event)\"\n\t\t\tclass=\"ml-1 btn btn-xs btn-circle btn-ghost\"\n\t\t\u003e\n\t\t\t@crossIcon()\n\t\t\u003c/button\u003e\n\t\t\u003cscript\u003e\n\t\t\tfunction uncheckAndRemoveBadge(evt) {\n\t\t\t\tvar div = evt.target.parentElement\n\t\t\t\twhile (div.nodeName !== \"DIV\") {\n\t\t\t\t\tdiv = div.parentElement\n\t\t\t\t}\n\t\t\t\tlet input = div.querySelector(\"input[type=hidden]\")\n\t\t\t\tlet name = input.getAttribute(\"name\")\n\t\t\t\tlet labelText = input.value\n\n\t\t\t\tlet details = div.closest(\"details\")\n\t\t\t\tlet ul = details.querySelector(\"ul\")\n\t\t\t\tlet checkboxes = ul.querySelectorAll(`input[name=\"${name}\"]`)\n\t\t\t\tcheckboxes.forEach((cb) =\u003e {\n\t\t\t\t\tif (cb.checked) {\n\t\t\t\t\t\tlet label = cb.parentElement\n\t\t\t\t\t\tlabel.querySelectorAll(\"span.label-text\").forEach((el) =\u003e {\n\t\t\t\t\t\t\tif (el.innerHTML === labelText) {\n\t\t\t\t\t\t\t\tcb.checked = false\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\tdiv.remove()\n\t\t\t}\n\t\t\u003c/script\u003e\n\t\u003c/div\u003e\n}\n\ntempl crossIcon() {\n\t\u003csvg\n\t\tclass=\"h-3 w-3\"\n\t\tviewBox=\"0 0 25 25\"\n\t\tversion=\"1.1\"\n\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\txmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\t\txmlns:sketch=\"http://www.bohemiancoding.com/sketch/ns\"\n\t\u003e\n\t\t\u003cg stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\" sketch:type=\"MSPage\"\u003e\n\t\t\t\u003cg class=\"fill-base-content\" id=\"Icon-Set-Filled\" sketch:type=\"MSLayerGroup\" transform=\"translate(-469.000000, -1041.000000)\"\u003e\n\t\t\t\t\u003cpath d=\"M487.148,1053.48 L492.813,1047.82 C494.376,1046.26 494.376,1043.72 492.813,1042.16 C491.248,1040.59 488.712,1040.59 487.148,1042.16 L481.484,1047.82 L475.82,1042.16 C474.257,1040.59 471.721,1040.59 470.156,1042.16 C468.593,1043.72 468.593,1046.26 470.156,1047.82 L475.82,1053.48 L470.156,1059.15 C468.593,1060.71 468.593,1063.25 470.156,1064.81 C471.721,1066.38 474.257,1066.38 475.82,1064.81 L481.484,1059.15 L487.148,1064.81 C488.712,1066.38 491.248,1066.38 492.813,1064.81 C494.376,1063.25 494.376,1060.71 492.813,1059.15 L487.148,1053.48\" sketch:type=\"MSShapeGroup\"\u003e\u003c/path\u003e\n\t\t\t\u003c/g\u003e\n\t\t\u003c/g\u003e\n\t\u003c/svg\u003e\n}\n```" }, { "name": "date_picker", "code": "```go\ntype DatePickerProps struct {\n\tYear int\n\tMonth int\n\tSelected time.Time\n\tStartOfWeek time.Weekday\n}\n\nfunc (props DatePickerProps) Days() []time.Time {\n\tdays := make([]time.Time, 0, 31)\n\tnow := time.Now().UTC()\n\tstart := time.Date(props.Year, time.Month(props.Month), 1, 0, 0, 0, 0, now.Location())\n\tend := start.AddDate(0, 1, -1)\n\tfor end.Weekday() != props.StartOfWeek {\n\t\tend = end.AddDate(0, 0, 1)\n\t}\n\tend = end.AddDate(0, 0, -1)\n\n\tfor start.Weekday() != props.StartOfWeek {\n\t\tstart = start.AddDate(0, 0, -1)\n\t}\n\tfor !start.After(end) {\n\t\tdays = append(days, start)\n\t\tstart = start.AddDate(0, 0, 1)\n\t}\n\treturn days\n}\n\nfunc (props DatePickerProps) Months() []time.Time {\n\tmonths := make([]time.Time, 12)\n\tfor i := 1; i \u003c= 12; i++ {\n\t\tdt := time.Date(props.Year, time.Month(i), 1, 0, 0, 0, 0, time.Now().Location())\n\t\tmonths[i-1] = dt\n\t}\n\treturn months\n}\n\ntempl DatePicker(props DatePickerProps) {\n\t{{ utcNow := time.Now().UTC() }}\n\t{{ days := props.Days() }}\n\t\u003cdiv\n\t\tname=\"datepicker-div\"\n\t\tclass=\"flex flex-col space-y-1 w-[300px] h-[405px]\"\n\t\u003e\n\t\t\u003cdiv class=\"flex space-x-1\"\u003e\n\t\t\t\u003cbutton\n\t\t\t\thx-get={ fmt.Sprintf(\"/datepicker?year=%d\u0026month=%d\", props.Year, props.Month-1) }\n\t\t\t\thx-target=\"div[name=datepicker-div]\"\n\t\t\t\thx-swap=\"outerHTML\"\n\t\t\t\tclass=\"btn btn-square btn-sm\"\n\t\t\t\u003e\n\t\t\t\t\u003csvg class=\"h-3 w-3\" viewBox=\"-5.5 0 26 26\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:sketch=\"http://www.bohemiancoding.com/sketch/ns\"\u003e\n\t\t\t\t\t\u003cg id=\"Page-1\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\" sketch:type=\"MSPage\"\u003e\n\t\t\t\t\t\t\u003cg class=\"fill-base-content\" id=\"Icon-Set-Filled\" sketch:type=\"MSLayerGroup\" transform=\"translate(-423.000000, -1196.000000)\"\u003e\n\t\t\t\t\t\t\t\u003cpath d=\"M428.115,1209 L437.371,1200.6 C438.202,1199.77 438.202,1198.43 437.371,1197.6 C436.541,1196.76 435.194,1196.76 434.363,1197.6 L423.596,1207.36 C423.146,1207.81 422.948,1208.41 422.985,1209 C422.948,1209.59 423.146,1210.19 423.596,1210.64 L434.363,1220.4 C435.194,1221.24 436.541,1221.24 437.371,1220.4 C438.202,1219.57 438.202,1218.23 437.371,1217.4 L428.115,1209\" id=\"chevron-left\" sketch:type=\"MSShapeGroup\"\u003e\u003c/path\u003e\n\t\t\t\t\t\t\u003c/g\u003e\n\t\t\t\t\t\u003c/g\u003e\n\t\t\t\t\u003c/svg\u003e\n\t\t\t\u003c/button\u003e\n\t\t\t\u003cdetails class=\"dropdown w-full max-w-[300px]\"\u003e\n\t\t\t\t\u003csummary class=\"btn btn-sm w-full\"\u003e{ fmt.Sprintf(\"%d\", props.Year) }\u003c/summary\u003e\n\t\t\t\t\u003cdiv\n\t\t\t\t\tclass={\n\t\t\t\t\t\t\"menu dropdown-content grid grid-cols-3 gap-1\",\n\t\t\t\t\t\t\"w-full max-w-[300px] p-1 bg-base-300 rounded-b-box\",\n\t\t\t\t\t}\n\t\t\t\t\thx-get={ fmt.Sprintf(\"/datepicker/yearpicker?year=%d\", props.Year) }\n\t\t\t\t\thx-trigger=\"datePickerDropdownClosed from:body\"\n\t\t\t\t\u003e\n\t\t\t\t\t@DatePickerYearPicker(props)\n\t\t\t\t\u003c/div\u003e\n\t\t\t\t\u003cscript\u003e\n ((details)=\u003e{\n details.querySelector(\"summary\").addEventListener(\"click\", (evt) =\u003e {\n if (details.hasAttribute(\"open\")) {\n let event = new Event(\"datePickerDropdownClosed\");\n document.querySelector(\"body\").dispatchEvent(event);\n }\n })\n })(document.currentScript.parentElement)\n \u003c/script\u003e\n\t\t\t\u003c/details\u003e\n\t\t\t\u003cbutton\n\t\t\t\thx-get={ fmt.Sprintf(\"/datepicker?year=%d\u0026month=%d\", props.Year, props.Month+1) }\n\t\t\t\thx-target=\"div[name=datepicker-div]\"\n\t\t\t\thx-swap=\"outerHTML\"\n\t\t\t\tclass=\"btn btn-square btn-sm\"\n\t\t\t\u003e\n\t\t\t\t\u003csvg class=\"h-3 w-3\" viewBox=\"-5.5 0 26 26\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:sketch=\"http://www.bohemiancoding.com/sketch/ns\"\u003e\n\t\t\t\t\t\u003cg id=\"Page-1\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\" sketch:type=\"MSPage\"\u003e\n\t\t\t\t\t\t\u003cg class=\"fill-base-content\" id=\"Icon-Set-Filled\" sketch:type=\"MSLayerGroup\" transform=\"translate(-474.000000, -1196.000000)\"\u003e\n\t\t\t\t\t\t\t\u003cpath d=\"M488.404,1207.36 L477.637,1197.6 C476.806,1196.76 475.459,1196.76 474.629,1197.6 C473.798,1198.43 473.798,1199.77 474.629,1200.6 L483.885,1209 L474.629,1217.4 C473.798,1218.23 473.798,1219.57 474.629,1220.4 C475.459,1221.24 476.806,1221.24 477.637,1220.4 L488.404,1210.64 C488.854,1210.19 489.052,1209.59 489.015,1209 C489.052,1208.41 488.854,1207.81 488.404,1207.36\" id=\"chevron-right\" sketch:type=\"MSShapeGroup\"\u003e\u003c/path\u003e\n\t\t\t\t\t\t\u003c/g\u003e\n\t\t\t\t\t\u003c/g\u003e\n\t\t\t\t\u003c/svg\u003e\n\t\t\t\u003c/button\u003e\n\t\t\u003c/div\u003e\n\t\t\u003cdiv class=\"grid grid-cols-7 gap-1 text-sm text-base-content/75\"\u003e\n\t\t\tif props.StartOfWeek == time.Sunday {\n\t\t\t\t\u003cspan class=\"text-center\"\u003eSu\u003c/span\u003e\n\t\t\t}\n\t\t\t\u003cspan class=\"text-center\"\u003eMo\u003c/span\u003e\n\t\t\t\u003cspan class=\"text-center\"\u003eTu\u003c/span\u003e\n\t\t\t\u003cspan class=\"text-center\"\u003eWe\u003c/span\u003e\n\t\t\t\u003cspan class=\"text-center\"\u003eTh\u003c/span\u003e\n\t\t\t\u003cspan class=\"text-center\"\u003eFr\u003c/span\u003e\n\t\t\t\u003cspan class=\"text-center\"\u003eSa\u003c/span\u003e\n\t\t\tif props.StartOfWeek == time.Monday {\n\t\t\t\t\u003cspan class=\"text-center\"\u003eSu\u003c/span\u003e\n\t\t\t}\n\t\t\u003c/div\u003e\n\t\t\u003cdiv\n\t\t\tclass=\"grid grid-cols-7 gap-1\"\n\t\t\u003e\n\t\t\t@DatePickerInput(props.Selected)\n\t\t\tfor _, d := range days {\n\t\t\t\t\u003cbutton\n\t\t\t\t\tname=\"datepicker-day-btn\"\n\t\t\t\t\tclass={\n\t\t\t\t\t\t\"btn btn-square border shadow-md shadow-base-300 h-10 w-10 p-3\",\n\t\t\t\t\t\ttempl.KV(\"text-primary\", d.Year() == utcNow.Year() \u0026\u0026 d.Month() == utcNow.Month() \u0026\u0026 utcNow.Day() == d.Day()),\n\t\t\t\t\t\ttempl.KV(\"btn-ghost\", d.Month() == time.Month(props.Month)),\n\t\t\t\t\t\ttempl.KV(\"btn-neutral\", d.Month() != time.Month(props.Month)),\n\t\t\t\t\t\ttempl.KV(\"border-primary bg-base-300\", !props.Selected.IsZero() \u0026\u0026 d.Day() == props.Selected.Day()),\n\t\t\t\t\t}\n\t\t\t\t\thx-post={ \"/datepicker/select?date=\" + d.Format(\"2006-01-02\") }\n\t\t\t\t\thx-target=\"#datepicker-input\"\n\t\t\t\t\thx-swap=\"outerHTML\"\n\t\t\t\t\tif d.Month() != time.Month(props.Month) {\n\t\t\t\t\t\tdisabled\n\t\t\t\t\t}\n\t\t\t\t\u003e\n\t\t\t\t\tif d.Day() == 1 {\n\t\t\t\t\t\t{ d.Format(\"Jan \") }\n\t\t\t\t\t}\n\t\t\t\t\t{ d.Format(\"2\") }\n\t\t\t\t\u003c/button\u003e\n\t\t\t}\n\t\t\t\u003cscript\u003e\n function addDayButtonEventHandler() {\n document.querySelectorAll(\"button[name=datepicker-day-btn]\").forEach((btn) =\u003e {\n btn.addEventListener(\"click\", () =\u003e {\n document.querySelectorAll(\"button[name=datepicker-day-btn]\").forEach((el) =\u003e {\n el.classList.remove(\"border-primary\", \"bg-base-300\")\n })\n btn.classList.add(\"border-primary\", \"bg-base-300\")\n })\n })\n }\n document.addEventListener(\"htmx:afterSettle\", () =\u003e {\n addDayButtonEventHandler();\n })\n document.addEventListener(\"DOMContentLoaded\", () =\u003e {\n addDayButtonEventHandler();\n })\n \u003c/script\u003e\n\t\t\u003c/div\u003e\n\t\u003c/div\u003e\n}\n\ntempl DatePickerInput(d time.Time) {\n\t\u003cinput id=\"datepicker-input\" type=\"hidden\" name=\"date\" value={ d.Format(\"2006-01-02\") }/\u003e\n}\n\ntempl DatePickerYearPicker(props DatePickerProps) {\n\t{{ utcNow := time.Now().UTC() }}\n\tfor i := range 12 {\n\t\t\u003cbutton\n\t\t\tclass=\"btn btn-sm border\"\n\t\t\thx-get={ fmt.Sprintf(\"/datepicker/monthpicker?year=%d\", props.Year-(12-i)) }\n\t\t\thx-target=\"closest div\"\n\t\t\u003e\n\t\t\t{ fmt.Sprintf(\"%d\", props.Year-(12-i)) }\n\t\t\u003c/button\u003e\n\t}\n\t\u003cbutton\n\t\tclass=\"btn btn-sm border col-span-3\"\n\t\thx-get={ fmt.Sprintf(\"/datepicker/monthpicker?year=%d\", utcNow.Year()) }\n\t\thx-target=\"closest div\"\n\t\u003e\n\t\t{ fmt.Sprintf(\"%d\", utcNow.Year()) }\n\t\u003c/button\u003e\n}\n\ntempl DatePickerMonthPicker(props DatePickerProps) {\n\t{{ months := props.Months() }}\n\tfor _, m := range months {\n\t\t\u003cbutton\n\t\t\tclass={\n\t\t\t\t\"btn btn-sm border\",\n\t\t\t\ttempl.KV(\"border-primary\", m.Month() == time.Month(props.Month)),\n\t\t\t}\n\t\t\thx-get={ fmt.Sprintf(\"/datepicker?year=%d\u0026month=%d\", props.Year, m.Month()) }\n\t\t\thx-target=\"div[name=datepicker-div]\"\n\t\t\thx-swap=\"outerHTML\"\n\t\t\u003e\n\t\t\t{ m.Format(\"Jan\") }\n\t\t\u003c/button\u003e\n\t}\n}\n```" }, { "name": "file_input", "code": "```go\ntype FileInputProps struct {\n\tName string\n\tLabel string\n\tValue string\n\tDescription string\n\tAttrs templ.Attributes\n\tDisabledMessage string\n\tRequired bool\n\tSize string\n}\n\ntempl FileInput(props FileInputProps) {\n\t\u003cfieldset\n\t\tclass=\"fieldset tooltip tooltip-top w-full\"\n\t\tif props.DisabledMessage != \"\" {\n\t\t\tdata-tip={ props.DisabledMessage }\n\t\t}\n\t\u003e\n\t\tif props.Label != \"\" {\n\t\t\t\u003clegend class=\"fieldset-legend\"\u003e{ props.Label }\u003c/legend\u003e\n\t\t}\n\t\t\u003cinput\n\t\t\ttype=\"file\"\n\t\t\tclass={\n\t\t\t\t\"file-input w-full\",\n\t\t\t\ttempl.KV(\"file-input-xs\", props.Size == \"xs\"),\n\t\t\t\ttempl.KV(\"file-input-sm\", props.Size == \"sm\"),\n\t\t\t\ttempl.KV(\"file-input-lg\", props.Size == \"lg\"),\n\t\t\t\ttempl.KV(\"file-input-xl\", props.Size == \"xl\"),\n\t\t\t}\n\t\t\tname={ props.Name }\n\t\t\tif props.DisabledMessage != \"\" {\n\t\t\t\tdisabled\n\t\t\t}\n\t\t\t{ props.Attrs... }\n\t\t/\u003e\n\t\tif props.Description != \"\" {\n\t\t\t\u003clabel class=\"fieldset-label\"\u003e{ props.Description }\u003c/label\u003e\n\t\t}\n\t\u003c/fieldset\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/file-input" }, { "name": "input", "code": "```go\ntype InputProps struct {\n\t// common\n\tName string\n\tLabel string\n\tType string // defaults to \"text\"\n\tValue string\n\tPlaceholder string\n\tDescription string\n\tError string\n\tDisabledMessage string\n\tSize string // xs sm lg xl, default: md\n\tRequired bool\n\tIcon templ.Component\n\tAttrs templ.Attributes\n\n\t// text input\n\tMinLength string\n\tMaxLength string\n\tPattern string\n\tValidatorHint string\n\n\t// integer/decimal input\n\tMin string\n\tMax string\n\n\t// decimal input\n\tStep string\n}\n\ntempl Input(props InputProps) {\n\t\u003cfieldset class=\"fieldset\"\u003e\n\t\tif props.Label != \"\" {\n\t\t\t\u003clegend class=\"fieldset-legend\"\u003e{ props.Label }\u003c/legend\u003e\n\t\t}\n\t\t\u003clabel\n\t\t\tfor={ props.Name }\n\t\t\tclass={\n\t\t\t\t\"input validator tooltip tooltip-top\",\n\t\t\t\ttempl.KV(\"tooltip tooltip-top\", props.DisabledMessage != \"\"),\n\t\t\t\ttempl.KV(\"input-xs\", props.Size == \"xs\"),\n\t\t\t\ttempl.KV(\"input-sm\", props.Size == \"sm\"),\n\t\t\t\ttempl.KV(\"input-lg\", props.Size == \"lg\"),\n\t\t\t\ttempl.KV(\"input-xl\", props.Size == \"xl\"),\n\t\t\t}\n\t\t\tif props.DisabledMessage != \"\" {\n\t\t\t\tdata-tip={ props.DisabledMessage }\n\t\t\t}\n\t\t\u003e\n\t\t\tif props.Icon != nil {\n\t\t\t\t@props.Icon\n\t\t\t}\n\t\t\t\u003cinput\n\t\t\t\tname={ props.Name }\n\t\t\t\tif props.Type == \"\" {\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t} else {\n\t\t\t\t\ttype={ props.Type }\n\t\t\t\t}\n\t\t\t\tvalue={ props.Value }\n\t\t\t\tplaceholder={ props.Placeholder }\n\t\t\t\tif props.Required {\n\t\t\t\t\trequired\n\t\t\t\t}\n\t\t\t\tif props.Pattern != \"\" {\n\t\t\t\t\tpattern={ props.Pattern }\n\t\t\t\t}\n\t\t\t\tif props.ValidatorHint != \"\" {\n\t\t\t\t\ttitle={ props.ValidatorHint }\n\t\t\t\t}\n\t\t\t\tif props.DisabledMessage != \"\" {\n\t\t\t\t\tdisabled\n\t\t\t\t}\n\t\t\t\t{ props.Attrs... }\n\t\t\t/\u003e\n\t\t\u003c/label\u003e\n\t\tif props.Description != \"\" {\n\t\t\t\u003cdiv class=\"label\"\u003e{ props.Description }\u003c/div\u003e\n\t\t}\n\t\tif props.Error != \"\" {\n\t\t\t\u003cp class=\"text-xs text-error max-w-xs\"\u003e{ props.Error }\u003c/p\u003e\n\t\t}\n\t\tif props.ValidatorHint != \"\" {\n\t\t\t\u003cp class=\"validator-hint hidden text-xs max-w-xs !mt-0\"\u003e{ props.ValidatorHint }\u003c/p\u003e\n\t\t}\n\t\u003c/fieldset\u003e\n}\n```" }, { "name": "radio", "code": "```go\ntype RadioProps struct {\n\tName string\n\tValues map[string]string\n\tClass string\n\tSize string\n}\n\ntempl Radio(props RadioProps) {\n\t\u003cdiv class=\"space-y-2 w-full\"\u003e\n\t\tfor l, v := range props.Values {\n\t\t\t\u003clabel for={ props.Name } class=\"label cursor-pointer grid grid-cols-7\"\u003e\n\t\t\t\t\u003cspan\n\t\t\t\t\tclass={\n\t\t\t\t\t\t\"col-span-6 text-wrap\",\n\t\t\t\t\t\ttempl.KV(\"text-xs\", props.Size == \"xs\"),\n\t\t\t\t\t\ttempl.KV(\"text-sm\", props.Size == \"sm\"),\n\t\t\t\t\t\ttempl.KV(\"text-lg\", props.Size == \"lg\"),\n\t\t\t\t\t\ttempl.KV(\"text-xl\", props.Size == \"xl\"),\n\t\t\t\t\t}\n\t\t\t\t\u003e{ l }\u003c/span\u003e\n\t\t\t\t\u003cinput\n\t\t\t\t\ttype=\"radio\"\n\t\t\t\t\tname={ props.Name }\n\t\t\t\t\tvalue={ v }\n\t\t\t\t\tclass={\n\t\t\t\t\t\t\"radio col-span-1\", props.Class,\n\t\t\t\t\t\ttempl.KV(\"radio-xs\", props.Size == \"xs\"),\n\t\t\t\t\t\ttempl.KV(\"radio-sm\", props.Size == \"sm\"),\n\t\t\t\t\t\ttempl.KV(\"radio-lg\", props.Size == \"lg\"),\n\t\t\t\t\t\ttempl.KV(\"radio-xl\", props.Size == \"xl\"),\n\t\t\t\t\t}\n\t\t\t\t/\u003e\n\t\t\t\u003c/label\u003e\n\t\t}\n\t\u003c/div\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/radio" }, { "name": "range", "code": "```go\ntype RangeProps struct {\n\tID string\n\tLabel string\n\tName string\n\tValue int\n\tMin int\n\tMax int\n\tStep int\n\tClass string\n\tSize string\n}\n\n// Note: usage requires alpine.js\ntempl Range(props RangeProps) {\n\t\u003cscript src=\"/static/js/alpine.js\" defer\u003e\u003c/script\u003e\n\t\u003cdiv class=\"form-control\"\u003e\n\t\t\u003clabel\n\t\t\tclass=\"label space-x-1\"\n\t\t\tx-data={ fmt.Sprintf(\"{ value: %d }\", props.Value) }\n\t\t\u003e\n\t\t\tif props.Label != \"\" {\n\t\t\t\t\u003cspan\n\t\t\t\t\tclass={\n\t\t\t\t\t\ttempl.KV(\"text-xs\", props.Size == \"xs\"),\n\t\t\t\t\t\ttempl.KV(\"text-sm\", props.Size == \"sm\"),\n\t\t\t\t\t\ttempl.KV(\"text-lg\", props.Size == \"lg\"),\n\t\t\t\t\t\ttempl.KV(\"text-xl\", props.Size == \"xl\"),\n\t\t\t\t\t}\n\t\t\t\t\u003e{ props.Label }\u003c/span\u003e\n\t\t\t}\n\t\t\t\u003cinput\n\t\t\t\ttype=\"range\"\n\t\t\t\tif props.ID != \"\" {\n\t\t\t\t\tid={ props.ID }\n\t\t\t\t}\n\t\t\t\tname={ props.Name }\n\t\t\t\tmin={ fmt.Sprintf(\"%d\", props.Min) }\n\t\t\t\tmax={ fmt.Sprintf(\"%d\", props.Max) }\n\t\t\t\tx-model=\"value\"\n\t\t\t\tclass={\n\t\t\t\t\t\"range\", props.Class,\n\t\t\t\t\ttempl.KV(\"range-xs\", props.Size == \"xs\"),\n\t\t\t\t\ttempl.KV(\"range-sm\", props.Size == \"sm\"),\n\t\t\t\t\ttempl.KV(\"range-lg\", props.Size == \"lg\"),\n\t\t\t\t\ttempl.KV(\"range-xl\", props.Size == \"xl\"),\n\t\t\t\t}\n\t\t\t\tstep={ fmt.Sprintf(\"%d\", props.Step) }\n\t\t\t/\u003e\n\t\t\t\u003cdiv x-text=\"value\" class=\"w-full max-w-7\"\u003e\u003c/div\u003e\n\t\t\u003c/label\u003e\n\t\u003c/div\u003e\n}\n\n// Note: usage requires datastar.js\ntempl DatastarRange(props RangeProps) {\n\t\u003cscript type=\"module\" src=\"/static/js/datastar.js\"\u003e\u003c/script\u003e\n\t\u003cdiv class=\"form-control\"\u003e\n\t\t\u003clabel\n\t\t\tclass=\"label space-x-1\"\n\t\t\tdata-signals={ fmt.Sprintf(\"{value: %d}\", props.Value) }\n\t\t\u003e\n\t\t\tif props.Label != \"\" {\n\t\t\t\t\u003cspan\n\t\t\t\t\tclass={\n\t\t\t\t\t\ttempl.KV(\"text-xs\", props.Size == \"xs\"),\n\t\t\t\t\t\ttempl.KV(\"text-sm\", props.Size == \"sm\"),\n\t\t\t\t\t\ttempl.KV(\"text-lg\", props.Size == \"lg\"),\n\t\t\t\t\t\ttempl.KV(\"text-xl\", props.Size == \"xl\"),\n\t\t\t\t\t}\n\t\t\t\t\u003e{ props.Label }\u003c/span\u003e\n\t\t\t}\n\t\t\t\u003cinput\n\t\t\t\ttype=\"range\"\n\t\t\t\tif props.ID != \"\" {\n\t\t\t\t\tid={ props.ID }\n\t\t\t\t}\n\t\t\t\tname={ props.Name }\n\t\t\t\tmin={ fmt.Sprintf(\"%d\", props.Min) }\n\t\t\t\tmax={ fmt.Sprintf(\"%d\", props.Max) }\n\t\t\t\tclass={\n\t\t\t\t\t\"range\", props.Class,\n\t\t\t\t\ttempl.KV(\"range-xs\", props.Size == \"xs\"),\n\t\t\t\t\ttempl.KV(\"range-sm\", props.Size == \"sm\"),\n\t\t\t\t\ttempl.KV(\"range-lg\", props.Size == \"lg\"),\n\t\t\t\t\ttempl.KV(\"range-xl\", props.Size == \"xl\"),\n\t\t\t\t}\n\t\t\t\tstep={ fmt.Sprintf(\"%d\", props.Step) }\n\t\t\t\tdata-bind-value\n\t\t\t/\u003e\n\t\t\t\u003cdiv\n\t\t\t\tclass=\"w-full max-w-7\"\n\t\t\t\tdata-text=\"$value\"\n\t\t\t\u003e\u003c/div\u003e\n\t\t\u003c/label\u003e\n\t\u003c/div\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/range" }, { "name": "rating", "code": "```go\ntype RatingProps struct {\n\tName string\n\tMin int\n\tMax int\n\tClass string\n\tValue int\n}\n\ntempl Rating(props RatingProps) {\n\t\u003cdiv class=\"rating\"\u003e\n\t\tfor i := props.Min; i \u003c= props.Max; i++ {\n\t\t\tif i == 0 {\n\t\t\t\t\u003cinput\n\t\t\t\t\ttype=\"radio\"\n\t\t\t\t\tname={ props.Name }\n\t\t\t\t\tvalue={ fmt.Sprintf(\"%d\", i) }\n\t\t\t\t\tclass=\"rating-hidden\"\n\t\t\t\t/\u003e\n\t\t\t} else {\n\t\t\t\t\u003cinput\n\t\t\t\t\ttype=\"radio\"\n\t\t\t\t\tname={ props.Name }\n\t\t\t\t\tvalue={ fmt.Sprintf(\"%d\", i) }\n\t\t\t\t\tclass={ \"mask mask-star-2 bg-yellow-400\", props.Class }\n\t\t\t\t\tif i+1 == props.Max {\n\t\t\t\t\t\tchecked=\"checked\"\n\t\t\t\t\t}\n\t\t\t\t/\u003e\n\t\t\t}\n\t\t}\n\t\u003c/div\u003e\n}\n\ntempl RatingDisplay(props RatingProps) {\n\t\u003cdiv class=\"rating\"\u003e\n\t\tfor i := props.Min; i \u003c= props.Max; i++ {\n\t\t\tif i == 0 {\n\t\t\t\t\u003cinput\n\t\t\t\t\ttype=\"radio\"\n\t\t\t\t\tname={ props.Name }\n\t\t\t\t\tvalue={ fmt.Sprintf(\"%d\", i) }\n\t\t\t\t\tclass=\"rating-hidden cursor-default\"\n\t\t\t\t\tdisabled\n\t\t\t\t/\u003e\n\t\t\t} else {\n\t\t\t\t\u003cinput\n\t\t\t\t\ttype=\"radio\"\n\t\t\t\t\tname={ props.Name }\n\t\t\t\t\tvalue={ fmt.Sprintf(\"%d\", i) }\n\t\t\t\t\tclass={ \"mask mask-star-2 bg-accent cursor-default\", props.Class }\n\t\t\t\t\tif i == props.Value {\n\t\t\t\t\t\tchecked=\"checked\"\n\t\t\t\t\t}\n\t\t\t\t\tdisabled\n\t\t\t\t/\u003e\n\t\t\t}\n\t\t}\n\t\u003c/div\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/rating" }, { "name": "select", "code": "```go\ntype SelectProps struct {\n\tID string\n\tLabel string\n\tName string\n\tDescription string\n\tClass string\n\tSize string\n\tRequired bool\n\tOptions []SelectOption\n\tAttrs templ.Attributes\n}\n\ntype SelectOption struct {\n\tLabel string\n\tValue string\n\tSelected bool\n\tDisabled bool\n}\n\ntempl Select(props SelectProps) {\n\t\u003cfieldset class=\"fieldset\"\u003e\n\t\tif props.Label != \"\" {\n\t\t\t\u003clegend class=\"fieldset-legend\"\u003e{ props.Label }\u003c/legend\u003e\n\t\t}\n\t\t\u003cselect\n\t\t\tclass={\n\t\t\t\t\"select w-full\",\n\t\t\t\tprops.Class,\n\t\t\t\ttempl.KV(\"select-xs\", props.Size == \"xs\"),\n\t\t\t\ttempl.KV(\"select-sm\", props.Size == \"sm\"),\n\t\t\t\ttempl.KV(\"select-lg\", props.Size == \"lg\"),\n\t\t\t\ttempl.KV(\"select-xl\", props.Size == \"xl\"),\n\t\t\t}\n\t\t\tif props.ID != \"\" {\n\t\t\t\tid={ props.ID }\n\t\t\t}\n\t\t\tname={ props.Name }\n\t\t\tif props.Required {\n\t\t\t\trequired\n\t\t\t}\n\t\t\t{ props.Attrs... }\n\t\t\u003e\n\t\t\t@SelectOptions(props.Options)\n\t\t\u003c/select\u003e\n\t\tif props.Description != \"\" {\n\t\t\t\u003cspan class=\"label\"\u003e{ props.Description }\u003c/span\u003e\n\t\t}\n\t\u003c/fieldset\u003e\n}\n\ntempl SelectOptions(options []SelectOption) {\n\tfor i := range options {\n\t\t\u003coption\n\t\t\tif options[i].Selected {\n\t\t\t\tselected\n\t\t\t}\n\t\t\tif options[i].Disabled {\n\t\t\t\tdisabled\n\t\t\t}\n\t\t\tvalue={ options[i].Value }\n\t\t\u003e\n\t\t\t{ options[i].Label }\n\t\t\u003c/option\u003e\n\t}\n}\n```", "daisy_ui_url": "https://daisyui.com/components/select" }, { "name": "textarea", "code": "```go\ntype TextareaProps struct {\n\tID string\n\tLabel string\n\tName string\n\tPlaceholder string\n\tValue string\n\tDescription string\n\tErr string\n\tClass string\n\tSize string\n\tRequired bool\n\tRows int\n\tAttrs templ.Attributes\n}\n\ntempl Textarea(props TextareaProps) {\n\t\u003cfieldset class=\"fieldset\"\u003e\n\t\tif props.Label != \"\" {\n\t\t\t\u003clegend class=\"fieldset-legend\"\u003e{ props.Label }\u003c/legend\u003e\n\t\t}\n\t\t\u003ctextarea\n\t\t\t{ props.Attrs... }\n\t\t\tif props.ID != \"\" {\n\t\t\t\tid={ props.ID }\n\t\t\t}\n\t\t\tname={ props.Name }\n\t\t\tplaceholder={ props.Placeholder }\n\t\t\tclass={\n\t\t\t\t\"textarea\",\n\t\t\t\tprops.Class,\n\t\t\t\ttempl.KV(\"textarea-error\", props.Err != \"\"),\n\t\t\t\ttempl.KV(\"textarea-xs\", props.Size == \"xs\"),\n\t\t\t\ttempl.KV(\"textarea-sm\", props.Size == \"sm\"),\n\t\t\t\ttempl.KV(\"textarea-lg\", props.Size == \"lg\"),\n\t\t\t\ttempl.KV(\"textarea-xl\", props.Size == \"xl\"),\n\t\t\t}\n\t\t\tif props.Rows \u003e 0 {\n\t\t\t\trows={ fmt.Sprintf(\"%d\", props.Rows) }\n\t\t\t} else {\n\t\t\t\trows=\"3\"\n\t\t\t}\n\t\t\tif props.Required {\n\t\t\t\trequired\n\t\t\t}\n\t\t\u003e\n\t\t\t{ props.Value }\n\t\t\u003c/textarea\u003e\n\t\tif props.Description != \"\" {\n\t\t\t\u003cdiv class=\"label\"\u003e{ props.Description }\u003c/div\u003e\n\t\t}\n\t\t\u003cdiv class=\"h-6 !p-0 fieldset-label text-error text-sm\"\u003e{ props.Err }\u003c/div\u003e\n\t\u003c/fieldset\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/textarea" }, { "name": "time_slot_picker", "code": "```go\ntype TimeSlotPickerProps struct {\n\tID string\n\tCurrentDate time.Time\n\tTimeSlots []TimeSlot\n\tPickerURL string\n\tReserveURL string\n}\n\ntype TimeSlot struct {\n\tStart time.Time\n\tEnd time.Time\n}\n\nfunc (x *TimeSlotPickerProps) Days() []time.Time {\n\tdate := time.Date(x.CurrentDate.Year(), x.CurrentDate.Month(), x.CurrentDate.Day(), 0, 0, 0, 0, x.CurrentDate.Location())\n\tdates := make([]time.Time, 7)\n\tdates[0] = date\n\tfor i := range 6 {\n\t\tdate = date.Add(24 * time.Hour)\n\t\tdates[i+1] = date\n\t}\n\treturn dates\n}\n\nfunc (x *TimeSlotPickerProps) GetSlots(day time.Time) []TimeSlot {\n\tslots := make([]TimeSlot, 0)\n\tnow := time.Now().UTC()\n\tnow = time.Date(\n\t\tnow.Year(), now.Month(), now.Day(),\n\t\tnow.Hour(), 0, 0, 0,\n\t\tnow.Location()).Add(time.Duration(24-now.Hour()) * time.Hour)\n\tfor _, s := range x.TimeSlots {\n\t\tif s.Start.After(now) \u0026\u0026 s.Start.Format(\"20060102\") == day.Format(\"20060102\") {\n\t\t\tslots = append(slots, s)\n\t\t}\n\t}\n\treturn slots\n}\n\ntempl TimeSlotPicker(props TimeSlotPickerProps) {\n\t\u003cdiv id={ props.ID } class=\"p-4\"\u003e\n\t\t\u003cdiv class=\"grid grid-cols-7 gap-1 h-[500px] w-full max-w-screen-xl\"\u003e\n\t\t\tfor _, day := range props.Days() {\n\t\t\t\t@timeSlotPickerDay(props, day)\n\t\t\t}\n\t\t\u003c/div\u003e\n\t\t@timeSlotPickerControls(props)\n\t\u003c/div\u003e\n}\n\ntempl timeSlotPickerDay(props TimeSlotPickerProps, day time.Time) {\n\t\u003cdiv\n\t\tclass={\n\t\t\t\"flex flex-col space-y-2 border border-base-content rounded-box text-sm overflow-y-auto\",\n\t\t\ttempl.KV(\"border-primary\", time.Now().UTC().Format(\"20060102\") == day.Format(\"20060102\")),\n\t\t}\n\t\u003e\n\t\t\u003cdiv class=\"text-center pt-1\"\u003e\n\t\t\t\u003cspan\u003e{ day.Format(\"Mon Jan 02\") }\u003c/span\u003e\n\t\t\t\u003cdiv class=\"divider !my-0 !py-0\"\u003e\u003c/div\u003e\n\t\t\u003c/div\u003e\n\t\t\u003cul class=\"space-y-1\"\u003e\n\t\t\tfor _, slot := range props.GetSlots(day) {\n\t\t\t\t{{ slotName := \"time_slot_\" + slot.Start.Format(\"200601021504\") }}\n\t\t\t\t@Modal(ModalProps{\n\t\t\t\t\tID: slotName,\n\t\t\t\t\tLabel: timeSlotButton(\n\t\t\t\t\t\tslot,\n\t\t\t\t\t\ttempl.Attributes{\"onclick\": slotName + \".showModal()\"},\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t\t) {\n\t\t\t\t\t@timeSlotModalContent(slot, slotName, props.ReserveURL)\n\t\t\t\t}\n\t\t\t}\n\t\t\u003c/ul\u003e\n\t\u003c/div\u003e\n}\n\ntempl timeSlotButton(slot TimeSlot, attrs templ.Attributes) {\n\t\u003cbutton class=\"btn btn-sm btn-ghost w-full\" { attrs... }\u003e\n\t\t{ slot.Start.Format(\"15:04\") } - { slot.End.Format(\"15:04\") }\n\t\u003c/button\u003e\n}\n\ntempl timeSlotModalContent(slot TimeSlot, slotName, reserveURL string) {\n\t\u003cdiv\u003e\n\t\t\u003cdiv class=\"flex items-center space-x-2\"\u003e\n\t\t\t\u003cdiv class=\"flex justify-center items-center\"\u003e\n\t\t\t\t\u003csvg\n\t\t\t\t\tclass=\"h-8 w-8\"\n\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\tfill=\"none\"\n\t\t\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\t\t\u003e\n\t\t\t\t\t\u003cpath class=\"stroke-current\" d=\"M2 12C2 7.28595 2 4.92893 3.46447 3.46447C4.92893 2 7.28595 2 12 2C16.714 2 19.0711 2 20.5355 3.46447C22 4.92893 22 7.28595 22 12C22 16.714 22 19.0711 20.5355 20.5355C19.0711 22 16.714 22 12 22C7.28595 22 4.92893 22 3.46447 20.5355C2 19.0711 2 16.714 2 12Z\" stroke-width=\"1.5\"\u003e\u003c/path\u003e\n\t\t\t\t\t\u003cpath class=\"stroke-current\" d=\"M10.125 8.875C10.125 7.83947 10.9645 7 12 7C13.0355 7 13.875 7.83947 13.875 8.875C13.875 9.56245 13.505 10.1635 12.9534 10.4899C12.478 10.7711 12 11.1977 12 11.75V13\" stroke-width=\"1.5\" stroke-linecap=\"round\"\u003e\u003c/path\u003e\n\t\t\t\t\t\u003ccircle class=\"fill-current\" cx=\"12\" cy=\"16\" r=\"1\"\u003e\u003c/circle\u003e\n\t\t\t\t\u003c/svg\u003e\n\t\t\t\u003c/div\u003e\n\t\t\t\u003cp class=\"col-span-7\"\u003e\n\t\t\t\tReserve a time slot \u003cb\u003e{ slot.Start.Format(\"15:04\") }\u003c/b\u003e - \u003cb\u003e{ slot.End.Format(\"15:04\") }\u003c/b\u003e, \u003cb\u003e{ slot.Start.Format(\"Monday January 02\") }\u003c/b\u003e?\n\t\t\t\u003c/p\u003e\n\t\t\u003c/div\u003e\n\t\t\u003cdiv class=\"flex justify-between items-center mt-8\"\u003e\n\t\t\t\u003cbutton\n\t\t\t\tid=\"modal-confirm-reservation\"\n\t\t\t\tclass=\"btn btn-sm btn-primary\"\n\t\t\t\thx-post={ reserveURL + \"?start=\" + slot.Start.Format(\"2006-01-02-15-04\") + \"\u0026end=\" + slot.End.Format(\"2006-01-02-15-04\") }\n\t\t\t\u003e\n\t\t\t\tReserve\n\t\t\t\u003c/button\u003e\n\t\t\t\u003cbutton class=\"btn btn-sm\" { templ.Attributes{\"onclick\": slotName + \".close()\"}... }\u003e\n\t\t\t\tCancel\n\t\t\t\u003c/button\u003e\n\t\t\u003c/div\u003e\n\t\t\u003cscript\u003e\n ((modal) =\u003e {\n document.addEventListener(\"htmx:afterRequest\", (evt) =\u003e {\n if (evt.detail.elt.id === \"modal-confirm-reservation\" \u0026\u0026 evt.detail.successful) {\n modal.close()\n }\n })\n })(document.currentScript.closest(\"dialog.modal\"))\n \u003c/script\u003e\n\t\u003c/div\u003e\n}\n\ntempl timeSlotPickerControls(props TimeSlotPickerProps) {\n\t\u003cdiv class=\"flex justify-between items-center py-2\"\u003e\n\t\t\u003cbutton\n\t\t\thx-get={ props.PickerURL + \"?date=\" + props.CurrentDate.Add(time.Duration(-7*24)*time.Hour).Format(\"2006-01-02\") }\n\t\t\thx-target={ \"#\" + props.ID }\n\t\t\thx-swap=\"outerHTML\"\n\t\t\tclass={ \"btn btn-ghost btn-sm disabled:opacity-50\", }\n\t\t\tif props.CurrentDate.Format(\"2006-01-02\") == time.Now().UTC().Format(\"2006-01-02\") {\n\t\t\t\tdisabled\n\t\t\t}\n\t\t\u003e\n\t\t\t@chevronLeft()\n\t\t\u003c/button\u003e\n\t\t\u003cbutton\n\t\t\thx-get={ props.PickerURL + \"?date=\" + props.CurrentDate.Add(time.Duration(7*24)*time.Hour).Format(\"2006-01-02\") }\n\t\t\thx-target={ \"#\" + props.ID }\n\t\t\thx-swap=\"outerHTML\"\n\t\t\tclass=\"btn btn-ghost btn-sm\"\n\t\t\u003e\n\t\t\t@chevronRight()\n\t\t\u003c/button\u003e\n\t\u003c/div\u003e\n}\n\ntempl chevronLeft() {\n\t\u003csvg\n\t\tclass=\"h-6 w-6\"\n\t\tviewBox=\"0 0 24 24\"\n\t\tfill=\"none\"\n\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\u003e\n\t\t\u003cpath class=\"stroke-base-content\" d=\"M15 6L9 12L15 18\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\u003e\u003c/path\u003e\n\t\u003c/svg\u003e\n}\n\ntempl chevronRight() {\n\t\u003csvg\n\t\tclass=\"h-6 w-6\"\n\t\tviewBox=\"0 0 24 24\"\n\t\tfill=\"none\"\n\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\u003e\n\t\t\u003cpath class=\"stroke-base-content\" d=\"M9 6L15 12L9 18\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\u003e\u003c/path\u003e\n\t\u003c/svg\u003e\n}\n```" }, { "name": "toggle", "code": "```go\ntype ToggleProps struct {\n\tID string\n\tBefore string\n\tAfter string\n\tName string\n\tChecked bool\n\tClass string\n\tHighlight bool\n\tAttrs templ.Attributes\n\tSize string\n}\n\ntempl Toggle(props ToggleProps) {\n\t\u003cdiv class=\"flex justify-center\"\u003e\n\t\t\u003clabel class=\"label cursor-pointer space-x-2 mx-auto\"\u003e\n\t\t\tif props.Before != \"\" {\n\t\t\t\t\u003cspan\n\t\t\t\t\tclass={\n\t\t\t\t\t\ttempl.KV(\"text-primary\", props.Highlight \u0026\u0026 !props.Checked),\n\t\t\t\t\t\ttempl.KV(\"text-xs\", props.Size == \"xs\"),\n\t\t\t\t\t\ttempl.KV(\"text-sm\", props.Size == \"sm\"),\n\t\t\t\t\t\ttempl.KV(\"text-lg\", props.Size == \"lg\"),\n\t\t\t\t\t\ttempl.KV(\"text-xl\", props.Size == \"xl\"),\n\t\t\t\t\t}\n\t\t\t\t\u003e\n\t\t\t\t\t{ props.Before }\n\t\t\t\t\u003c/span\u003e\n\t\t\t}\n\t\t\t\u003cinput\n\t\t\t\t{ props.Attrs... }\n\t\t\t\ttype=\"checkbox\"\n\t\t\t\tif props.ID != \"\" {\n\t\t\t\t\tid={ props.ID }\n\t\t\t\t}\n\t\t\t\tname={ props.Name }\n\t\t\t\tclass={\n\t\t\t\t\t\"toggle\", props.Class,\n\t\t\t\t\ttempl.KV(\"toggle-xs\", props.Size == \"xs\"),\n\t\t\t\t\ttempl.KV(\"toggle-sm\", props.Size == \"sm\"),\n\t\t\t\t\ttempl.KV(\"toggle-lg\", props.Size == \"lg\"),\n\t\t\t\t\ttempl.KV(\"toggle-xl\", props.Size == \"xl\"),\n\t\t\t\t}\n\t\t\t\tif props.Checked {\n\t\t\t\t\tchecked=\"checked\"\n\t\t\t\t}\n\t\t\t\tif props.Highlight {\n\t\t\t\t\tonclick=\"toggler(event)\"\n\t\t\t\t}\n\t\t\t/\u003e\n\t\t\tif props.After != \"\" {\n\t\t\t\t\u003cspan\n\t\t\t\t\tclass={\n\t\t\t\t\t\t\"label-text\",\n\t\t\t\t\t\ttempl.KV(\"text-primary\", props.Highlight \u0026\u0026 props.Checked),\n\t\t\t\t\t}\n\t\t\t\t\u003e\n\t\t\t\t\t{ props.After }\n\t\t\t\t\u003c/span\u003e\n\t\t\t}\n\t\t\u003c/label\u003e\n\t\tif props.Highlight {\n\t\t\t\u003cscript\u003e\n function toggler(evt) {\n evt.target.previousElementSibling.classList.toggle(\"text-primary\")\n evt.target.nextElementSibling.classList.toggle(\"text-primary\")\n }\n \u003c/script\u003e\n\t\t}\n\t\u003c/div\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/toggle" } ], "feedback": [ { "name": "alert", "code": "```go\ntempl Alert(class string) {\n\t\u003cdiv role=\"alert\" class={ \"alert\", class }\u003e\n\t\t{ children... }\n\t\u003c/div\u003e\n}\n\ntempl AlertInfo(message string) {\n\t@Alert(\"alert-info\") {\n\t\t\u003csvg\n\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\tfill=\"none\"\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tclass=\"stroke-info-content h-6 w-6 shrink-0\"\n\t\t\u003e\n\t\t\t\u003cpath\n\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\tstroke-width=\"2\"\n\t\t\t\td=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n\t\t\t\u003e\u003c/path\u003e\n\t\t\u003c/svg\u003e\n\t\t\u003cspan\u003e{ message }\u003c/span\u003e\n\t\t{ children... }\n\t}\n}\n\ntempl AlertSuccess(message string) {\n\t@Alert(\"alert-success\") {\n\t\t\u003csvg\n\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\tfill=\"none\"\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tclass=\"stroke-success-content h-6 w-6 shrink-0\"\n\t\t\u003e\n\t\t\t\u003cpath\n\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\tstroke-width=\"2\"\n\t\t\t\td=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\"\n\t\t\t\u003e\u003c/path\u003e\n\t\t\u003c/svg\u003e\n\t\t\u003cspan\u003e{ message }\u003c/span\u003e\n\t\t{ children... }\n\t}\n}\n\ntempl AlertWarning(message string) {\n\t@Alert(\"alert-warning\") {\n\t\t\u003csvg\n\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\tfill=\"none\"\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tclass=\"stroke-warning-content h-6 w-6 shrink-0\"\n\t\t\u003e\n\t\t\t\u003cpath\n\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\tstroke-width=\"2\"\n\t\t\t\td=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n\t\t\t\u003e\u003c/path\u003e\n\t\t\u003c/svg\u003e\n\t\t\u003cspan\u003e{ message }\u003c/span\u003e\n\t\t{ children... }\n\t}\n}\n\ntempl AlertError(message string) {\n\t@Alert(\"alert-error\") {\n\t\t\u003csvg\n\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\tfill=\"none\"\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tclass=\"stroke-error-content h-6 w-6 shrink-0\"\n\t\t\u003e\n\t\t\t\u003cpath\n\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\tstroke-width=\"2\"\n\t\t\t\td=\"M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z\"\n\t\t\t\u003e\u003c/path\u003e\n\t\t\u003c/svg\u003e\n\t\t\u003cspan\u003e{ message }\u003c/span\u003e\n\t\t{ children... }\n\t}\n}\n```", "daisy_ui_url": "https://daisyui.com/components/alert" }, { "name": "skeleton", "code": "```go\ntempl Skeleton() {\n\t\u003cdiv class=\"flex flex-col gap-4\"\u003e\n\t\t\u003cdiv class=\"skeleton h-32 w-full\"\u003e\u003c/div\u003e\n\t\t\u003cdiv class=\"skeleton h-4 w-28\"\u003e\u003c/div\u003e\n\t\t\u003cdiv class=\"skeleton h-4 w-full\"\u003e\u003c/div\u003e\n\t\t\u003cdiv class=\"skeleton h-4 w-full\"\u003e\u003c/div\u003e\n\t\u003c/div\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/skeleton" }, { "name": "status", "code": "```go\ntype StatusProps struct {\n\tCode int\n\tTitle string\n\tDescription string\n\tReturnButtonLabel string\n\tReturnButtonAttrs templ.Attributes\n}\n\ntempl Status(props StatusProps) {\n\t\u003csection class=\"grid min-h-dvh place-content-center px-4\"\u003e\n\t\t\u003cdiv class=\"text-center\"\u003e\n\t\t\t\u003ch1 class=\"text-9xl font-black text-base-content/70\"\u003e\n\t\t\t\t{ fmt.Sprintf(\"%d\", props.Code) }\n\t\t\t\u003c/h1\u003e\n\t\t\t\u003cp class=\"text-2xl font-bold tracking-tight text-base-content sm:text-4xl\"\u003e\n\t\t\t\t{ props.Title }\n\t\t\t\u003c/p\u003e\n\t\t\t\u003cp class=\"mt-4 text-base-content/70\"\u003e\n\t\t\t\t{ props.Description }\n\t\t\t\u003c/p\u003e\n\t\t\t\u003ca\n\t\t\t\t{ props.ReturnButtonAttrs... }\n\t\t\t\tclass=\"mt-4 btn btn-primary\"\n\t\t\t\u003e\n\t\t\t\t{ props.ReturnButtonLabel }\n\t\t\t\u003c/a\u003e\n\t\t\u003c/div\u003e\n\t\u003c/section\u003e\n}\n```" }, { "name": "toast", "code": "```go\ntype ToastProps struct {\n\tName string\n\tToastClass string\n\tAlertClass string\n}\n\ntempl Toast(props ToastProps) {\n\t\u003cdiv name={ props.Name } class={ \"toast\", props.ToastClass }\u003e\n\t\t\u003cdiv class={ \"alert\", props.AlertClass }\u003e\n\t\t\t{ children... }\n\t\t\u003c/div\u003e\n\t\u003c/div\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/toast" }, { "name": "tooltip", "code": "```go\ntype TooltipProps struct {\n\tTip string\n\tClass string\n}\n\ntempl Tooltip(props TooltipProps) {\n\t\u003cdiv class={ \"tooltip\", props.Class } data-tip={ props.Tip }\u003e\n\t\t{ children... }\n\t\u003c/div\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/tooltip" } ], "layout": [ { "name": "banner", "code": "```go\ntype BannerProps struct {\n\tTitle templ.Component\n\tDescription string\n}\n\ntempl Banner(props BannerProps) {\n\t\u003csection\u003e\n\t\t\u003cdiv class=\"mx-auto max-w-screen-xl px-4 py-32 lg:flex lg:items-center\"\u003e\n\t\t\t\u003cdiv class=\"mx-auto max-w-xl text-center\"\u003e\n\t\t\t\t@props.Title\n\t\t\t\t\u003cp class=\"mt-4 sm:text-xl/relaxed\"\u003e\n\t\t\t\t\t{ props.Description }\n\t\t\t\t\u003c/p\u003e\n\t\t\t\t\u003cdiv class=\"mt-8 flex flex-wrap justify-center gap-4\"\u003e\n\t\t\t\t\t// call to action\n\t\t\t\t\t{ children... }\n\t\t\t\t\u003c/div\u003e\n\t\t\t\u003c/div\u003e\n\t\t\u003c/div\u003e\n\t\u003c/section\u003e\n}\n```" }, { "name": "drawer", "code": "```go\ntempl Drawer(toggle templ.Component, sidebar templ.Component) {\n\t\u003cdiv class=\"drawer\"\u003e\n\t\t\u003cinput id=\"my-drawer\" type=\"checkbox\" class=\"drawer-toggle\"/\u003e\n\t\t\u003cdiv class=\"drawer-content\"\u003e\n\t\t\t\u003clabel for=\"my-drawer\" class=\"btn btn-primary drawer-button\"\u003e\n\t\t\t\t@toggle\n\t\t\t\u003c/label\u003e\n\t\t\t{ children... }\n\t\t\u003c/div\u003e\n\t\t\u003cdiv class=\"drawer-side\"\u003e\n\t\t\t\u003clabel for=\"my-drawer\" aria-label=\"close sidebar\" class=\"drawer-overlay\"\u003e\u003c/label\u003e\n\t\t\t\u003cul class=\"menu bg-base-200 text-base-content min-h-full w-80 p-4\"\u003e\n\t\t\t\t@sidebar\n\t\t\t\u003c/ul\u003e\n\t\t\u003c/div\u003e\n\t\u003c/div\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/drawer" }, { "name": "footer", "code": "```go\ntype FooterProps struct {\n\tIcon templ.Component\n\tName string\n\tDescription string\n\tCopyright string\n\tAnchors []AnchorProps\n}\n\ntempl Footer(props FooterProps) {\n\t\u003cfooter class=\"footer bg-base-200 text-base-content p-10 mt-24\"\u003e\n\t\t{ children... }\n\t\u003c/footer\u003e\n\t\u003cfooter class=\"footer bg-base-200 text-base-content border-base-300 border-t px-10 py-4\"\u003e\n\t\t\u003caside class=\"grid-flow-col items-center\"\u003e\n\t\t\tif props.Icon != nil {\n\t\t\t\t\u003cdiv class=\"w-6 h-6\"\u003e\n\t\t\t\t\t@props.Icon\n\t\t\t\t\u003c/div\u003e\n\t\t\t}\n\t\t\t\u003cp\u003e\n\t\t\t\tif props.Copyright != \"\" {\n\t\t\t\t\t\u003cspan\u003e\u0026copy; { props.Copyright }\u003c/span\u003e\n\t\t\t\t}\n\t\t\t\t{ props.Name }\n\t\t\t\t\u003cbr/\u003e\n\t\t\t\t{ props.Description }\n\t\t\t\u003c/p\u003e\n\t\t\u003c/aside\u003e\n\t\t\u003cnav class=\"md:place-self-center md:justify-self-end\"\u003e\n\t\t\t\u003cdiv class=\"grid grid-flow-col gap-4\"\u003e\n\t\t\t\tfor _, anchor := range props.Anchors {\n\t\t\t\t\t@Anchor(anchor)\n\t\t\t\t}\n\t\t\t\u003c/div\u003e\n\t\t\u003c/nav\u003e\n\t\u003c/footer\u003e\n}\n\ntempl FooterNav(title string, links []AnchorProps) {\n\t\u003cnav\u003e\n\t\t\u003ch6 class=\"footer-title\"\u003e{ title }\u003c/h6\u003e\n\t\tfor _, link := range links {\n\t\t\t\u003ca { link.Attrs... } class=\"link link-hover\"\u003e{ link.Label }\u003c/a\u003e\n\t\t}\n\t\u003c/nav\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/footer" }, { "name": "hero", "code": "```go\ntype HeroProps struct {\n\tSource string\n\tAlt string\n\tReverse bool\n\tClass string\n}\n\ntempl Hero(props HeroProps) {\n\t\u003cdiv class={ \"hero\", props.Class }\u003e\n\t\t\u003cdiv\n\t\t\tclass={\n\t\t\t\t\"hero-content\",\n\t\t\t\t\"flex-col\",\n\t\t\t\ttempl.KV(\"xl:flex-row\", !props.Reverse),\n\t\t\t\ttempl.KV(\"xl:flex-row-reverse\", props.Reverse),\n\t\t\t}\n\t\t\u003e\n\t\t\tif props.Source != \"\" {\n\t\t\t\t\u003cimg\n\t\t\t\t\tsrc={ props.Source }\n\t\t\t\t\talt={ props.Alt }\n\t\t\t\t\tclass=\"max-w-sm rounded-box shadow-2xl\"\n\t\t\t\t/\u003e\n\t\t\t}\n\t\t\t\u003cdiv\u003e\n\t\t\t\t{ children... }\n\t\t\t\u003c/div\u003e\n\t\t\u003c/div\u003e\n\t\u003c/div\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/hero" } ], "navigation": [ { "name": "anchor", "code": "```go\ntype AnchorProps struct {\n\tHref string\n\tLabel string\n\tLeftIcon templ.Component\n\tRightIcon templ.Component\n\tAttrs templ.Attributes\n\tClass string\n}\n\ntempl Anchor(props AnchorProps) {\n\t\u003ca\n\t\tif props.Href != \"\" {\n\t\t\thref={ templ.SafeURL(props.Href) }\n\t\t}\n\t\tclass={ \"group items-center cursor-pointer\", props.Class }\n\t\t{ props.Attrs... }\n\t\u003e\n\t\tif props.LeftIcon != nil {\n\t\t\t@props.LeftIcon\n\t\t}\n\t\t{ props.Label }\n\t\tif props.RightIcon != nil {\n\t\t\t@props.RightIcon\n\t\t}\n\t\u003c/a\u003e\n}\n```" }, { "name": "breadcrumbs", "code": "```go\ntype BreadcrumbsProps []BreadcrumbsProp\n\ntype BreadcrumbsProp struct {\n\tLabel string\n\tAttrs templ.Attributes\n}\n\ntempl Breadcrumbs(props BreadcrumbsProps) {\n\t\u003cdiv class=\"breadcrumbs text-sm\"\u003e\n\t\t\u003cul\u003e\n\t\t\tfor i, prop := range props {\n\t\t\t\t\u003cli class=\"select-none\"\u003e\n\t\t\t\t\tif i \u003c len(props) - 1 {\n\t\t\t\t\t\t\u003ca { prop.Attrs... }\u003e{ prop.Label }\u003c/a\u003e\n\t\t\t\t\t} else {\n\t\t\t\t\t\t{ prop.Label }\n\t\t\t\t\t}\n\t\t\t\t\u003c/li\u003e\n\t\t\t}\n\t\t\u003c/ul\u003e\n\t\u003c/div\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/breadcrumbs" }, { "name": "menu", "code": "```go\ntype MenuProps struct {\n\tTitle string\n\tClass string\n}\n\ntempl Menu(props MenuProps) {\n\t\u003cul class={ \"menu\", props.Class }\u003e\n\t\tif props.Title != \"\" {\n\t\t\t\u003ch2 class=\"font-bold\"\u003e{ props.Title }\u003c/h2\u003e\n\t\t}\n\t\t{ children... }\n\t\u003c/ul\u003e\n}\n\ntype MenuItemProps struct {\n\tLabel string\n\tAttrs templ.Attributes\n\tIcon templ.Component\n\tIconAfter bool\n}\n\ntempl MenuItem(props MenuItemProps) {\n\t\u003cli\u003e\n\t\t\u003ca { props.Attrs... }\u003e\n\t\t\tif props.Icon != nil \u0026\u0026 !props.IconAfter {\n\t\t\t\t@props.Icon\n\t\t\t}\n\t\t\t{ props.Label }\n\t\t\tif props.Icon != nil \u0026\u0026 props.IconAfter {\n\t\t\t\t@props.Icon\n\t\t\t}\n\t\t\u003c/a\u003e\n\t\t\u003cul\u003e\n\t\t\t{ children... }\n\t\t\u003c/ul\u003e\n\t\u003c/li\u003e\n}\n\ntype SubmenuProps struct {\n\tTitle string\n\tAttrs templ.Attributes\n\tIcon templ.Component\n\tIconAfter bool\n}\n\ntempl Submenu(props SubmenuProps) {\n\t\u003cli\u003e\n\t\t\u003cdetails { props.Attrs... }\u003e\n\t\t\t\u003csummary\u003e\n\t\t\t\tif props.Icon != nil \u0026\u0026 !props.IconAfter {\n\t\t\t\t\t@props.Icon\n\t\t\t\t}\n\t\t\t\t{ props.Title }\n\t\t\t\tif props.Icon != nil \u0026\u0026 props.IconAfter {\n\t\t\t\t\t@props.Icon\n\t\t\t\t}\n\t\t\t\u003c/summary\u003e\n\t\t\t\u003cul\u003e\n\t\t\t\t{ children... }\n\t\t\t\u003c/ul\u003e\n\t\t\u003c/details\u003e\n\t\u003c/li\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/menu" }, { "name": "pagination", "code": "```go\ntype PaginationProps struct {\n\tURL string\n\tPage int\n\tLow int\n\tHigh int\n\tMaxPages int\n}\n\ntempl Pagination(id string, props PaginationProps) {\n\t\u003cdiv id={ id }\u003e\n\t\t\u003c!-- paginated content goes here --\u003e\n\t\t{ children... }\n\t\t\u003c!-- --\u003e\n\t\t\u003cdiv class=\"join\"\u003e\n\t\t\t@PaginationButton(id, props.URL, 1, props.Page == 1) {\n\t\t\t\t@AnglesLeft()\n\t\t\t}\n\t\t\t@PaginationButton(id, props.URL, props.Page-1, props.Page == 1) {\n\t\t\t\t@ChevronLeft()\n\t\t\t}\n\t\t\tfor i := props.Low; i \u003c= props.High; i++ {\n\t\t\t\t@PaginationButton(id, props.URL, i+1, props.Page == i+1) {\n\t\t\t\t\t{ fmt.Sprintf(\"%d\", i+1) }\n\t\t\t\t}\n\t\t\t}\n\t\t\t@PaginationButton(id, props.URL, props.Page+1, props.Page == props.MaxPages) {\n\t\t\t\t@ChevronRight()\n\t\t\t}\n\t\t\t@PaginationButton(id, props.URL, props.MaxPages, props.Page == props.MaxPages) {\n\t\t\t\t@AnglesRight()\n\t\t\t}\n\t\t\u003c/div\u003e\n\t\u003c/div\u003e\n}\n\ntempl PaginationButton(id, url string, urlPage int, disabled bool) {\n\t\u003cbutton\n\t\tif url != \"\" {\n\t\t\thx-get={ fmt.Sprintf(\"%s?page=%d\", url, urlPage) }\n\t\t\thx-target={ fmt.Sprintf(\"#%s\", id) }\n\t\t\thx-swap=\"outerHTML\"\n\t\t}\n\t\tclass={\n\t\t\t\"join-item btn btn-square disabled:opacity-40\",\n\t\t\ttempl.KV(\"btn-disabled\", disabled),\n\t\t}\n\t\tif disabled {\n\t\t\tdisabled\n\t\t}\n\t\u003e\n\t\t{ children... }\n\t\u003c/button\u003e\n}\n\ntempl AnglesRight() {\n\t\u003csvg\n\t\tclass=\"w-4 h-4\"\n\t\tviewBox=\"0 0 24 24\"\n\t\tfill=\"none\"\n\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\u003e\n\t\t\u003cpath\n\t\t\td=\"M6 17L11 12L6 7M13 17L18 12L13 7\"\n\t\t\tclass=\"stroke-base-content\"\n\t\t\tstroke-width=\"2\"\n\t\t\tstroke-linecap=\"round\"\n\t\t\tstroke-linejoin=\"round\"\n\t\t\u003e\u003c/path\u003e\n\t\u003c/svg\u003e\n}\n\ntempl AnglesLeft() {\n\t\u003csvg\n\t\tclass=\"w-4 h-4\"\n\t\tviewBox=\"0 0 24 24\"\n\t\tfill=\"none\"\n\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\u003e\n\t\t\u003cpath\n\t\t\td=\"M18 17L13 12L18 7M11 17L6 12L11 7\"\n\t\t\tclass=\"stroke-base-content\"\n\t\t\tstroke-width=\"2\"\n\t\t\tstroke-linecap=\"round\"\n\t\t\tstroke-linejoin=\"round\"\n\t\t\u003e\u003c/path\u003e\n\t\u003c/svg\u003e\n}\n\ntempl ChevronRight() {\n\t\u003csvg\n\t\tclass=\"w-4 h-4\"\n\t\tviewBox=\"0 0 24 24\"\n\t\tfill=\"none\"\n\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\u003e\n\t\t\u003cpath\n\t\t\td=\"M9 6L15 12L9 18\"\n\t\t\tclass=\"stroke-base-content\"\n\t\t\tstroke-width=\"2\"\n\t\t\tstroke-linecap=\"round\"\n\t\t\tstroke-linejoin=\"round\"\n\t\t\u003e\u003c/path\u003e\n\t\u003c/svg\u003e\n}\n\ntempl ChevronLeft() {\n\t\u003csvg\n\t\tclass=\"w-4 h-4\"\n\t\tviewBox=\"0 0 24 24\"\n\t\tfill=\"none\"\n\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\u003e\n\t\t\u003cpath\n\t\t\td=\"M15 6L9 12L15 18\"\n\t\t\tclass=\"stroke-base-content\"\n\t\t\tstroke-width=\"2\"\n\t\t\tstroke-linecap=\"round\"\n\t\t\tstroke-linejoin=\"round\"\n\t\t\u003e\u003c/path\u003e\n\t\u003c/svg\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/pagination" }, { "name": "steps", "code": "```go\ntype StepProps struct {\n\tLabel string\n\tDone bool\n\tAttrs templ.Attributes\n}\n\ntempl Steps() {\n\t\u003cul class=\"steps\"\u003e\n\t\t{ children... }\n\t\u003c/ul\u003e\n}\n\ntempl Step(props StepProps) {\n\t\u003cli\n\t\tclass={ \"step\", templ.KV(\"step-primary\", props.Done) }\n\t\u003e\n\t\t\u003ca { props.Attrs... }\u003e{ props.Label }\u003c/a\u003e\n\t\u003c/li\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/steps" }, { "name": "tabs", "code": "```go\ntype TabsProps struct {\n\tName string\n\tClass string\n\tTabs []TabProps\n\tContentClass string\n}\n\ntype TabProps struct {\n\tLabel string\n\tContent templ.Component\n}\n\ntempl Tabs(props TabsProps) {\n\t\u003cdiv role=\"tablist\" class={ \"tabs\", props.Class }\u003e\n\t\tfor i, tab := range props.Tabs {\n\t\t\t\u003cinput\n\t\t\t\ttype=\"radio\"\n\t\t\t\tname={ props.Name }\n\t\t\t\trole=\"tab\"\n\t\t\t\tclass=\"tab\"\n\t\t\t\taria-label={ tab.Label }\n\t\t\t\tif i == 0 {\n\t\t\t\t\tchecked=\"checked\"\n\t\t\t\t}\n\t\t\t/\u003e\n\t\t\t\u003cdiv class={ \"tab-content\", props.ContentClass }\u003e\n\t\t\t\t@tab.Content\n\t\t\t\u003c/div\u003e\n\t\t}\n\t\u003c/div\u003e\n}\n```", "daisy_ui_url": "https://daisyui.com/components/tab" } ] } ================================================ FILE: internal/assets/generated/component_example_code_map.json ================================================ { "accordion": [ { "name": "AccordionWithCheckbox", "code": "```go\n\ntempl AccordionWithCheckbox() {\n\t\u003cdiv class=\"w-full flex join join-vertical pt-4\"\u003e\n\t\t@components.AccordionRow(components.AccordionRowProps{Label: \"Label 1\", Name: \"accordion-example-1\"}) {\n\t\t\t\u003cp class=\"pt-4\"\u003eLabel 1 content\u003c/p\u003e\n\t\t}\n\t\t@components.AccordionRow(components.AccordionRowProps{Label: \"Label 2\", Name: \"accordion-example-2\"}) {\n\t\t\t\u003ch2 class=\"text-xl font-bold py-4\"\u003eContent 2\u003c/h2\u003e\n\t\t\t\u003cp\u003eLabel 2 content\u003c/p\u003e\n\t\t}\n\t\t@components.AccordionRow(components.AccordionRowProps{Label: \"Label 3\", Name: \"accordion-example-3\"}) {\n\t\t\t\u003ch2 class=\"text-xl font-bold py-4\"\u003eContent 3\u003c/h2\u003e\n\t\t\t\u003cul class=\"list-disc [\u0026\u003eli]:ml-4\"\u003e\n\t\t\t\t\u003cli\u003eItem 1\u003c/li\u003e\n\t\t\t\t\u003cli\u003eItem 2\u003c/li\u003e\n\t\t\t\t\u003cli\u003eItem 3\u003c/li\u003e\n\t\t\t\u003c/ul\u003e\n\t\t}\n\t\u003c/div\u003e\n}\n\n```", "description": "Accordion with input type 'checkbox': multiple rows can be open at a time." }, { "name": "AccordionWithRadio", "code": "```go\n\ntempl AccordionWithRadio() {\n\t\u003cdiv class=\"w-full flex join join-vertical pt-4\"\u003e\n\t\t@components.AccordionRow(components.AccordionRowProps{Label: \"Label 1\", Name: \"accordion-example-2\", Type: \"radio\"}) {\n\t\t\t\u003cp class=\"pt-4\"\u003eLabel 1 content\u003c/p\u003e\n\t\t}\n\t\t@components.AccordionRow(components.AccordionRowProps{Label: \"Label 2\", Name: \"accordion-example-2\", Type: \"radio\"}) {\n\t\t\t\u003ch2 class=\"text-xl font-bold py-4\"\u003eContent 2\u003c/h2\u003e\n\t\t\t\u003cp\u003eLabel 2 content\u003c/p\u003e\n\t\t}\n\t\t@components.AccordionRow(components.AccordionRowProps{Label: \"Label 3\", Name: \"accordion-example-2\", Type: \"radio\"}) {\n\t\t\t\u003ch2 class=\"text-xl font-bold py-4\"\u003eContent 3\u003c/h2\u003e\n\t\t\t\u003cul class=\"list-disc [\u0026\u003eli]:ml-4\"\u003e\n\t\t\t\t\u003cli\u003eItem 1\u003c/li\u003e\n\t\t\t\t\u003cli\u003eItem 2\u003c/li\u003e\n\t\t\t\t\u003cli\u003eItem 3\u003c/li\u003e\n\t\t\t\u003c/ul\u003e\n\t\t}\n\t\u003c/div\u003e\n}\n```", "description": "Accordion with input type 'radio': only a single row can be open at a time." } ], "active_search": [ { "name": "ActiveSearchExampleTable", "code": "```go\ntempl ActiveSearchExampleTable() {\n\t\u003cdiv class=\"h-[600px]\"\u003e\n\t\t@ActiveSearchExample(\n\t\t\t\"active-search-example-table\",\n\t\t\t[]templ.Component{\n\t\t\t\tcomponents.PlainText(\"First name\"),\n\t\t\t\tcomponents.PlainText(\"Last name\"),\n\t\t\t\tcomponents.PlainText(\"Email\"),\n\t\t\t},\n\t\t\tactiveSearchTableDataComponents(),\n\t\t)\n\t\u003c/div\u003e\n}\n\ntempl ActiveSearchExample(id string, headers []templ.Component, rows []templ.Component) {\n\t\u003cdiv class=\"py-8\"\u003e\n\t\t@components.ActiveSearchInput(\n\t\t\tcomponents.ActiveSearchInputProps{\n\t\t\t\tID: \"active-search-example-input\",\n\t\t\t\tURL: \"/active-search\",\n\t\t\t\tTarget: fmt.Sprintf(\"#%s \u003e tbody\", id),\n\t\t\t\tInputProps: components.InputProps{\n\t\t\t\t\tIcon: searchIcon(),\n\t\t\t\t\tName: \"active-search-example\",\n\t\t\t\t\tType: \"search\",\n\t\t\t\t\tPlaceholder: \"Filter table...\",\n\t\t\t\t\tSize: \"sm\",\n\t\t\t\t},\n\t\t\t})\n\t\t@components.Table(\n\t\t\theaders,\n\t\t\tactiveSearchTableDataComponents(),\n\t\t\ttempl.Attributes{\"id\": \"active-search-example-table\"},\n\t\t)\n\t\u003c/div\u003e\n}\n\ntempl searchIcon() {\n\t\u003csvg\n\t\tclass=\"w-5 h-5\"\n\t\tviewBox=\"0 0 32 32\"\n\t\tversion=\"1.1\"\n\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\txmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\t\txmlns:sketch=\"http://www.bohemiancoding.com/sketch/ns\"\n\t\u003e\n\t\t\u003cg stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\" sketch:type=\"MSPage\"\u003e\n\t\t\t\u003cg class=\"fill-base-content/60\" id=\"Icon-Set\" sketch:type=\"MSLayerGroup\" transform=\"translate(-256.000000, -1139.000000)\"\u003e\n\t\t\t\t\u003cpath d=\"M269.46,1163.45 C263.17,1163.45 258.071,1158.44 258.071,1152.25 C258.071,1146.06 263.17,1141.04 269.46,1141.04 C275.75,1141.04 280.85,1146.06 280.85,1152.25 C280.85,1158.44 275.75,1163.45 269.46,1163.45 L269.46,1163.45 Z M287.688,1169.25 L279.429,1161.12 C281.591,1158.77 282.92,1155.67 282.92,1152.25 C282.92,1144.93 276.894,1139 269.46,1139 C262.026,1139 256,1144.93 256,1152.25 C256,1159.56 262.026,1165.49 269.46,1165.49 C272.672,1165.49 275.618,1164.38 277.932,1162.53 L286.224,1170.69 C286.629,1171.09 287.284,1171.09 287.688,1170.69 C288.093,1170.3 288.093,1169.65 287.688,1169.25 L287.688,1169.25 Z\" id=\"search\" sketch:type=\"MSShapeGroup\"\u003e\u003c/path\u003e\n\t\t\t\u003c/g\u003e\n\t\t\u003c/g\u003e\n\t\u003c/svg\u003e\n}\n\nfunc activeSearchTableDataComponents() []templ.Component {\n\tcoms := make([]templ.Component, len(ActiveSearchTableData))\n\tfor i := range ActiveSearchTableData {\n\t\tcoms[i] = ActiveSearchTableRow(\n\t\t\tActiveSearchTableData[i].FirstName,\n\t\t\tActiveSearchTableData[i].LastName,\n\t\t\tActiveSearchTableData[i].Email)\n\t}\n\treturn coms\n}\n\nvar ActiveSearchTableData = []struct {\n\tFirstName string\n\tLastName string\n\tEmail string\n}{\n\t{FirstName: \"John\", LastName: \"Smith\", Email: \"john.smith@email.com\"},\n\t{FirstName: \"Emily\", LastName: \"Johnson\", Email: \"emily.johnson@email.com\"},\n\t{FirstName: \"Michael\", LastName: \"Brown\", Email: \"michael.brown@email.com\"},\n\t{FirstName: \"Jessica\", LastName: \"Williams\", Email: \"jessica.williams@email.com\"},\n\t{FirstName: \"David\", LastName: \"Jones\", Email: \"david.jones@email.com\"},\n\t{FirstName: \"Sarah\", LastName: \"Miller\", Email: \"sarah.miller@email.com\"},\n\t{FirstName: \"Christopher\", LastName: \"Davis\", Email: \"chris.davis@email.com\"},\n\t{FirstName: \"Amanda\", LastName: \"Wilson\", Email: \"amanda.wilson@email.com\"},\n\t{FirstName: \"James\", LastName: \"Taylor\", Email: \"james.taylor@email.com\"},\n\t{FirstName: \"Laura\", LastName: \"Moore\", Email: \"laura.moore@email.com\"},\n}\n\ntempl ActiveSearchTableRows(rows []templ.Component) {\n\tfor _, r := range rows {\n\t\t@r\n\t}\n}\n\ntempl ActiveSearchTableRow(firstName, lastName, email string) {\n\t\u003ctr\u003e\n\t\t\u003ctd\u003e{ firstName }\u003c/td\u003e\n\t\t\u003ctd\u003e{ lastName }\u003c/td\u003e\n\t\t\u003ctd\u003e{ email }\u003c/td\u003e\n\t\u003c/tr\u003e\n}\n```", "handler": "```go\nfunc GetActiveSearchExample(c echo.Context) error {\n\ttime.Sleep(500 * time.Millisecond)\n\n\tsearch := strings.ToLower(strings.TrimSpace(c.FormValue(\"active-search-example\")))\n\n\tout := make([]templ.Component, 0, len(examples.ActiveSearchTableData))\n\n\tfor _, rowData := range examples.ActiveSearchTableData {\n\t\tif search == \"\" || (strings.Contains(strings.ToLower(rowData.FirstName), search) ||\n\t\t\tstrings.Contains(strings.ToLower(rowData.LastName), search) ||\n\t\t\tstrings.Contains(strings.ToLower(rowData.Email), search)) {\n\t\t\tout = append(out, examples.ActiveSearchTableRow(\n\t\t\t\trowData.FirstName, rowData.LastName, rowData.Email))\n\t\t}\n\t}\n\n\treturn render(c, http.StatusOK, examples.ActiveSearchTableRows(out))\n}\n\n```", "title": "Active search input for a table" } ], "alert": [ { "name": "AlertInfoExample", "code": "```go\ntempl AlertInfoExample() {\n\t\u003cdiv class=\"max-w-md mx-auto pt-4\"\u003e\n\t\t@components.AlertInfo(\n\t\t\t\"Your profile has been successfully updated. Please review your changes.\",\n\t\t)\n\t\u003c/div\u003e\n}\n\n```", "title": "Info-type alert" }, { "name": "AlertSuccessExample", "code": "```go\ntempl AlertSuccessExample() {\n\t\u003cdiv class=\"max-w-md mx-auto pt-4\"\u003e\n\t\t@components.AlertSuccess(\n\t\t\t\"Your payment was processed successfully! Thank you for your purchase.\",\n\t\t) {\n\t\t\t\u003cbutton class=\"btn btn-sm\"\u003eOK\u003c/button\u003e\n\t\t}\n\t\u003c/div\u003e\n}\n\n```", "title": "Success-type alert" }, { "name": "AlertWarningExample", "code": "```go\ntempl AlertWarningExample() {\n\t\u003cdiv class=\"max-w-md mx-auto pt-4\"\u003e\n\t\t@components.AlertWarning(\n\t\t\t\"Your password will expire in 7 days. Please update it to avoid any disruptions.\",\n\t\t)\n\t\u003c/div\u003e\n}\n\n```", "title": "Warning-type alert" }, { "name": "AlertErrorExample", "code": "```go\ntempl AlertErrorExample() {\n\t\u003cdiv class=\"max-w-md mx-auto pt-4\"\u003e\n\t\t@components.AlertError(\n\t\t\t\"Failed to connect to the server. Please try again later or contact support if the issue persists.\",\n\t\t) {\n\t\t\t\u003cbutton class=\"btn btn-sm\"\u003eOK\u003c/button\u003e\n\t\t}\n\t\u003c/div\u003e\n}\n```", "title": "Error-type alert" } ], "anchor": [ { "name": "BasicAnchor", "code": "```go\ntempl BasicAnchor() {\n\t@components.Anchor(components.AnchorProps{\n\t\tLabel: \"Basic anchor\",\n\t\tClass: \"link\",\n\t})\n}\n\n```", "title": "Basic anchor" }, { "name": "PrimaryAnchor", "code": "```go\ntempl PrimaryAnchor() {\n\t@components.Anchor(components.AnchorProps{\n\t\tLabel: \"Primary anchor\",\n\t\tClass: \"link link-primary\",\n\t})\n}\n\n```", "title": "Primary anchor" }, { "name": "AnchorWithIcon", "code": "```go\ntempl AnchorWithIcon() {\n\t@components.Anchor(components.AnchorProps{\n\t\tLabel: \"GitHub profile\",\n\t\tLeftIcon: GithubIcon(),\n\t\tClass: \"link\",\n\t})\n}\n\n```", "title": "Anchor with icon" }, { "name": "SocialAnchors", "code": "```go\ntempl SocialAnchors() {\n\t\u003cdiv class=\"mx-auto\"\u003e\n\t\t\u003cdiv class=\"flex space-x-4\"\u003e\n\t\t\t@components.Anchor(components.AnchorProps{LeftIcon: XIcon()})\n\t\t\t@components.Anchor(components.AnchorProps{LeftIcon: YoutubeIcon()})\n\t\t\t@components.Anchor(components.AnchorProps{LeftIcon: FacebookIcon()})\n\t\t\t@components.Anchor(components.AnchorProps{LeftIcon: GithubIcon()})\n\t\t\t@components.Anchor(components.AnchorProps{LeftIcon: LinkedInIcon()})\n\t\t\u003c/div\u003e\n\t\u003c/div\u003e\n}\n\ntempl XIcon() {\n\t\u003csvg\n\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\tx=\"0px\"\n\t\ty=\"0px\"\n\t\tviewBox=\"0 0 50 50\"\n\t\tclass=\"h-6 w-6 fill-base-content/80 hover:fill-base-content inline-block mr-1\"\n\t\u003e\n\t\t\u003cpath d=\"M 5.9199219 6 L 20.582031 27.375 L 6.2304688 44 L 9.4101562 44 L 21.986328 29.421875 L 31.986328 44 L 44 44 L 28.681641 21.669922 L 42.199219 6 L 39.029297 6 L 27.275391 19.617188 L 17.933594 6 L 5.9199219 6 z M 9.7167969 8 L 16.880859 8 L 40.203125 42 L 33.039062 42 L 9.7167969 8 z\"\u003e\u003c/path\u003e\n\t\u003c/svg\u003e\n}\n\ntempl YoutubeIcon() {\n\t\u003csvg\n\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\tviewBox=\"0 0 24 24\"\n\t\tclass=\"h-6 w-6 fill-base-content/80 hover:fill-base-content inline-block mr-1\"\n\t\u003e\n\t\t\u003cpath\n\t\t\td=\"M19.615 3.184c-3.604-.246-11.631-.245-15.23 0-3.897.266-4.356 2.62-4.385 8.816.029 6.185.484 8.549 4.385 8.816 3.6.245 11.626.246 15.23 0 3.897-.266 4.356-2.62 4.385-8.816-.029-6.185-.484-8.549-4.385-8.816zm-10.615 12.816v-8l8 3.993-8 4.007z\"\n\t\t\u003e\u003c/path\u003e\n\t\u003c/svg\u003e\n}\n\ntempl FacebookIcon() {\n\t\u003csvg\n\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\tviewBox=\"0 0 24 24\"\n\t\tclass=\"h-6 w-6 fill-base-content/80 hover:fill-base-content inline-block mr-1\"\n\t\u003e\n\t\t\u003cpath\n\t\t\td=\"M9 8h-3v4h3v12h5v-12h3.642l.358-4h-4v-1.667c0-.955.192-1.333 1.115-1.333h2.885v-5h-3.808c-3.596 0-5.192 1.583-5.192 4.615v3.385z\"\n\t\t\u003e\u003c/path\u003e\n\t\u003c/svg\u003e\n}\n\ntempl GithubIcon() {\n\t\u003csvg\n\t\tclass=\"h-6 w-6 inline-block mr-1\"\n\t\tviewBox=\"0 0 20 20\"\n\t\tversion=\"1.1\"\n\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\txmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\t\u003e\n\t\t\u003cg stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\"\u003e\n\t\t\t\u003cg class=\"fill-base-content/80 hover:fill-base-content group-hover:fill-base-content\" id=\"Dribbble-Light-Preview\" transform=\"translate(-140.000000, -7559.000000)\"\u003e\n\t\t\t\t\u003cg id=\"icons\" transform=\"translate(56.000000, 160.000000)\"\u003e\n\t\t\t\t\t\u003cpath d=\"M94,7399 C99.523,7399 104,7403.59 104,7409.253 C104,7413.782 101.138,7417.624 97.167,7418.981 C96.66,7419.082 96.48,7418.762 96.48,7418.489 C96.48,7418.151 96.492,7417.047 96.492,7415.675 C96.492,7414.719 96.172,7414.095 95.813,7413.777 C98.04,7413.523 100.38,7412.656 100.38,7408.718 C100.38,7407.598 99.992,7406.684 99.35,7405.966 C99.454,7405.707 99.797,7404.664 99.252,7403.252 C99.252,7403.252 98.414,7402.977 96.505,7404.303 C95.706,7404.076 94.85,7403.962 94,7403.958 C93.15,7403.962 92.295,7404.076 91.497,7404.303 C89.586,7402.977 88.746,7403.252 88.746,7403.252 C88.203,7404.664 88.546,7405.707 88.649,7405.966 C88.01,7406.684 87.619,7407.598 87.619,7408.718 C87.619,7412.646 89.954,7413.526 92.175,7413.785 C91.889,7414.041 91.63,7414.493 91.54,7415.156 C90.97,7415.418 89.522,7415.871 88.63,7414.304 C88.63,7414.304 88.101,7413.319 87.097,7413.247 C87.097,7413.247 86.122,7413.234 87.029,7413.87 C87.029,7413.87 87.684,7414.185 88.139,7415.37 C88.139,7415.37 88.726,7417.2 91.508,7416.58 C91.513,7417.437 91.522,7418.245 91.522,7418.489 C91.522,7418.76 91.338,7419.077 90.839,7418.982 C86.865,7417.627 84,7413.783 84,7409.253 C84,7403.59 88.478,7399 94,7399\"\u003e\u003c/path\u003e\n\t\t\t\t\u003c/g\u003e\n\t\t\t\u003c/g\u003e\n\t\t\u003c/g\u003e\n\t\u003c/svg\u003e\n}\n\ntempl LinkedInIcon() {\n\t\u003csvg\n\t\tclass=\"h-6 w-6 fill-base-content/80 hover:fill-base-content inline-block mr-1\"\n\t\tviewBox=\"0 0 32 32\"\n\t\tversion=\"1.1\"\n\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\u003e\n\t\t\u003cpath d=\"M28.778 1.004h-25.56c-0.008-0-0.017-0-0.027-0-1.199 0-2.172 0.964-2.186 2.159v25.672c0.014 1.196 0.987 2.161 2.186 2.161 0.010 0 0.019-0 0.029-0h25.555c0.008 0 0.018 0 0.028 0 1.2 0 2.175-0.963 2.194-2.159l0-0.002v-25.67c-0.019-1.197-0.994-2.161-2.195-2.161-0.010 0-0.019 0-0.029 0h0.001zM9.9 26.562h-4.454v-14.311h4.454zM7.674 10.293c-1.425 0-2.579-1.155-2.579-2.579s1.155-2.579 2.579-2.579c1.424 0 2.579 1.154 2.579 2.578v0c0 0.001 0 0.002 0 0.004 0 1.423-1.154 2.577-2.577 2.577-0.001 0-0.002 0-0.003 0h0zM26.556 26.562h-4.441v-6.959c0-1.66-0.034-3.795-2.314-3.795-2.316 0-2.669 1.806-2.669 3.673v7.082h-4.441v-14.311h4.266v1.951h0.058c0.828-1.395 2.326-2.315 4.039-2.315 0.061 0 0.121 0.001 0.181 0.003l-0.009-0c4.5 0 5.332 2.962 5.332 6.817v7.855z\"\u003e\u003c/path\u003e\n\t\u003c/svg\u003e\n}\n```", "title": "Socials anchors" } ], "avatar": [ { "name": "MultipleAvatarSizes", "code": "```go\ntempl MultipleAvatarSizes() {\n\t\u003cdiv class=\"flex justify-center items-center space-x-4 pt-4\"\u003e\n\t\t@components.Avatar(components.AvatarProps{\n\t\t\tContainerClass: \"rounded w-8\", Source: \"/static/images/avatar.jpg\"})\n\t\t@components.Avatar(components.AvatarProps{\n\t\t\tContainerClass: \"rounded w-12\", Source: \"/static/images/avatar.jpg\"})\n\t\t@components.Avatar(components.AvatarProps{\n\t\t\tContainerClass: \"rounded w-16\", Source: \"/static/images/avatar.jpg\"})\n\t\t@components.Avatar(components.AvatarProps{\n\t\t\tContainerClass: \"rounded w-20\", Source: \"/static/images/avatar.jpg\"})\n\t\t@components.Avatar(components.AvatarProps{\n\t\t\tContainerClass: \"rounded w-24\", Source: \"/static/images/avatar.jpg\"})\n\t\u003c/div\u003e\n}\n\n```", "title": "Multiple avatar sizes" }, { "name": "GroupOfAvatars", "code": "```go\ntempl GroupOfAvatars() {\n\t\u003cdiv class=\"flex justify-center items-center space-x-4 pt-4\"\u003e\n\t\t@components.AvatarGroup(\"-space-x-8\") {\n\t\t\t@components.Avatar(components.AvatarProps{\n\t\t\t\tContainerClass: \"rounded-full w-12\", Source: \"/static/images/avatar.jpg\"})\n\t\t\t@components.Avatar(components.AvatarProps{\n\t\t\t\tContainerClass: \"rounded-full w-12\", Source: \"/static/images/avatar.jpg\"})\n\t\t\t@components.Avatar(components.AvatarProps{\n\t\t\t\tContainerClass: \"rounded-full w-12\", Source: \"/static/images/avatar.jpg\"})\n\t\t\t@components.Avatar(components.AvatarProps{\n\t\t\t\tContainerClass: \"rounded-full w-12\", Source: \"/static/images/avatar.jpg\"})\n\t\t\t@components.Avatar(components.AvatarProps{\n\t\t\t\tContainerClass: \"rounded-full w-12\", Source: \"/static/images/avatar.jpg\"})\n\t\t}\n\t\u003c/div\u003e\n}\n\n```", "title": "Avatar group" }, { "name": "OnlineAndOffline", "code": "```go\ntempl OnlineAndOffline() {\n\t\u003cdiv class=\"flex justify-center items-center space-x-4 pt-4\"\u003e\n\t\t@components.Avatar(components.AvatarProps{\n\t\t\tAvatarClass: \"avatar-online\",\n\t\t\tContainerClass: \"rounded-full w-12\",\n\t\t\tSource: \"/static/images/avatar.jpg\",\n\t\t})\n\t\t@components.Avatar(components.AvatarProps{\n\t\t\tAvatarClass: \"avatar-offline\",\n\t\t\tContainerClass: \"rounded-full w-12\",\n\t\t\tSource: \"/static/images/avatar.jpg\",\n\t\t})\n\t\u003c/div\u003e\n}\n```", "title": "Avatar with online/offline indicator" } ], "banner": [ { "name": "BannerExample", "code": "```go\ntempl BannerExample() {\n\t@components.Banner(components.BannerProps{\n\t\tTitle: basicBannerTitle(),\n\t\tDescription: `Lorem ipsum dolor sit amet consectetur adipisicing elit. \nSapiente iure non, quia perspiciatis sed temporibus quos nihil, voluptatibus tempore \nplaceat ipsa est facilis, nobis illum in magni illo neque libero.`,\n\t}) {\n\t\t\u003ca class=\"btn btn-primary\"\u003e\n\t\t\tGet started\n\t\t\u003c/a\u003e\n\t\t\u003ca class=\"btn btn-neutral\"\u003e\n\t\t\tLearn more\n\t\t\u003c/a\u003e\n\t}\n}\n\ntempl basicBannerTitle() {\n\t\u003ch1 class=\"text-3xl font-extrabold sm:text-5xl\"\u003e\n\t\tLorem ipsum dolor.\n\t\t\u003cstrong class=\"font-extrabold text-primary sm:block\"\u003eSit amet consectetur. \u003c/strong\u003e\n\t\u003c/h1\u003e\n}\n```" } ], "breadcrumbs": [ { "name": "BreadcrumbsExample", "code": "```go\ntempl BreadcrumbsExample() {\n\t\u003cdiv class=\"flex justify-center items-center pt-4\"\u003e\n\t\t@components.Breadcrumbs(\n\t\t\tcomponents.BreadcrumbsProps{\n\t\t\t\t{\n\t\t\t\t\tLabel: \"Laptops\",\n\t\t\t\t\tAttrs: templ.Attributes{\"href\": \"/laptops\", \"class\": \"link\"},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tLabel: \"Macbooks\",\n\t\t\t\t\tAttrs: templ.Attributes{\"href\": \"/laptops/macbooks\", \"class\": \"link\"},\n\t\t\t\t},\n\t\t\t\t{Label: \"Macbook Pro 14\"},\n\t\t\t},\n\t\t)\n\t\u003c/div\u003e\n}\n```" } ], "card": [ { "name": "BasicCard", "code": "```go\ntempl BasicCard() {\n\t\u003cdiv class=\"flex justify-center items-center py-4\"\u003e\n\t\t@components.Card(\n\t\t\tcomponents.CardProps{\n\t\t\t\tTitle: \"This is a card\",\n\t\t\t\tContent: \"And this is the card's content.\",\n\t\t\t\tClass: \"bg-base-100 w-96 shadow-xl\",\n\t\t\t},\n\t\t)\n\t\u003c/div\u003e\n}\n\n```", "title": "Basic card" }, { "name": "BasicCardWithImage", "code": "```go\ntempl BasicCardWithImage() {\n\t\u003cdiv class=\"flex justify-center items-center py-4\"\u003e\n\t\t@components.Card(\n\t\t\tcomponents.CardProps{\n\t\t\t\tTitle: \"Card with image\",\n\t\t\t\tContent: \"Card with image content\",\n\t\t\t\tSource: \"/static/images/avatar.jpg\",\n\t\t\t\tAlt: \"avatar image\",\n\t\t\t\tClass: \"card-bordered bg-base-100 w-96 shadow-xl\",\n\t\t\t},\n\t\t) {\n\t\t\t\u003cbutton class=\"btn btn-sm btn-primary\"\u003eContact\u003c/button\u003e\n\t\t}\n\t\u003c/div\u003e\n}\n```", "title": "Card with image" } ], "carousel": [ { "name": "CarouselExample", "code": "```go\ntempl CarouselExample() {\n\t\u003cdiv class=\"max-h-80 pt-4 max-w-screen-md\"\u003e\n\t\t@components.Carousel(\n\t\t\tcomponents.CarouselProps{\n\t\t\t\t{Source: \"/static/images/avatar.jpg\", Alt: \"avatar image\"},\n\t\t\t\t{Source: \"/static/images/avatar.jpg\", Alt: \"avatar image\"},\n\t\t\t\t{Source: \"/static/images/avatar.jpg\", Alt: \"avatar image\"},\n\t\t\t\t{Source: \"/static/images/avatar.jpg\", Alt: \"avatar image\"},\n\t\t\t\t{Source: \"/static/images/avatar.jpg\", Alt: \"avatar image\"},\n\t\t\t\t{Source: \"/static/images/avatar.jpg\", Alt: \"avatar image\"},\n\t\t\t},\n\t\t)\n\t\u003c/div\u003e\n}\n```" } ], "chat": [ { "name": "BasicChat", "code": "```go\ntempl BasicChat() {\n\t\u003cdiv class=\"w-full max-w-screen-sm rounded-box mx-auto border border-base-content/30 p-4\"\u003e\n\t\t@components.Chat(components.ChatProps{\n\t\t\t{\n\t\t\t\tAvatarURL: \"/static/images/avatar.jpg\",\n\t\t\t\tSender: \"Me\",\n\t\t\t\tTime: time.Now().UTC().Add(-4 * time.Minute).Format(\"15:04:05\"),\n\t\t\t\tMessage: `I started learning how to cook more dishes from scratch, \nand it's been way more satisfying than I expected. Last night, I made homemade pasta!`,\n\t\t\t\tFooter: \"✓✓\",\n\t\t\t\tLocation: \"start\",\n\t\t\t\tClass: \"chat-bubble-primary\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tAvatarURL: \"/static/images/avatar-reverse.jpg\",\n\t\t\t\tSender: \"Myself\",\n\t\t\t\tTime: time.Now().UTC().Add(-2 * time.Minute).Format(\"15:04:05\"),\n\t\t\t\tMessage: `That's awesome! Homemade pasta is no joke—it's a workout, \ntoo! Did you use a pasta machine, or go for the classic rolling pin method?`,\n\t\t\t\tFooter: \"✓✓\",\n\t\t\t\tLocation: \"end\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tAvatarURL: \"/static/images/avatar.jpg\",\n\t\t\t\tSender: \"Me\",\n\t\t\t\tTime: time.Now().UTC().Add(-1 * time.Minute).Format(\"15:04:05\"),\n\t\t\t\tMessage: `Went old-school with the rolling pin! Took forever, but it \nwas worth it. I made a simple marinara sauce to go with it, and honestly, it was better \nthan what I usually order.`,\n\t\t\t\tFooter: \"✓✓\",\n\t\t\t\tLocation: \"start\",\n\t\t\t\tClass: \"chat-bubble-primary\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tAvatarURL: \"/static/images/avatar-reverse.jpg\",\n\t\t\t\tSender: \"Myself\",\n\t\t\t\tTime: time.Now().UTC().Format(\"15:04:05\"),\n\t\t\t\tMessage: `Love it! Next up, you've got to try ravioli. It's a bit \nmore work, but filling them with something like ricotta and spinach makes it feel \nsuper fancy. You'll impress everyone!`,\n\t\t\t\tFooter: \"✓✓\",\n\t\t\t\tLocation: \"end\",\n\t\t\t},\n\t\t})\n\t\u003c/div\u003e\n}\n```" } ], "checkbox": [ { "name": "DifferentSizeCheckboxes", "code": "```go\ntempl DifferentSizeCheckboxes() {\n\t\u003cdiv class=\"flex flex-col space-y-4 mx-auto pt-4\"\u003e\n\t\t@components.Checkbox(\n\t\t\tcomponents.CheckboxProps{\n\t\t\t\tBefore: \"Remember me\",\n\t\t\t\tName: \"remember_me\",\n\t\t\t\tChecked: false,\n\t\t\t\tSize: \"xs\",\n\t\t\t},\n\t\t)\n\t\t@components.Checkbox(\n\t\t\tcomponents.CheckboxProps{\n\t\t\t\tBefore: \"Remember me\",\n\t\t\t\tName: \"remember_me\",\n\t\t\t\tChecked: false,\n\t\t\t\tSize: \"sm\",\n\t\t\t},\n\t\t)\n\t\t@components.Checkbox(\n\t\t\tcomponents.CheckboxProps{\n\t\t\t\tBefore: \"Remember me\",\n\t\t\t\tName: \"remember_me\",\n\t\t\t\tChecked: false,\n\t\t\t},\n\t\t)\n\t\t@components.Checkbox(\n\t\t\tcomponents.CheckboxProps{\n\t\t\t\tBefore: \"Remember me\",\n\t\t\t\tName: \"remember_me\",\n\t\t\t\tChecked: false,\n\t\t\t\tSize: \"lg\",\n\t\t\t},\n\t\t)\n\t\t@components.Checkbox(\n\t\t\tcomponents.CheckboxProps{\n\t\t\t\tBefore: \"Remember me\",\n\t\t\t\tName: \"remember_me\",\n\t\t\t\tChecked: false,\n\t\t\t\tSize: \"xl\",\n\t\t\t},\n\t\t)\n\t\u003c/div\u003e\n}\n\n```", "title": "Different size checkboxes" }, { "name": "PrimaryCheckbox", "code": "```go\ntempl PrimaryCheckbox() {\n\t\u003cdiv class=\"mx-auto pt-4\"\u003e\n\t\t@components.Checkbox(\n\t\t\tcomponents.CheckboxProps{\n\t\t\t\tAfter: \"Remember me\",\n\t\t\t\tName: \"remember_me\",\n\t\t\t\tChecked: true,\n\t\t\t\tClass: \"checkbox-primary\",\n\t\t\t},\n\t\t)\n\t\u003c/div\u003e\n}\n```", "title": "Primary color checkbox with label after" } ], "collapse": [ { "name": "CollapseExample", "code": "```go\ntempl CollapseExample() {\n\t\u003cdiv class=\"flex justify-center items-center pt-4\"\u003e\n\t\t@components.Collapse(\n\t\t\tcomponents.CollapseProps{\n\t\t\t\tTitle: \"Click me to show/hide content\",\n\t\t\t\tTitleClass: \"text-xl font-medium\",\n\t\t\t\tClass: \"collapse-plus bg-base-300\",\n\t\t\t\tContentClass: \"bg-base-200\",\n\t\t\t}) {\n\t\t\t\u003cdiv class=\"pt-4\"\u003e\n\t\t\t\t\u003cp\u003eCollapse content\u003c/p\u003e\n\t\t\t\u003c/div\u003e\n\t\t}\n\t\u003c/div\u003e\n}\n```", "title": "Basic collapse" } ], "combobox": [ { "name": "BasicCombobox", "code": "```go\ntempl BasicCombobox() {\n\t\u003cdiv class=\"h-96\"\u003e\n\t\t\u003cform hx-post=\"/combobox-submit/example_combo\" class=\"space-x-4\"\u003e\n\t\t\t@components.Combobox(components.ComboboxProps{\n\t\t\t\tName: \"example_combo\",\n\t\t\t\tLabel: \"Example\",\n\t\t\t\tURL: \"/combobox/%s/%s\",\n\t\t\t\tOptions: []string{\n\t\t\t\t\t\"Thing 1\", \"Thing 2\", \"Thing 3\", \"Thing 4\", \"Thing 5\", \"Thing 6\", \"Thing 7\",\n\t\t\t\t},\n\t\t\t})\n\t\t\t\u003cbutton type=\"submit\" class=\"btn btn-sm btn-primary\"\u003eSubmit\u003c/button\u003e\n\t\t\t\u003cscript\u003e\n\t\t\t\t// update the form to only include half of the input values\n\t\t\t\t// corresponding to the combobox's name since it will contain enabled checkbox values as well\n\t\t\t\t((form) =\u003e {\n\t\t\t\t\tform.addEventListener(\"htmx:configRequest\", (evt) =\u003e {\n\t\t\t\t\t\tlet values = evt.detail.parameters[\"example_combo\"]\n\t\t\t\t\t\tif (values !== undefined) {\n\t\t\t\t\t\t\tevt.detail.parameters[\"example_combo\"] = values.slice(0, Math.floor(values.length / 2))\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t})(document.currentScript.parentElement)\n\t\t\t\u003c/script\u003e\n\t\t\u003c/form\u003e\n\t\u003c/div\u003e\n}\n```", "handler": "```go\nfunc PostCombobox(c echo.Context) error {\n\tname := c.Param(\"name\")\n\tvalue := c.Param(\"value\")\n\n\treturn render(c, http.StatusOK, components.ComboBadge(name, value))\n}\n\nfunc PostComboboxSubmit(c echo.Context) error {\n\turlValues, err := c.FormParams()\n\tif err != nil {\n\t\treturn newErrorToast(http.StatusUnprocessableEntity, \"unable to parse form data\")\n\t}\n\n\tname := c.Param(\"name\")\n\tcomboboxPostData := urlValues[name]\n\n\treturn renderInfoFade(c, http.StatusOK, comboboxPostData)\n}\n\n```" } ], "countdown": [ { "name": "CountdownFullExample", "code": "```go\ntempl CountdownFullExample() {\n\t\u003cdiv class=\"flex justify-center items-center pt-4\"\u003e\n\t\t@components.Countdown(\n\t\t\tcomponents.CountdownProps{\n\t\t\t\tExpires: time.Now().UTC().Add(25*time.Hour + 10*time.Second),\n\t\t\t\tDays: true,\n\t\t\t\tHours: true,\n\t\t\t\tSeconds: true,\n\t\t\t\tMinutes: true,\n\t\t\t})\n\t\u003c/div\u003e\n}\n\n```" }, { "name": "CountdownHoursExample", "code": "```go\ntempl CountdownHoursExample() {\n\t\u003cdiv class=\"flex justify-center items-center pt-4\"\u003e\n\t\t@components.Countdown(\n\t\t\tcomponents.CountdownProps{\n\t\t\t\tExpires: time.Now().UTC().Add(10*time.Hour + 10*time.Second),\n\t\t\t\tHours: true,\n\t\t\t\tSeconds: true,\n\t\t\t\tMinutes: true,\n\t\t\t})\n\t\u003c/div\u003e\n}\n\n```" }, { "name": "CountdownMinutesExample", "code": "```go\ntempl CountdownMinutesExample() {\n\t\u003cdiv class=\"flex justify-center items-center pt-4\"\u003e\n\t\t@components.Countdown(\n\t\t\tcomponents.CountdownProps{\n\t\t\t\tExpires: time.Now().UTC().Add(15*time.Minute + 10*time.Second),\n\t\t\t\tSeconds: true,\n\t\t\t\tMinutes: true,\n\t\t\t})\n\t\u003c/div\u003e\n}\n\n```" }, { "name": "CountdownSecondsExample", "code": "```go\ntempl CountdownSecondsExample() {\n\t\u003cdiv class=\"flex justify-center items-center pt-4\"\u003e\n\t\t@components.Countdown(\n\t\t\tcomponents.CountdownProps{\n\t\t\t\tExpires: time.Now().UTC().Add(20 * time.Second),\n\t\t\t\tSeconds: true,\n\t\t\t})\n\t\u003c/div\u003e\n}\n```" } ], "date_picker": [ { "name": "BasicDatePicker", "code": "```go\ntempl BasicDatePicker() {\n\t{{ now := time.Now().UTC() }}\n\t@components.DatePicker(\n\t\tcomponents.DatePickerProps{\n\t\t\tYear: now.Year(),\n\t\t\tMonth: int(now.Month()),\n\t\t\tSelected: now,\n\t\t\tStartOfWeek: time.Monday,\n\t\t},\n\t)\n}\n```", "handler": "```go\nfunc GetDatePicker(c echo.Context) error {\n\tyearStr := c.QueryParam(\"year\")\n\tmonthStr := c.QueryParam(\"month\")\n\n\tyear, yErr := strconv.Atoi(yearStr)\n\tmonth, mErr := strconv.Atoi(monthStr)\n\tif month \u003e 12 {\n\t\tyear += 1\n\t\tmonth = 1\n\t}\n\tif month \u003c 1 {\n\t\tyear -= 1\n\t\tmonth = 12\n\t}\n\n\tif yErr != nil || mErr != nil {\n\t\treturn newErrorToast(http.StatusUnprocessableEntity, \"Invalid year or month\")\n\t}\n\n\tvar selected time.Time\n\tnow := time.Now().UTC()\n\tif year == now.Year() \u0026\u0026 time.Month(month) == now.Month() {\n\t\tselected = now\n\t}\n\n\treturn render(\n\t\tc,\n\t\thttp.StatusOK,\n\t\tcomponents.DatePicker(\n\t\t\tcomponents.DatePickerProps{\n\t\t\t\tYear: year,\n\t\t\t\tMonth: month,\n\t\t\t\tSelected: selected,\n\t\t\t\tStartOfWeek: time.Monday,\n\t\t\t}),\n\t)\n}\n\nfunc PostDatePickerSelectDay(c echo.Context) error {\n\tvalue := c.FormValue(\"date\")\n\td, err := time.Parse(\"2006-01-02\", value)\n\tif err != nil {\n\t\treturn newErrorToast(\n\t\t\thttp.StatusUnprocessableEntity,\n\t\t\tfmt.Sprintf(\"Invalid date '%s'\", value),\n\t\t)\n\t}\n\n\treturn render(c, http.StatusOK, components.DatePickerInput(d))\n}\n\nfunc GetDatePickerMonthPicker(c echo.Context) error {\n\tyearStr := c.QueryParam(\"year\")\n\n\tyear, yErr := strconv.Atoi(yearStr)\n\tif yErr != nil {\n\t\treturn newErrorToast(http.StatusUnprocessableEntity, \"Invalid year\")\n\t}\n\n\tdpp := components.DatePickerProps{\n\t\tYear: year,\n\t}\n\n\treturn render(c, http.StatusOK, components.DatePickerMonthPicker(dpp))\n}\n\nfunc GetDatePickerYearPicker(c echo.Context) error {\n\tyearStr := c.QueryParam(\"year\")\n\n\tyear, yErr := strconv.Atoi(yearStr)\n\tif yErr != nil {\n\t\treturn newErrorToast(http.StatusUnprocessableEntity, \"Invalid year\")\n\t}\n\n\tdpp := components.DatePickerProps{\n\t\tYear: year,\n\t}\n\n\treturn render(c, http.StatusOK, components.DatePickerYearPicker(dpp))\n}\n\n```" } ], "diff": [ { "name": "ImageDiff", "code": "```go\ntempl ImageDiff() {\n\t\u003cdiv class=\"flex mx-auto pt-4 min-h-[240px] sm:min-h-[300px] lg:min-h-[480px]\"\u003e\n\t\t@components.Diff(\n\t\t\tcomponents.DiffProps{\n\t\t\t\tWidth: 16,\n\t\t\t\tHeight: 19,\n\t\t\t\tImage1: components.DiffImage{\n\t\t\t\t\tSource: \"/static/images/diff1.png\", Alt: \"diff image 1\",\n\t\t\t\t},\n\t\t\t\tImage2: components.DiffImage{\n\t\t\t\t\tSource: \"/static/images/diff2.png\", Alt: \"diff image 2\",\n\t\t\t\t},\n\t\t\t},\n\t\t)\n\t\u003c/div\u003e\n}\n```" } ], "drawer": [ { "name": "BasicDrawer", "code": "```go\ntempl BasicDrawer() {\n\t\u003cdiv class=\"w-full h-[400px] flex flex-wrap items-center justify-center overflow-x-hidden\"\u003e\n\t\t@DrawerPreview(DrawerExampleToggle(), DrawerExampleMenu())\n\t\u003c/div\u003e\n}\n\ntempl DrawerPreview(toggle templ.Component, sidebar templ.Component) {\n\t\u003cdiv class=\"drawer h-[400px]\"\u003e\n\t\t\u003cinput id=\"my-drawer\" type=\"checkbox\" class=\"drawer-toggle\"/\u003e\n\t\t\u003cdiv class=\"flex flex-col items-center justify-center drawer-content\"\u003e\n\t\t\t{ children... }\n\t\t\t\u003clabel for=\"my-drawer\" class=\"btn btn-primary drawer-button\"\u003e\n\t\t\t\t@toggle\n\t\t\t\u003c/label\u003e\n\t\t\u003c/div\u003e\n\t\t\u003cdiv class=\"drawer-side h-full absolute\"\u003e\n\t\t\t\u003clabel for=\"my-drawer\" aria-label=\"close sidebar\" class=\"drawer-overlay\"\u003e\u003c/label\u003e\n\t\t\t\u003cul class=\"menu bg-base-200 text-base-content min-h-full w-60 md:w-80 p-4\"\u003e\n\t\t\t\t@sidebar\n\t\t\t\u003c/ul\u003e\n\t\t\u003c/div\u003e\n\t\u003c/div\u003e\n}\n\ntempl DrawerExampleToggle() {\n\t\u003cspan\u003eClick me\u003c/span\u003e\n}\n\ntempl DrawerExampleMenu() {\n\t@components.MenuItem(components.MenuItemProps{Label: \"Section 1\", Attrs: templ.Attributes{\"class\": \"menu-title\"}})\n\t@components.MenuItem(components.MenuItemProps{Label: \"Section 2\", Attrs: templ.Attributes{\"class\": \"menu-title\"}}) {\n\t\t@components.MenuItem(components.MenuItemProps{Label: \"2.1\"}) {\n\t\t\t@components.MenuItem(components.MenuItemProps{Label: \"2.1.1\"})\n\t\t\t@components.MenuItem(components.MenuItemProps{Label: \"2.1.2\"})\n\t\t}\n\t\t@components.MenuItem(components.MenuItemProps{Label: \"2.2\"}) {\n\t\t\t@components.MenuItem(components.MenuItemProps{Label: \"2.2.1\"})\n\t\t}\n\t\t@components.MenuItem(components.MenuItemProps{Label: \"2.3\"})\n\t}\n\t@components.MenuItem(components.MenuItemProps{Label: \"Section 3\", Attrs: templ.Attributes{\"class\": \"menu-title\"}}) {\n\t\t@components.MenuItem(components.MenuItemProps{Label: \"3.1\"})\n\t}\n}\n```" } ], "dropdown": [ { "name": "BasicDropdown", "code": "```go\ntempl BasicDropdown() {\n\t\u003cdiv class=\"h-48\"\u003e\n\t\t@components.Dropdown(\n\t\t\tcomponents.DropdownProps{\n\t\t\t\tLabel: \"Dropdown label\",\n\t\t\t\tItems: []components.DropdownItem{\n\t\t\t\t\t{Label: \"Item 1\"},\n\t\t\t\t\t{Label: \"Item 2\"},\n\t\t\t\t\t{Label: \"Item 3\"},\n\t\t\t\t},\n\t\t\t\tListClass: \"bg-base-200 rounded-box z-50 w-52 p-2 shadow-sm\",\n\t\t\t},\n\t\t)\n\t\u003c/div\u003e\n}\n```" } ], "fab": [ { "name": "BasicFAB", "code": "```go\ntempl BasicFAB() {\n\t\u003cdiv class=\"h-68 relative\"\u003e\n\t\t@components.FAB(\n\t\t\tcomponents.FABProps{\n\t\t\t\tClass: \"absolute z-1\", // !! preview purpose only\n\t\t\t\tToggle: \"F\",\n\t\t\t\tToggleClass: \"btn-lg btn-circle\",\n\t\t\t}) {\n\t\t\t\u003cbutton class=\"btn btn-lg btn-circle\"\u003eA\u003c/button\u003e\n\t\t\t\u003cbutton class=\"btn btn-lg btn-circle\"\u003eB\u003c/button\u003e\n\t\t\t\u003cbutton class=\"btn btn-lg btn-circle\"\u003eC\u003c/button\u003e\n\t\t}\n\t\u003c/div\u003e\n}\n\n```" }, { "name": "FABWithSVGButtons", "code": "```go\ntempl FABWithSVGButtons() {\n\t\u003cdiv class=\"h-68 relative\"\u003e\n\t\t@components.FAB(\n\t\t\tcomponents.FABProps{\n\t\t\t\tClass: \"absolute z-1\", // !! preview purpose only\n\t\t\t\tToggle: fabSVGToggle(),\n\t\t\t\tToggleClass: \"btn-lg btn-circle\",\n\t\t\t}) {\n\t\t\t@fabSVGVoiceButton()\n\t\t\t@fabSVGImageButton()\n\t\t\t@fabSVGCameraButton()\n\t\t}\n\t\u003c/div\u003e\n}\n\ntempl fabSVGToggle() {\n\t\u003csvg\n\t\taria-label=\"New\"\n\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\tfill=\"none\"\n\t\tviewBox=\"0 0 24 24\"\n\t\tstroke-width=\"2\"\n\t\tstroke=\"currentColor\"\n\t\tclass=\"size-6\"\n\t\u003e\n\t\t\u003cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 4.5v15m7.5-7.5h-15\"\u003e\u003c/path\u003e\n\t\u003c/svg\u003e\n}\n\ntempl fabSVGVoiceButton() {\n\t\u003cbutton class=\"btn btn-lg btn-circle\"\u003e\n\t\t\u003csvg\n\t\t\taria-label=\"Camera\"\n\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\tfill=\"none\"\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tstroke-width=\"1.5\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tclass=\"size-6\"\n\t\t\u003e\n\t\t\t\u003cpath\n\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\td=\"M6.827 6.175A2.31 2.31 0 0 1 5.186 7.23c-.38.054-.757.112-1.134.175C2.999 7.58 2.25 8.507 2.25 9.574V18a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18V9.574c0-1.067-.75-1.994-1.802-2.169a47.865 47.865 0 0 0-1.134-.175 2.31 2.31 0 0 1-1.64-1.055l-.822-1.316a2.192 2.192 0 0 0-1.736-1.039 48.774 48.774 0 0 0-5.232 0 2.192 2.192 0 0 0-1.736 1.039l-.821 1.316Z\"\n\t\t\t\u003e\u003c/path\u003e\n\t\t\t\u003cpath\n\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\td=\"M16.5 12.75a4.5 4.5 0 1 1-9 0 4.5 4.5 0 0 1 9 0ZM18.75 10.5h.008v.008h-.008V10.5Z\"\n\t\t\t\u003e\u003c/path\u003e\n\t\t\u003c/svg\u003e\n\t\u003c/button\u003e\n}\n\ntempl fabSVGImageButton() {\n\t\u003cbutton class=\"btn btn-lg btn-circle\"\u003e\n\t\t\u003csvg\n\t\t\taria-label=\"Gallery\"\n\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\tfill=\"none\"\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tstroke-width=\"1.5\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tclass=\"size-6\"\n\t\t\u003e\n\t\t\t\u003cpath\n\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\td=\"m2.25 15.75 5.159-5.159a2.25 2.25 0 0 1 3.182 0l5.159 5.159m-1.5-1.5 1.409-1.409a2.25 2.25 0 0 1 3.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 0 0 1.5-1.5V6a1.5 1.5 0 0 0-1.5-1.5H3.75A1.5 1.5 0 0 0 2.25 6v12a1.5 1.5 0 0 0 1.5 1.5Zm10.5-11.25h.008v.008h-.008V8.25Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z\"\n\t\t\t\u003e\u003c/path\u003e\n\t\t\u003c/svg\u003e\n\t\u003c/button\u003e\n}\n\ntempl fabSVGCameraButton() {\n\t\u003cbutton class=\"btn btn-lg btn-circle\"\u003e\n\t\t\u003csvg\n\t\t\taria-label=\"Voice\"\n\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\tfill=\"none\"\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tstroke-width=\"1.5\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tclass=\"size-6\"\n\t\t\u003e\n\t\t\t\u003cpath\n\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\td=\"M12 18.75a6 6 0 0 0 6-6v-1.5m-6 7.5a6 6 0 0 1-6-6v-1.5m6 7.5v3.75m-3.75 0h7.5M12 15.75a3 3 0 0 1-3-3V4.5a3 3 0 1 1 6 0v8.25a3 3 0 0 1-3 3Z\"\n\t\t\t\u003e\u003c/path\u003e\n\t\t\u003c/svg\u003e\n\t\u003c/button\u003e\n}\n\n```", "title": "FAB with SVGs as toggle and content buttons" }, { "name": "FABFlowerExample", "code": "```go\ntempl FABFlowerExample() {\n\t\u003cdiv class=\"h-68 relative\"\u003e\n\t\t@components.FAB(components.FABProps{\n\t\t\tClass: \"fab-flower absolute z-1\", // !! absolute \u0026 z-1 preview purpose only\n\t\t\tToggle: \"F\",\n\t\t\tToggleClass: \"btn btn-lg btn-circle\",\n\t\t\tMainAction: fabFlowerMainAction(),\n\t\t}) {\n\t\t\t\u003cbutton class=\"btn btn-lg btn-circle\"\u003eA\u003c/button\u003e\n\t\t\t\u003cbutton class=\"btn btn-lg btn-circle\"\u003eB\u003c/button\u003e\n\t\t\t\u003cbutton class=\"btn btn-lg btn-circle\"\u003eC\u003c/button\u003e\n\t\t\t\u003cbutton class=\"btn btn-lg btn-circle\"\u003eD\u003c/button\u003e\n\t\t}\n\t\u003c/div\u003e\n}\n\ntempl fabFlowerMainAction() {\n\t\u003cbutton class=\"btn btn-lg btn-circle btn-primary\"\u003eM\u003c/button\u003e\n}\n```", "title": "FAB flower" } ], "features": [ { "name": "FeaturesExample", "code": "```go\ntempl FeaturesExample() {\n\t@components.Features(\n\t\tcomponents.FeaturesProps{\n\t\t\tTitle: \"Discover our exclusive features\",\n\t\t\tFeatures: []components.FeatureProps{\n\t\t\t\t{\n\t\t\t\t\tIcon: CustomizationIcon(),\n\t\t\t\t\tTitle: \"Customization\",\n\t\t\t\t\tDescription: \"Tailor our product to suit your needs.\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tIcon: SecurityIcon(),\n\t\t\t\t\tTitle: \"Security\",\n\t\t\t\t\tDescription: \"Your data is protected by the latest security measures.\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tIcon: SupportIcon(),\n\t\t\t\t\tTitle: \"Support\",\n\t\t\t\t\tDescription: \"24/7 customer support for all your inquiries.\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tIcon: PerformanceIcon(),\n\t\t\t\t\tTitle: \"Performance\",\n\t\t\t\t\tDescription: \"Experience blazing-fast performance with our product.\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tIcon: GlobalReachIcon(),\n\t\t\t\t\tTitle: \"Global reach\",\n\t\t\t\t\tDescription: `Tailor our product to suit your needs. Expand your reach \nwith our global network.`,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tIcon: CommunicationIcon(),\n\t\t\t\t\tTitle: \"Communication\",\n\t\t\t\t\tDescription: \"Seamless communication for your team.\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t)\n}\n\ntempl CustomizationIcon() {\n\t\u003csvg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" class=\"w-8 mb-6 inline-block\" viewBox=\"0 0 32 32\"\u003e\n\t\t\u003cpath d=\"M28.068 12h-.128a.934.934 0 0 1-.864-.6.924.924 0 0 1 .2-1.01l.091-.091a2.938 2.938 0 0 0 0-4.147l-1.511-1.51a2.935 2.935 0 0 0-4.146 0l-.091.091A.956.956 0 0 1 20 4.061v-.129A2.935 2.935 0 0 0 17.068 1h-2.136A2.935 2.935 0 0 0 12 3.932v.129a.956.956 0 0 1-1.614.668l-.086-.091a2.935 2.935 0 0 0-4.146 0l-1.516 1.51a2.938 2.938 0 0 0 0 4.147l.091.091a.935.935 0 0 1 .185 1.035.924.924 0 0 1-.854.579h-.128A2.935 2.935 0 0 0 1 14.932v2.136A2.935 2.935 0 0 0 3.932 20h.128a.934.934 0 0 1 .864.6.924.924 0 0 1-.2 1.01l-.091.091a2.938 2.938 0 0 0 0 4.147l1.51 1.509a2.934 2.934 0 0 0 4.147 0l.091-.091a.936.936 0 0 1 1.035-.185.922.922 0 0 1 .579.853v.129A2.935 2.935 0 0 0 14.932 31h2.136A2.935 2.935 0 0 0 20 28.068v-.129a.956.956 0 0 1 1.614-.668l.091.091a2.935 2.935 0 0 0 4.146 0l1.511-1.509a2.938 2.938 0 0 0 0-4.147l-.091-.091a.935.935 0 0 1-.185-1.035.924.924 0 0 1 .854-.58h.128A2.935 2.935 0 0 0 31 17.068v-2.136A2.935 2.935 0 0 0 28.068 12ZM29 17.068a.933.933 0 0 1-.932.932h-.128a2.956 2.956 0 0 0-2.083 5.028l.09.091a.934.934 0 0 1 0 1.319l-1.511 1.509a.932.932 0 0 1-1.318 0l-.09-.091A2.957 2.957 0 0 0 18 27.939v.129a.933.933 0 0 1-.932.932h-2.136a.933.933 0 0 1-.932-.932v-.129a2.951 2.951 0 0 0-5.028-2.082l-.091.091a.934.934 0 0 1-1.318 0l-1.51-1.509a.934.934 0 0 1 0-1.319l.091-.091A2.956 2.956 0 0 0 4.06 18h-.128A.933.933 0 0 1 3 17.068v-2.136A.933.933 0 0 1 3.932 14h.128a2.956 2.956 0 0 0 2.083-5.028l-.09-.091a.933.933 0 0 1 0-1.318l1.51-1.511a.932.932 0 0 1 1.318 0l.09.091A2.957 2.957 0 0 0 14 4.061v-.129A.933.933 0 0 1 14.932 3h2.136a.933.933 0 0 1 .932.932v.129a2.956 2.956 0 0 0 5.028 2.082l.091-.091a.932.932 0 0 1 1.318 0l1.51 1.511a.933.933 0 0 1 0 1.318l-.091.091A2.956 2.956 0 0 0 27.94 14h.128a.933.933 0 0 1 .932.932Z\" data-original=\"#000000\"\u003e\u003c/path\u003e\n\t\t\u003cpath d=\"M16 9a7 7 0 1 0 7 7 7.008 7.008 0 0 0-7-7Zm0 12a5 5 0 1 1 5-5 5.006 5.006 0 0 1-5 5Z\" data-original=\"#000000\"\u003e\u003c/path\u003e\n\t\u003c/svg\u003e\n}\n\ntempl SecurityIcon() {\n\t\u003csvg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" class=\"w-8 mb-6 inline-block\" viewBox=\"0 0 682.667 682.667\"\u003e\n\t\t\u003cdefs\u003e\n\t\t\t\u003cclipPath id=\"a\" clipPathUnits=\"userSpaceOnUse\"\u003e\n\t\t\t\t\u003cpath d=\"M0 512h512V0H0Z\" data-original=\"#000000\"\u003e\u003c/path\u003e\n\t\t\t\u003c/clipPath\u003e\n\t\t\u003c/defs\u003e\n\t\t\u003cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" stroke-width=\"40\" clip-path=\"url(#a)\" transform=\"matrix(1.33 0 0 -1.33 0 682.667)\"\u003e\n\t\t\t\u003cpath d=\"M256 492 60 410.623v-98.925C60 183.674 137.469 68.38 256 20c118.53 48.38 196 163.674 196 291.698v98.925z\" data-original=\"#000000\"\u003e\u003c/path\u003e\n\t\t\t\u003cpath d=\"M178 271.894 233.894 216 334 316.105\" data-original=\"#000000\"\u003e\u003c/path\u003e\n\t\t\u003c/g\u003e\n\t\u003c/svg\u003e\n}\n\ntempl SupportIcon() {\n\t\u003csvg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" class=\"w-8 mb-6 inline-block\" viewBox=\"0 0 512.001 512.001\"\u003e\n\t\t\u003cpath d=\"M271.029 0c-33.091 0-61 27.909-61 61s27.909 61 61 61 60-27.909 60-61-26.909-61-60-61zm66.592 122c-16.485 18.279-40.096 30-66.592 30-26.496 0-51.107-11.721-67.592-30-14.392 15.959-23.408 36.866-23.408 60v15c0 8.291 6.709 15 15 15h151c8.291 0 15-6.709 15-15v-15c0-23.134-9.016-44.041-23.408-60zM144.946 460.404 68.505 307.149c-7.381-14.799-25.345-20.834-40.162-13.493l-19.979 9.897c-7.439 3.689-10.466 12.73-6.753 20.156l90 180c3.701 7.423 12.704 10.377 20.083 6.738l19.722-9.771c14.875-7.368 20.938-25.417 13.53-40.272zM499.73 247.7c-12.301-9-29.401-7.2-39.6 3.9l-82 100.8c-5.7 6-16.5 9.6-22.2 9.6h-69.901c-8.401 0-15-6.599-15-15s6.599-15 15-15h60c16.5 0 30-13.5 30-30s-13.5-30-30-30h-78.6c-7.476 0-11.204-4.741-17.1-9.901-23.209-20.885-57.949-30.947-93.119-22.795-19.528 4.526-32.697 12.415-46.053 22.993l-.445-.361-21.696 19.094L174.28 452h171.749c28.2 0 55.201-13.5 72.001-36l87.999-126c9.9-13.201 7.2-32.399-6.299-42.3z\" data-original=\"#000000\"\u003e\u003c/path\u003e\n\t\u003c/svg\u003e\n}\n\ntempl PerformanceIcon() {\n\t\u003csvg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" class=\"w-8 mb-6 inline-block\" viewBox=\"0 0 24 24\"\u003e\n\t\t\u003cg fill-rule=\"evenodd\" clip-rule=\"evenodd\"\u003e\n\t\t\t\u003cpath d=\"M17.03 8.97a.75.75 0 0 1 0 1.06l-4.2 4.2a.75.75 0 0 1-1.154-.114l-1.093-1.639L8.03 15.03a.75.75 0 0 1-1.06-1.06l3.2-3.2a.75.75 0 0 1 1.154.114l1.093 1.639L15.97 8.97a.75.75 0 0 1 1.06 0z\" data-original=\"#000000\"\u003e\u003c/path\u003e\n\t\t\t\u003cpath d=\"M13.75 9.5a.75.75 0 0 1 .75-.75h2a.75.75 0 0 1 .75.75v2a.75.75 0 0 1-1.5 0v-1.25H14.5a.75.75 0 0 1-.75-.75z\" data-original=\"#000000\"\u003e\u003c/path\u003e\n\t\t\t\u003cpath d=\"M3.095 3.095C4.429 1.76 6.426 1.25 9 1.25h6c2.574 0 4.57.51 5.905 1.845C22.24 4.429 22.75 6.426 22.75 9v6c0 2.574-.51 4.57-1.845 5.905C19.571 22.24 17.574 22.75 15 22.75H9c-2.574 0-4.57-.51-5.905-1.845C1.76 19.571 1.25 17.574 1.25 15V9c0-2.574.51-4.57 1.845-5.905zm1.06 1.06C3.24 5.071 2.75 6.574 2.75 9v6c0 2.426.49 3.93 1.405 4.845.916.915 2.419 1.405 4.845 1.405h6c2.426 0 3.93-.49 4.845-1.405.915-.916 1.405-2.419 1.405-4.845V9c0-2.426-.49-3.93-1.405-4.845C18.929 3.24 17.426 2.75 15 2.75H9c-2.426 0-3.93.49-4.845 1.405z\" data-original=\"#000000\"\u003e\u003c/path\u003e\n\t\t\u003c/g\u003e\n\t\u003c/svg\u003e\n}\n\ntempl GlobalReachIcon() {\n\t\u003csvg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" class=\"w-8 mb-6 inline-block\" viewBox=\"0 0 504.69 504.69\"\u003e\n\t\t\u003cpath d=\"M252.343 262.673c-49.32 0-89.447-40.127-89.447-89.447s40.127-89.447 89.447-89.447 89.447 40.127 89.447 89.447-40.121 89.447-89.447 89.447zm0-158.235c-37.926 0-68.787 30.861-68.787 68.787s30.861 68.787 68.787 68.787 68.787-30.861 68.787-68.787-30.855-68.787-68.787-68.787z\" data-original=\"#000000\"\u003e\u003c/path\u003e\n\t\t\u003cpath d=\"M391.787 405.309c-5.645 0-10.253-4.54-10.325-10.201-.883-70.306-58.819-127.503-129.15-127.503-49.264 0-93.543 27.405-115.561 71.52-8.724 17.473-13.269 36.31-13.517 55.988-.072 5.702-4.757 10.273-10.459 10.201s-10.273-4.757-10.201-10.459c.289-22.814 5.568-44.667 15.691-64.955 25.541-51.164 76.907-82.95 134.047-82.95 81.581 0 148.788 66.349 149.81 147.905.072 5.702-4.494 10.392-10.201 10.459-.046-.005-.087-.005-.134-.005z\" data-original=\"#000000\"\u003e\u003c/path\u003e\n\t\t\u003cpath d=\"M252.343 463.751c-116.569 0-211.408-94.834-211.408-211.408 0-116.569 94.839-211.408 211.408-211.408 116.574 0 211.408 94.839 211.408 211.408 0 116.574-94.834 211.408-211.408 211.408zm0-402.156c-105.18 0-190.748 85.568-190.748 190.748s85.568 190.748 190.748 190.748 190.748-85.568 190.748-190.748S357.523 61.595 252.343 61.595zM71.827 90.07 14.356 32.599c-4.034-4.034-4.034-10.573 0-14.607 4.029-4.034 10.573-4.034 14.607 0l57.466 57.471c4.034 4.034 3.951 10.49 0 14.607-3.792 3.951-11.039 3.698-14.602 0z\" data-original=\"#000000\"\u003e\u003c/path\u003e\n\t\t\u003cpath d=\"M14.717 92.254a10.332 10.332 0 0 1-10.299-9.653L.023 15.751a10.317 10.317 0 0 1 2.929-7.908 10.2 10.2 0 0 1 7.851-3.089L77.56 7.796c5.697.258 10.108 5.093 9.85 10.79s-5.041 10.154-10.79 9.85l-55.224-2.521 3.641 55.327c.377 5.692-3.936 10.614-9.628 10.986a7.745 7.745 0 0 1-.692.026zm403.541-2.184c-4.256-3.796-4.034-10.573 0-14.607l58.116-58.116c4.034-4.034 10.573-4.034 14.607 0s4.034 10.573 0 14.607L432.864 90.07c-4.085 3.951-9.338 4.7-14.606 0z\" data-original=\"#000000\"\u003e\u003c/path\u003e\n\t\t\u003cpath d=\"M489.974 92.254a9.85 9.85 0 0 1-.687-.021c-5.697-.372-10.01-5.294-9.633-10.986l3.641-55.327-55.224 2.515c-5.511.238-10.526-4.147-10.79-9.85-.258-5.702 4.153-10.531 9.85-10.79l66.757-3.042c2.934-.134 5.79.992 7.851 3.089s3.12 4.974 2.929 7.908l-4.401 66.85c-.361 5.465-4.896 9.654-10.293 9.654zM11.711 489.339c-3.791-4.266-4.034-10.573 0-14.607l60.115-60.11c4.029-4.034 10.578-4.034 14.607 0 4.034 4.034 4.034 10.573 0 14.607l-60.115 60.11c-3.827 3.884-11.156 3.884-14.607 0z\" data-original=\"#000000\"\u003e\u003c/path\u003e\n\t\t\u003cpath d=\"M10.327 499.947a10.33 10.33 0 0 1-7.376-3.104 10.312 10.312 0 0 1-2.929-7.902l4.401-66.85c.372-5.697 5.191-10.036 10.986-9.633 5.692.377 10.005 5.294 9.628 10.986l-3.641 55.332 55.224-2.515c5.645-.191 10.531 4.153 10.79 9.85.258 5.697-4.153 10.526-9.85 10.79l-66.763 3.037c-.155.004-.31.009-.47.009zm465.639-13.01-57.708-57.708c-4.034-4.034-4.034-10.573 0-14.607s10.573-4.034 14.607 0l57.708 57.708c4.034 4.034 3.962 10.5 0 14.607-3.817 3.951-10.062 3.951-14.607 0z\" data-original=\"#000000\"\u003e\u003c/path\u003e\n\t\t\u003cpath d=\"M494.359 499.947c-.155 0-.315-.005-.47-.01l-66.757-3.042c-5.702-.263-10.108-5.088-9.85-10.79.263-5.702 5.113-9.984 10.79-9.85l55.219 2.515-3.641-55.332c-.372-5.692 3.941-10.609 9.633-10.986 5.625-.398 10.609 3.946 10.986 9.633l4.401 66.85a10.33 10.33 0 0 1-2.929 7.902 10.323 10.323 0 0 1-7.382 3.11z\" data-original=\"#000000\"\u003e\u003c/path\u003e\n\t\u003c/svg\u003e\n}\n\ntempl CommunicationIcon() {\n\t\u003csvg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" class=\"w-8 mb-6 inline-block\" viewBox=\"0 0 682.667 682.667\"\u003e\n\t\t\u003cdefs\u003e\n\t\t\t\u003cclipPath id=\"a\" clipPathUnits=\"userSpaceOnUse\"\u003e\n\t\t\t\t\u003cpath d=\"M0 512h512V0H0Z\" data-original=\"#000000\"\u003e\u003c/path\u003e\n\t\t\t\u003c/clipPath\u003e\n\t\t\u003c/defs\u003e\n\t\t\u003cg fill=\"none\" stroke=\"currentColor\" stroke-miterlimit=\"10\" stroke-width=\"30\" clip-path=\"url(#a)\" transform=\"matrix(1.33 0 0 -1.33 0 682.667)\"\u003e\n\t\t\t\u003cpath d=\"M226 15v60c0 16.568-13.432 30-30 30H76c-16.568 0-30-13.432-30-30V15Zm-45 165c0-24.853-20.147-45-45-45s-45 20.147-45 45 20.147 45 45 45 45-20.147 45-45ZM466 15v60c0 16.568-13.432 30-30 30H316c-16.568 0-30-13.432-30-30V15Zm-45 165c0-24.853-20.147-45-45-45s-45 20.147-45 45 20.147 45 45 45 45-20.147 45-45Zm-75 167v-50.294L286 347h-60.002L166 296.706V347h-15c-41.421 0-75 33.579-75 75s33.579 75 75 75h210c41.421 0 75-33.579 75-75s-33.579-75-75-75Zm-105 75h30m-90 0h30m90 0h30\" data-original=\"#000000\"\u003e\u003c/path\u003e\n\t\t\u003c/g\u003e\n\t\u003c/svg\u003e\n}\n```" } ], "file_input": [ { "name": "DifferentSizeFileInputs", "code": "```go\ntempl DifferentSizeFileInputs() {\n\t@components.FileInput(components.FileInputProps{\n\t\tLabel: \"File upload\",\n\t\tSize: \"xs\",\n\t})\n\t@components.FileInput(components.FileInputProps{\n\t\tLabel: \"File upload\",\n\t\tSize: \"sm\",\n\t})\n\t@components.FileInput(components.FileInputProps{\n\t\tLabel: \"File upload\",\n\t})\n\t@components.FileInput(components.FileInputProps{\n\t\tLabel: \"File upload\",\n\t\tSize: \"lg\",\n\t})\n\t@components.FileInput(components.FileInputProps{\n\t\tLabel: \"File upload\",\n\t\tSize: \"xl\",\n\t})\n}\n```", "title": "Different size file inputs" } ], "footer": [ { "name": "BasicFooterWithLinks", "code": "```go\ntempl BasicFooterWithLinks() {\n\t@components.Footer(\n\t\tcomponents.FooterProps{\n\t\t\tIcon: FooterCompanyInfoIconExample(),\n\t\t\tName: \"ACME Industries Ltd.\",\n\t\t\tDescription: \"Providing reliable tech since 1992\",\n\t\t\tAnchors: []components.AnchorProps{\n\t\t\t\t{LeftIcon: XIcon()},\n\t\t\t\t{LeftIcon: YoutubeIcon()},\n\t\t\t\t{LeftIcon: FacebookIcon()},\n\t\t\t},\n\t\t},\n\t) {\n\t\t@components.FooterNav(\n\t\t\t\"Services\",\n\t\t\t[]components.AnchorProps{\n\t\t\t\t{Label: \"Branding\"},\n\t\t\t\t{Label: \"Design\"},\n\t\t\t\t{Label: \"Marketing\"},\n\t\t\t\t{Label: \"Advertisement\"},\n\t\t\t},\n\t\t)\n\t\t@components.FooterNav(\n\t\t\t\"Company\",\n\t\t\t[]components.AnchorProps{\n\t\t\t\t{Label: \"About us\"},\n\t\t\t\t{Label: \"Contact\"},\n\t\t\t\t{Label: \"Jobs\"},\n\t\t\t\t{Label: \"Press kit\"},\n\t\t\t},\n\t\t)\n\t\t@components.FooterNav(\n\t\t\t\"Legal\",\n\t\t\t[]components.AnchorProps{\n\t\t\t\t{Label: \"Terms of use\"},\n\t\t\t\t{Label: \"Privacy policy\"},\n\t\t\t\t{Label: \"Cookie policy\"},\n\t\t\t},\n\t\t)\n\t}\n}\n\ntempl FooterCompanyInfoIconExample() {\n\t\u003csvg\n\t\twidth=\"24\"\n\t\theight=\"24\"\n\t\tviewBox=\"0 0 24 24\"\n\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\tfill-rule=\"evenodd\"\n\t\tclip-rule=\"evenodd\"\n\t\tclass=\"fill-current\"\n\t\u003e\n\t\t\u003cpath\n\t\t\td=\"M22.672 15.226l-2.432.811.841 2.515c.33 1.019-.209 2.127-1.23 2.456-1.15.325-2.148-.321-2.463-1.226l-.84-2.518-5.013 1.677.84 2.517c.391 1.203-.434 2.542-1.831 2.542-.88 0-1.601-.564-1.86-1.314l-.842-2.516-2.431.809c-1.135.328-2.145-.317-2.463-1.229-.329-1.018.211-2.127 1.231-2.456l2.432-.809-1.621-4.823-2.432.808c-1.355.384-2.558-.59-2.558-1.839 0-.817.509-1.582 1.327-1.846l2.433-.809-.842-2.515c-.33-1.02.211-2.129 1.232-2.458 1.02-.329 2.13.209 2.461 1.229l.842 2.515 5.011-1.677-.839-2.517c-.403-1.238.484-2.553 1.843-2.553.819 0 1.585.509 1.85 1.326l.841 2.517 2.431-.81c1.02-.33 2.131.211 2.461 1.229.332 1.018-.21 2.126-1.23 2.456l-2.433.809 1.622 4.823 2.433-.809c1.242-.401 2.557.484 2.557 1.838 0 .819-.51 1.583-1.328 1.847m-8.992-6.428l-5.01 1.675 1.619 4.828 5.011-1.674-1.62-4.829z\"\n\t\t\u003e\u003c/path\u003e\n\t\u003c/svg\u003e\n}\n```" } ], "hero": [ { "name": "BasicHero", "code": "```go\ntempl BasicHero() {\n\t\u003cdiv class=\"pt-4\"\u003e\n\t\t@components.Hero(components.HeroProps{\n\t\t\tSource: \"/static/images/avatar.jpg\",\n\t\t\tAlt: \"hero avatar\",\n\t\t\tClass: \"bg-base-200 min-h-[600px]\",\n\t\t}) {\n\t\t\t\u003cdiv class=\"prose\"\u003e\n\t\t\t\t\u003ch1\u003eLorem ipsum!\u003c/h1\u003e\n\t\t\t\t\u003cp\u003e\n\t\t\t\t\tLorem ipsum dolor sit, amet consectetur adipisicing elit.\n\t\t\t\t\tEx quibusdam dicta necessitatibus! Deleniti temporibus iure\n\t\t\t\t\tporro cupiditate dolorum modi voluptate perferendis velit\n\t\t\t\t\ttempora repudiandae expedita, impedit omnis vitae. Laborum,\n\t\t\t\t\tdignissimos?\n\t\t\t\t\u003c/p\u003e\n\t\t\t\t\u003cbutton class=\"btn btn-primary\"\u003eGet Started\u003c/button\u003e\n\t\t\t\u003c/div\u003e\n\t\t}\n\t\t@components.Hero(components.HeroProps{\n\t\t\tSource: \"/static/images/avatar.jpg\",\n\t\t\tAlt: \"hero avatar\",\n\t\t\tClass: \"bg-base-200 min-h-[600px]\",\n\t\t\tReverse: true,\n\t\t}) {\n\t\t\t\u003cdiv class=\"prose\"\u003e\n\t\t\t\t\u003ch1\u003eLorem ipsum!\u003c/h1\u003e\n\t\t\t\t\u003cp\u003e\n\t\t\t\t\tLorem ipsum dolor sit, amet consectetur adipisicing elit.\n\t\t\t\t\tEx quibusdam dicta necessitatibus! Deleniti temporibus iure\n\t\t\t\t\tporro cupiditate dolorum modi voluptate perferendis velit\n\t\t\t\t\ttempora repudiandae expedita, impedit omnis vitae. Laborum,\n\t\t\t\t\tdignissimos?\n\t\t\t\t\u003c/p\u003e\n\t\t\t\t\u003cbutton class=\"btn btn-primary\"\u003eGet Started\u003c/button\u003e\n\t\t\t\u003c/div\u003e\n\t\t}\n\t\u003c/div\u003e\n}\n```" } ], "hover_3d_card": [ { "name": "Hover3DCardExample", "code": "```go\ntempl Hover3DCardExample() {\n\t@components.Hover3DCard() {\n\t\t@components.Card(\n\t\t\tcomponents.CardProps{\n\t\t\t\tTitle: \"Hover 3D Card\",\n\t\t\t\tContent: \"This is a hover 3D card. Move your mouse around the card.\",\n\t\t\t\tClass: \"h-[200px] card-border\",\n\t\t\t},\n\t\t)\n\t}\n}\n```" } ], "infinite_scroll": [ { "name": "InfiniteScrollTableExample", "code": "```go\ntempl InfiniteScrollTableExample() {\n\t@components.Table(\n\t\t[]templ.Component{components.PlainText(\"Name\"), components.PlainText(\"Email\")},\n\t\tinitialRows(),\n\t\tnil,\n\t)\n\t\u003cdiv class=\"flex justify-center\"\u003e\u003cspan id=\"spinner\" class=\"htmx-indicator loading loading-spinner\"\u003e\u003c/span\u003e\u003c/div\u003e\n}\n\nfunc initialRows() []templ.Component {\n\tdata := make([]templ.Component, 10)\n\tfor i := 0; i \u003c 10; i++ {\n\t\tdata[i] = components.InfiniteScrollRow(\"John Doe\", fmt.Sprintf(\"john.doe%d@email.com\", i), 0, i == 9)\n\t}\n\n\treturn data\n}\n```", "handler": "```go\nfunc GetInfiniteScrollExample(c echo.Context) error {\n\t// generate dummy row data\n\tdata := make([][]string, 0, 10)\n\tfor i := 0; i \u003c 10; i++ {\n\t\tdata = append(data, []string{\"John Doe\", fmt.Sprintf(\"john.doe%d@email.com\", i)})\n\t}\n\n\thasMore := true\n\n\trows := make([]templ.Component, 0, len(data))\n\tfor i := range data {\n\t\trows = append(\n\t\t\trows,\n\t\t\tcomponents.InfiniteScrollRow(data[i][0], data[i][1], 1, i+1 == 10 \u0026\u0026 hasMore),\n\t\t)\n\t}\n\n\treturn render(c, http.StatusOK, components.InfiniteScrollTable(rows))\n}\n\nfunc GetInfiniteScrollExampleRows(c echo.Context) error {\n\t// short delay for loading indicator\n\ttime.Sleep(500 * time.Millisecond)\n\n\tpageStr := c.QueryParam(\"page\")\n\tpage, err := strconv.Atoi(pageStr)\n\tif err != nil {\n\t\treturn newErrorToast(\n\t\t\thttp.StatusUnprocessableEntity,\n\t\t\tfmt.Sprintf(\"invalid page '%s'\", pageStr),\n\t\t)\n\t}\n\n\t// generate dummy date for the page\n\tdata := make([][]string, 0, 10)\n\tfor i := (page - 1) * 10; i \u003c (page-1)*10+10; i++ {\n\t\tdata = append(data, []string{\"John Doe\", fmt.Sprintf(\"john.doe%d@email.com\", i)})\n\t}\n\n\thasMore := len(data) == 10 \u0026\u0026 page \u003c 10\n\n\trows := make([]templ.Component, 0, len(data))\n\tfor i := range data {\n\t\trows = append(\n\t\t\trows,\n\t\t\tcomponents.InfiniteScrollRow(data[i][0], data[i][1], page, i+1 == 10 \u0026\u0026 hasMore),\n\t\t)\n\t}\n\n\treturn render(c, http.StatusOK, components.InfiniteScrollRows(rows))\n}\n\n```" } ], "input": [ { "name": "DifferentSizeInputs", "code": "```go\ntempl DifferentSizeInputs() {\n\t@components.Input(components.InputProps{\n\t\tLabel: \"Name\",\n\t\tPlaceholder: \"Your name...\",\n\t\tSize: \"xs\",\n\t})\n\t@components.Input(components.InputProps{\n\t\tLabel: \"Name\",\n\t\tPlaceholder: \"Your name...\",\n\t\tSize: \"sm\",\n\t})\n\t@components.Input(components.InputProps{\n\t\tLabel: \"Name\",\n\t\tPlaceholder: \"Your name...\",\n\t})\n\t@components.Input(components.InputProps{\n\t\tLabel: \"Name\",\n\t\tPlaceholder: \"Your name...\",\n\t\tSize: \"lg\",\n\t})\n\t@components.Input(components.InputProps{\n\t\tLabel: \"Name\",\n\t\tPlaceholder: \"Your name...\",\n\t\tSize: \"xl\",\n\t})\n}\n\n```", "title": "Different size inputs" }, { "name": "IntegerInput", "code": "```go\ntempl IntegerInput() {\n\t@components.Input(components.InputProps{\n\t\tName: \"my_number\",\n\t\tType: \"number\",\n\t\tPlaceholder: \"Give a number...\",\n\t})\n}\n\n```", "title": "Integer input" }, { "name": "DecimalInput", "code": "```go\ntempl DecimalInput() {\n\t@components.Input(components.InputProps{\n\t\tName: \"my_number\",\n\t\tType: \"number\",\n\t\tPlaceholder: \"Give a decimal number...\",\n\t\tAttrs: templ.Attributes{\n\t\t\t\"min\": \"0.00\",\n\t\t\t\"max\": \"2.00\",\n\t\t\t\"step\": \"0.25\",\n\t\t},\n\t})\n}\n\n```", "title": "Decimal input" }, { "name": "PasswordFieldWithValidator", "code": "```go\ntempl PasswordFieldWithValidator() {\n\t@components.Input(components.InputProps{\n\t\tName: \"password\",\n\t\tType: \"password\",\n\t\tPlaceholder: \"Password\",\n\t\tRequired: true,\n\t\tPattern: `(?=.*(\\W|_))(?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{8,255}`,\n\t\tValidatorHint: \"Must be at least 8 characters long, including a digit, lower- and uppercase letter, and a special character\",\n\t\tIcon: keyIcon(),\n\t\tAttrs: templ.Attributes{\n\t\t\t\"minlength\": \"8\",\n\t\t\t\"maxlength\": \"255\",\n\t\t},\n\t})\n}\n\ntempl keyIcon() {\n\t\u003csvg class=\"fill-current/75 h-5 w-5\" viewBox=\"0 0 32 32\" id=\"icon\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\n\t\t\u003cpath d=\"M21,2a8.9977,8.9977,0,0,0-8.6119,11.6118L2,24v6H8L18.3881,19.6118A9,9,0,1,0,21,2Zm0,16a7.0125,7.0125,0,0,1-2.0322-.3022L17.821,17.35l-.8472.8472-3.1811,3.1812L12.4141,20,11,21.4141l1.3787,1.3786-1.5859,1.586L9.4141,23,8,24.4141l1.3787,1.3786L7.1716,28H4V24.8284l9.8023-9.8023.8472-.8474-.3473-1.1467A7,7,0,1,1,21,18Z\"\u003e\u003c/path\u003e\n\t\t\u003ccircle cx=\"22\" cy=\"10\" r=\"2\"\u003e\u003c/circle\u003e\n\t\t\u003crect class=\"fill-none\" id=\"_Transparent_Rectangle_\"\u003e\u003c/rect\u003e\n\t\u003c/svg\u003e\n}\n\n```", "title": "Password field with validator" }, { "name": "EmailFieldWithValidator", "code": "```go\ntempl EmailFieldWithValidator() {\n\t@components.Input(components.InputProps{\n\t\tName: \"email\",\n\t\tType: \"email\",\n\t\tPlaceholder: \"Email\",\n\t\tRequired: true,\n\t\tPattern: `^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$`,\n\t\tValidatorHint: \"Please enter a valid email address\",\n\t\tIcon: emailIcon(),\n\t})\n}\n\ntempl emailIcon() {\n\t\u003csvg class=\"h-5 w-5\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\n\t\t\u003cpath class=\"stroke-current/75\" d=\"M4 7.00005L10.2 11.65C11.2667 12.45 12.7333 12.45 13.8 11.65L20 7\" stroke-width=\"1\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\u003e\u003c/path\u003e\n\t\t\u003crect class=\"stroke-current/75\" x=\"3\" y=\"5\" width=\"18\" height=\"14\" rx=\"2\" stroke-width=\"1\" stroke-linecap=\"round\"\u003e\u003c/rect\u003e\n\t\u003c/svg\u003e\n}\n\n```", "title": "Email field with validtor" }, { "name": "SignUpFormExample", "code": "```go\ntempl SignUpFormExample() {\n\t\u003cdiv class=\"w-full max-w-xs mx-auto pt-12 pb-4\"\u003e\n\t\t\u003ch2 class=\"text-xl text-center mb-8\"\u003eSign Up\u003c/h2\u003e\n\t\t@OAuthButtons()\n\t\t\u003cdiv class=\"divider !my-6\"\u003eOR\u003c/div\u003e\n\t\t@SignUpForm(\n\t\t\t\"\", \"\",\n\t\t\t\"\", \"\",\n\t\t\t\"\", \"\",\n\t\t\t\"\", \"\",\n\t\t)\n\t\u003c/div\u003e\n}\n\ntempl SignUpForm(\n\tfirstName, firstNameError,\n\tlastName, lastNameError,\n\temail, emailError,\n\tpassword, passwordError string,\n) {\n\t\u003cdiv class=\"w-full max-w-xs mx-auto\"\u003e\n\t\t\u003cdiv\n\t\t\tclass=\"form space-y-1\"\n\t\t\thx-swap=\"outerHTML\"\n\t\t\u003e\n\t\t\t@components.Input(\n\t\t\t\tcomponents.InputProps{\n\t\t\t\t\tLabel: \"First name\",\n\t\t\t\t\tName: \"first_name\",\n\t\t\t\t\tPlaceholder: \"Your first name...\",\n\t\t\t\t\tValue: firstName,\n\t\t\t\t\tError: firstNameError,\n\t\t\t\t\tRequired: true,\n\t\t\t\t\tAttrs: templ.Attributes{\n\t\t\t\t\t\t\"hx-post\": \"/validate/string/first_name?v=notempty\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t)\n\t\t\t@components.Input(\n\t\t\t\tcomponents.InputProps{\n\t\t\t\t\tLabel: \"Last name\",\n\t\t\t\t\tName: \"last_name\",\n\t\t\t\t\tPlaceholder: \"Your last name...\",\n\t\t\t\t\tValue: lastName,\n\t\t\t\t\tError: lastNameError,\n\t\t\t\t\tRequired: true,\n\t\t\t\t\tAttrs: templ.Attributes{\n\t\t\t\t\t\t\"hx-post\": \"/validate/string/last_name?v=notempty\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t)\n\t\t\t@components.Input(\n\t\t\t\tcomponents.InputProps{\n\t\t\t\t\tLabel: \"Email\",\n\t\t\t\t\tName: \"email\",\n\t\t\t\t\tType: \"email\",\n\t\t\t\t\tPlaceholder: \"Your email...\",\n\t\t\t\t\tValue: email,\n\t\t\t\t\tError: emailError,\n\t\t\t\t\tRequired: true,\n\t\t\t\t\tAttrs: templ.Attributes{\n\t\t\t\t\t\t\"hx-post\": \"/validate/string/email?v=email\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t)\n\t\t\t@components.Input(\n\t\t\t\tcomponents.InputProps{\n\t\t\t\t\tLabel: \"Password\",\n\t\t\t\t\tName: \"password\",\n\t\t\t\t\tType: \"password\",\n\t\t\t\t\tPlaceholder: \"Your new password...\",\n\t\t\t\t\tRequired: true,\n\t\t\t\t\tMinLength: \"8\",\n\t\t\t\t\tMaxLength: \"255\",\n\t\t\t\t\tPattern: `(?=.*(\\W|_))(?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{8,255}`,\n\t\t\t\t\tValidatorHint: \"Must be at least 8 characters long, including a digit, lower- and uppercase letter, and a special character\",\n\t\t\t\t\tAttrs: templ.Attributes{\n\t\t\t\t\t\t\"hx-post\": \"/validate/string/password?v=hasupper\u0026v=haslower\u0026v=hasdigit\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t)\n\t\t\t\u003cdiv class=\"pt-4\"\u003e\n\t\t\t\t\u003cbutton type=\"submit\" class=\"w-full btn btn-primary\"\u003e\n\t\t\t\t\tSign up\n\t\t\t\t\u003c/button\u003e\n\t\t\t\u003c/div\u003e\n\t\t\u003c/div\u003e\n\t\u003c/div\u003e\n}\n\ntempl OAuthButtons() {\n\t\u003cdiv class=\"w-full max-w-xs flex flex-col space-y-4 items-center justify-center\"\u003e\n\t\t@GoogleOAuthLink(\"\")\n\t\t@GithubOAuthLink(\"\")\n\t\u003c/div\u003e\n}\n\ntempl GoogleOAuthLink(href string) {\n\t\u003ca\n\t\thref={ templ.SafeURL(href) }\n\t\tclass=\"w-full max-w-sm py-3 flex justify-center gap-2 bg-gray-50 dark:bg-gray-900 hover:bg-gray-100 dark:hover:bg-black text-slate-950 dark:text-slate-50 rounded-box shadow-md transition-colors duration-300\"\n\t\u003e\n\t\t\u003csvg class=\"w-6 h-6\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\" fill=\"none\"\u003e\n\t\t\t\u003cpath d=\"M30.0014 16.3109C30.0014 15.1598 29.9061 14.3198 29.6998 13.4487H16.2871V18.6442H24.1601C24.0014 19.9354 23.1442 21.8798 21.2394 23.1864L21.2127 23.3604L25.4536 26.58L25.7474 26.6087C28.4458 24.1665 30.0014 20.5731 30.0014 16.3109Z\" fill=\"#4285F4\"\u003e\u003c/path\u003e\n\t\t\t\u003cpath d=\"M16.2863 29.9998C20.1434 29.9998 23.3814 28.7553 25.7466 26.6086L21.2386 23.1863C20.0323 24.0108 18.4132 24.5863 16.2863 24.5863C12.5086 24.5863 9.30225 22.1441 8.15929 18.7686L7.99176 18.7825L3.58208 22.127L3.52441 22.2841C5.87359 26.8574 10.699 29.9998 16.2863 29.9998Z\" fill=\"#34A853\"\u003e\u003c/path\u003e\n\t\t\t\u003cpath d=\"M8.15964 18.769C7.85806 17.8979 7.68352 16.9645 7.68352 16.0001C7.68352 15.0356 7.85806 14.1023 8.14377 13.2312L8.13578 13.0456L3.67083 9.64746L3.52475 9.71556C2.55654 11.6134 2.00098 13.7445 2.00098 16.0001C2.00098 18.2556 2.55654 20.3867 3.52475 22.2845L8.15964 18.769Z\" fill=\"#FBBC05\"\u003e\u003c/path\u003e\n\t\t\t\u003cpath d=\"M16.2864 7.4133C18.9689 7.4133 20.7784 8.54885 21.8102 9.4978L25.8419 5.64C23.3658 3.38445 20.1435 2 16.2864 2C10.699 2 5.8736 5.1422 3.52441 9.71549L8.14345 13.2311C9.30229 9.85555 12.5086 7.4133 16.2864 7.4133Z\" fill=\"#EB4335\"\u003e\u003c/path\u003e\n\t\t\u003c/svg\u003e\n\t\t\u003cspan\u003eSign in with Google\u003c/span\u003e\n\t\u003c/a\u003e\n}\n\ntempl GithubOAuthLink(href string) {\n\t\u003ca\n\t\thref={ templ.SafeURL(href) }\n\t\tclass=\"py-3 px-4 max-w-md flex justify-center items-center bg-gray-600 hover:bg-gray-700 focus:ring-gray-500 focus:ring-offset-gray-200 text-white w-full transition-colors duration-300 text-center text-base font-semibold shadow-md focus:outline-hidden focus:ring-2 focus:ring-offset-2 rounded-box\"\n\t\u003e\n\t\t\u003csvg class=\"w-6 h-6 mr-2\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" class=\"mr-2\" viewBox=\"0 0 1792 1792\"\u003e\n\t\t\t\u003cpath\n\t\t\t\td=\"M896 128q209 0 385.5 103t279.5 279.5 103 385.5q0 251-146.5 451.5t-378.5 277.5q-27 5-40-7t-13-30q0-3 .5-76.5t.5-134.5q0-97-52-142 57-6 102.5-18t94-39 81-66.5 53-105 20.5-150.5q0-119-79-206 37-91-8-204-28-9-81 11t-92 44l-38 24q-93-26-192-26t-192 26q-16-11-42.5-27t-83.5-38.5-85-13.5q-45 113-8 204-79 87-79 206 0 85 20.5 150t52.5 105 80.5 67 94 39 102.5 18q-39 36-49 103-21 10-45 15t-57 5-65.5-21.5-55.5-62.5q-19-32-48.5-52t-49.5-24l-20-3q-21 0-29 4.5t-5 11.5 9 14 13 12l7 5q22 10 43.5 38t31.5 51l10 23q13 38 44 61.5t67 30 69.5 7 55.5-3.5l23-4q0 38 .5 88.5t.5 54.5q0 18-13 30t-40 7q-232-77-378.5-277.5t-146.5-451.5q0-209 103-385.5t279.5-279.5 385.5-103zm-477 1103q3-7-7-12-10-3-13 2-3 7 7 12 9 6 13-2zm31 34q7-5-2-16-10-9-16-3-7 5 2 16 10 10 16 3zm30 45q9-7 0-19-8-13-17-6-9 5 0 18t17 7zm42 42q8-8-4-19-12-12-20-3-9 8 4 19 12 12 20 3zm57 25q3-11-13-16-15-4-19 7t13 15q15 6 19-6zm63 5q0-13-17-11-16 0-16 11 0 13 17 11 16 0 16-11zm58-10q-2-11-18-9-16 3-14 15t18 8 14-14z\"\n\t\t\t\u003e\u003c/path\u003e\n\t\t\u003c/svg\u003e\n\t\t\u003cspan\u003eSign in with GitHub\u003c/span\u003e\n\t\u003c/a\u003e\n}\n```", "title": "Sign up form with inline validation" } ], "lazy_load": [ { "name": "LazyLoadExample", "code": "```go\ntempl LazyLoadExample() {\n\t@components.LazyLoad(\"/lazy-load\")\n}\n\ntempl LazyLoadResult() {\n\t\u003cdiv\n\t\tclass={\n\t\t\t\"bg-warning text-warning-content rounded-box\",\n\t\t\t\"w-28 h-28 flex justify-center items-center text-center\",\n\t\t}\n\t\u003e\n\t\tLAZY\n\t\t\u003cbr/\u003e\n\t\tLOAD\n\t\t\u003cbr/\u003e\n\t\tCOMPLETE\n\t\u003c/div\u003e\n}\n```", "handler": "```go\nfunc GetLazyLoadExample(c echo.Context) error {\n\ttime.Sleep(2 * time.Second)\n\n\treturn render(c, http.StatusOK, examples.LazyLoadResult())\n}\n\n```" } ], "menu": [ { "name": "MenuExample", "code": "```go\ntempl MenuExample() {\n\t\u003cdiv class=\"w-full mx-auto max-w-xs pt-4\"\u003e\n\t\t@components.Menu(components.MenuProps{Title: \"Basic menu\", Class: \"w-52\"}) {\n\t\t\t@components.MenuItem(components.MenuItemProps{Label: \"Section 1\", Attrs: templ.Attributes{\"class\": \"menu-title\"}})\n\t\t\t@components.MenuItem(components.MenuItemProps{Label: \"Section 2\", Attrs: templ.Attributes{\"class\": \"menu-title\"}}) {\n\t\t\t\t@components.MenuItem(components.MenuItemProps{Label: \"2.1\"}) {\n\t\t\t\t\t@components.MenuItem(components.MenuItemProps{Label: \"2.1.1\"})\n\t\t\t\t\t@components.MenuItem(components.MenuItemProps{Label: \"2.1.2\"})\n\t\t\t\t}\n\t\t\t\t@components.MenuItem(components.MenuItemProps{Label: \"2.2\"}) {\n\t\t\t\t\t@components.MenuItem(components.MenuItemProps{Label: \"2.2.1\"})\n\t\t\t\t}\n\t\t\t\t@components.MenuItem(components.MenuItemProps{Label: \"2.3\"})\n\t\t\t}\n\t\t\t@components.MenuItem(components.MenuItemProps{Label: \"Section 3\", Attrs: templ.Attributes{\"class\": \"menu-title\"}}) {\n\t\t\t\t@components.MenuItem(components.MenuItemProps{Label: \"3.1\"})\n\t\t\t}\n\t\t}\n\t\u003c/div\u003e\n}\n\n```", "title": "Basic menu" }, { "name": "MenuWithSubmenusExample", "code": "```go\ntempl MenuWithSubmenusExample() {\n\t\u003cdiv class=\"w-full mx-auto max-w-xs pt-4 min-h-72\"\u003e\n\t\t@components.Menu(components.MenuProps{Class: \"w-52\"}) {\n\t\t\t@components.MenuItem(\n\t\t\t\tcomponents.MenuItemProps{\n\t\t\t\t\tLabel: \"Section 1\",\n\t\t\t\t\tAttrs: templ.Attributes{\"class\": \"menu-title\"},\n\t\t\t\t})\n\t\t\t@components.Submenu(\n\t\t\t\tcomponents.SubmenuProps{\n\t\t\t\t\tTitle: \"Section 2\",\n\t\t\t\t\tAttrs: templ.Attributes{\"open\": \"\"},\n\t\t\t\t},\n\t\t\t) {\n\t\t\t\t@components.MenuItem(components.MenuItemProps{Label: \"2.1\"})\n\t\t\t\t@components.MenuItem(components.MenuItemProps{Label: \"2.2\"})\n\t\t\t}\n\t\t}\n\t\u003c/div\u003e\n}\n\n```", "title": "Menu with a submenu" }, { "name": "DashboardMenuExample", "code": "```go\ntempl DashboardMenuExample() {\n\t@components.Menu(\n\t\tcomponents.MenuProps{Class: \"w-64 bg-base-200 rounded-box\"}) {\n\t\t@components.MenuItem(\n\t\t\tcomponents.MenuItemProps{\n\t\t\t\tLabel: \"Dashboard\",\n\t\t\t\tIcon: HomeIcon(),\n\t\t\t\tAttrs: templ.Attributes{\"href\": \"#dashboard\"},\n\t\t\t})\n\t\t@components.MenuItem(\n\t\t\tcomponents.MenuItemProps{\n\t\t\t\tLabel: \"Users\",\n\t\t\t\tIcon: UsersIcon(),\n\t\t\t\tAttrs: templ.Attributes{\"href\": \"#users\"},\n\t\t\t})\n\t\t@components.Submenu(components.SubmenuProps{Title: \"Content\", Icon: ContentIcon()}) {\n\t\t\t@components.MenuItem(\n\t\t\t\tcomponents.MenuItemProps{\n\t\t\t\t\tLabel: \"Posts\",\n\t\t\t\t\tIcon: PostsIcon(),\n\t\t\t\t\tAttrs: templ.Attributes{\"href\": \"#posts\"},\n\t\t\t\t})\n\t\t\t@components.MenuItem(\n\t\t\t\tcomponents.MenuItemProps{\n\t\t\t\t\tLabel: \"Images\",\n\t\t\t\t\tIcon: ImagesIcon(),\n\t\t\t\t\tAttrs: templ.Attributes{\"href\": \"#images\"},\n\t\t\t\t})\n\t\t\t@components.MenuItem(\n\t\t\t\tcomponents.MenuItemProps{\n\t\t\t\t\tLabel: \"Videos\",\n\t\t\t\t\tIcon: VideosIcon(),\n\t\t\t\t\tAttrs: templ.Attributes{\"href\": \"#videos\"},\n\t\t\t\t})\n\t\t}\n\t}\n}\n\ntempl HomeIcon() {\n\t\u003csvg class=\"h-5 w-5 opacity-75\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\n\t\t\u003cpath class=\"stroke-current\" d=\"M2 12.2039C2 9.91549 2 8.77128 2.5192 7.82274C3.0384 6.87421 3.98695 6.28551 5.88403 5.10813L7.88403 3.86687C9.88939 2.62229 10.8921 2 12 2C13.1079 2 14.1106 2.62229 16.116 3.86687L18.116 5.10812C20.0131 6.28551 20.9616 6.87421 21.4808 7.82274C22 8.77128 22 9.91549 22 12.2039V13.725C22 17.6258 22 19.5763 20.8284 20.7881C19.6569 22 17.7712 22 14 22H10C6.22876 22 4.34315 22 3.17157 20.7881C2 19.5763 2 17.6258 2 13.725V12.2039Z\" stroke-width=\"1.5\"\u003e\u003c/path\u003e\n\t\t\u003cpath class=\"stroke-current\" d=\"M15 18H9\" stroke-width=\"1.5\" stroke-linecap=\"round\"\u003e\u003c/path\u003e\n\t\u003c/svg\u003e\n}\n\ntempl UsersIcon() {\n\t\u003csvg class=\"h-5 w-5 opacity-75\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\n\t\t\u003cpath class=\"stroke-current\" d=\"M16 7C16 9.20914 14.2091 11 12 11C9.79086 11 8 9.20914 8 7C8 4.79086 9.79086 3 12 3C14.2091 3 16 4.79086 16 7Z\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\u003e\u003c/path\u003e\n\t\t\u003cpath class=\"stroke-current\" d=\"M12 14C8.13401 14 5 17.134 5 21H19C19 17.134 15.866 14 12 14Z\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\u003e\u003c/path\u003e\n\t\u003c/svg\u003e\n}\n\ntempl ContentIcon() {\n\t\u003csvg class=\"h-5 w-5 opacity-75\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\n\t\t\u003cpath class=\"stroke-current\" opacity=\"0.5\" d=\"M18 10L13 10\" stroke-width=\"1.5\" stroke-linecap=\"round\"\u003e\u003c/path\u003e\n\t\t\u003cpath class=\"stroke-current\" d=\"M2 6.94975C2 6.06722 2 5.62595 2.06935 5.25839C2.37464 3.64031 3.64031 2.37464 5.25839 2.06935C5.62595 2 6.06722 2 6.94975 2C7.33642 2 7.52976 2 7.71557 2.01738C8.51665 2.09229 9.27652 2.40704 9.89594 2.92051C10.0396 3.03961 10.1763 3.17633 10.4497 3.44975L11 4C11.8158 4.81578 12.2237 5.22367 12.7121 5.49543C12.9804 5.64471 13.2651 5.7626 13.5604 5.84678C14.0979 6 14.6747 6 15.8284 6H16.2021C18.8345 6 20.1506 6 21.0062 6.76946C21.0849 6.84024 21.1598 6.91514 21.2305 6.99383C22 7.84935 22 9.16554 22 11.7979V14C22 17.7712 22 19.6569 20.8284 20.8284C19.6569 22 17.7712 22 14 22H10C6.22876 22 4.34315 22 3.17157 20.8284C2 19.6569 2 17.7712 2 14V6.94975Z\" stroke-width=\"1.5\"\u003e\u003c/path\u003e\n\t\u003c/svg\u003e\n}\n\ntempl PostsIcon() {\n\t\u003csvg class=\"h-5 w-5 opacity-75\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\n\t\t\u003cpath class=\"stroke-current\" d=\"M1 6v12h9V6zm8 11H2V7h7zm-8 3h22v1H1zM1 3h22v1H1zm11 4h11v1H12zm0 3h11v1H12zm0 3h11v1H12zm0 3h11v1H12z\"\u003e\u003c/path\u003e\u003cpath fill=\"none\" d=\"M0 0h24v24H0z\"\u003e\u003c/path\u003e\n\t\u003c/svg\u003e\n}\n\ntempl ImagesIcon() {\n\t\u003csvg class=\"h-5 w-5 opacity-75\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\n\t\t\u003crect width=\"24\" height=\"24\" class=\"fill-base-100\"\u003e\u003c/rect\u003e\n\t\t\u003cpath class=\"stroke-current\" d=\"M21 16V20C21 20.5523 20.5523 21 20 21H4C3.44772 21 3 20.5523 3 20V18M21 16V4C21 3.44772 20.5523 3 20 3H4C3.44772 3 3 3.44772 3 4V18M21 16L15.4829 12.3219C15.1843 12.1228 14.8019 12.099 14.4809 12.2595L3 18\" stroke-linejoin=\"round\"\u003e\u003c/path\u003e\n\t\t\u003ccircle class=\"stroke-current\" cx=\"8\" cy=\"9\" r=\"2\" stroke-linejoin=\"round\"\u003e\u003c/circle\u003e\n\t\u003c/svg\u003e\n}\n\ntempl VideosIcon() {\n\t\u003csvg class=\"h-5 w-5 opacity-75\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"\u003e\n\t\t\u003cpath class=\"fill-current\" fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M9.98966 10.91C10.4482 10.609 11.0657 10.5541 11.6048 10.8884L14.9764 12.9785C15.5136 13.3116 15.75 13.8874 15.75 14.4102C15.75 14.933 15.5136 15.5088 14.9764 15.8418L11.6048 17.932C11.0657 18.2662 10.4482 18.2114 9.98966 17.9103C9.539 17.6144 9.25 17.0934 9.25 16.5003V12.32C9.25 11.727 9.539 11.2059 9.98966 10.91ZM10.8129 12.1639C10.7945 12.176 10.75 12.2203 10.75 12.32V16.5003C10.75 16.6 10.7945 16.6443 10.8129 16.6564L10.8142 16.6572L14.186 14.5669C14.2072 14.5538 14.25 14.5085 14.25 14.4102C14.25 14.3118 14.2072 14.2665 14.186 14.2534L10.8145 12.1633L10.8129 12.1639Z\"\u003e\u003c/path\u003e\n\t\t\u003cpath class=\"fill-current\" fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M8.69935 1.25001C8.4795 1.24995 8.31094 1.2499 8.16359 1.26571C6.80943 1.41104 5.77295 2.52304 5.71005 3.87007C4.51917 4.22559 3.67058 5.3275 3.65528 6.5913C3.05445 6.77164 2.53621 7.05606 2.11196 7.51432C1.45997 8.21857 1.25792 9.08649 1.25023 10.1003C1.24283 11.075 1.41651 12.3067 1.63219 13.8363L2.07118 16.9499C2.23979 18.146 2.37676 19.1176 2.58989 19.879C2.81286 20.6756 3.14152 21.331 3.75003 21.8349C4.35372 22.3347 5.06993 22.5502 5.91647 22.6518C6.73501 22.75 7.76474 22.75 9.04682 22.75H14.9531C16.2352 22.75 17.265 22.75 18.0835 22.6518C18.9301 22.5502 19.6463 22.3347 20.25 21.8349C20.8585 21.331 21.1871 20.6756 21.4101 19.879C21.6232 19.1176 21.7602 18.146 21.9288 16.9499L22.3678 13.8363C22.5835 12.3067 22.7572 11.075 22.7498 10.1003C22.7421 9.08649 22.54 8.21857 21.888 7.51432C21.4637 7.05595 20.9453 6.77151 20.3443 6.59118C20.3289 5.3275 19.4805 4.22571 18.2897 3.87013C18.2268 2.52307 17.1903 1.41104 15.8362 1.26571C15.6888 1.2499 15.5203 1.24995 15.3004 1.25001H8.69935ZM18.8105 6.32781C18.6734 5.72018 18.1306 5.25001 17.4617 5.25001H6.53787C5.86896 5.25001 5.32618 5.72019 5.18902 6.32785C6.11481 6.24999 7.24973 6.25 8.61594 6.25001H15.384C16.75 6.25 17.8848 6.24999 18.8105 6.32781ZM16.7677 3.75001C16.6611 3.22633 16.2263 2.8162 15.6761 2.75715C15.6198 2.75111 15.5396 2.75001 15.2588 2.75001H8.74099C8.46013 2.75001 8.37993 2.75111 8.32365 2.75715C7.77344 2.8162 7.33862 3.22633 7.2321 3.75001H16.7677ZM3.21267 8.53336C3.51557 8.20618 3.97106 7.98917 4.85612 7.87145C5.75726 7.75159 6.96357 7.75001 8.67239 7.75001H15.3276C17.0364 7.75001 18.2427 7.75159 19.1439 7.87145C20.0289 7.98917 20.4844 8.20618 20.7873 8.53336C21.0832 8.85293 21.2436 9.28782 21.2498 10.1117C21.2563 10.9618 21.1002 12.0828 20.8738 13.6883L20.4509 16.6883C20.2731 17.9491 20.1486 18.821 19.9656 19.4747C19.7894 20.1042 19.582 20.4405 19.2934 20.6795C18.9999 20.9225 18.6058 21.0784 17.9048 21.1625C17.1861 21.2488 16.2465 21.25 14.9046 21.25H9.09536C7.75347 21.25 6.81393 21.2488 6.09519 21.1625C5.39417 21.0784 5.00014 20.9225 4.70664 20.6795C4.41795 20.4405 4.21058 20.1042 4.03437 19.4747C3.8514 18.821 3.7269 17.9491 3.54913 16.6883L3.12616 13.6883C2.89981 12.0828 2.74373 10.9618 2.75018 10.1117C2.75644 9.28782 2.91681 8.85293 3.21267 8.53336Z\"\u003e\u003c/path\u003e\n\t\u003c/svg\u003e\n}\n```", "title": "Menu with icons and a submenu" } ], "modal": [ { "name": "BasicModal", "code": "```go\ntempl BasicModal() {\n\t\u003cdiv class=\"flex justify-center items-center py-8\"\u003e\n\t\t@components.Modal(components.ModalProps{ID: \"my_modal\", Label: \"Click me\"}) {\n\t\t\t\u003ch3 class=\"text-3xl\"\u003eModal title\u003c/h3\u003e\n\t\t\t\u003cp\u003eModal content goes here\u003c/p\u003e\n\t\t}\n\t\u003c/div\u003e\n}\n\n```", "title": "Basic modal" }, { "name": "MultipleModals", "code": "```go\ntempl MultipleModals() {\n\t\u003cdiv class=\"flex justify-center items-center py-8\"\u003e\n\t\t\u003cdiv class=\"grid grid-cols-3 gap-4\"\u003e\n\t\t\tfor i := range 3 {\n\t\t\t\t@components.Modal(components.ModalProps{ID: fmt.Sprintf(\"my_modal_%d\", i), Label: \"Click me\"}) {\n\t\t\t\t\t\u003ch3 class=\"text-3xl\"\u003eModal title { fmt.Sprintf(\"%d\", i) }\u003c/h3\u003e\n\t\t\t\t\t\u003cp\u003eModal content { fmt.Sprintf(\"%d\", i) }\u003c/p\u003e\n\t\t\t\t}\n\t\t\t}\n\t\t\u003c/div\u003e\n\t\u003c/div\u003e\n}\n\n```", "title": "Multiple modals" }, { "name": "ModalWithAction", "code": "```go\ntempl ModalWithAction() {\n\t\u003cdiv class=\"flex justify-center items-center py-8\"\u003e\n\t\t@components.Modal(components.ModalProps{ID: \"my_modal_actions\", Label: \"Click me\"}) {\n\t\t\t\u003ch3 class=\"text-3xl\"\u003eModal title\u003c/h3\u003e\n\t\t\t\u003cp\u003eModal content goes here\u003c/p\u003e\n\t\t\t\u003cdiv class=\"modal-action\"\u003e\n\t\t\t\t\u003cbutton class=\"btn\" onclick=\"my_modal_actions.close()\"\u003eClose\u003c/button\u003e\n\t\t\t\u003c/div\u003e\n\t\t}\n\t\u003c/div\u003e\n}\n\n```", "title": "Modal with action button" }, { "name": "ModalConfirmDelete", "code": "```go\ntempl ModalConfirmDelete() {\n\t\u003cdiv class=\"flex justify-center items-center\"\u003e\n\t\t\u003cdiv class=\"grid grid-cols-3 gap-4\"\u003e\n\t\t\tfor i := range 6 {\n\t\t\t\t\u003cdiv name=\"modal-confirm-example\"\u003e\n\t\t\t\t\t@components.Modal(\n\t\t\t\t\t\tcomponents.ModalProps{\n\t\t\t\t\t\t\tID: fmt.Sprintf(\"modal_confirm_%d\", i),\n\t\t\t\t\t\t\tLabel: modalConfirmDeleteButton(i, templ.Attributes{\"onclick\": fmt.Sprintf(\"modal_confirm_%d.showModal()\", i)}),\n\t\t\t\t\t\t},\n\t\t\t\t\t) {\n\t\t\t\t\t\t\u003cdiv class=\"flex flex-col gap-8\"\u003e\n\t\t\t\t\t\t\t\u003cdiv class=\"grid grid-cols-9 gap-6\"\u003e\n\t\t\t\t\t\t\t\t\u003cdiv class=\"col-span-2 flex justify-center items-center\"\u003e\n\t\t\t\t\t\t\t\t\t\u003csvg\n\t\t\t\t\t\t\t\t\t\tclass=\"col-span-1 h-10 w-10\"\n\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\t\t\t\t\t\t\t\u003e\u003cpath class=\"fill-error\" fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10zm-1.5-5.009c0-.867.659-1.491 1.491-1.491.85 0 1.509.624 1.509 1.491 0 .867-.659 1.509-1.509 1.509-.832 0-1.491-.642-1.491-1.509zM11.172 6a.5.5 0 0 0-.499.522l.306 7a.5.5 0 0 0 .5.478h1.043a.5.5 0 0 0 .5-.478l.305-7a.5.5 0 0 0-.5-.522h-1.655z\"\u003e\u003c/path\u003e\u003c/svg\u003e\n\t\t\t\t\t\t\t\t\u003c/div\u003e\n\t\t\t\t\t\t\t\t\u003cdiv class=\"flex justify-center items-center col-span-7\"\u003e\n\t\t\t\t\t\t\t\t\t\u003cp\u003e\n\t\t\t\t\t\t\t\t\t\tAre you sure you want to delete { fmt.Sprintf(\"%d\", i) }?\n\t\t\t\t\t\t\t\t\t\u003c/p\u003e\n\t\t\t\t\t\t\t\t\u003c/div\u003e\n\t\t\t\t\t\t\t\u003c/div\u003e\n\t\t\t\t\t\t\t\u003cdiv class=\"flex justify-between items-center\"\u003e\n\t\t\t\t\t\t\t\t\u003cbutton\n\t\t\t\t\t\t\t\t\tid=\"modal-confirm-example-delete-button\"\n\t\t\t\t\t\t\t\t\tclass=\"btn btn-error\"\n\t\t\t\t\t\t\t\t\thx-delete={ fmt.Sprintf(\"/modal-confirm?value=%d\", i) }\n\t\t\t\t\t\t\t\t\thx-target=\"closest div[name=modal-confirm-example]\"\n\t\t\t\t\t\t\t\t\thx-swap=\"outerHTML\"\n\t\t\t\t\t\t\t\t\u003e\n\t\t\t\t\t\t\t\t\tDelete\n\t\t\t\t\t\t\t\t\u003c/button\u003e\n\t\t\t\t\t\t\t\t\u003cbutton class=\"btn\" { templ.Attributes{\"onclick\": fmt.Sprintf(\"modal_confirm_%d.close()\", i)}... }\u003e\n\t\t\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t\t\u003c/button\u003e\n\t\t\t\t\t\t\t\u003c/div\u003e\n\t\t\t\t\t\t\t\u003cscript\u003e\n\t\t\t\t\t\t\t\t((modal) =\u003e {\n\t\t\t\t\t\t\t\t\tdocument.addEventListener(\"htmx:afterRequest\", (evt) =\u003e {\n\t\t\t\t\t\t\t\t\t\tif (evt.detail.elt.id === \"modal-confirm-example-delete-button\" \u0026\u0026 evt.detail.successful) {\n\t\t\t\t\t\t\t\t\t\t\tmodal.close()\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t})(document.currentScript.closest(\"dialog.modal\"))\n\t\t\t\t\t\t\t\u003c/script\u003e\n\t\t\t\t\t\t\u003c/div\u003e\n\t\t\t\t\t}\n\t\t\t\t\u003c/div\u003e\n\t\t\t}\n\t\t\u003c/div\u003e\n\t\u003c/div\u003e\n}\n\ntempl modalConfirmDeleteButton(i int, attrs templ.Attributes) {\n\t\u003cbutton { attrs... } class=\"btn\"\u003e\n\t\tClick to delete { fmt.Sprintf(\"%d\", i) }\n\t\u003c/button\u003e\n}\n```", "handler": "```go\n// endpoint to log deleted 'element' and return an empty string\n// to replace the element, i.e. remove it from the DOM\nfunc DeleteModalExample(c echo.Context) error {\n\tvalue := c.QueryParam(\"value\")\n\tlog.Println(\"deleting modal\", value)\n\treturn c.String(http.StatusOK, \"\")\n}\n\n```", "title": "Modal to confirm delete action" } ], "pagination": [ { "name": "BasicPaginationExample", "code": "```go\ntempl BasicPaginationExample() {\n\t@BasicPagination(\n\t\t\"example-pagination\",\n\t\tcomponents.PaginationProps{\n\t\t\tURL: \"/pagination-pages\",\n\t\t\tPage: 1,\n\t\t\tLow: 1,\n\t\t\tHigh: 7,\n\t\t\tMaxPages: 20,\n\t\t},\n\t\t[][]string{\n\t\t\t{\"Key0\", \"Value0\"},\n\t\t\t{\"Key1\", \"Value1\"},\n\t\t\t{\"Key2\", \"Value2\"},\n\t\t\t{\"Key3\", \"Value3\"},\n\t\t\t{\"Key4\", \"Value4\"},\n\t\t\t{\"Key5\", \"Value5\"},\n\t\t\t{\"Key6\", \"Value6\"},\n\t\t\t{\"Key7\", \"Value7\"},\n\t\t\t{\"Key8\", \"Value8\"},\n\t\t\t{\"Key9\", \"Value9\"},\n\t\t},\n\t)\n}\n\ntempl BasicPagination(id string, p components.PaginationProps, data [][]string) {\n\t@components.Pagination(\"example-pagination\", p) {\n\t\t\u003cdiv class=\"overflow-x-auto\"\u003e\n\t\t\t\u003ctable class=\"table\"\u003e\n\t\t\t\t\u003cthead\u003e\n\t\t\t\t\t\u003ctr\u003e\n\t\t\t\t\t\t\u003cth\u003eKey\u003c/th\u003e\n\t\t\t\t\t\t\u003cth\u003eValue\u003c/th\u003e\n\t\t\t\t\t\u003c/tr\u003e\n\t\t\t\t\u003c/thead\u003e\n\t\t\t\t\u003ctbody\u003e\n\t\t\t\t\tfor _, d := range data {\n\t\t\t\t\t\t\u003ctr\u003e\n\t\t\t\t\t\t\t\u003ctd\u003e{ d[0] }\u003c/td\u003e\n\t\t\t\t\t\t\t\u003ctd\u003e{ d[1] }\u003c/td\u003e\n\t\t\t\t\t\t\u003c/tr\u003e\n\t\t\t\t\t}\n\t\t\t\t\u003c/tbody\u003e\n\t\t\t\u003c/table\u003e\n\t\t\u003c/div\u003e\n\t}\n}\n```", "handler": "```go\nconst (\n\tItemsPerPage = 10\n\tPagesPerSide = 3\n)\n\nfunc GetPaginationExamplePage(c echo.Context) error {\n\tpageStr := c.QueryParam(\"page\")\n\tpage, err := strconv.Atoi(pageStr)\n\tif err != nil {\n\t\treturn newErrorToast(\n\t\t\thttp.StatusUnprocessableEntity,\n\t\t\tfmt.Sprintf(\"invalid page '%s'\", pageStr),\n\t\t)\n\t}\n\n\t// generate dummy data for the page\n\tdata := make([][]string, 0, 200)\n\tfor i := range 200 {\n\t\tdata = append(data, []string{fmt.Sprintf(\"Key%d\", i), fmt.Sprintf(\"Value%d\", i)})\n\t}\n\tout := data[(page-1)*ItemsPerPage : (page-1)*ItemsPerPage+ItemsPerPage]\n\n\tmaxPages := int(math.Ceil(200 / ItemsPerPage))\n\tlow, high := getPaginationLowAndHigh(page, maxPages, PagesPerSide)\n\n\tcom := examples.BasicPagination(\n\t\t\"pagination-example-1\",\n\t\tcomponents.PaginationProps{\n\t\t\tURL: c.Request().URL.Path,\n\t\t\tPage: page,\n\t\t\tLow: low,\n\t\t\tHigh: high,\n\t\t\tMaxPages: maxPages,\n\t\t},\n\t\tout)\n\n\treturn render(c, http.StatusOK, com)\n}\n\nfunc getPaginationLowAndHigh(page, maxPages, pagePerSide int) (int, int) {\n\tlow := max(0, page-pagePerSide-1)\n\thigh := min(maxPages-1, page+pagePerSide-1)\n\n\t// if there are less than 'pagePerSide' pages to the left\n\t// of the current page, we add more pages to the high end of the\n\t// pagination element by adding to 'high'\n\tadd := pagePerSide - page\n\tif add \u003e= 0 {\n\t\thigh += add + 1\n\t}\n\n\t// if there are less than 'pagePerSide' pages to the right\n\t// of the current page, we add more pages to the low end of the\n\t// pagination element, by subtracting from 'low'\n\tdistanceFromMaxPages := maxPages - page\n\tif distanceFromMaxPages \u003c pagePerSide {\n\t\tlow -= (pagePerSide - distanceFromMaxPages)\n\t}\n\treturn low, high\n}\n\n```" } ], "pricing": [ { "name": "PricingExample", "code": "```go\ntempl PricingExample() {\n\t@components.Pricing(\n\t\tcomponents.PricingProps{\n\t\t\tChecked: true,\n\t\t\tPrices: []components.PriceProps{\n\t\t\t\t{\n\t\t\t\t\tTitle: \"Free\",\n\t\t\t\t\tDescription: \"My free plan\",\n\t\t\t\t\tPriceMonthly: \"$ 0\",\n\t\t\t\t\tPerMonthly: \"/ month\",\n\t\t\t\t\tPriceAnnually: \"$ 0\",\n\t\t\t\t\tPerAnnually: \"/ month\",\n\t\t\t\t\tIncludedFeatures: []string{\"Feature 1\", \"Feature 2\", \"Feature 3\"},\n\t\t\t\t\tExcludedFeatures: []string{\"Feature 4\", \"Feature 5\"},\n\t\t\t\t\tCallToAction: components.PriceButtonProps{\n\t\t\t\t\t\tLabel: \"Start free\",\n\t\t\t\t\t\tAttrs: templ.Attributes{\n\t\t\t\t\t\t\t\"class\": \"btn btn-outline mt-8\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tTitle: \"Starter\",\n\t\t\t\t\tDescription: \"Starter plan\",\n\t\t\t\t\tPriceMonthly: \"$ 12\",\n\t\t\t\t\tPerMonthly: \"/ month\",\n\t\t\t\t\tPriceAnnually: \"$ 10\",\n\t\t\t\t\tPerAnnually: \"/ month\",\n\t\t\t\t\tIncludedFeatures: []string{\"Feature 1\", \"Feature 2\", \"Feature 3\"},\n\t\t\t\t\tExcludedFeatures: []string{\"Feature 4\", \"Feature 5\"},\n\t\t\t\t\tCallToAction: components.PriceButtonProps{\n\t\t\t\t\t\tLabel: \"Get started\",\n\t\t\t\t\t\tAttrs: templ.Attributes{\n\t\t\t\t\t\t\t\"class\": \"btn btn-primary mt-8\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tTitle: \"Professional\",\n\t\t\t\t\tDescription: \"Professional plan\",\n\t\t\t\t\tPriceMonthly: \"$ 20\",\n\t\t\t\t\tPerMonthly: \"/ month\",\n\t\t\t\t\tPriceAnnually: \"$ 16\",\n\t\t\t\t\tPerAnnually: \"/ month\",\n\t\t\t\t\tIncludedFeatures: []string{\"Feature 1\", \"Feature 2\", \"Feature 3\"},\n\t\t\t\t\tExcludedFeatures: []string{\"Feature 4\", \"Feature 5\"},\n\t\t\t\t\tCallToAction: components.PriceButtonProps{\n\t\t\t\t\t\tLabel: \"Get started\",\n\t\t\t\t\t\tAttrs: templ.Attributes{\n\t\t\t\t\t\t\t\"class\": \"btn btn-primary mt-8\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t)\n}\n\n```", "title": "Basic pricing section" }, { "name": "PricingWithPromotionExample", "code": "```go\ntempl PricingWithPromotionExample() {\n\t@components.Pricing(\n\t\tcomponents.PricingProps{\n\t\t\tChecked: true,\n\t\t\tPrices: []components.PriceProps{\n\t\t\t\t{\n\t\t\t\t\tTitle: \"Free\",\n\t\t\t\t\tDescription: \"My free plan\",\n\t\t\t\t\tPriceMonthly: \"$ 0\",\n\t\t\t\t\tPerMonthly: \"/ month\",\n\t\t\t\t\tPriceAnnually: \"$ 0\",\n\t\t\t\t\tPerAnnually: \"/ month\",\n\t\t\t\t\tIncludedFeatures: []string{\"Feature 1\", \"Feature 2\", \"Feature 3\"},\n\t\t\t\t\tExcludedFeatures: []string{\"Feature 4\", \"Feature 5\"},\n\t\t\t\t\tCallToAction: components.PriceButtonProps{\n\t\t\t\t\t\tLabel: \"Start free\",\n\t\t\t\t\t\tAttrs: templ.Attributes{\n\t\t\t\t\t\t\t\"class\": \"btn btn-outline mt-8\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tTitle: \"Starter\",\n\t\t\t\t\tDescription: \"Starter plan\",\n\t\t\t\t\tPriceMonthly: \"$ 12\",\n\t\t\t\t\tPerMonthly: \"/ month\",\n\t\t\t\t\tPriceAnnually: \"$ 10\",\n\t\t\t\t\tPerAnnually: \"/ month\",\n\t\t\t\t\tIncludedFeatures: []string{\"Feature 1\", \"Feature 2\", \"Feature 3\"},\n\t\t\t\t\tExcludedFeatures: []string{\"Feature 4\", \"Feature 5\"},\n\t\t\t\t\tCallToAction: components.PriceButtonProps{\n\t\t\t\t\t\tLabel: \"Get started\",\n\t\t\t\t\t\tAttrs: templ.Attributes{\n\t\t\t\t\t\t\t\"class\": \"btn btn-primary mt-8\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tTitle: \"Professional\",\n\t\t\t\t\tDescription: \"Professional plan\",\n\t\t\t\t\tPriceMonthly: \"$ 20\",\n\t\t\t\t\tPerMonthly: \"/ month\",\n\t\t\t\t\tPriceAnnually: \"$ 16\",\n\t\t\t\t\tPerAnnually: \"/ month\",\n\t\t\t\t\tPromotion: \"Popular\",\n\t\t\t\t\tIncludedFeatures: []string{\"Feature 1\", \"Feature 2\", \"Feature 3\"},\n\t\t\t\t\tExcludedFeatures: []string{\"Feature 4\", \"Feature 5\"},\n\t\t\t\t\tCallToAction: components.PriceButtonProps{\n\t\t\t\t\t\tLabel: \"Get started\",\n\t\t\t\t\t\tAttrs: templ.Attributes{\n\t\t\t\t\t\t\t\"class\": \"btn btn-primary mt-8\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tTitle: \"Ultimate\",\n\t\t\t\t\tDescription: \"Ultimate plan\",\n\t\t\t\t\tPriceMonthly: \"$ 30\",\n\t\t\t\t\tPerMonthly: \"/ month\",\n\t\t\t\t\tPriceAnnually: \"$ 25\",\n\t\t\t\t\tPerAnnually: \"/ month\",\n\t\t\t\t\tIncludedFeatures: []string{\"Feature 1\", \"Feature 2\", \"Feature 3\", \"Feature 4\", \"Feature 5\"},\n\t\t\t\t\tCallToAction: components.PriceButtonProps{\n\t\t\t\t\t\tLabel: \"Get started\",\n\t\t\t\t\t\tAttrs: templ.Attributes{\n\t\t\t\t\t\t\t\"class\": \"btn btn-primary mt-8\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t)\n}\n```", "title": "Pricing section with promotion" } ], "radio": [ { "name": "DefaultRadio", "code": "```go\ntempl DefaultRadio() {\n\t\u003cdiv class=\"max-w-xs mx-auto pt-4\"\u003e\n\t\t@components.Radio(\n\t\t\tcomponents.RadioProps{\n\t\t\t\tName: \"my-radio-group1\",\n\t\t\t\tValues: map[string]string{\n\t\t\t\t\t\"Apples\": \"apples\",\n\t\t\t\t\t\"Oranges\": \"oranges\",\n\t\t\t\t},\n\t\t\t\tSize: \"xs\",\n\t\t\t},\n\t\t)\n\t\t\u003cdiv class=\"divider\"\u003e\u003c/div\u003e\n\t\t@components.Radio(\n\t\t\tcomponents.RadioProps{\n\t\t\t\tName: \"my-radio-group1\",\n\t\t\t\tValues: map[string]string{\n\t\t\t\t\t\"Apples\": \"apples\",\n\t\t\t\t\t\"Oranges\": \"oranges\",\n\t\t\t\t},\n\t\t\t\tSize: \"sm\",\n\t\t\t},\n\t\t)\n\t\t\u003cdiv class=\"divider\"\u003e\u003c/div\u003e\n\t\t@components.Radio(\n\t\t\tcomponents.RadioProps{\n\t\t\t\tName: \"my-radio-group1\",\n\t\t\t\tValues: map[string]string{\n\t\t\t\t\t\"Apples\": \"apples\",\n\t\t\t\t\t\"Oranges\": \"oranges\",\n\t\t\t\t},\n\t\t\t},\n\t\t)\n\t\t\u003cdiv class=\"divider\"\u003e\u003c/div\u003e\n\t\t@components.Radio(\n\t\t\tcomponents.RadioProps{\n\t\t\t\tName: \"my-radio-group1\",\n\t\t\t\tValues: map[string]string{\n\t\t\t\t\t\"Apples\": \"apples\",\n\t\t\t\t\t\"Oranges\": \"oranges\",\n\t\t\t\t},\n\t\t\t\tSize: \"lg\",\n\t\t\t},\n\t\t)\n\t\t\u003cdiv class=\"divider\"\u003e\u003c/div\u003e\n\t\t@components.Radio(\n\t\t\tcomponents.RadioProps{\n\t\t\t\tName: \"my-radio-group1\",\n\t\t\t\tValues: map[string]string{\n\t\t\t\t\t\"Apples\": \"apples\",\n\t\t\t\t\t\"Oranges\": \"oranges\",\n\t\t\t\t},\n\t\t\t\tSize: \"xl\",\n\t\t\t},\n\t\t)\n\t\u003c/div\u003e\n}\n\n```", "title": "Different size radio groups" }, { "name": "PrimaryRadio", "code": "```go\ntempl PrimaryRadio() {\n\t\u003cdiv class=\"max-w-xs mx-auto pt-4\"\u003e\n\t\t@components.Radio(\n\t\t\tcomponents.RadioProps{\n\t\t\t\tName: \"my-radio-group2\",\n\t\t\t\tValues: map[string]string{\n\t\t\t\t\t\"Apples\": \"apples\",\n\t\t\t\t\t\"Oranges\": \"oranges\",\n\t\t\t\t\t\"And something truly special here to see how this works with a longer key\": \"blaaaaa\",\n\t\t\t\t},\n\t\t\t\tClass: \"radio-primary\",\n\t\t\t},\n\t\t)\n\t\u003c/div\u003e\n}\n```", "title": "Primary radio group" } ], "range": [ { "name": "BasicRange", "code": "```go\ntempl BasicRange() {\n\t\u003cdiv class=\"max-w-sm mx-auto pt-4 space-y-2\"\u003e\n\t\t@components.Range(\n\t\t\tcomponents.RangeProps{\n\t\t\t\tName: \"my-range\",\n\t\t\t\tValue: 25,\n\t\t\t\tMin: 0,\n\t\t\t\tMax: 100,\n\t\t\t\tStep: 5,\n\t\t\t\tSize: \"xs\",\n\t\t\t},\n\t\t)\n\t\t@components.Range(\n\t\t\tcomponents.RangeProps{\n\t\t\t\tName: \"my-range\",\n\t\t\t\tValue: 25,\n\t\t\t\tMin: 0,\n\t\t\t\tMax: 100,\n\t\t\t\tStep: 5,\n\t\t\t\tSize: \"sm\",\n\t\t\t},\n\t\t)\n\t\t@components.Range(\n\t\t\tcomponents.RangeProps{\n\t\t\t\tName: \"my-range\",\n\t\t\t\tValue: 25,\n\t\t\t\tMin: 0,\n\t\t\t\tMax: 100,\n\t\t\t\tStep: 5,\n\t\t\t},\n\t\t)\n\t\t@components.Range(\n\t\t\tcomponents.RangeProps{\n\t\t\t\tName: \"my-range\",\n\t\t\t\tValue: 25,\n\t\t\t\tMin: 0,\n\t\t\t\tMax: 100,\n\t\t\t\tStep: 5,\n\t\t\t\tSize: \"lg\",\n\t\t\t},\n\t\t)\n\t\t@components.Range(\n\t\t\tcomponents.RangeProps{\n\t\t\t\tName: \"my-range\",\n\t\t\t\tValue: 25,\n\t\t\t\tMin: 0,\n\t\t\t\tMax: 100,\n\t\t\t\tStep: 5,\n\t\t\t\tSize: \"xl\",\n\t\t\t},\n\t\t)\n\t\u003c/div\u003e\n}\n\n```", "title": "Range component using alpine.js" }, { "name": "DatastarRange", "code": "```go\ntempl DatastarRange() {\n\t\u003cdiv class=\"max-w-sm mx-auto pt-4\"\u003e\n\t\t@components.DatastarRange(\n\t\t\tcomponents.RangeProps{\n\t\t\t\tName: \"my-range\",\n\t\t\t\tValue: 25,\n\t\t\t\tMin: 0,\n\t\t\t\tMax: 100,\n\t\t\t\tStep: 5,\n\t\t\t},\n\t\t)\n\t\u003c/div\u003e\n}\n```", "title": "Range component using datastar.js" } ], "rating": [ { "name": "RatingFromOneToFive", "code": "```go\ntempl RatingFromOneToFive() {\n\t\u003cdiv class=\"flex justify-center items-center pt-4\"\u003e\n\t\t@components.Rating(\n\t\t\tcomponents.RatingProps{\n\t\t\t\tName: \"my-rating\",\n\t\t\t\tMin: 1,\n\t\t\t\tMax: 5,\n\t\t\t},\n\t\t)\n\t\u003c/div\u003e\n}\n\n```", "title": "Rating from 1 to 5" }, { "name": "RatingFromZeroToFive", "code": "```go\ntempl RatingFromZeroToFive() {\n\t\u003cdiv class=\"flex justify-center items-center pt-4\"\u003e\n\t\t@components.Rating(\n\t\t\tcomponents.RatingProps{\n\t\t\t\tName: \"my-rating2\",\n\t\t\t\tMin: 0,\n\t\t\t\tMax: 5,\n\t\t\t},\n\t\t)\n\t\u003c/div\u003e\n}\n```", "title": "Rating from 0 to 5" } ], "select": [ { "name": "DifferentSizeSelects", "code": "```go\ntempl DifferentSizeSelects() {\n\t\u003cdiv class=\"max-w-xs mx-auto pt-4\"\u003e\n\t\t@components.Select(\n\t\t\tcomponents.SelectProps{\n\t\t\t\tLabel: \"Make a choice\",\n\t\t\t\tName: \"my-select\",\n\t\t\t\tOptions: []components.SelectOption{\n\t\t\t\t\t{Label: \"Which one?\", Selected: true, Disabled: true},\n\t\t\t\t\t{Label: \"Apples\", Value: \"apples\"},\n\t\t\t\t\t{Label: \"Oranges\", Value: \"oranges\"},\n\t\t\t\t},\n\t\t\t\tSize: \"xs\",\n\t\t\t},\n\t\t)\n\t\t@components.Select(\n\t\t\tcomponents.SelectProps{\n\t\t\t\tLabel: \"Make a choice\",\n\t\t\t\tName: \"my-select\",\n\t\t\t\tOptions: []components.SelectOption{\n\t\t\t\t\t{Label: \"Which one?\", Selected: true, Disabled: true},\n\t\t\t\t\t{Label: \"Apples\", Value: \"apples\"},\n\t\t\t\t\t{Label: \"Oranges\", Value: \"oranges\"},\n\t\t\t\t},\n\t\t\t\tSize: \"sm\",\n\t\t\t},\n\t\t)\n\t\t@components.Select(\n\t\t\tcomponents.SelectProps{\n\t\t\t\tLabel: \"Make a choice\",\n\t\t\t\tName: \"my-select\",\n\t\t\t\tOptions: []components.SelectOption{\n\t\t\t\t\t{Label: \"Which one?\", Selected: true, Disabled: true},\n\t\t\t\t\t{Label: \"Apples\", Value: \"apples\"},\n\t\t\t\t\t{Label: \"Oranges\", Value: \"oranges\"},\n\t\t\t\t},\n\t\t\t},\n\t\t)\n\t\t@components.Select(\n\t\t\tcomponents.SelectProps{\n\t\t\t\tLabel: \"Make a choice\",\n\t\t\t\tName: \"my-select\",\n\t\t\t\tOptions: []components.SelectOption{\n\t\t\t\t\t{Label: \"Which one?\", Selected: true, Disabled: true},\n\t\t\t\t\t{Label: \"Apples\", Value: \"apples\"},\n\t\t\t\t\t{Label: \"Oranges\", Value: \"oranges\"},\n\t\t\t\t},\n\t\t\t\tSize: \"lg\",\n\t\t\t},\n\t\t)\n\t\t@components.Select(\n\t\t\tcomponents.SelectProps{\n\t\t\t\tLabel: \"Make a choice\",\n\t\t\t\tName: \"my-select\",\n\t\t\t\tOptions: []components.SelectOption{\n\t\t\t\t\t{Label: \"Which one?\", Selected: true, Disabled: true},\n\t\t\t\t\t{Label: \"Apples\", Value: \"apples\"},\n\t\t\t\t\t{Label: \"Oranges\", Value: \"oranges\"},\n\t\t\t\t},\n\t\t\t\tSize: \"xl\",\n\t\t\t},\n\t\t)\n\t\u003c/div\u003e\n}\n\n```", "title": "Different size selects" }, { "name": "CascadingSelect", "code": "```go\ntempl CascadingSelect() {\n\t\u003cdiv class=\"max-w-xs mx-auto py-8\"\u003e\n\t\t@components.Select(\n\t\t\tcomponents.SelectProps{\n\t\t\t\tLabel: \"Make\",\n\t\t\t\tName: \"make\",\n\t\t\t\tOptions: []components.SelectOption{\n\t\t\t\t\t{Label: \"Audi\", Value: \"audi\"},\n\t\t\t\t\t{Label: \"BMW\", Value: \"bmw\"},\n\t\t\t\t\t{Label: \"Toyota\", Value: \"toyota\"},\n\t\t\t\t},\n\t\t\t\tAttrs: templ.Attributes{\n\t\t\t\t\t\"hx-get\": \"/models\",\n\t\t\t\t\t\"hx-target\": \"#models\",\n\t\t\t\t},\n\t\t\t},\n\t\t)\n\t\t@components.Select(\n\t\t\tcomponents.SelectProps{\n\t\t\t\tLabel: \"Model\",\n\t\t\t\tName: \"model\",\n\t\t\t\tOptions: []components.SelectOption{\n\t\t\t\t\t{Label: \"A1\", Value: \"a1\"},\n\t\t\t\t\t{Label: \"A4\", Value: \"a4\"},\n\t\t\t\t\t{Label: \"A6\", Value: \"a6\"},\n\t\t\t\t},\n\t\t\t\tAttrs: templ.Attributes{\n\t\t\t\t\t\"id\": \"models\",\n\t\t\t\t},\n\t\t\t},\n\t\t)\n\t\u003c/div\u003e\n}\n```", "handler": "```go\nvar modelOptions = map[string][]components.SelectOption{\n\t\"audi\": {\n\t\t{Label: \"A1\", Value: \"a1\"},\n\t\t{Label: \"A4\", Value: \"a4\"},\n\t\t{Label: \"A6\", Value: \"a6\"},\n\t},\n\t\"bmw\": {\n\t\t{Label: \"316i\", Value: \"316i\"},\n\t\t{Label: \"320d\", Value: \"320d\"},\n\t},\n\t\"toyota\": {\n\t\t{Label: \"Corolla\", Value: \"corolla\"},\n\t\t{Label: \"Yaris\", Value: \"yaris\"},\n\t\t{Label: \"RAV4\", Value: \"rav4\"},\n\t},\n}\n\nfunc GetCascadingSelectExample(c echo.Context) error {\n\tmake := c.FormValue(\"make\")\n\n\treturn render(c, http.StatusOK, components.SelectOptions(modelOptions[make]))\n}\n\n```", "title": "Cascading select" } ], "skeleton": [ { "name": "SkeletonExample", "code": "```go\ntempl SkeletonExample() {\n\t\u003cdiv class=\"max-w-xs mx-auto pt-4\"\u003e\n\t\t@components.Skeleton()\n\t\u003c/div\u003e\n}\n```" } ], "stats": [ { "name": "BasicStats", "code": "```go\ntempl BasicStats() {\n\t\u003cdiv class=\"flex justify-center items-center pt-4\"\u003e\n\t\t@components.Stats() {\n\t\t\t@components.Stat(components.StatProps{\n\t\t\t\tTitle: \"Downloads\",\n\t\t\t\tValue: \"31k\",\n\t\t\t\tDescription: \"Jan 1st - Feb 1st\",\n\t\t\t})\n\t\t\t@components.Stat(components.StatProps{\n\t\t\t\tTitle: \"New Users\",\n\t\t\t\tValue: \"4,200\",\n\t\t\t\tDescription: \"↗︎ 400 (22%)\",\n\t\t\t})\n\t\t\t@components.Stat(components.StatProps{\n\t\t\t\tTitle: \"New Registers\",\n\t\t\t\tValue: \"1,200\",\n\t\t\t\tDescription: \"↘︎ 90 (14%)\",\n\t\t\t})\n\t\t}\n\t\u003c/div\u003e\n}\n```" } ], "status": [ { "name": "StatusNotFound", "code": "```go\ntempl StatusNotFound() {\n\t@components.Status(components.StatusProps{\n\t\tCode: 404,\n\t\tTitle: \"Not Found\",\n\t\tDescription: \"Looks like there's nothing here...\",\n\t\tReturnButtonLabel: \"Go back\",\n\t})\n}\n\n```", "title": "404 Not Found status" }, { "name": "StatusForbidden", "code": "```go\ntempl StatusForbidden() {\n\t@components.Status(components.StatusProps{\n\t\tCode: 403,\n\t\tTitle: \"Forbidden\",\n\t\tDescription: \"Invalid permissions to view this page.\",\n\t\tReturnButtonLabel: \"Go back\",\n\t})\n}\n\n```", "title": "403 Forbidden status" }, { "name": "StatusUnauthorized", "code": "```go\ntempl StatusUnauthorized() {\n\t@components.Status(components.StatusProps{\n\t\tCode: 401,\n\t\tTitle: \"Unauthorized\",\n\t\tDescription: \"This page is only available to authenticated users.\",\n\t\tReturnButtonLabel: \"Go back\",\n\t})\n}\n\n```", "title": "401 Unauthorized status" }, { "name": "StatusInternalServerError", "code": "```go\ntempl StatusInternalServerError() {\n\t@components.Status(components.StatusProps{\n\t\tCode: 500,\n\t\tTitle: \"Internal Server Error\",\n\t\tDescription: \"Something went terribly wrong...\",\n\t\tReturnButtonLabel: \"Go back\",\n\t})\n}\n```", "title": "500 Internal Server Error status" } ], "steps": [ { "name": "BasicSteps", "code": "```go\ntempl BasicSteps() {\n\t\u003cdiv class=\"flex justify-center items-center pt-4\"\u003e\n\t\t@components.Steps() {\n\t\t\t@components.Step(components.StepProps{Label: \"Register\", Done: true})\n\t\t\t@components.Step(components.StepProps{Label: \"Choose plan\", Done: true})\n\t\t\t@components.Step(components.StepProps{Label: \"Purchase\"})\n\t\t\t@components.Step(components.StepProps{Label: \"Receive product\"})\n\t\t}\n\t\u003c/div\u003e\n}\n```" } ], "swap": [ { "name": "BasicSwap", "code": "```go\ntempl BasicSwap() {\n\t\u003cdiv class=\"flex justify-center items-center h-20\"\u003e\n\t\t@components.Swap(\n\t\t\tcomponents.SwapProps{\n\t\t\t\tOn: SwapExampleOn(),\n\t\t\t\tOff: SwapExampleOff(),\n\t\t\t\tClass: \"swap-flip\",\n\t\t\t},\n\t\t)\n\t\u003c/div\u003e\n}\n\ntempl SwapExampleOn() {\n\t\u003cspan class=\"font-bold text-success\"\u003eON\u003c/span\u003e\n}\n\ntempl SwapExampleOff() {\n\t\u003cspan class=\"font-bold text-base-content/70\"\u003eOFF\u003c/span\u003e\n}\n```" } ], "table": [ { "name": "BasicTable", "code": "```go\ntempl BasicTable() {\n\t@components.Table(\n\t\t[]templ.Component{\n\t\t\tcomponents.Checkbox(\n\t\t\t\tcomponents.CheckboxProps{\n\t\t\t\t\tName: \"all\",\n\t\t\t\t},\n\t\t\t),\n\t\t\tcomponents.PlainText(\"Name\"),\n\t\t\tcomponents.PlainText(\"Email\"),\n\t\t},\n\t\t[]templ.Component{\n\t\t\tTableExampleRow(\"John Doe\", \"john.doe@example.com\"),\n\t\t\tTableExampleRow(\"Jane Doe\", \"Jane.doe@example.com\"),\n\t\t\tTableExampleRow(\"Jim Smith\", \"jim.smith@example.com\"),\n\t\t\tTableExampleRow(\"Julie Smith\", \"julie.smith@example.com\"),\n\t\t},\n\t\tnil,\n\t)\n}\n\ntempl TableExampleRow(name, email string) {\n\t\u003ctr\u003e\n\t\t\u003ctd\u003e\n\t\t\t@components.Checkbox(components.CheckboxProps{Name: email})\n\t\t\u003c/td\u003e\n\t\t\u003ctd\u003e\n\t\t\t@components.PlainText(name)\n\t\t\u003c/td\u003e\n\t\t\u003ctd\u003e\n\t\t\t@components.PlainText(email)\n\t\t\u003c/td\u003e\n\t\u003c/tr\u003e\n}\n```" } ], "tabs": [ { "name": "BasicTabs", "code": "```go\ntempl BasicTabs() {\n\t@components.Tabs(\n\t\tcomponents.TabsProps{\n\t\t\tName: \"basic-tabs\",\n\t\t\tClass: \"tabs-border\",\n\t\t\tContentClass: \"bg-base-100 py-8\",\n\t\t\tTabs: []components.TabProps{\n\t\t\t\t{\n\t\t\t\t\tLabel: \"Home\",\n\t\t\t\t\tContent: homeTabContent(),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tLabel: \"Info\",\n\t\t\t\t\tContent: infoTabContent(),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tLabel: \"Stats\",\n\t\t\t\t\tContent: statsTabContent(),\n\t\t\t\t},\n\t\t\t},\n\t\t})\n}\n\ntempl homeTabContent() {\n\t\u003cp\u003eThis is the home tab\u003c/p\u003e\n}\n\ntempl infoTabContent() {\n\t\u003cp\u003eThis is the info tab\u003c/p\u003e\n}\n\ntempl statsTabContent() {\n\t\u003cp\u003eThis is the stats tab\u003c/p\u003e\n}\n```" } ], "testimonial": [ { "name": "TestimonialGridExample", "code": "```go\ntempl TestimonialGridExample() {\n\t@components.TestimonialGrid(\n\t\t\"Read what our customers think\",\n\t\tcomponents.TestimonialProps{\n\t\t\t{\n\t\t\t\tAvatar: components.Avatar(components.AvatarProps{\n\t\t\t\t\tContainerClass: \"rounded h-20\",\n\t\t\t\t\tSource: \"/static/images/avatar.jpg\",\n\t\t\t\t}),\n\t\t\t\tName: \"Jane Doe\",\n\t\t\t\tRating: 5,\n\t\t\t\tContent: `Lorem ipsum dolor sit amet consectetur adipisicing elit. \nIure impedit, placeat sed provident enim fuga possimus ducimus est iusto inventore \nearum aliquid officia minus, maiores quia dicta magni ex labore? Lorem ipsum dolor \nsit amet consectetur adipisicing elit. Libero maxime quos laboriosam natus illum \nsimilique id nam, rerum, sunt veritatis dolorum accusamus voluptas odio minus \nnecessitatibus perspiciatis, aliquid repellat iste.`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tAvatar: components.Avatar(\n\t\t\t\t\tcomponents.AvatarProps{\n\t\t\t\t\t\tContainerClass: \"rounded h-20\",\n\t\t\t\t\t\tSource: \"/static/images/avatar.jpg\"},\n\t\t\t\t),\n\t\t\t\tName: \"Jane Doe\",\n\t\t\t\tRating: 4,\n\t\t\t\tContent: `maiores quia dicta magni ex labore? Lorem ipsum dolor \nsit amet consectetur adipisicing elit. Libero maxime quos laboriosam natus illum \nsimilique id nam, rerum, sunt veritatis dolorum accusamus voluptas odio minus \nnecessitatibus perspiciatis, aliquid repellat iste.`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tAvatar: components.Avatar(components.AvatarProps{\n\t\t\t\t\tContainerClass: \"rounded h-20\",\n\t\t\t\t\tSource: \"/static/images/avatar.jpg\",\n\t\t\t\t}),\n\t\t\t\tName: \"Jane Doe\",\n\t\t\t\tRating: 3,\n\t\t\t\tContent: `Iure impedit, placeat sed provident enim fuga possimus \nducimus est iusto inventore earum aliquid officia minus, maiores quia dicta magni \nex labore? Lorem ipsum dolor sit amet consectetur adipisicing elit. Libero maxime \nquos laboriosam natus illum similique id nam, rerum, sunt veritatis dolorum \naccusamus voluptas odio minus necessitatibus perspiciatis, aliquid repellat iste.`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tAvatar: components.Avatar(components.AvatarProps{\n\t\t\t\t\tContainerClass: \"rounded h-20\",\n\t\t\t\t\tSource: \"/static/images/avatar.jpg\",\n\t\t\t\t}),\n\t\t\t\tName: \"Jane Doe\",\n\t\t\t\tRating: 5,\n\t\t\t\tContent: `Lorem ipsum dolor sit amet consectetur adipisicing elit. \nIure impedit, placeat sed provident enim fuga possimus ducimus est iusto inventore \nearum aliquid officia minus, maiores quia dicta magni ex labore? Lorem ipsum dolor \nsit amet consectetur adipisicing elit. Libero maxime quos laboriosam natus illum \nsimilique id nam, rerum, sunt veritatis dolorum accusamus voluptas odio minus \nnecessitatibus perspiciatis, aliquid repellat iste.`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tAvatar: components.Avatar(components.AvatarProps{\n\t\t\t\t\tContainerClass: \"rounded h-20\",\n\t\t\t\t\tSource: \"/static/images/avatar.jpg\",\n\t\t\t\t}),\n\t\t\t\tName: \"Jane Doe\",\n\t\t\t\tRating: 5,\n\t\t\t\tContent: \"Lorem ipsum dolor sit amet consectetur adipisicing elit.\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tAvatar: components.Avatar(components.AvatarProps{\n\t\t\t\t\tContainerClass: \"rounded h-20\",\n\t\t\t\t\tSource: \"/static/images/avatar.jpg\",\n\t\t\t\t}),\n\t\t\t\tName: \"Jane Doe\",\n\t\t\t\tRating: 1,\n\t\t\t\tContent: `Lorem ipsum dolor sit amet consectetur adipisicing elit. \nIure impedit, placeat sed provident enim fuga possimus ducimus est iusto inventore \nearum aliquid officia minus.`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tAvatar: components.Avatar(components.AvatarProps{\n\t\t\t\t\tContainerClass: \"rounded h-20\",\n\t\t\t\t\tSource: \"/static/images/avatar.jpg\",\n\t\t\t\t}),\n\t\t\t\tName: \"Jane Doe\",\n\t\t\t\tRating: 2,\n\t\t\t\tContent: `Lorem ipsum dolor sit amet consectetur adipisicing elit. \nIure impedit, placeat sed provident enim fuga possimus ducimus est iusto inventore \nearum aliquid officia minus, maiores quia dicta magni ex labore? Lorem ipsum dolor \nsit amet consectetur adipisicing elit.`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tAvatar: components.Avatar(components.AvatarProps{\n\t\t\t\t\tContainerClass: \"rounded h-20\",\n\t\t\t\t\tSource: \"/static/images/avatar.jpg\",\n\t\t\t\t}),\n\t\t\t\tName: \"Jane Doe\",\n\t\t\t\tRating: 5,\n\t\t\t\tContent: `Lorem ipsum dolor sit amet consectetur adipisicing elit. \nIure impedit, placeat sed provident enim fuga possimus ducimus est iusto inventore\nearum aliquid officia minus, maiores quia dicta magni ex labore? Lorem ipsum dolor \nsit amet consectetur adipisicing elit. Libero maxime quos laboriosam natus illum \nsimilique id nam, rerum, sunt veritatis.`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tAvatar: components.Avatar(components.AvatarProps{\n\t\t\t\t\tContainerClass: \"rounded h-20\",\n\t\t\t\t\tSource: \"/static/images/avatar.jpg\",\n\t\t\t\t}),\n\t\t\t\tName: \"Jane Doe\",\n\t\t\t\tRating: 4,\n\t\t\t\tContent: `Lorem ipsum dolor sit amet consectetur adipisicing elit. \nIure impedit, placeat sed provident enim fuga possimus ducimus est iusto inventore \nearum aliquid officia minus, maiores quia dicta magni ex labore? Lorem ipsum dolor \nsit amet consectetur adipisicing elit. Libero maxime quos laboriosam natus illum \nsimilique id nam, rerum.`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tAvatar: components.Avatar(components.AvatarProps{\n\t\t\t\t\tContainerClass: \"rounded h-20\",\n\t\t\t\t\tSource: \"/static/images/avatar.jpg\",\n\t\t\t\t}),\n\t\t\t\tName: \"Jane Doe\",\n\t\t\t\tRating: 4,\n\t\t\t\tContent: `Lorem ipsum dolor sit amet consectetur adipisicing elit. \nIure impedit, placeat sed provident enim fuga possimus ducimus est iusto inventore \nearum aliquid officia minus, maiores quia dicta magni ex labore? Lorem ipsum dolor \nsit amet consectetur adipisicing elit. Libero maxime quos laboriosam natus illum \nsimilique id nam, rerum, sunt veritatis dolorum accusamus voluptas.`,\n\t\t\t},\n\t\t},\n\t)\n}\n```" } ], "text_rotate": [ { "name": "TextRotateExample", "code": "```go\ntempl TextRotateExample() {\n\t@components.TextRotate(\n\t\tcomponents.TextRotateProps{\n\t\t\tClass: \"text-5xl font-bold\",\n\t\t\tItems: []components.TextRotateItem{\n\t\t\t\t{Text: \"ONE\", Class: \"text-primary\"},\n\t\t\t\t{Text: \"TWO\", Class: \"text-accent\"},\n\t\t\t\t{Text: \"THREE\", Class: \"text-secondary\"},\n\t\t\t},\n\t\t},\n\t)\n}\n```" } ], "textarea": [ { "name": "BasicTextarea", "code": "```go\ntempl BasicTextarea() {\n\t\u003cdiv class=\"pt-4\"\u003e\n\t\t@components.Textarea(\n\t\t\tcomponents.TextareaProps{\n\t\t\t\tLabel: \"Description\",\n\t\t\t\tName: \"description\",\n\t\t\t\tClass: \"textarea-bordered resize-none\",\n\t\t\t},\n\t\t)\n\t\u003c/div\u003e\n}\n\n```", "title": "Basic textarea" }, { "name": "DifferentSizeTextareas", "code": "```go\ntempl DifferentSizeTextareas() {\n\t\u003cdiv class=\"pt-4\"\u003e\n\t\t@components.Textarea(\n\t\t\tcomponents.TextareaProps{\n\t\t\t\tLabel: \"Description\",\n\t\t\t\tClass: \"textarea-bordered resize-none\",\n\t\t\t\tValue: \"Extra small\",\n\t\t\t\tSize: \"xs\",\n\t\t\t},\n\t\t)\n\t\t@components.Textarea(\n\t\t\tcomponents.TextareaProps{\n\t\t\t\tLabel: \"Description\",\n\t\t\t\tClass: \"textarea-bordered resize-none\",\n\t\t\t\tValue: \"Small\",\n\t\t\t\tSize: \"sm\",\n\t\t\t},\n\t\t)\n\t\t@components.Textarea(\n\t\t\tcomponents.TextareaProps{\n\t\t\t\tLabel: \"Description\",\n\t\t\t\tClass: \"textarea-bordered resize-none\",\n\t\t\t\tValue: \"Medium\",\n\t\t\t},\n\t\t)\n\t\t@components.Textarea(\n\t\t\tcomponents.TextareaProps{\n\t\t\t\tLabel: \"Description\",\n\t\t\t\tClass: \"textarea-bordered resize-none\",\n\t\t\t\tValue: \"Large\",\n\t\t\t\tSize: \"lg\",\n\t\t\t},\n\t\t)\n\t\t@components.Textarea(\n\t\t\tcomponents.TextareaProps{\n\t\t\t\tLabel: \"Description\",\n\t\t\t\tClass: \"textarea-bordered resize-none\",\n\t\t\t\tValue: \"Extra Large\",\n\t\t\t\tSize: \"xl\",\n\t\t\t},\n\t\t)\n\t\u003c/div\u003e\n}\n\n```", "title": "Different sizes" }, { "name": "BasicTextareaWithError", "code": "```go\ntempl BasicTextareaWithError() {\n\t\u003cdiv class=\"pt-4\"\u003e\n\t\t@components.Textarea(\n\t\t\tcomponents.TextareaProps{\n\t\t\t\tLabel: \"Description\",\n\t\t\t\tName: \"description\",\n\t\t\t\tErr: \"Description cannot be empty\",\n\t\t\t\tClass: \"textarea-bordered resize-none\",\n\t\t\t},\n\t\t)\n\t\u003c/div\u003e\n}\n```", "title": "Textarea with error" } ], "time_slot_picker": [ { "name": "BasicTimeSlotPicker", "code": "```go\ntempl BasicTimeSlotPicker() {\n\t\u003cdiv hx-get={ \"/timeslotpicker?date=\" + time.Now().UTC().Format(\"2006-01-02\") } hx-trigger=\"intersect\" hx-swap=\"outerHTML\"\u003e\u003c/div\u003e\n}\n```", "handler": "```go\nfunc GetTimeSlotPicker(c echo.Context) error {\n\tdateStr := c.QueryParam(\"date\")\n\tdate, err := time.Parse(\"2006-01-02\", dateStr)\n\tif err != nil {\n\t\treturn newErrorToast(http.StatusUnprocessableEntity, \"Invalid date\")\n\t}\n\n\tprops := components.TimeSlotPickerProps{\n\t\tID: \"time-slot-picker-example\",\n\t\tCurrentDate: date,\n\t\tPickerURL: \"/timeslotpicker\",\n\t\tReserveURL: \"/timeslotpicker/reserve\",\n\t\tTimeSlots: getTimeSlots(date),\n\t}\n\treturn render(c, http.StatusOK, components.TimeSlotPicker(props))\n}\n\nfunc getTimeSlots(date time.Time) []components.TimeSlot {\n\tslots := make([]components.TimeSlot, 32)\n\n\tfor i := range 32 {\n\t\tslots[i] = components.TimeSlot{\n\t\t\tStart: date.Add(time.Duration(10+i*2) * time.Hour),\n\t\t\tEnd: date.Add(time.Duration(11+i*2) * time.Hour),\n\t\t}\n\t}\n\n\treturn slots\n}\n\nfunc PostTimeSlotPickerReserve(c echo.Context) error {\n\tstartStr := c.QueryParam(\"start\")\n\tstart, err := time.Parse(\"2006-01-02-15-04\", startStr)\n\tif err != nil {\n\t\treturn newErrorToast(http.StatusUnprocessableEntity, \"Invalid start date\")\n\t}\n\tendStr := c.QueryParam(\"end\")\n\tend, err := time.Parse(\"2006-01-02-15-04\", endStr)\n\tif err != nil {\n\t\treturn newErrorToast(http.StatusUnprocessableEntity, \"Invalid end date\")\n\t}\n\n\treturn renderInfoFade(\n\t\tc,\n\t\thttp.StatusOK,\n\t\t[]string{\n\t\t\tfmt.Sprintf(\n\t\t\t\t\"Time slot on %s, %s - %s reserved!\",\n\t\t\t\tstart.Format(\"Mon Jan 02\"),\n\t\t\t\tstart.Format(\"15:04\"),\n\t\t\t\tend.Format(\"15:04\"),\n\t\t\t),\n\t\t},\n\t)\n}\n\n```", "title": "Basic time slot picker" } ], "timeline": [ { "name": "BasicTimeline", "code": "```go\ntempl BasicTimeline() {\n\t\u003cdiv class=\"flex justify-center items-center pt-4\"\u003e\n\t\t@components.Timeline(\n\t\t\tcomponents.TimelineProps{\n\t\t\t\t{Start: \"1984\", Middle: components.TimelineCheckbox(true), End: \"First Macintosh computer\"},\n\t\t\t\t{Start: \"1998\", Middle: components.TimelineCheckbox(true), End: \"iMac\"},\n\t\t\t\t{Start: \"2001\", Middle: components.TimelineCheckbox(false), End: \"iPod\"},\n\t\t\t\t{Start: \"2007\", Middle: components.TimelineCheckbox(false), End: \"iPhone\"},\n\t\t\t\t{Start: \"2015\", Middle: components.TimelineCheckbox(false), End: \"Apple Watch\"},\n\t\t\t},\n\t\t)\n\t\u003c/div\u003e\n}\n```" } ], "toast": [ { "name": "InfoToast", "code": "```go\ntempl InfoToast() {\n\t\u003cdiv class=\"relative min-h-60\"\u003e\n\t\t@components.Toast(\n\t\t\tcomponents.ToastProps{\n\t\t\t\tName: \"info-toast\",\n\t\t\t\tToastClass: \"absolute toast-end toast-top\",\n\t\t\t\tAlertClass: \"alert-info\",\n\t\t\t},\n\t\t) {\n\t\t\t\u003cspan\u003eInfo toast\u003c/span\u003e\n\t\t}\n\t\u003c/div\u003e\n}\n\n```", "title": "Info-type toast" }, { "name": "WarningToast", "code": "```go\ntempl WarningToast() {\n\t\u003cdiv class=\"relative min-h-60\"\u003e\n\t\t@components.Toast(\n\t\t\tcomponents.ToastProps{\n\t\t\t\tName: \"warning-toast\",\n\t\t\t\tToastClass: \"absolute toast-end toast-bottom\",\n\t\t\t\tAlertClass: \"alert-warning\",\n\t\t\t},\n\t\t) {\n\t\t\t\u003cspan\u003eWarning toast\u003c/span\u003e\n\t\t}\n\t\u003c/div\u003e\n}\n\n```", "title": "Warning-type toast" }, { "name": "ErrorToast", "code": "```go\ntempl ErrorToast() {\n\t\u003cdiv class=\"relative min-h-60\"\u003e\n\t\t@components.Toast(\n\t\t\tcomponents.ToastProps{\n\t\t\t\tName: \"error-toast\",\n\t\t\t\tToastClass: \"absolute toast-center toast-top\",\n\t\t\t\tAlertClass: \"alert-error\",\n\t\t\t},\n\t\t) {\n\t\t\t\u003cspan\u003eError toast\u003c/span\u003e\n\t\t}\n\t\u003c/div\u003e\n}\n\n```", "title": "Error-type toast" }, { "name": "InfoToastConfirm", "code": "```go\ntempl InfoToastConfirm() {\n\t\u003cdiv class=\"relative min-h-60\"\u003e\n\t\t@components.Toast(components.ToastProps{\n\t\t\tName: \"error-toast\",\n\t\t\tToastClass: \"absolute toast-end toast-top\",\n\t\t\tAlertClass: \"alert-info\",\n\t\t},\n\t\t) {\n\t\t\t\u003cspan\u003eInfo toast\u003c/span\u003e\n\t\t\t\u003cbutton id=\"toast-remover-btn\" class=\"btn btn-info\"\u003eOK\u003c/button\u003e\n\t\t\t\u003cscript\u003e\n ((toast) =\u003e {\n document.getElementById(\"toast-remover-btn\").addEventListener(\"click\", () =\u003e {\n toast.remove()\n })\n })(document.currentScript.closest(\"div.toast\"))\n \u003c/script\u003e\n\t\t}\n\t\u003c/div\u003e\n}\n```", "title": "Info-type toast with button to remove it" } ], "toggle": [ { "name": "DifferentSizeToggles", "code": "```go\ntempl DifferentSizeToggles() {\n\t\u003cdiv class=\"max-w-52 mx-auto pt-4 space-y-2\"\u003e\n\t\t@components.Toggle(\n\t\t\tcomponents.ToggleProps{\n\t\t\t\tBefore: \"Check me out\",\n\t\t\t\tName: \"checkbox1\",\n\t\t\t\tSize: \"xs\",\n\t\t\t},\n\t\t)\n\t\t@components.Toggle(\n\t\t\tcomponents.ToggleProps{\n\t\t\t\tBefore: \"Check me out\",\n\t\t\t\tName: \"checkbox1\",\n\t\t\t\tSize: \"sm\",\n\t\t\t},\n\t\t)\n\t\t@components.Toggle(\n\t\t\tcomponents.ToggleProps{\n\t\t\t\tBefore: \"Check me out\",\n\t\t\t\tName: \"checkbox1\",\n\t\t\t},\n\t\t)\n\t\t@components.Toggle(\n\t\t\tcomponents.ToggleProps{\n\t\t\t\tBefore: \"Check me out\",\n\t\t\t\tName: \"checkbox1\",\n\t\t\t\tSize: \"lg\",\n\t\t\t},\n\t\t)\n\t\t@components.Toggle(\n\t\t\tcomponents.ToggleProps{\n\t\t\t\tBefore: \"Check me out\",\n\t\t\t\tName: \"checkbox1\",\n\t\t\t\tSize: \"xl\",\n\t\t\t},\n\t\t)\n\t\u003c/div\u003e\n}\n\n```", "title": "Different size toggles" }, { "name": "PrimaryToggle", "code": "```go\ntempl PrimaryToggle() {\n\t\u003cdiv class=\"max-w-52 mx-auto pt-4\"\u003e\n\t\t@components.Toggle(\n\t\t\tcomponents.ToggleProps{\n\t\t\t\tAfter: \"Check me out\",\n\t\t\t\tName: \"checkbox2\",\n\t\t\t\tChecked: true,\n\t\t\t\tClass: \"toggle-primary\",\n\t\t\t},\n\t\t)\n\t\u003c/div\u003e\n}\n\n```", "title": "Primary toggle with label after" }, { "name": "PrimaryToggleWithHighlight", "code": "```go\ntempl PrimaryToggleWithHighlight() {\n\t\u003cdiv class=\"max-w-xs mx-auto pt-4\"\u003e\n\t\t@components.Toggle(\n\t\t\tcomponents.ToggleProps{\n\t\t\t\tBefore: \"Paid monthly\",\n\t\t\t\tAfter: \"Paid annually\",\n\t\t\t\tName: \"checkbox3\",\n\t\t\t\tHighlight: true,\n\t\t\t\tClass: \"toggle-primary\",\n\t\t\t},\n\t\t)\n\t\u003c/div\u003e\n}\n```", "title": "Primary toggle with highlight" } ], "tooltip": [ { "name": "BasicTooltip", "code": "```go\ntempl BasicTooltip() {\n\t\u003cdiv class=\"min-h-60 flex justify-center items-center\"\u003e\n\t\t@components.Tooltip(components.TooltipProps{Tip: \"Hello\"}) {\n\t\t\t\u003cbutton class=\"btn\"\u003eHover me\u003c/button\u003e\n\t\t}\n\t\u003c/div\u003e\n}\n\n```", "title": "Basic tooltip at the top" }, { "name": "BasicTooltipError", "code": "```go\ntempl BasicTooltipError() {\n\t\u003cdiv class=\"min-h-60 flex justify-center items-center\"\u003e\n\t\t@components.Tooltip(\n\t\t\tcomponents.TooltipProps{\n\t\t\t\tTip: \"Hello\",\n\t\t\t\tClass: \"tooltip-bottom tooltip-error\",\n\t\t\t},\n\t\t) {\n\t\t\t\u003cbutton class=\"btn\"\u003eHover me\u003c/button\u003e\n\t\t}\n\t\u003c/div\u003e\n}\n```", "title": "Error-type tooltip on the bottom" } ] } ================================================ FILE: internal/assets/generated/components.go ================================================ package generated import ( "github.com/a-h/templ" "github.com/haatos/goshipit/internal/views/examples" ) var ExampleComponents = map[string]templ.Component{ "AccordionWithCheckbox": examples.AccordionWithCheckbox(), "AccordionWithRadio": examples.AccordionWithRadio(), "ActiveSearchExampleTable": examples.ActiveSearchExampleTable(), "AlertInfoExample": examples.AlertInfoExample(), "AlertSuccessExample": examples.AlertSuccessExample(), "AlertWarningExample": examples.AlertWarningExample(), "AlertErrorExample": examples.AlertErrorExample(), "BasicAnchor": examples.BasicAnchor(), "PrimaryAnchor": examples.PrimaryAnchor(), "AnchorWithIcon": examples.AnchorWithIcon(), "SocialAnchors": examples.SocialAnchors(), "MultipleAvatarSizes": examples.MultipleAvatarSizes(), "GroupOfAvatars": examples.GroupOfAvatars(), "OnlineAndOffline": examples.OnlineAndOffline(), "BannerExample": examples.BannerExample(), "BreadcrumbsExample": examples.BreadcrumbsExample(), "BasicCard": examples.BasicCard(), "BasicCardWithImage": examples.BasicCardWithImage(), "CarouselExample": examples.CarouselExample(), "BasicChat": examples.BasicChat(), "DifferentSizeCheckboxes": examples.DifferentSizeCheckboxes(), "PrimaryCheckbox": examples.PrimaryCheckbox(), "CollapseExample": examples.CollapseExample(), "BasicCombobox": examples.BasicCombobox(), "CountdownFullExample": examples.CountdownFullExample(), "CountdownHoursExample": examples.CountdownHoursExample(), "CountdownMinutesExample": examples.CountdownMinutesExample(), "CountdownSecondsExample": examples.CountdownSecondsExample(), "BasicDatePicker": examples.BasicDatePicker(), "ImageDiff": examples.ImageDiff(), "BasicDrawer": examples.BasicDrawer(), "BasicDropdown": examples.BasicDropdown(), "BasicFAB": examples.BasicFAB(), "FABWithSVGButtons": examples.FABWithSVGButtons(), "FABFlowerExample": examples.FABFlowerExample(), "FeaturesExample": examples.FeaturesExample(), "DifferentSizeFileInputs": examples.DifferentSizeFileInputs(), "BasicFooterWithLinks": examples.BasicFooterWithLinks(), "BasicHero": examples.BasicHero(), "Hover3DCardExample": examples.Hover3DCardExample(), "InfiniteScrollTableExample": examples.InfiniteScrollTableExample(), "DifferentSizeInputs": examples.DifferentSizeInputs(), "IntegerInput": examples.IntegerInput(), "DecimalInput": examples.DecimalInput(), "PasswordFieldWithValidator": examples.PasswordFieldWithValidator(), "EmailFieldWithValidator": examples.EmailFieldWithValidator(), "SignUpFormExample": examples.SignUpFormExample(), "LazyLoadExample": examples.LazyLoadExample(), "MenuExample": examples.MenuExample(), "MenuWithSubmenusExample": examples.MenuWithSubmenusExample(), "DashboardMenuExample": examples.DashboardMenuExample(), "BasicModal": examples.BasicModal(), "MultipleModals": examples.MultipleModals(), "ModalWithAction": examples.ModalWithAction(), "ModalConfirmDelete": examples.ModalConfirmDelete(), "BasicPaginationExample": examples.BasicPaginationExample(), "PricingExample": examples.PricingExample(), "PricingWithPromotionExample": examples.PricingWithPromotionExample(), "DefaultRadio": examples.DefaultRadio(), "PrimaryRadio": examples.PrimaryRadio(), "BasicRange": examples.BasicRange(), "DatastarRange": examples.DatastarRange(), "RatingFromOneToFive": examples.RatingFromOneToFive(), "RatingFromZeroToFive": examples.RatingFromZeroToFive(), "DifferentSizeSelects": examples.DifferentSizeSelects(), "CascadingSelect": examples.CascadingSelect(), "SkeletonExample": examples.SkeletonExample(), "BasicStats": examples.BasicStats(), "StatusNotFound": examples.StatusNotFound(), "StatusForbidden": examples.StatusForbidden(), "StatusUnauthorized": examples.StatusUnauthorized(), "StatusInternalServerError": examples.StatusInternalServerError(), "BasicSteps": examples.BasicSteps(), "BasicSwap": examples.BasicSwap(), "BasicTable": examples.BasicTable(), "BasicTabs": examples.BasicTabs(), "TestimonialGridExample": examples.TestimonialGridExample(), "TextRotateExample": examples.TextRotateExample(), "BasicTextarea": examples.BasicTextarea(), "DifferentSizeTextareas": examples.DifferentSizeTextareas(), "BasicTextareaWithError": examples.BasicTextareaWithError(), "BasicTimeSlotPicker": examples.BasicTimeSlotPicker(), "BasicTimeline": examples.BasicTimeline(), "InfoToast": examples.InfoToast(), "WarningToast": examples.WarningToast(), "ErrorToast": examples.ErrorToast(), "InfoToastConfirm": examples.InfoToastConfirm(), "DifferentSizeToggles": examples.DifferentSizeToggles(), "PrimaryToggle": examples.PrimaryToggle(), "PrimaryToggleWithHighlight": examples.PrimaryToggleWithHighlight(), "BasicTooltip": examples.BasicTooltip(), "BasicTooltipError": examples.BasicTooltipError(), } ================================================ FILE: internal/assets/public/static/css/chroma.css ================================================ .bg { color: #b0c4de; background-color: #282c34; } .chroma { color: #b0c4de; background-color: #111318; } .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; } .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; } .chroma .hl { background-color: #3d4148; } .chroma .lnt { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em; color: #58626f; } .chroma .ln { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em; color: #58626f; } .chroma .line { display: flex; } .chroma .k { color: #76a9f9; } .chroma .kc { color: #e5c07b; } .chroma .kd { color: #76a9f9; } .chroma .kn { color: #76a9f9; } .chroma .kp { color: #76a9f9; } .chroma .kr { color: #76a9f9; } .chroma .kt { color: #e5c07b; } .chroma .n { color: #aa89ea; } .chroma .na { color: #cebc3a; } .chroma .nb { color: #e5c07b; } .chroma .bp { color: #aa89ea; } .chroma .nc { color: #ca72ff; } .chroma .no { color: #aa89ea; font-weight: bold; } .chroma .nd { color: #e5c07b; } .chroma .ni { color: #bda26f; } .chroma .ne { color: #fd7474; font-weight: bold; } .chroma .nf { color: #00b1f7; } .chroma .fm { color: #aa89ea; } .chroma .nl { color: #f5a40d; } .chroma .nn { color: #ca72ff; } .chroma .nx { color: #aa89ea; } .chroma .py { color: #cebc3a; } .chroma .nt { color: #76a9f9; } .chroma .nv { color: #dcaeea; } .chroma .vc { color: #dcaeea; } .chroma .vg { color: #dcaeea; font-weight: bold; } .chroma .vi { color: #e06c75; } .chroma .vm { color: #dcaeea; } .chroma .l { color: #98c379; } .chroma .ld { color: #98c379; } .chroma .s { color: #98c379; } .chroma .sa { color: #98c379; } .chroma .sb { color: #98c379; } .chroma .sc { color: #98c379; } .chroma .dl { color: #98c379; } .chroma .sd { color: #7e97c3; } .chroma .s2 { color: #63c381; } .chroma .se { color: #d26464; font-weight: bold; } .chroma .sh { color: #98c379; } .chroma .si { color: #98c379; } .chroma .sx { color: #70b33f; } .chroma .sr { color: #56b6c2; } .chroma .s1 { color: #98c379; } .chroma .ss { color: #56b6c2; } .chroma .m { color: #d19a66; } .chroma .mb { color: #d19a66; } .chroma .mf { color: #d19a66; } .chroma .mh { color: #d19a66; } .chroma .mi { color: #d19a66; } .chroma .il { color: #d19a66; } .chroma .mo { color: #d19a66; } .chroma .o { color: #54b1c7; } .chroma .ow { color: #b756ff; font-weight: bold; } .chroma .p { color: #abb2bf; } .chroma .c { color: #8a93a5; font-style: italic; } .chroma .ch { color: #8a93a5; font-weight: bold; font-style: italic; } .chroma .cm { color: #8a93a5; font-style: italic; } .chroma .c1 { color: #8a93a5; font-style: italic; } .chroma .cs { color: #8a93a5; font-style: italic; } .chroma .cp { color: #8a93a5; font-style: italic; } .chroma .cpf { color: #8a93a5; font-style: italic; } .chroma .ge { font-style: italic; } .chroma .gh { color: #a2cbff; font-weight: bold; } .chroma .gi { color: #a6e22e; } .chroma .go { color: #a6e22e; } .chroma .gp { color: #a6e22e; } .chroma .gs { font-weight: bold; } .chroma .gu { color: #a2cbff; } .chroma .gt { color: #a2cbff; } .chroma .gl { text-decoration: underline; } .chroma { padding: 5px; border-radius: 16px; max-width: 1024px; overflow-x: auto; font-size: 14px; } .chroma code { font-family: "JetBrains Mono", monospace !important; font-weight: 500 !important; font-style: normal !important; } @media screen and (min-width: 768px) { .chroma { padding: 10px; } } ================================================ FILE: internal/assets/public/static/css/custom.css ================================================ .font-saira-black { font-family: "Saira", sans-serif; font-weight: 900; font-style: normal; } body, html { margin: 0; padding: 0; width: 100%; font-family: "Montserrat", sans-serif; } .htmx-indicator { opacity: 0; transition: opacity 500ms ease-in; } .htmx-request .htmx-indicator { opacity: 1; } .htmx-request.htmx-indicator { opacity: 1; } article > p > img { margin: auto; } pre:not([class="chroma"]) { background-color: #111318; padding: 0px; } pre[class="chroma"] { position: relative; overflow: auto; margin: 5px 0; padding: 1.75rem 0 1.75rem 1rem; border-radius: 10px; } pre[class="chroma"] button { position: absolute; top: 5px; right: 5px; } pre[class="chroma"] button:hover { cursor: pointer; } ================================================ FILE: internal/assets/public/static/js/alpine.js ================================================ (() => { var rt = !1, nt = !1, U = [], it = -1; function qt(e) { Cn(e) } function Cn(e) { U.includes(e) || U.push(e), Tn() } function Ee(e) { let t = U.indexOf(e); t !== -1 && t > it && U.splice(t, 1) } function Tn() { !nt && !rt && (rt = !0, queueMicrotask(Rn)) } function Rn() { rt = !1, nt = !0; for (let e = 0; e < U.length; e++)U[e](), it = e; U.length = 0, it = -1, nt = !1 } var R, D, L, st, ot = !0; function Ut(e) { ot = !1, e(), ot = !0 } function Wt(e) { R = e.reactive, L = e.release, D = t => e.effect(t, { scheduler: r => { ot ? qt(r) : r() } }), st = e.raw } function at(e) { D = e } function Gt(e) { let t = () => { }; return [n => { let i = D(n); return e._x_effects || (e._x_effects = new Set, e._x_runEffects = () => { e._x_effects.forEach(o => o()) }), e._x_effects.add(i), t = () => { i !== void 0 && (e._x_effects.delete(i), L(i)) }, i }, () => { t() }] } function ve(e, t) { let r = !0, n, i = D(() => { let o = e(); JSON.stringify(o), r ? n = o : queueMicrotask(() => { t(o, n), n = o }), r = !1 }); return () => L(i) } var Jt = [], Yt = [], Xt = []; function Zt(e) { Xt.push(e) } function ee(e, t) { typeof t == "function" ? (e._x_cleanups || (e._x_cleanups = []), e._x_cleanups.push(t)) : (t = e, Yt.push(t)) } function Ae(e) { Jt.push(e) } function Oe(e, t, r) { e._x_attributeCleanups || (e._x_attributeCleanups = {}), e._x_attributeCleanups[t] || (e._x_attributeCleanups[t] = []), e._x_attributeCleanups[t].push(r) } function ct(e, t) { e._x_attributeCleanups && Object.entries(e._x_attributeCleanups).forEach(([r, n]) => { (t === void 0 || t.includes(r)) && (n.forEach(i => i()), delete e._x_attributeCleanups[r]) }) } function Qt(e) { if (e._x_cleanups) for (; e._x_cleanups.length;)e._x_cleanups.pop()() } var lt = new MutationObserver(pt), ut = !1; function le() { lt.observe(document, { subtree: !0, childList: !0, attributes: !0, attributeOldValue: !0 }), ut = !0 } function ft() { Mn(), lt.disconnect(), ut = !1 } var ce = []; function Mn() { let e = lt.takeRecords(); ce.push(() => e.length > 0 && pt(e)); let t = ce.length; queueMicrotask(() => { if (ce.length === t) for (; ce.length > 0;)ce.shift()() }) } function _(e) { if (!ut) return e(); ft(); let t = e(); return le(), t } var dt = !1, Se = []; function er() { dt = !0 } function tr() { dt = !1, pt(Se), Se = [] } function pt(e) { if (dt) { Se = Se.concat(e); return } let t = new Set, r = new Set, n = new Map, i = new Map; for (let o = 0; o < e.length; o++)if (!e[o].target._x_ignoreMutationObserver && (e[o].type === "childList" && (e[o].addedNodes.forEach(s => s.nodeType === 1 && t.add(s)), e[o].removedNodes.forEach(s => s.nodeType === 1 && r.add(s))), e[o].type === "attributes")) { let s = e[o].target, a = e[o].attributeName, c = e[o].oldValue, l = () => { n.has(s) || n.set(s, []), n.get(s).push({ name: a, value: s.getAttribute(a) }) }, u = () => { i.has(s) || i.set(s, []), i.get(s).push(a) }; s.hasAttribute(a) && c === null ? l() : s.hasAttribute(a) ? (u(), l()) : u() } i.forEach((o, s) => { ct(s, o) }), n.forEach((o, s) => { Jt.forEach(a => a(s, o)) }); for (let o of r) t.has(o) || Yt.forEach(s => s(o)); t.forEach(o => { o._x_ignoreSelf = !0, o._x_ignore = !0 }); for (let o of t) r.has(o) || o.isConnected && (delete o._x_ignoreSelf, delete o._x_ignore, Xt.forEach(s => s(o)), o._x_ignore = !0, o._x_ignoreSelf = !0); t.forEach(o => { delete o._x_ignoreSelf, delete o._x_ignore }), t = null, r = null, n = null, i = null } function Ce(e) { return F(j(e)) } function P(e, t, r) { return e._x_dataStack = [t, ...j(r || e)], () => { e._x_dataStack = e._x_dataStack.filter(n => n !== t) } } function j(e) { return e._x_dataStack ? e._x_dataStack : typeof ShadowRoot == "function" && e instanceof ShadowRoot ? j(e.host) : e.parentNode ? j(e.parentNode) : [] } function F(e) { return new Proxy({ objects: e }, Nn) } var Nn = { ownKeys({ objects: e }) { return Array.from(new Set(e.flatMap(t => Object.keys(t)))) }, has({ objects: e }, t) { return t == Symbol.unscopables ? !1 : e.some(r => Object.prototype.hasOwnProperty.call(r, t) || Reflect.has(r, t)) }, get({ objects: e }, t, r) { return t == "toJSON" ? Dn : Reflect.get(e.find(n => Reflect.has(n, t)) || {}, t, r) }, set({ objects: e }, t, r, n) { let i = e.find(s => Object.prototype.hasOwnProperty.call(s, t)) || e[e.length - 1], o = Object.getOwnPropertyDescriptor(i, t); return o?.set && o?.get ? o.set.call(n, r) || !0 : Reflect.set(i, t, r) } }; function Dn() { return Reflect.ownKeys(this).reduce((t, r) => (t[r] = Reflect.get(this, r), t), {}) } function Te(e) { let t = n => typeof n == "object" && !Array.isArray(n) && n !== null, r = (n, i = "") => { Object.entries(Object.getOwnPropertyDescriptors(n)).forEach(([o, { value: s, enumerable: a }]) => { if (a === !1 || s === void 0 || typeof s == "object" && s !== null && s.__v_skip) return; let c = i === "" ? o : `${i}.${o}`; typeof s == "object" && s !== null && s._x_interceptor ? n[o] = s.initialize(e, c, o) : t(s) && s !== n && !(s instanceof Element) && r(s, c) }) }; return r(e) } function Re(e, t = () => { }) { let r = { initialValue: void 0, _x_interceptor: !0, initialize(n, i, o) { return e(this.initialValue, () => Pn(n, i), s => mt(n, i, s), i, o) } }; return t(r), n => { if (typeof n == "object" && n !== null && n._x_interceptor) { let i = r.initialize.bind(r); r.initialize = (o, s, a) => { let c = n.initialize(o, s, a); return r.initialValue = c, i(o, s, a) } } else r.initialValue = n; return r } } function Pn(e, t) { return t.split(".").reduce((r, n) => r[n], e) } function mt(e, t, r) { if (typeof t == "string" && (t = t.split(".")), t.length === 1) e[t[0]] = r; else { if (t.length === 0) throw error; return e[t[0]] || (e[t[0]] = {}), mt(e[t[0]], t.slice(1), r) } } var rr = {}; function y(e, t) { rr[e] = t } function ue(e, t) { return Object.entries(rr).forEach(([r, n]) => { let i = null; function o() { if (i) return i; { let [s, a] = _t(t); return i = { interceptor: Re, ...s }, ee(t, a), i } } Object.defineProperty(e, `$${r}`, { get() { return n(t, o()) }, enumerable: !1 }) }), e } function nr(e, t, r, ...n) { try { return r(...n) } catch (i) { te(i, e, t) } } function te(e, t, r = void 0) { e = Object.assign(e ?? { message: "No error message given." }, { el: t, expression: r }), console.warn(`Alpine Expression Error: ${e.message} ${r ? 'Expression: "' + r + `" `: ""}`, t), setTimeout(() => { throw e }, 0) } var Me = !0; function De(e) { let t = Me; Me = !1; let r = e(); return Me = t, r } function M(e, t, r = {}) { let n; return x(e, t)(i => n = i, r), n } function x(...e) { return ir(...e) } var ir = gt; function or(e) { ir = e } function gt(e, t) { let r = {}; ue(r, e); let n = [r, ...j(e)], i = typeof t == "function" ? In(n, t) : Ln(n, t, e); return nr.bind(null, e, t, i) } function In(e, t) { return (r = () => { }, { scope: n = {}, params: i = [] } = {}) => { let o = t.apply(F([n, ...e]), i); Ne(r, o) } } var ht = {}; function kn(e, t) { if (ht[e]) return ht[e]; let r = Object.getPrototypeOf(async function () { }).constructor, n = /^[\n\s]*if.*\(.*\)/.test(e.trim()) || /^(let|const)\s/.test(e.trim()) ? `(async()=>{ ${e} })()` : e, o = (() => { try { let s = new r(["__self", "scope"], `with (scope) { __self.result = ${n} }; __self.finished = true; return __self.result;`); return Object.defineProperty(s, "name", { value: `[Alpine] ${e}` }), s } catch (s) { return te(s, t, e), Promise.resolve() } })(); return ht[e] = o, o } function Ln(e, t, r) { let n = kn(t, r); return (i = () => { }, { scope: o = {}, params: s = [] } = {}) => { n.result = void 0, n.finished = !1; let a = F([o, ...e]); if (typeof n == "function") { let c = n(n, a).catch(l => te(l, r, t)); n.finished ? (Ne(i, n.result, a, s, r), n.result = void 0) : c.then(l => { Ne(i, l, a, s, r) }).catch(l => te(l, r, t)).finally(() => n.result = void 0) } } } function Ne(e, t, r, n, i) { if (Me && typeof t == "function") { let o = t.apply(r, n); o instanceof Promise ? o.then(s => Ne(e, s, r, n)).catch(s => te(s, i, t)) : e(o) } else typeof t == "object" && t instanceof Promise ? t.then(o => e(o)) : e(t) } var bt = "x-"; function C(e = "") { return bt + e } function sr(e) { bt = e } var Pe = {}; function d(e, t) { return Pe[e] = t, { before(r) { if (!Pe[r]) { console.warn(String.raw`Cannot find directive \`${r}\`. \`${e}\` will use the default order of execution`); return } let n = W.indexOf(r); W.splice(n >= 0 ? n : W.indexOf("DEFAULT"), 0, e) } } } function ar(e) { return Object.keys(Pe).includes(e) } function de(e, t, r) { if (t = Array.from(t), e._x_virtualDirectives) { let o = Object.entries(e._x_virtualDirectives).map(([a, c]) => ({ name: a, value: c })), s = wt(o); o = o.map(a => s.find(c => c.name === a.name) ? { name: `x-bind:${a.name}`, value: `"${a.value}"` } : a), t = t.concat(o) } let n = {}; return t.map(ur((o, s) => n[o] = s)).filter(dr).map(jn(n, r)).sort(Fn).map(o => $n(e, o)) } function wt(e) { return Array.from(e).map(ur()).filter(t => !dr(t)) } var xt = !1, fe = new Map, cr = Symbol(); function lr(e) { xt = !0; let t = Symbol(); cr = t, fe.set(t, []); let r = () => { for (; fe.get(t).length;)fe.get(t).shift()(); fe.delete(t) }, n = () => { xt = !1, r() }; e(r), n() } function _t(e) { let t = [], r = a => t.push(a), [n, i] = Gt(e); return t.push(i), [{ Alpine: B, effect: n, cleanup: r, evaluateLater: x.bind(x, e), evaluate: M.bind(M, e) }, () => t.forEach(a => a())] } function $n(e, t) { let r = () => { }, n = Pe[t.type] || r, [i, o] = _t(e); Oe(e, t.original, o); let s = () => { e._x_ignore || e._x_ignoreSelf || (n.inline && n.inline(e, t, i), n = n.bind(n, e, t, i), xt ? fe.get(cr).push(n) : n()) }; return s.runCleanups = o, s } var Ie = (e, t) => ({ name: r, value: n }) => (r.startsWith(e) && (r = r.replace(e, t)), { name: r, value: n }), ke = e => e; function ur(e = () => { }) { return ({ name: t, value: r }) => { let { name: n, value: i } = fr.reduce((o, s) => s(o), { name: t, value: r }); return n !== t && e(n, t), { name: n, value: i } } } var fr = []; function re(e) { fr.push(e) } function dr({ name: e }) { return pr().test(e) } var pr = () => new RegExp(`^${bt}([^:^.]+)\\b`); function jn(e, t) { return ({ name: r, value: n }) => { let i = r.match(pr()), o = r.match(/:([a-zA-Z0-9\-_:]+)/), s = r.match(/\.[^.\]]+(?=[^\]]*$)/g) || [], a = t || e[r] || r; return { type: i ? i[1] : null, value: o ? o[1] : null, modifiers: s.map(c => c.replace(".", "")), expression: n, original: a } } } var yt = "DEFAULT", W = ["ignore", "ref", "data", "id", "anchor", "bind", "init", "for", "model", "modelable", "transition", "show", "if", yt, "teleport"]; function Fn(e, t) { let r = W.indexOf(e.type) === -1 ? yt : e.type, n = W.indexOf(t.type) === -1 ? yt : t.type; return W.indexOf(r) - W.indexOf(n) } function G(e, t, r = {}) { e.dispatchEvent(new CustomEvent(t, { detail: r, bubbles: !0, composed: !0, cancelable: !0 })) } function T(e, t) { if (typeof ShadowRoot == "function" && e instanceof ShadowRoot) { Array.from(e.children).forEach(i => T(i, t)); return } let r = !1; if (t(e, () => r = !0), r) return; let n = e.firstElementChild; for (; n;)T(n, t, !1), n = n.nextElementSibling } function E(e, ...t) { console.warn(`Alpine Warning: ${e}`, ...t) } var mr = !1; function _r() { mr && E("Alpine has already been initialized on this page. Calling Alpine.start() more than once can cause problems."), mr = !0, document.body || E("Unable to initialize. Trying to load Alpine before `` is available. Did you forget to add `defer` in Alpine's ` } templ ComboBadge(name, value string) {
{ value }
} templ crossIcon() { } ================================================ FILE: internal/views/components/countdown.templ ================================================ // data_display package components import ( "fmt" "time" ) type CountdownProps struct { Expires time.Time Days bool Hours bool Minutes bool Seconds bool } templ Countdown(props CountdownProps) { {{ spanID := time.Now().UnixNano() }}
15 days
10 hours
24 min
59 sec
} ================================================ FILE: internal/views/components/date_picker.templ ================================================ // data_input package components import "fmt" import "time" type DatePickerProps struct { Year int Month int Selected time.Time StartOfWeek time.Weekday } func (props DatePickerProps) Days() []time.Time { days := make([]time.Time, 0, 31) now := time.Now().UTC() start := time.Date(props.Year, time.Month(props.Month), 1, 0, 0, 0, 0, now.Location()) end := start.AddDate(0, 1, -1) for end.Weekday() != props.StartOfWeek { end = end.AddDate(0, 0, 1) } end = end.AddDate(0, 0, -1) for start.Weekday() != props.StartOfWeek { start = start.AddDate(0, 0, -1) } for !start.After(end) { days = append(days, start) start = start.AddDate(0, 0, 1) } return days } func (props DatePickerProps) Months() []time.Time { months := make([]time.Time, 12) for i := 1; i <= 12; i++ { dt := time.Date(props.Year, time.Month(i), 1, 0, 0, 0, 0, time.Now().Location()) months[i-1] = dt } return months } templ DatePicker(props DatePickerProps) { {{ utcNow := time.Now().UTC() }} {{ days := props.Days() }}
if props.StartOfWeek == time.Sunday { Su } Mo Tu We Th Fr Sa if props.StartOfWeek == time.Monday { Su }
@DatePickerInput(props.Selected) for _, d := range days { }
} templ DatePickerInput(d time.Time) { } templ DatePickerYearPicker(props DatePickerProps) { {{ utcNow := time.Now().UTC() }} for i := range 12 { } } templ DatePickerMonthPicker(props DatePickerProps) { {{ months := props.Months() }} for _, m := range months { } } ================================================ FILE: internal/views/components/diff.templ ================================================ // data_display // https://daisyui.com/components/diff package components import "fmt" type DiffProps struct { Width int Height int Image1 DiffImage Image2 DiffImage } type DiffImage struct { Source string Alt string } templ Diff(props DiffProps) {
{
{
} ================================================ FILE: internal/views/components/drawer.templ ================================================ // layout // https://daisyui.com/components/drawer package components templ Drawer(toggle templ.Component, sidebar templ.Component) {
{ children... }
} ================================================ FILE: internal/views/components/dropdown.templ ================================================ // actions // https://daisyui.com/components/dropdown package components type DropdownProps struct { Label string Class string ListClass string Items []DropdownItem } type DropdownItem struct { Label string Attrs templ.Attributes } templ Dropdown(props DropdownProps) {
{ props.Label }
} ================================================ FILE: internal/views/components/fab.templ ================================================ // actions // https://daisyui.com/components/fab package components type FABProps struct { Class string Toggle any // string or templ.Component ToggleClass string Close templ.Component MainAction templ.Component } templ FAB(props FABProps) {
if t, ok := props.Toggle.(string); ok { { t } } else { @props.Toggle.(templ.Component) }
if props.Close != nil {
@props.Close
} if props.MainAction != nil {
@props.MainAction
} { children... }
} ================================================ FILE: internal/views/components/features.templ ================================================ // data_display package components type FeaturesProps struct { Title string Features []FeatureProps } type FeatureProps struct { Icon templ.Component Title string Description string URL string } templ Features(props FeaturesProps) {
if props.Title != "" {

{ props.Title }

}
for _, feature := range props.Features { @Feature(feature) }
} templ Feature(feature FeatureProps) {
@feature.Icon if feature.Title != "" {

{ feature.Title }

}

{ feature.Description }

if feature.URL != "" { Learn more }
} ================================================ FILE: internal/views/components/file_input.templ ================================================ // data_input // https://daisyui.com/components/file-input package components type FileInputProps struct { Name string Label string Value string Description string Attrs templ.Attributes DisabledMessage string Required bool Size string } templ FileInput(props FileInputProps) {
if props.Label != "" { { props.Label } } if props.Description != "" { }
} ================================================ FILE: internal/views/components/footer.templ ================================================ // layout // https://daisyui.com/components/footer package components type FooterProps struct { Icon templ.Component Name string Description string Copyright string Anchors []AnchorProps } templ Footer(props FooterProps) {
{ children... }
} templ FooterNav(title string, links []AnchorProps) { } ================================================ FILE: internal/views/components/hero.templ ================================================ // layout // https://daisyui.com/components/hero package components type HeroProps struct { Source string Alt string Reverse bool Class string } templ Hero(props HeroProps) {
if props.Source != "" { { }
{ children... }
} ================================================ FILE: internal/views/components/hover_3d_card.templ ================================================ // data_display package components templ Hover3DCard() {
{ children... }
} ================================================ FILE: internal/views/components/infinite_scroll.templ ================================================ // data_display package components import "fmt" templ InfiniteScrollTable(rows []templ.Component) { @Table( []templ.Component{PlainText("Name"), PlainText("Email")}, rows, nil, )
} templ InfiniteScrollRows(rows []templ.Component) { for _, r := range rows { @r } } templ InfiniteScrollRow(name, email string, page int, hasMore bool) { { name } { email } } ================================================ FILE: internal/views/components/input.templ ================================================ // data_input package components type InputProps struct { // common Name string Label string Type string // defaults to "text" Value string Placeholder string Description string Error string DisabledMessage string Size string // xs sm lg xl, default: md Required bool Icon templ.Component Attrs templ.Attributes // text input MinLength string MaxLength string Pattern string ValidatorHint string // integer/decimal input Min string Max string // decimal input Step string } templ Input(props InputProps) {
if props.Label != "" { { props.Label } } if props.Description != "" {
{ props.Description }
} if props.Error != "" {

{ props.Error }

} if props.ValidatorHint != "" { }
} ================================================ FILE: internal/views/components/lazy_load.templ ================================================ // data_display package components templ LazyLoad(url string) {
} ================================================ FILE: internal/views/components/menu.templ ================================================ // navigation // https://daisyui.com/components/menu package components type MenuProps struct { Title string Class string } templ Menu(props MenuProps) {
    if props.Title != "" {

    { props.Title }

    } { children... }
} type MenuItemProps struct { Label string Attrs templ.Attributes Icon templ.Component IconAfter bool } templ MenuItem(props MenuItemProps) {
  • if props.Icon != nil && !props.IconAfter { @props.Icon } { props.Label } if props.Icon != nil && props.IconAfter { @props.Icon }
      { children... }
  • } type SubmenuProps struct { Title string Attrs templ.Attributes Icon templ.Component IconAfter bool } templ Submenu(props SubmenuProps) {
  • if props.Icon != nil && !props.IconAfter { @props.Icon } { props.Title } if props.Icon != nil && props.IconAfter { @props.Icon }
      { children... }
  • } ================================================ FILE: internal/views/components/modal.templ ================================================ // actions // https://daisyui.com/components/modal package components import "fmt" type ModalProps struct { ID string Label any } templ Modal(props ModalProps) { @modalWrapper( props, templ.Attributes{"onclick": fmt.Sprintf("%s.showModal()", props.ID)}, ) { { children... } } } templ modalWrapper(props ModalProps, attrs templ.Attributes) { // you can use a string or a templ.Component as the 'label' // of the modal button if s, ok := props.Label.(string); ok {
    { s }
    } else if c, ok := props.Label.(templ.Component); ok { @c } } ================================================ FILE: internal/views/components/pagination.templ ================================================ // navigation // https://daisyui.com/components/pagination package components import "fmt" type PaginationProps struct { URL string Page int Low int High int MaxPages int } templ Pagination(id string, props PaginationProps) {
    { children... }
    @PaginationButton(id, props.URL, 1, props.Page == 1) { @AnglesLeft() } @PaginationButton(id, props.URL, props.Page-1, props.Page == 1) { @ChevronLeft() } for i := props.Low; i <= props.High; i++ { @PaginationButton(id, props.URL, i+1, props.Page == i+1) { { fmt.Sprintf("%d", i+1) } } } @PaginationButton(id, props.URL, props.Page+1, props.Page == props.MaxPages) { @ChevronRight() } @PaginationButton(id, props.URL, props.MaxPages, props.Page == props.MaxPages) { @AnglesRight() }
    } templ PaginationButton(id, url string, urlPage int, disabled bool) { } templ AnglesRight() { } templ AnglesLeft() { } templ ChevronRight() { } templ ChevronLeft() { } ================================================ FILE: internal/views/components/pricing.templ ================================================ // data_display package components import "slices" type PricingProps struct { Checked bool Prices []PriceProps } type PriceProps struct { Title string Description string PriceMonthly string PerMonthly string PriceAnnually string PerAnnually string PerUser bool Promotion string IncludedFeatures []string ExcludedFeatures []string CallToAction PriceButtonProps Footer templ.Component } type PriceButtonProps struct { Label string Attrs templ.Attributes } // NOTE: Requires Alpine.js templ Pricing(props PricingProps) {
    @Toggle(ToggleProps{ Before: "Billed monthly", After: "Billed annually", Name: "period", Checked: props.Checked, Highlight: true, Attrs: templ.Attributes{ "x-on:click": "yearly = !yearly", }, })
    @PriceGrid(props.Prices)
    } templ PriceGrid(prices []PriceProps) {
    for i := range prices { @Price(prices[i], nil) }
    } templ Price(price PriceProps, footer templ.Component) {
    if price.Promotion != "" { { price.Promotion } }

    { price.Title }

    { price.PriceAnnually } { price.PerAnnually } if price.PerUser { { " / user" } }

    { price.PriceMonthly } { price.PerMonthly }

      for i := range price.IncludedFeatures {
    • { price.IncludedFeatures[i] }
    • }
    if len(price.ExcludedFeatures) > 0 {
    }
      for i := range price.ExcludedFeatures {
    • { price.ExcludedFeatures[i] }
    • }
    if footer != nil { @footer }
    } ================================================ FILE: internal/views/components/radio.templ ================================================ // data_input // https://daisyui.com/components/radio package components type RadioProps struct { Name string Values map[string]string Class string Size string } templ Radio(props RadioProps) {
    for l, v := range props.Values { }
    } ================================================ FILE: internal/views/components/range.templ ================================================ // data_input // https://daisyui.com/components/range package components import "fmt" type RangeProps struct { ID string Label string Name string Value int Min int Max int Step int Class string Size string } // Note: usage requires alpine.js templ Range(props RangeProps) {
    } // Note: usage requires datastar.js templ DatastarRange(props RangeProps) {
    } ================================================ FILE: internal/views/components/rating.templ ================================================ // data_input // https://daisyui.com/components/rating package components import "fmt" type RatingProps struct { Name string Min int Max int Class string Value int } templ Rating(props RatingProps) {
    for i := props.Min; i <= props.Max; i++ { if i == 0 { } else { } }
    } templ RatingDisplay(props RatingProps) {
    for i := props.Min; i <= props.Max; i++ { if i == 0 { } else { } }
    } ================================================ FILE: internal/views/components/select.templ ================================================ // data_input // https://daisyui.com/components/select package components type SelectProps struct { ID string Label string Name string Description string Class string Size string Required bool Options []SelectOption Attrs templ.Attributes } type SelectOption struct { Label string Value string Selected bool Disabled bool } templ Select(props SelectProps) {
    if props.Label != "" { { props.Label } } if props.Description != "" { { props.Description } }
    } templ SelectOptions(options []SelectOption) { for i := range options { } } ================================================ FILE: internal/views/components/skeleton.templ ================================================ // feedback // https://daisyui.com/components/skeleton package components templ Skeleton() {
    } ================================================ FILE: internal/views/components/stats.templ ================================================ // data_display // https://daisyui.com/components/stat package components type StatProps struct { Title string Value string Description string } templ Stats() {
    { children... }
    } templ Stat(props StatProps) {
    { props.Title }
    { props.Value }
    if props.Description != "" {
    { props.Description }
    }
    { children... }
    } ================================================ FILE: internal/views/components/status.templ ================================================ // feedback package components import "fmt" type StatusProps struct { Code int Title string Description string ReturnButtonLabel string ReturnButtonAttrs templ.Attributes } templ Status(props StatusProps) {

    { fmt.Sprintf("%d", props.Code) }

    { props.Title }

    { props.Description }

    { props.ReturnButtonLabel }
    } ================================================ FILE: internal/views/components/steps.templ ================================================ // navigation // https://daisyui.com/components/steps package components type StepProps struct { Label string Done bool Attrs templ.Attributes } templ Steps() {
      { children... }
    } templ Step(props StepProps) {
  • { props.Label }
  • } ================================================ FILE: internal/views/components/swap.templ ================================================ // actions // https://daisyui.com/components/swap package components type SwapProps struct { On templ.Component Off templ.Component Class string } templ Swap(props SwapProps) { } ================================================ FILE: internal/views/components/table.templ ================================================ // data_display // https://daisyui.com/components/table package components templ Table(headers []templ.Component, rows []templ.Component, attrs templ.Attributes) {
    for _, header := range headers { } for _, trow := range rows { @trow }
    @header
    } // Component to use as plain text when // templ.Component is used as argument templ PlainText(content string) { { content } } ================================================ FILE: internal/views/components/tabs.templ ================================================ // navigation // https://daisyui.com/components/tab package components type TabsProps struct { Name string Class string Tabs []TabProps ContentClass string } type TabProps struct { Label string Content templ.Component } templ Tabs(props TabsProps) {
    for i, tab := range props.Tabs {
    @tab.Content
    }
    } ================================================ FILE: internal/views/components/testimonial.templ ================================================ // data_display package components import "fmt" type TestimonialProps []TestimonialProp type TestimonialProp struct { Avatar templ.Component Name string Rating int Content string } templ TestimonialGrid(title string, props TestimonialProps) {

    { title }

    for i := range props {
    if props[i].Avatar != nil { @props[i].Avatar }
    @RatingDisplay(RatingProps{ Name: fmt.Sprintf("review-rating-%d", i), Min: 1, Max: 5, Value: props[i].Rating, })

    { props[i].Name }

    { props[i].Content }

    }
    } ================================================ FILE: internal/views/components/text_rotate.templ ================================================ // data_display package components type TextRotateProps struct { Class string Items []TextRotateItem } type TextRotateItem struct { Text string Class string } templ TextRotate(props TextRotateProps) { for _, item := range props.Items { { item.Text } } } ================================================ FILE: internal/views/components/textarea.templ ================================================ // data_input // https://daisyui.com/components/textarea package components import "fmt" type TextareaProps struct { ID string Label string Name string Placeholder string Value string Description string Err string Class string Size string Required bool Rows int Attrs templ.Attributes } templ Textarea(props TextareaProps) {
    if props.Label != "" { { props.Label } } if props.Description != "" {
    { props.Description }
    }
    { props.Err }
    } ================================================ FILE: internal/views/components/time_slot_picker.templ ================================================ // data_input package components import "time" type TimeSlotPickerProps struct { ID string CurrentDate time.Time TimeSlots []TimeSlot PickerURL string ReserveURL string } type TimeSlot struct { Start time.Time End time.Time } func (x *TimeSlotPickerProps) Days() []time.Time { date := time.Date(x.CurrentDate.Year(), x.CurrentDate.Month(), x.CurrentDate.Day(), 0, 0, 0, 0, x.CurrentDate.Location()) dates := make([]time.Time, 7) dates[0] = date for i := range 6 { date = date.Add(24 * time.Hour) dates[i+1] = date } return dates } func (x *TimeSlotPickerProps) GetSlots(day time.Time) []TimeSlot { slots := make([]TimeSlot, 0) now := time.Now().UTC() now = time.Date( now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location()).Add(time.Duration(24-now.Hour()) * time.Hour) for _, s := range x.TimeSlots { if s.Start.After(now) && s.Start.Format("20060102") == day.Format("20060102") { slots = append(slots, s) } } return slots } templ TimeSlotPicker(props TimeSlotPickerProps) {
    for _, day := range props.Days() { @timeSlotPickerDay(props, day) }
    @timeSlotPickerControls(props)
    } templ timeSlotPickerDay(props TimeSlotPickerProps, day time.Time) {
    { day.Format("Mon Jan 02") }
      for _, slot := range props.GetSlots(day) { {{ slotName := "time_slot_" + slot.Start.Format("200601021504") }} @Modal(ModalProps{ ID: slotName, Label: timeSlotButton( slot, templ.Attributes{"onclick": slotName + ".showModal()"}, ), }, ) { @timeSlotModalContent(slot, slotName, props.ReserveURL) } }
    } templ timeSlotButton(slot TimeSlot, attrs templ.Attributes) { } templ timeSlotModalContent(slot TimeSlot, slotName, reserveURL string) {

    Reserve a time slot { slot.Start.Format("15:04") } - { slot.End.Format("15:04") }, { slot.Start.Format("Monday January 02") }?

    } templ timeSlotPickerControls(props TimeSlotPickerProps) {
    } templ chevronLeft() { } templ chevronRight() { } ================================================ FILE: internal/views/components/timeline.templ ================================================ // data_display // https://daisyui.com/components/timeline package components type TimelineProps []TimelineProp type TimelineProp struct { Start string Middle templ.Component End string } templ Timeline(props TimelineProps) {
      for i, prop := range props {
    • if i > 0 {
      } if prop.Start != "" {
      { prop.Start }
      } if prop.Middle != nil {
      @prop.Middle
      } if prop.End != "" {
      { prop.End }
      } if i < len(props) - 1 {
      }
    • }
    } templ TimelineCheckbox(checked bool) { } ================================================ FILE: internal/views/components/toast.templ ================================================ // feedback // https://daisyui.com/components/toast package components type ToastProps struct { Name string ToastClass string AlertClass string } templ Toast(props ToastProps) {
    { children... }
    } ================================================ FILE: internal/views/components/toggle.templ ================================================ // data_input // https://daisyui.com/components/toggle package components type ToggleProps struct { ID string Before string After string Name string Checked bool Class string Highlight bool Attrs templ.Attributes Size string } templ Toggle(props ToggleProps) {
    if props.Highlight { }
    } ================================================ FILE: internal/views/components/tooltip.templ ================================================ // feedback // https://daisyui.com/components/tooltip package components type TooltipProps struct { Tip string Class string } templ Tooltip(props TooltipProps) {
    { children... }
    } ================================================ FILE: internal/views/custom/toast.templ ================================================ package custom import "github.com/haatos/goshipit/internal/views/components" templ ToastErrorConfirm(errs ...string) { @components.Toast( components.ToastProps{ Name: "toast-error", ToastClass: "toast-top toast-center", AlertClass: "alert-error", }, ) {
      for _, e := range errs {
    • { e }
    • }
    } } templ HXToastInfoFade(messages ...string) { @components.Toast( components.ToastProps{ Name: "toast-info", ToastClass: "toast-bottom toast-end w-full max-w-xs", AlertClass: "alert-info w-full max-w-xs space-x-4", }, ) {
      1) } > for _, m := range messages {
    • { m }
    • }
    } } ================================================ FILE: internal/views/embed.go ================================================ package views import "embed" //go:embed components/*.templ var ComponentFS embed.FS ================================================ FILE: internal/views/examples/accordion.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example /* Accordion with input type 'checkbox': multiple rows can be open at a time. */ templ AccordionWithCheckbox() {
    @components.AccordionRow(components.AccordionRowProps{Label: "Label 1", Name: "accordion-example-1"}) {

    Label 1 content

    } @components.AccordionRow(components.AccordionRowProps{Label: "Label 2", Name: "accordion-example-2"}) {

    Content 2

    Label 2 content

    } @components.AccordionRow(components.AccordionRowProps{Label: "Label 3", Name: "accordion-example-3"}) {

    Content 3

    • Item 1
    • Item 2
    • Item 3
    }
    } // example /* Accordion with input type 'radio': only a single row can be open at a time. */ templ AccordionWithRadio() {
    @components.AccordionRow(components.AccordionRowProps{Label: "Label 1", Name: "accordion-example-2", Type: "radio"}) {

    Label 1 content

    } @components.AccordionRow(components.AccordionRowProps{Label: "Label 2", Name: "accordion-example-2", Type: "radio"}) {

    Content 2

    Label 2 content

    } @components.AccordionRow(components.AccordionRowProps{Label: "Label 3", Name: "accordion-example-2", Type: "radio"}) {

    Content 3

    • Item 1
    • Item 2
    • Item 3
    }
    } ================================================ FILE: internal/views/examples/active_search.templ ================================================ package examples import ( "fmt" "github.com/haatos/goshipit/internal/views/components" ) // example // Active search input for a table templ ActiveSearchExampleTable() {
    @ActiveSearchExample( "active-search-example-table", []templ.Component{ components.PlainText("First name"), components.PlainText("Last name"), components.PlainText("Email"), }, activeSearchTableDataComponents(), )
    } templ ActiveSearchExample(id string, headers []templ.Component, rows []templ.Component) {
    @components.ActiveSearchInput( components.ActiveSearchInputProps{ ID: "active-search-example-input", URL: "/active-search", Target: fmt.Sprintf("#%s > tbody", id), InputProps: components.InputProps{ Icon: searchIcon(), Name: "active-search-example", Type: "search", Placeholder: "Filter table...", Size: "sm", }, }) @components.Table( headers, activeSearchTableDataComponents(), templ.Attributes{"id": "active-search-example-table"}, )
    } templ searchIcon() { } func activeSearchTableDataComponents() []templ.Component { coms := make([]templ.Component, len(ActiveSearchTableData)) for i := range ActiveSearchTableData { coms[i] = ActiveSearchTableRow( ActiveSearchTableData[i].FirstName, ActiveSearchTableData[i].LastName, ActiveSearchTableData[i].Email) } return coms } var ActiveSearchTableData = []struct { FirstName string LastName string Email string }{ {FirstName: "John", LastName: "Smith", Email: "john.smith@email.com"}, {FirstName: "Emily", LastName: "Johnson", Email: "emily.johnson@email.com"}, {FirstName: "Michael", LastName: "Brown", Email: "michael.brown@email.com"}, {FirstName: "Jessica", LastName: "Williams", Email: "jessica.williams@email.com"}, {FirstName: "David", LastName: "Jones", Email: "david.jones@email.com"}, {FirstName: "Sarah", LastName: "Miller", Email: "sarah.miller@email.com"}, {FirstName: "Christopher", LastName: "Davis", Email: "chris.davis@email.com"}, {FirstName: "Amanda", LastName: "Wilson", Email: "amanda.wilson@email.com"}, {FirstName: "James", LastName: "Taylor", Email: "james.taylor@email.com"}, {FirstName: "Laura", LastName: "Moore", Email: "laura.moore@email.com"}, } templ ActiveSearchTableRows(rows []templ.Component) { for _, r := range rows { @r } } templ ActiveSearchTableRow(firstName, lastName, email string) { { firstName } { lastName } { email } } ================================================ FILE: internal/views/examples/alert.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example // Info-type alert templ AlertInfoExample() {
    @components.AlertInfo( "Your profile has been successfully updated. Please review your changes.", )
    } // example // Success-type alert templ AlertSuccessExample() {
    @components.AlertSuccess( "Your payment was processed successfully! Thank you for your purchase.", ) { }
    } // example // Warning-type alert templ AlertWarningExample() {
    @components.AlertWarning( "Your password will expire in 7 days. Please update it to avoid any disruptions.", )
    } // example // Error-type alert templ AlertErrorExample() {
    @components.AlertError( "Failed to connect to the server. Please try again later or contact support if the issue persists.", ) { }
    } ================================================ FILE: internal/views/examples/anchor.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example // Basic anchor templ BasicAnchor() { @components.Anchor(components.AnchorProps{ Label: "Basic anchor", Class: "link", }) } // example // Primary anchor templ PrimaryAnchor() { @components.Anchor(components.AnchorProps{ Label: "Primary anchor", Class: "link link-primary", }) } // example // Anchor with icon templ AnchorWithIcon() { @components.Anchor(components.AnchorProps{ Label: "GitHub profile", LeftIcon: GithubIcon(), Class: "link", }) } // example // Socials anchors templ SocialAnchors() {
    @components.Anchor(components.AnchorProps{LeftIcon: XIcon()}) @components.Anchor(components.AnchorProps{LeftIcon: YoutubeIcon()}) @components.Anchor(components.AnchorProps{LeftIcon: FacebookIcon()}) @components.Anchor(components.AnchorProps{LeftIcon: GithubIcon()}) @components.Anchor(components.AnchorProps{LeftIcon: LinkedInIcon()})
    } templ XIcon() { } templ YoutubeIcon() { } templ FacebookIcon() { } templ GithubIcon() { } templ LinkedInIcon() { } ================================================ FILE: internal/views/examples/avatar.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example // Multiple avatar sizes templ MultipleAvatarSizes() {
    @components.Avatar(components.AvatarProps{ ContainerClass: "rounded w-8", Source: "/static/images/avatar.jpg"}) @components.Avatar(components.AvatarProps{ ContainerClass: "rounded w-12", Source: "/static/images/avatar.jpg"}) @components.Avatar(components.AvatarProps{ ContainerClass: "rounded w-16", Source: "/static/images/avatar.jpg"}) @components.Avatar(components.AvatarProps{ ContainerClass: "rounded w-20", Source: "/static/images/avatar.jpg"}) @components.Avatar(components.AvatarProps{ ContainerClass: "rounded w-24", Source: "/static/images/avatar.jpg"})
    } // example // Avatar group templ GroupOfAvatars() {
    @components.AvatarGroup("-space-x-8") { @components.Avatar(components.AvatarProps{ ContainerClass: "rounded-full w-12", Source: "/static/images/avatar.jpg"}) @components.Avatar(components.AvatarProps{ ContainerClass: "rounded-full w-12", Source: "/static/images/avatar.jpg"}) @components.Avatar(components.AvatarProps{ ContainerClass: "rounded-full w-12", Source: "/static/images/avatar.jpg"}) @components.Avatar(components.AvatarProps{ ContainerClass: "rounded-full w-12", Source: "/static/images/avatar.jpg"}) @components.Avatar(components.AvatarProps{ ContainerClass: "rounded-full w-12", Source: "/static/images/avatar.jpg"}) }
    } // example // Avatar with online/offline indicator templ OnlineAndOffline() {
    @components.Avatar(components.AvatarProps{ AvatarClass: "avatar-online", ContainerClass: "rounded-full w-12", Source: "/static/images/avatar.jpg", }) @components.Avatar(components.AvatarProps{ AvatarClass: "avatar-offline", ContainerClass: "rounded-full w-12", Source: "/static/images/avatar.jpg", })
    } ================================================ FILE: internal/views/examples/banner.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example templ BannerExample() { @components.Banner(components.BannerProps{ Title: basicBannerTitle(), Description: `Lorem ipsum dolor sit amet consectetur adipisicing elit. Sapiente iure non, quia perspiciatis sed temporibus quos nihil, voluptatibus tempore placeat ipsa est facilis, nobis illum in magni illo neque libero.`, }) { Get started Learn more } } templ basicBannerTitle() {

    Lorem ipsum dolor. Sit amet consectetur.

    } ================================================ FILE: internal/views/examples/breadcrumbs.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example templ BreadcrumbsExample() {
    @components.Breadcrumbs( components.BreadcrumbsProps{ { Label: "Laptops", Attrs: templ.Attributes{"href": "/laptops", "class": "link"}, }, { Label: "Macbooks", Attrs: templ.Attributes{"href": "/laptops/macbooks", "class": "link"}, }, {Label: "Macbook Pro 14"}, }, )
    } ================================================ FILE: internal/views/examples/card.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example // Basic card templ BasicCard() {
    @components.Card( components.CardProps{ Title: "This is a card", Content: "And this is the card's content.", Class: "bg-base-100 w-96 shadow-xl", }, )
    } // example // Card with image templ BasicCardWithImage() {
    @components.Card( components.CardProps{ Title: "Card with image", Content: "Card with image content", Source: "/static/images/avatar.jpg", Alt: "avatar image", Class: "card-bordered bg-base-100 w-96 shadow-xl", }, ) { }
    } ================================================ FILE: internal/views/examples/carousel.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example templ CarouselExample() {
    @components.Carousel( components.CarouselProps{ {Source: "/static/images/avatar.jpg", Alt: "avatar image"}, {Source: "/static/images/avatar.jpg", Alt: "avatar image"}, {Source: "/static/images/avatar.jpg", Alt: "avatar image"}, {Source: "/static/images/avatar.jpg", Alt: "avatar image"}, {Source: "/static/images/avatar.jpg", Alt: "avatar image"}, {Source: "/static/images/avatar.jpg", Alt: "avatar image"}, }, )
    } ================================================ FILE: internal/views/examples/chat.templ ================================================ package examples import ( "time" "github.com/haatos/goshipit/internal/views/components" ) // example templ BasicChat() {
    @components.Chat(components.ChatProps{ { AvatarURL: "/static/images/avatar.jpg", Sender: "Me", Time: time.Now().UTC().Add(-4 * time.Minute).Format("15:04:05"), Message: `I started learning how to cook more dishes from scratch, and it's been way more satisfying than I expected. Last night, I made homemade pasta!`, Footer: "✓✓", Location: "start", Class: "chat-bubble-primary", }, { AvatarURL: "/static/images/avatar-reverse.jpg", Sender: "Myself", Time: time.Now().UTC().Add(-2 * time.Minute).Format("15:04:05"), Message: `That's awesome! Homemade pasta is no joke—it's a workout, too! Did you use a pasta machine, or go for the classic rolling pin method?`, Footer: "✓✓", Location: "end", }, { AvatarURL: "/static/images/avatar.jpg", Sender: "Me", Time: time.Now().UTC().Add(-1 * time.Minute).Format("15:04:05"), Message: `Went old-school with the rolling pin! Took forever, but it was worth it. I made a simple marinara sauce to go with it, and honestly, it was better than what I usually order.`, Footer: "✓✓", Location: "start", Class: "chat-bubble-primary", }, { AvatarURL: "/static/images/avatar-reverse.jpg", Sender: "Myself", Time: time.Now().UTC().Format("15:04:05"), Message: `Love it! Next up, you've got to try ravioli. It's a bit more work, but filling them with something like ricotta and spinach makes it feel super fancy. You'll impress everyone!`, Footer: "✓✓", Location: "end", }, })
    } ================================================ FILE: internal/views/examples/checkbox.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example // Different size checkboxes templ DifferentSizeCheckboxes() {
    @components.Checkbox( components.CheckboxProps{ Before: "Remember me", Name: "remember_me", Checked: false, Size: "xs", }, ) @components.Checkbox( components.CheckboxProps{ Before: "Remember me", Name: "remember_me", Checked: false, Size: "sm", }, ) @components.Checkbox( components.CheckboxProps{ Before: "Remember me", Name: "remember_me", Checked: false, }, ) @components.Checkbox( components.CheckboxProps{ Before: "Remember me", Name: "remember_me", Checked: false, Size: "lg", }, ) @components.Checkbox( components.CheckboxProps{ Before: "Remember me", Name: "remember_me", Checked: false, Size: "xl", }, )
    } // example // Primary color checkbox with label after templ PrimaryCheckbox() {
    @components.Checkbox( components.CheckboxProps{ After: "Remember me", Name: "remember_me", Checked: true, Class: "checkbox-primary", }, )
    } ================================================ FILE: internal/views/examples/collapse.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example // Basic collapse templ CollapseExample() {
    @components.Collapse( components.CollapseProps{ Title: "Click me to show/hide content", TitleClass: "text-xl font-medium", Class: "collapse-plus bg-base-300", ContentClass: "bg-base-200", }) {

    Collapse content

    }
    } ================================================ FILE: internal/views/examples/combobox.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example templ BasicCombobox() {
    @components.Combobox(components.ComboboxProps{ Name: "example_combo", Label: "Example", URL: "/combobox/%s/%s", Options: []string{ "Thing 1", "Thing 2", "Thing 3", "Thing 4", "Thing 5", "Thing 6", "Thing 7", }, })
    } ================================================ FILE: internal/views/examples/countdown.templ ================================================ package examples import ( "github.com/haatos/goshipit/internal/views/components" "time" ) // example templ CountdownFullExample() {
    @components.Countdown( components.CountdownProps{ Expires: time.Now().UTC().Add(25*time.Hour + 10*time.Second), Days: true, Hours: true, Seconds: true, Minutes: true, })
    } // example templ CountdownHoursExample() {
    @components.Countdown( components.CountdownProps{ Expires: time.Now().UTC().Add(10*time.Hour + 10*time.Second), Hours: true, Seconds: true, Minutes: true, })
    } // example templ CountdownMinutesExample() {
    @components.Countdown( components.CountdownProps{ Expires: time.Now().UTC().Add(15*time.Minute + 10*time.Second), Seconds: true, Minutes: true, })
    } // example templ CountdownSecondsExample() {
    @components.Countdown( components.CountdownProps{ Expires: time.Now().UTC().Add(20 * time.Second), Seconds: true, })
    } ================================================ FILE: internal/views/examples/date_picker.templ ================================================ package examples import "time" import "github.com/haatos/goshipit/internal/views/components" // example templ BasicDatePicker() { {{ now := time.Now().UTC() }} @components.DatePicker( components.DatePickerProps{ Year: now.Year(), Month: int(now.Month()), Selected: now, StartOfWeek: time.Monday, }, ) } ================================================ FILE: internal/views/examples/diff.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example templ ImageDiff() {
    @components.Diff( components.DiffProps{ Width: 16, Height: 19, Image1: components.DiffImage{ Source: "/static/images/diff1.png", Alt: "diff image 1", }, Image2: components.DiffImage{ Source: "/static/images/diff2.png", Alt: "diff image 2", }, }, )
    } ================================================ FILE: internal/views/examples/drawer.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example templ BasicDrawer() {
    @DrawerPreview(DrawerExampleToggle(), DrawerExampleMenu())
    } templ DrawerPreview(toggle templ.Component, sidebar templ.Component) {
    { children... }
    } templ DrawerExampleToggle() { Click me } templ DrawerExampleMenu() { @components.MenuItem(components.MenuItemProps{Label: "Section 1", Attrs: templ.Attributes{"class": "menu-title"}}) @components.MenuItem(components.MenuItemProps{Label: "Section 2", Attrs: templ.Attributes{"class": "menu-title"}}) { @components.MenuItem(components.MenuItemProps{Label: "2.1"}) { @components.MenuItem(components.MenuItemProps{Label: "2.1.1"}) @components.MenuItem(components.MenuItemProps{Label: "2.1.2"}) } @components.MenuItem(components.MenuItemProps{Label: "2.2"}) { @components.MenuItem(components.MenuItemProps{Label: "2.2.1"}) } @components.MenuItem(components.MenuItemProps{Label: "2.3"}) } @components.MenuItem(components.MenuItemProps{Label: "Section 3", Attrs: templ.Attributes{"class": "menu-title"}}) { @components.MenuItem(components.MenuItemProps{Label: "3.1"}) } } ================================================ FILE: internal/views/examples/dropdown.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example templ BasicDropdown() {
    @components.Dropdown( components.DropdownProps{ Label: "Dropdown label", Items: []components.DropdownItem{ {Label: "Item 1"}, {Label: "Item 2"}, {Label: "Item 3"}, }, ListClass: "bg-base-200 rounded-box z-50 w-52 p-2 shadow-sm", }, )
    } ================================================ FILE: internal/views/examples/fab.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example templ BasicFAB() {
    @components.FAB( components.FABProps{ Class: "absolute z-1", // !! preview purpose only Toggle: "F", ToggleClass: "btn-lg btn-circle", }) { }
    } // example // FAB with SVGs as toggle and content buttons templ FABWithSVGButtons() {
    @components.FAB( components.FABProps{ Class: "absolute z-1", // !! preview purpose only Toggle: fabSVGToggle(), ToggleClass: "btn-lg btn-circle", }) { @fabSVGVoiceButton() @fabSVGImageButton() @fabSVGCameraButton() }
    } templ fabSVGToggle() { } templ fabSVGVoiceButton() { } templ fabSVGImageButton() { } templ fabSVGCameraButton() { } // example // FAB flower templ FABFlowerExample() {
    @components.FAB(components.FABProps{ Class: "fab-flower absolute z-1", // !! absolute & z-1 preview purpose only Toggle: "F", ToggleClass: "btn btn-lg btn-circle", MainAction: fabFlowerMainAction(), }) { }
    } templ fabFlowerMainAction() { } ================================================ FILE: internal/views/examples/features.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example templ FeaturesExample() { @components.Features( components.FeaturesProps{ Title: "Discover our exclusive features", Features: []components.FeatureProps{ { Icon: CustomizationIcon(), Title: "Customization", Description: "Tailor our product to suit your needs.", }, { Icon: SecurityIcon(), Title: "Security", Description: "Your data is protected by the latest security measures.", }, { Icon: SupportIcon(), Title: "Support", Description: "24/7 customer support for all your inquiries.", }, { Icon: PerformanceIcon(), Title: "Performance", Description: "Experience blazing-fast performance with our product.", }, { Icon: GlobalReachIcon(), Title: "Global reach", Description: `Tailor our product to suit your needs. Expand your reach with our global network.`, }, { Icon: CommunicationIcon(), Title: "Communication", Description: "Seamless communication for your team.", }, }, }, ) } templ CustomizationIcon() { } templ SecurityIcon() { } templ SupportIcon() { } templ PerformanceIcon() { } templ GlobalReachIcon() { } templ CommunicationIcon() { } ================================================ FILE: internal/views/examples/file_input.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example // Different size file inputs templ DifferentSizeFileInputs() { @components.FileInput(components.FileInputProps{ Label: "File upload", Size: "xs", }) @components.FileInput(components.FileInputProps{ Label: "File upload", Size: "sm", }) @components.FileInput(components.FileInputProps{ Label: "File upload", }) @components.FileInput(components.FileInputProps{ Label: "File upload", Size: "lg", }) @components.FileInput(components.FileInputProps{ Label: "File upload", Size: "xl", }) } ================================================ FILE: internal/views/examples/footer.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example templ BasicFooterWithLinks() { @components.Footer( components.FooterProps{ Icon: FooterCompanyInfoIconExample(), Name: "ACME Industries Ltd.", Description: "Providing reliable tech since 1992", Anchors: []components.AnchorProps{ {LeftIcon: XIcon()}, {LeftIcon: YoutubeIcon()}, {LeftIcon: FacebookIcon()}, }, }, ) { @components.FooterNav( "Services", []components.AnchorProps{ {Label: "Branding"}, {Label: "Design"}, {Label: "Marketing"}, {Label: "Advertisement"}, }, ) @components.FooterNav( "Company", []components.AnchorProps{ {Label: "About us"}, {Label: "Contact"}, {Label: "Jobs"}, {Label: "Press kit"}, }, ) @components.FooterNav( "Legal", []components.AnchorProps{ {Label: "Terms of use"}, {Label: "Privacy policy"}, {Label: "Cookie policy"}, }, ) } } templ FooterCompanyInfoIconExample() { } ================================================ FILE: internal/views/examples/hero.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example templ BasicHero() {
    @components.Hero(components.HeroProps{ Source: "/static/images/avatar.jpg", Alt: "hero avatar", Class: "bg-base-200 min-h-[600px]", }) {

    Lorem ipsum!

    Lorem ipsum dolor sit, amet consectetur adipisicing elit. Ex quibusdam dicta necessitatibus! Deleniti temporibus iure porro cupiditate dolorum modi voluptate perferendis velit tempora repudiandae expedita, impedit omnis vitae. Laborum, dignissimos?

    } @components.Hero(components.HeroProps{ Source: "/static/images/avatar.jpg", Alt: "hero avatar", Class: "bg-base-200 min-h-[600px]", Reverse: true, }) {

    Lorem ipsum!

    Lorem ipsum dolor sit, amet consectetur adipisicing elit. Ex quibusdam dicta necessitatibus! Deleniti temporibus iure porro cupiditate dolorum modi voluptate perferendis velit tempora repudiandae expedita, impedit omnis vitae. Laborum, dignissimos?

    }
    } ================================================ FILE: internal/views/examples/hover_3d_card.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example templ Hover3DCardExample() { @components.Hover3DCard() { @components.Card( components.CardProps{ Title: "Hover 3D Card", Content: "This is a hover 3D card. Move your mouse around the card.", Class: "h-[200px] card-border", }, ) } } ================================================ FILE: internal/views/examples/infinite_scroll.templ ================================================ package examples import ( "fmt" "github.com/haatos/goshipit/internal/views/components" ) // example templ InfiniteScrollTableExample() { @components.Table( []templ.Component{components.PlainText("Name"), components.PlainText("Email")}, initialRows(), nil, )
    } func initialRows() []templ.Component { data := make([]templ.Component, 10) for i := 0; i < 10; i++ { data[i] = components.InfiniteScrollRow("John Doe", fmt.Sprintf("john.doe%d@email.com", i), 0, i == 9) } return data } ================================================ FILE: internal/views/examples/input.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example // Different size inputs templ DifferentSizeInputs() { @components.Input(components.InputProps{ Label: "Name", Placeholder: "Your name...", Size: "xs", }) @components.Input(components.InputProps{ Label: "Name", Placeholder: "Your name...", Size: "sm", }) @components.Input(components.InputProps{ Label: "Name", Placeholder: "Your name...", }) @components.Input(components.InputProps{ Label: "Name", Placeholder: "Your name...", Size: "lg", }) @components.Input(components.InputProps{ Label: "Name", Placeholder: "Your name...", Size: "xl", }) } // example // Integer input templ IntegerInput() { @components.Input(components.InputProps{ Name: "my_number", Type: "number", Placeholder: "Give a number...", }) } // example // Decimal input templ DecimalInput() { @components.Input(components.InputProps{ Name: "my_number", Type: "number", Placeholder: "Give a decimal number...", Attrs: templ.Attributes{ "min": "0.00", "max": "2.00", "step": "0.25", }, }) } // example // Password field with validator templ PasswordFieldWithValidator() { @components.Input(components.InputProps{ Name: "password", Type: "password", Placeholder: "Password", Required: true, Pattern: `(?=.*(\W|_))(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,255}`, ValidatorHint: "Must be at least 8 characters long, including a digit, lower- and uppercase letter, and a special character", Icon: keyIcon(), Attrs: templ.Attributes{ "minlength": "8", "maxlength": "255", }, }) } templ keyIcon() { } // example // Email field with validtor templ EmailFieldWithValidator() { @components.Input(components.InputProps{ Name: "email", Type: "email", Placeholder: "Email", Required: true, Pattern: `^[^\s@]+@[^\s@]+\.[^\s@]+$`, ValidatorHint: "Please enter a valid email address", Icon: emailIcon(), }) } templ emailIcon() { } // example templ SignUpFormExample() {

    Sign Up

    @OAuthButtons()
    OR
    @SignUpForm( "", "", "", "", "", "", "", "", )
    } // Sign up form with inline validation templ SignUpForm( firstName, firstNameError, lastName, lastNameError, email, emailError, password, passwordError string, ) {
    @components.Input( components.InputProps{ Label: "First name", Name: "first_name", Placeholder: "Your first name...", Value: firstName, Error: firstNameError, Required: true, Attrs: templ.Attributes{ "hx-post": "/validate/string/first_name?v=notempty", }, }, ) @components.Input( components.InputProps{ Label: "Last name", Name: "last_name", Placeholder: "Your last name...", Value: lastName, Error: lastNameError, Required: true, Attrs: templ.Attributes{ "hx-post": "/validate/string/last_name?v=notempty", }, }, ) @components.Input( components.InputProps{ Label: "Email", Name: "email", Type: "email", Placeholder: "Your email...", Value: email, Error: emailError, Required: true, Attrs: templ.Attributes{ "hx-post": "/validate/string/email?v=email", }, }, ) @components.Input( components.InputProps{ Label: "Password", Name: "password", Type: "password", Placeholder: "Your new password...", Required: true, MinLength: "8", MaxLength: "255", Pattern: `(?=.*(\W|_))(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,255}`, ValidatorHint: "Must be at least 8 characters long, including a digit, lower- and uppercase letter, and a special character", Attrs: templ.Attributes{ "hx-post": "/validate/string/password?v=hasupper&v=haslower&v=hasdigit", }, }, )
    } templ OAuthButtons() {
    @GoogleOAuthLink("") @GithubOAuthLink("")
    } templ GoogleOAuthLink(href string) { Sign in with Google } templ GithubOAuthLink(href string) { Sign in with GitHub } ================================================ FILE: internal/views/examples/lazy_load.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example templ LazyLoadExample() { @components.LazyLoad("/lazy-load") } templ LazyLoadResult() {
    LAZY
    LOAD
    COMPLETE
    } ================================================ FILE: internal/views/examples/menu.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example // Basic menu templ MenuExample() {
    @components.Menu(components.MenuProps{Title: "Basic menu", Class: "w-52"}) { @components.MenuItem(components.MenuItemProps{Label: "Section 1", Attrs: templ.Attributes{"class": "menu-title"}}) @components.MenuItem(components.MenuItemProps{Label: "Section 2", Attrs: templ.Attributes{"class": "menu-title"}}) { @components.MenuItem(components.MenuItemProps{Label: "2.1"}) { @components.MenuItem(components.MenuItemProps{Label: "2.1.1"}) @components.MenuItem(components.MenuItemProps{Label: "2.1.2"}) } @components.MenuItem(components.MenuItemProps{Label: "2.2"}) { @components.MenuItem(components.MenuItemProps{Label: "2.2.1"}) } @components.MenuItem(components.MenuItemProps{Label: "2.3"}) } @components.MenuItem(components.MenuItemProps{Label: "Section 3", Attrs: templ.Attributes{"class": "menu-title"}}) { @components.MenuItem(components.MenuItemProps{Label: "3.1"}) } }
    } // example // Menu with a submenu templ MenuWithSubmenusExample() {
    @components.Menu(components.MenuProps{Class: "w-52"}) { @components.MenuItem( components.MenuItemProps{ Label: "Section 1", Attrs: templ.Attributes{"class": "menu-title"}, }) @components.Submenu( components.SubmenuProps{ Title: "Section 2", Attrs: templ.Attributes{"open": ""}, }, ) { @components.MenuItem(components.MenuItemProps{Label: "2.1"}) @components.MenuItem(components.MenuItemProps{Label: "2.2"}) } }
    } // example // Menu with icons and a submenu templ DashboardMenuExample() { @components.Menu( components.MenuProps{Class: "w-64 bg-base-200 rounded-box"}) { @components.MenuItem( components.MenuItemProps{ Label: "Dashboard", Icon: HomeIcon(), Attrs: templ.Attributes{"href": "#dashboard"}, }) @components.MenuItem( components.MenuItemProps{ Label: "Users", Icon: UsersIcon(), Attrs: templ.Attributes{"href": "#users"}, }) @components.Submenu(components.SubmenuProps{Title: "Content", Icon: ContentIcon()}) { @components.MenuItem( components.MenuItemProps{ Label: "Posts", Icon: PostsIcon(), Attrs: templ.Attributes{"href": "#posts"}, }) @components.MenuItem( components.MenuItemProps{ Label: "Images", Icon: ImagesIcon(), Attrs: templ.Attributes{"href": "#images"}, }) @components.MenuItem( components.MenuItemProps{ Label: "Videos", Icon: VideosIcon(), Attrs: templ.Attributes{"href": "#videos"}, }) } } } templ HomeIcon() { } templ UsersIcon() { } templ ContentIcon() { } templ PostsIcon() { } templ ImagesIcon() { } templ VideosIcon() { } ================================================ FILE: internal/views/examples/modal.templ ================================================ package examples import ( "fmt" "github.com/haatos/goshipit/internal/views/components" ) // example // Basic modal templ BasicModal() {
    @components.Modal(components.ModalProps{ID: "my_modal", Label: "Click me"}) {

    Modal title

    Modal content goes here

    }
    } // example // Multiple modals templ MultipleModals() {
    for i := range 3 { @components.Modal(components.ModalProps{ID: fmt.Sprintf("my_modal_%d", i), Label: "Click me"}) {

    Modal title { fmt.Sprintf("%d", i) }

    Modal content { fmt.Sprintf("%d", i) }

    } }
    } // example // Modal with action button templ ModalWithAction() {
    @components.Modal(components.ModalProps{ID: "my_modal_actions", Label: "Click me"}) {

    Modal title

    Modal content goes here

    }
    } // example // Modal to confirm delete action templ ModalConfirmDelete() {
    for i := range 6 {
    @components.Modal( components.ModalProps{ ID: fmt.Sprintf("modal_confirm_%d", i), Label: modalConfirmDeleteButton(i, templ.Attributes{"onclick": fmt.Sprintf("modal_confirm_%d.showModal()", i)}), }, ) {

    Are you sure you want to delete { fmt.Sprintf("%d", i) }?

    }
    }
    } templ modalConfirmDeleteButton(i int, attrs templ.Attributes) { } ================================================ FILE: internal/views/examples/pagination.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example templ BasicPaginationExample() { @BasicPagination( "example-pagination", components.PaginationProps{ URL: "/pagination-pages", Page: 1, Low: 1, High: 7, MaxPages: 20, }, [][]string{ {"Key0", "Value0"}, {"Key1", "Value1"}, {"Key2", "Value2"}, {"Key3", "Value3"}, {"Key4", "Value4"}, {"Key5", "Value5"}, {"Key6", "Value6"}, {"Key7", "Value7"}, {"Key8", "Value8"}, {"Key9", "Value9"}, }, ) } templ BasicPagination(id string, p components.PaginationProps, data [][]string) { @components.Pagination("example-pagination", p) {
    for _, d := range data { }
    Key Value
    { d[0] } { d[1] }
    } } ================================================ FILE: internal/views/examples/pricing.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example // Basic pricing section templ PricingExample() { @components.Pricing( components.PricingProps{ Checked: true, Prices: []components.PriceProps{ { Title: "Free", Description: "My free plan", PriceMonthly: "$ 0", PerMonthly: "/ month", PriceAnnually: "$ 0", PerAnnually: "/ month", IncludedFeatures: []string{"Feature 1", "Feature 2", "Feature 3"}, ExcludedFeatures: []string{"Feature 4", "Feature 5"}, CallToAction: components.PriceButtonProps{ Label: "Start free", Attrs: templ.Attributes{ "class": "btn btn-outline mt-8", }, }, }, { Title: "Starter", Description: "Starter plan", PriceMonthly: "$ 12", PerMonthly: "/ month", PriceAnnually: "$ 10", PerAnnually: "/ month", IncludedFeatures: []string{"Feature 1", "Feature 2", "Feature 3"}, ExcludedFeatures: []string{"Feature 4", "Feature 5"}, CallToAction: components.PriceButtonProps{ Label: "Get started", Attrs: templ.Attributes{ "class": "btn btn-primary mt-8", }, }, }, { Title: "Professional", Description: "Professional plan", PriceMonthly: "$ 20", PerMonthly: "/ month", PriceAnnually: "$ 16", PerAnnually: "/ month", IncludedFeatures: []string{"Feature 1", "Feature 2", "Feature 3"}, ExcludedFeatures: []string{"Feature 4", "Feature 5"}, CallToAction: components.PriceButtonProps{ Label: "Get started", Attrs: templ.Attributes{ "class": "btn btn-primary mt-8", }, }, }, }, }, ) } // example // Pricing section with promotion templ PricingWithPromotionExample() { @components.Pricing( components.PricingProps{ Checked: true, Prices: []components.PriceProps{ { Title: "Free", Description: "My free plan", PriceMonthly: "$ 0", PerMonthly: "/ month", PriceAnnually: "$ 0", PerAnnually: "/ month", IncludedFeatures: []string{"Feature 1", "Feature 2", "Feature 3"}, ExcludedFeatures: []string{"Feature 4", "Feature 5"}, CallToAction: components.PriceButtonProps{ Label: "Start free", Attrs: templ.Attributes{ "class": "btn btn-outline mt-8", }, }, }, { Title: "Starter", Description: "Starter plan", PriceMonthly: "$ 12", PerMonthly: "/ month", PriceAnnually: "$ 10", PerAnnually: "/ month", IncludedFeatures: []string{"Feature 1", "Feature 2", "Feature 3"}, ExcludedFeatures: []string{"Feature 4", "Feature 5"}, CallToAction: components.PriceButtonProps{ Label: "Get started", Attrs: templ.Attributes{ "class": "btn btn-primary mt-8", }, }, }, { Title: "Professional", Description: "Professional plan", PriceMonthly: "$ 20", PerMonthly: "/ month", PriceAnnually: "$ 16", PerAnnually: "/ month", Promotion: "Popular", IncludedFeatures: []string{"Feature 1", "Feature 2", "Feature 3"}, ExcludedFeatures: []string{"Feature 4", "Feature 5"}, CallToAction: components.PriceButtonProps{ Label: "Get started", Attrs: templ.Attributes{ "class": "btn btn-primary mt-8", }, }, }, { Title: "Ultimate", Description: "Ultimate plan", PriceMonthly: "$ 30", PerMonthly: "/ month", PriceAnnually: "$ 25", PerAnnually: "/ month", IncludedFeatures: []string{"Feature 1", "Feature 2", "Feature 3", "Feature 4", "Feature 5"}, CallToAction: components.PriceButtonProps{ Label: "Get started", Attrs: templ.Attributes{ "class": "btn btn-primary mt-8", }, }, }, }, }, ) } ================================================ FILE: internal/views/examples/radio.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example // Different size radio groups templ DefaultRadio() {
    @components.Radio( components.RadioProps{ Name: "my-radio-group1", Values: map[string]string{ "Apples": "apples", "Oranges": "oranges", }, Size: "xs", }, )
    @components.Radio( components.RadioProps{ Name: "my-radio-group1", Values: map[string]string{ "Apples": "apples", "Oranges": "oranges", }, Size: "sm", }, )
    @components.Radio( components.RadioProps{ Name: "my-radio-group1", Values: map[string]string{ "Apples": "apples", "Oranges": "oranges", }, }, )
    @components.Radio( components.RadioProps{ Name: "my-radio-group1", Values: map[string]string{ "Apples": "apples", "Oranges": "oranges", }, Size: "lg", }, )
    @components.Radio( components.RadioProps{ Name: "my-radio-group1", Values: map[string]string{ "Apples": "apples", "Oranges": "oranges", }, Size: "xl", }, )
    } // example // Primary radio group templ PrimaryRadio() {
    @components.Radio( components.RadioProps{ Name: "my-radio-group2", Values: map[string]string{ "Apples": "apples", "Oranges": "oranges", "And something truly special here to see how this works with a longer key": "blaaaaa", }, Class: "radio-primary", }, )
    } ================================================ FILE: internal/views/examples/range.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example // Range component using alpine.js templ BasicRange() {
    @components.Range( components.RangeProps{ Name: "my-range", Value: 25, Min: 0, Max: 100, Step: 5, Size: "xs", }, ) @components.Range( components.RangeProps{ Name: "my-range", Value: 25, Min: 0, Max: 100, Step: 5, Size: "sm", }, ) @components.Range( components.RangeProps{ Name: "my-range", Value: 25, Min: 0, Max: 100, Step: 5, }, ) @components.Range( components.RangeProps{ Name: "my-range", Value: 25, Min: 0, Max: 100, Step: 5, Size: "lg", }, ) @components.Range( components.RangeProps{ Name: "my-range", Value: 25, Min: 0, Max: 100, Step: 5, Size: "xl", }, )
    } // example // Range component using datastar.js templ DatastarRange() {
    @components.DatastarRange( components.RangeProps{ Name: "my-range", Value: 25, Min: 0, Max: 100, Step: 5, }, )
    } ================================================ FILE: internal/views/examples/rating.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example // Rating from 1 to 5 templ RatingFromOneToFive() {
    @components.Rating( components.RatingProps{ Name: "my-rating", Min: 1, Max: 5, }, )
    } // example // Rating from 0 to 5 templ RatingFromZeroToFive() {
    @components.Rating( components.RatingProps{ Name: "my-rating2", Min: 0, Max: 5, }, )
    } ================================================ FILE: internal/views/examples/select.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example // Different size selects templ DifferentSizeSelects() {
    @components.Select( components.SelectProps{ Label: "Make a choice", Name: "my-select", Options: []components.SelectOption{ {Label: "Which one?", Selected: true, Disabled: true}, {Label: "Apples", Value: "apples"}, {Label: "Oranges", Value: "oranges"}, }, Size: "xs", }, ) @components.Select( components.SelectProps{ Label: "Make a choice", Name: "my-select", Options: []components.SelectOption{ {Label: "Which one?", Selected: true, Disabled: true}, {Label: "Apples", Value: "apples"}, {Label: "Oranges", Value: "oranges"}, }, Size: "sm", }, ) @components.Select( components.SelectProps{ Label: "Make a choice", Name: "my-select", Options: []components.SelectOption{ {Label: "Which one?", Selected: true, Disabled: true}, {Label: "Apples", Value: "apples"}, {Label: "Oranges", Value: "oranges"}, }, }, ) @components.Select( components.SelectProps{ Label: "Make a choice", Name: "my-select", Options: []components.SelectOption{ {Label: "Which one?", Selected: true, Disabled: true}, {Label: "Apples", Value: "apples"}, {Label: "Oranges", Value: "oranges"}, }, Size: "lg", }, ) @components.Select( components.SelectProps{ Label: "Make a choice", Name: "my-select", Options: []components.SelectOption{ {Label: "Which one?", Selected: true, Disabled: true}, {Label: "Apples", Value: "apples"}, {Label: "Oranges", Value: "oranges"}, }, Size: "xl", }, )
    } // example // Cascading select templ CascadingSelect() {
    @components.Select( components.SelectProps{ Label: "Make", Name: "make", Options: []components.SelectOption{ {Label: "Audi", Value: "audi"}, {Label: "BMW", Value: "bmw"}, {Label: "Toyota", Value: "toyota"}, }, Attrs: templ.Attributes{ "hx-get": "/models", "hx-target": "#models", }, }, ) @components.Select( components.SelectProps{ Label: "Model", Name: "model", Options: []components.SelectOption{ {Label: "A1", Value: "a1"}, {Label: "A4", Value: "a4"}, {Label: "A6", Value: "a6"}, }, Attrs: templ.Attributes{ "id": "models", }, }, )
    } ================================================ FILE: internal/views/examples/skeleton.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example templ SkeletonExample() {
    @components.Skeleton()
    } ================================================ FILE: internal/views/examples/stats.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example templ BasicStats() {
    @components.Stats() { @components.Stat(components.StatProps{ Title: "Downloads", Value: "31k", Description: "Jan 1st - Feb 1st", }) @components.Stat(components.StatProps{ Title: "New Users", Value: "4,200", Description: "↗︎ 400 (22%)", }) @components.Stat(components.StatProps{ Title: "New Registers", Value: "1,200", Description: "↘︎ 90 (14%)", }) }
    } ================================================ FILE: internal/views/examples/status.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example // 404 Not Found status templ StatusNotFound() { @components.Status(components.StatusProps{ Code: 404, Title: "Not Found", Description: "Looks like there's nothing here...", ReturnButtonLabel: "Go back", }) } // example // 403 Forbidden status templ StatusForbidden() { @components.Status(components.StatusProps{ Code: 403, Title: "Forbidden", Description: "Invalid permissions to view this page.", ReturnButtonLabel: "Go back", }) } // example // 401 Unauthorized status templ StatusUnauthorized() { @components.Status(components.StatusProps{ Code: 401, Title: "Unauthorized", Description: "This page is only available to authenticated users.", ReturnButtonLabel: "Go back", }) } // example // 500 Internal Server Error status templ StatusInternalServerError() { @components.Status(components.StatusProps{ Code: 500, Title: "Internal Server Error", Description: "Something went terribly wrong...", ReturnButtonLabel: "Go back", }) } ================================================ FILE: internal/views/examples/steps.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example templ BasicSteps() {
    @components.Steps() { @components.Step(components.StepProps{Label: "Register", Done: true}) @components.Step(components.StepProps{Label: "Choose plan", Done: true}) @components.Step(components.StepProps{Label: "Purchase"}) @components.Step(components.StepProps{Label: "Receive product"}) }
    } ================================================ FILE: internal/views/examples/swap.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example templ BasicSwap() {
    @components.Swap( components.SwapProps{ On: SwapExampleOn(), Off: SwapExampleOff(), Class: "swap-flip", }, )
    } templ SwapExampleOn() { ON } templ SwapExampleOff() { OFF } ================================================ FILE: internal/views/examples/table.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example templ BasicTable() { @components.Table( []templ.Component{ components.Checkbox( components.CheckboxProps{ Name: "all", }, ), components.PlainText("Name"), components.PlainText("Email"), }, []templ.Component{ TableExampleRow("John Doe", "john.doe@example.com"), TableExampleRow("Jane Doe", "Jane.doe@example.com"), TableExampleRow("Jim Smith", "jim.smith@example.com"), TableExampleRow("Julie Smith", "julie.smith@example.com"), }, nil, ) } templ TableExampleRow(name, email string) { @components.Checkbox(components.CheckboxProps{Name: email}) @components.PlainText(name) @components.PlainText(email) } ================================================ FILE: internal/views/examples/tabs.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example templ BasicTabs() { @components.Tabs( components.TabsProps{ Name: "basic-tabs", Class: "tabs-border", ContentClass: "bg-base-100 py-8", Tabs: []components.TabProps{ { Label: "Home", Content: homeTabContent(), }, { Label: "Info", Content: infoTabContent(), }, { Label: "Stats", Content: statsTabContent(), }, }, }) } templ homeTabContent() {

    This is the home tab

    } templ infoTabContent() {

    This is the info tab

    } templ statsTabContent() {

    This is the stats tab

    } ================================================ FILE: internal/views/examples/testimonial.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example templ TestimonialGridExample() { @components.TestimonialGrid( "Read what our customers think", components.TestimonialProps{ { Avatar: components.Avatar(components.AvatarProps{ ContainerClass: "rounded h-20", Source: "/static/images/avatar.jpg", }), Name: "Jane Doe", Rating: 5, Content: `Lorem ipsum dolor sit amet consectetur adipisicing elit. Iure impedit, placeat sed provident enim fuga possimus ducimus est iusto inventore earum aliquid officia minus, maiores quia dicta magni ex labore? Lorem ipsum dolor sit amet consectetur adipisicing elit. Libero maxime quos laboriosam natus illum similique id nam, rerum, sunt veritatis dolorum accusamus voluptas odio minus necessitatibus perspiciatis, aliquid repellat iste.`, }, { Avatar: components.Avatar( components.AvatarProps{ ContainerClass: "rounded h-20", Source: "/static/images/avatar.jpg"}, ), Name: "Jane Doe", Rating: 4, Content: `maiores quia dicta magni ex labore? Lorem ipsum dolor sit amet consectetur adipisicing elit. Libero maxime quos laboriosam natus illum similique id nam, rerum, sunt veritatis dolorum accusamus voluptas odio minus necessitatibus perspiciatis, aliquid repellat iste.`, }, { Avatar: components.Avatar(components.AvatarProps{ ContainerClass: "rounded h-20", Source: "/static/images/avatar.jpg", }), Name: "Jane Doe", Rating: 3, Content: `Iure impedit, placeat sed provident enim fuga possimus ducimus est iusto inventore earum aliquid officia minus, maiores quia dicta magni ex labore? Lorem ipsum dolor sit amet consectetur adipisicing elit. Libero maxime quos laboriosam natus illum similique id nam, rerum, sunt veritatis dolorum accusamus voluptas odio minus necessitatibus perspiciatis, aliquid repellat iste.`, }, { Avatar: components.Avatar(components.AvatarProps{ ContainerClass: "rounded h-20", Source: "/static/images/avatar.jpg", }), Name: "Jane Doe", Rating: 5, Content: `Lorem ipsum dolor sit amet consectetur adipisicing elit. Iure impedit, placeat sed provident enim fuga possimus ducimus est iusto inventore earum aliquid officia minus, maiores quia dicta magni ex labore? Lorem ipsum dolor sit amet consectetur adipisicing elit. Libero maxime quos laboriosam natus illum similique id nam, rerum, sunt veritatis dolorum accusamus voluptas odio minus necessitatibus perspiciatis, aliquid repellat iste.`, }, { Avatar: components.Avatar(components.AvatarProps{ ContainerClass: "rounded h-20", Source: "/static/images/avatar.jpg", }), Name: "Jane Doe", Rating: 5, Content: "Lorem ipsum dolor sit amet consectetur adipisicing elit.", }, { Avatar: components.Avatar(components.AvatarProps{ ContainerClass: "rounded h-20", Source: "/static/images/avatar.jpg", }), Name: "Jane Doe", Rating: 1, Content: `Lorem ipsum dolor sit amet consectetur adipisicing elit. Iure impedit, placeat sed provident enim fuga possimus ducimus est iusto inventore earum aliquid officia minus.`, }, { Avatar: components.Avatar(components.AvatarProps{ ContainerClass: "rounded h-20", Source: "/static/images/avatar.jpg", }), Name: "Jane Doe", Rating: 2, Content: `Lorem ipsum dolor sit amet consectetur adipisicing elit. Iure impedit, placeat sed provident enim fuga possimus ducimus est iusto inventore earum aliquid officia minus, maiores quia dicta magni ex labore? Lorem ipsum dolor sit amet consectetur adipisicing elit.`, }, { Avatar: components.Avatar(components.AvatarProps{ ContainerClass: "rounded h-20", Source: "/static/images/avatar.jpg", }), Name: "Jane Doe", Rating: 5, Content: `Lorem ipsum dolor sit amet consectetur adipisicing elit. Iure impedit, placeat sed provident enim fuga possimus ducimus est iusto inventore earum aliquid officia minus, maiores quia dicta magni ex labore? Lorem ipsum dolor sit amet consectetur adipisicing elit. Libero maxime quos laboriosam natus illum similique id nam, rerum, sunt veritatis.`, }, { Avatar: components.Avatar(components.AvatarProps{ ContainerClass: "rounded h-20", Source: "/static/images/avatar.jpg", }), Name: "Jane Doe", Rating: 4, Content: `Lorem ipsum dolor sit amet consectetur adipisicing elit. Iure impedit, placeat sed provident enim fuga possimus ducimus est iusto inventore earum aliquid officia minus, maiores quia dicta magni ex labore? Lorem ipsum dolor sit amet consectetur adipisicing elit. Libero maxime quos laboriosam natus illum similique id nam, rerum.`, }, { Avatar: components.Avatar(components.AvatarProps{ ContainerClass: "rounded h-20", Source: "/static/images/avatar.jpg", }), Name: "Jane Doe", Rating: 4, Content: `Lorem ipsum dolor sit amet consectetur adipisicing elit. Iure impedit, placeat sed provident enim fuga possimus ducimus est iusto inventore earum aliquid officia minus, maiores quia dicta magni ex labore? Lorem ipsum dolor sit amet consectetur adipisicing elit. Libero maxime quos laboriosam natus illum similique id nam, rerum, sunt veritatis dolorum accusamus voluptas.`, }, }, ) } ================================================ FILE: internal/views/examples/text_rotate.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example templ TextRotateExample() { @components.TextRotate( components.TextRotateProps{ Class: "text-5xl font-bold", Items: []components.TextRotateItem{ {Text: "ONE", Class: "text-primary"}, {Text: "TWO", Class: "text-accent"}, {Text: "THREE", Class: "text-secondary"}, }, }, ) } ================================================ FILE: internal/views/examples/textarea.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example // Basic textarea templ BasicTextarea() {
    @components.Textarea( components.TextareaProps{ Label: "Description", Name: "description", Class: "textarea-bordered resize-none", }, )
    } // example // Different sizes templ DifferentSizeTextareas() {
    @components.Textarea( components.TextareaProps{ Label: "Description", Class: "textarea-bordered resize-none", Value: "Extra small", Size: "xs", }, ) @components.Textarea( components.TextareaProps{ Label: "Description", Class: "textarea-bordered resize-none", Value: "Small", Size: "sm", }, ) @components.Textarea( components.TextareaProps{ Label: "Description", Class: "textarea-bordered resize-none", Value: "Medium", }, ) @components.Textarea( components.TextareaProps{ Label: "Description", Class: "textarea-bordered resize-none", Value: "Large", Size: "lg", }, ) @components.Textarea( components.TextareaProps{ Label: "Description", Class: "textarea-bordered resize-none", Value: "Extra Large", Size: "xl", }, )
    } // example // Textarea with error templ BasicTextareaWithError() {
    @components.Textarea( components.TextareaProps{ Label: "Description", Name: "description", Err: "Description cannot be empty", Class: "textarea-bordered resize-none", }, )
    } ================================================ FILE: internal/views/examples/time_slot_picker.templ ================================================ package examples import "time" // example // Basic time slot picker templ BasicTimeSlotPicker() {
    } ================================================ FILE: internal/views/examples/timeline.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example templ BasicTimeline() {
    @components.Timeline( components.TimelineProps{ {Start: "1984", Middle: components.TimelineCheckbox(true), End: "First Macintosh computer"}, {Start: "1998", Middle: components.TimelineCheckbox(true), End: "iMac"}, {Start: "2001", Middle: components.TimelineCheckbox(false), End: "iPod"}, {Start: "2007", Middle: components.TimelineCheckbox(false), End: "iPhone"}, {Start: "2015", Middle: components.TimelineCheckbox(false), End: "Apple Watch"}, }, )
    } ================================================ FILE: internal/views/examples/toast.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example // Info-type toast templ InfoToast() {
    @components.Toast( components.ToastProps{ Name: "info-toast", ToastClass: "absolute toast-end toast-top", AlertClass: "alert-info", }, ) { Info toast }
    } // example // Warning-type toast templ WarningToast() {
    @components.Toast( components.ToastProps{ Name: "warning-toast", ToastClass: "absolute toast-end toast-bottom", AlertClass: "alert-warning", }, ) { Warning toast }
    } // example // Error-type toast templ ErrorToast() {
    @components.Toast( components.ToastProps{ Name: "error-toast", ToastClass: "absolute toast-center toast-top", AlertClass: "alert-error", }, ) { Error toast }
    } // example // Info-type toast with button to remove it templ InfoToastConfirm() {
    @components.Toast(components.ToastProps{ Name: "error-toast", ToastClass: "absolute toast-end toast-top", AlertClass: "alert-info", }, ) { Info toast }
    } ================================================ FILE: internal/views/examples/toggle.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example // Different size toggles templ DifferentSizeToggles() {
    @components.Toggle( components.ToggleProps{ Before: "Check me out", Name: "checkbox1", Size: "xs", }, ) @components.Toggle( components.ToggleProps{ Before: "Check me out", Name: "checkbox1", Size: "sm", }, ) @components.Toggle( components.ToggleProps{ Before: "Check me out", Name: "checkbox1", }, ) @components.Toggle( components.ToggleProps{ Before: "Check me out", Name: "checkbox1", Size: "lg", }, ) @components.Toggle( components.ToggleProps{ Before: "Check me out", Name: "checkbox1", Size: "xl", }, )
    } // example // Primary toggle with label after templ PrimaryToggle() {
    @components.Toggle( components.ToggleProps{ After: "Check me out", Name: "checkbox2", Checked: true, Class: "toggle-primary", }, )
    } // example // Primary toggle with highlight templ PrimaryToggleWithHighlight() {
    @components.Toggle( components.ToggleProps{ Before: "Paid monthly", After: "Paid annually", Name: "checkbox3", Highlight: true, Class: "toggle-primary", }, )
    } ================================================ FILE: internal/views/examples/tooltip.templ ================================================ package examples import "github.com/haatos/goshipit/internal/views/components" // example // Basic tooltip at the top templ BasicTooltip() {
    @components.Tooltip(components.TooltipProps{Tip: "Hello"}) { }
    } // example // Error-type tooltip on the bottom templ BasicTooltipError() {
    @components.Tooltip( components.TooltipProps{ Tip: "Hello", Class: "tooltip-bottom tooltip-error", }, ) { }
    } ================================================ FILE: internal/views/pages/base.templ ================================================ package pages import ( "fmt" "github.com/haatos/goshipit/internal" "github.com/haatos/goshipit/internal/model" "github.com/haatos/goshipit/internal/views/components" "github.com/haatos/goshipit/internal/views/examples" "time" ) type Script struct { Source string Defer bool } templ DefaultHead() { @Head( "goship.it", []string{ "/static/css/tw.css", "/static/css/custom.css", "/static/css/chroma.css", }, []Script{ {Source: "/static/js/htmx.min.js"}, }, ) } templ Head(title string, stylesheets []string, scripts []Script) { for _, st := range stylesheets { } for _, sc := range scripts { } { title } } templ SideNavLayout(head templ.Component) { @DefaultHead()
    { children... }
    @components.Footer( components.FooterProps{ Name: "Haatos Ltd", Copyright: fmt.Sprintf("%s", time.Now().UTC().Format("2006")), Anchors: []components.AnchorProps{ {LeftIcon: examples.GithubIcon(), Href: "https://github.com/haatos"}, {LeftIcon: examples.LinkedInIcon(), Href: "https://linkedin.com/in/tomihaapalainen"}, }, }) { @components.FooterNav( "Links", []components.AnchorProps{ { Label: "DaisyUI 4 components", Attrs: templ.Attributes{ "name": "hx-anchor", "href": "https://old.goship.it", }, }, { Label: "Getting started", Attrs: templ.Attributes{ "name": "hx-anchor", "hx-get": "/get-started", "hx-target": "main", "hx-swap": "innerHTML", "hx-push-url": "true", }, }, { Label: "Types", Attrs: templ.Attributes{ "name": "hx-anchor", "hx-get": "/types", "hx-target": "main", "hx-swap": "innerHTML", "hx-push-url": "true", }, }, { Label: "CLI", Attrs: templ.Attributes{ "name": "hx-anchor", "hx-get": "/cli", "hx-target": "main", "hx-swap": "innerHTML", "hx-push-url": "true", }, }, { Label: "GitHub", LeftIcon: examples.GithubIcon(), Attrs: templ.Attributes{ "href": "https://github.com/haatos/goshipit", }, }, { Label: "About the creator", Attrs: templ.Attributes{ "name": "hx-anchor", "hx-get": "/about", "hx-target": "main", "hx-swap": "innerHTML", "hx-push-url": "true", }, }, }) @components.FooterNav( "Legal", []components.AnchorProps{ { Label: "Privacy policy", Attrs: templ.Attributes{ "name": "hx-anchor", "hx-get": "/privacy", "hx-target": "main", "hx-swap": "innerHTML", "hx-push-url": "true", }, }, { Label: "Terms of Service", Attrs: templ.Attributes{ "name": "hx-anchor", "hx-get": "/terms-of-service", "hx-target": "main", "hx-swap": "innerHTML", "hx-push-url": "true", }, }, }) }
    @drawerSide( []components.AnchorProps{ { Label: "DaisyUI 4 components", Attrs: templ.Attributes{ "name": "hx-anchor", "href": "https://old.goship.it", }, }, { Label: "Getting started", Attrs: templ.Attributes{ "name": "hx-anchor", "hx-get": "/get-started", "hx-target": "main", "hx-swap": "innerHTML", "hx-push-url": "true", }, }, { Label: "Types", Attrs: templ.Attributes{ "name": "hx-anchor", "hx-get": "/types", "hx-target": "main", "hx-swap": "innerHTML", "hx-push-url": "true", }, }, { Label: "CLI", Attrs: templ.Attributes{ "name": "hx-anchor", "hx-get": "/cli", "hx-target": "main", "hx-swap": "innerHTML", "hx-push-url": "true", }, }, { Label: "GitHub", LeftIcon: examples.GithubIcon(), Attrs: templ.Attributes{ "href": "https://github.com/haatos/goshipit", }, }, }, )
    } templ drawerSide(anchors []components.AnchorProps) {
    } templ searchIcon() { } type ComponentSearchItem struct { Category string Name string } templ ComponentSearchListItems(cs []ComponentSearchItem) { for i := range cs { } } templ ComponentAnchors(keys []string, m model.ComponentCodeMap) { } ================================================ FILE: internal/views/pages/cli.templ ================================================ package pages templ CLIPage(html string) { @SideNavLayout(nil) { @CLIPageMain(html) } } templ CLIPageMain(html string) {
    @templ.Raw(html)
    } ================================================ FILE: internal/views/pages/client_error.templ ================================================ package pages import "github.com/haatos/goshipit/internal/views/components" templ NotFound() { @SideNavLayout(nil) { @components.Status(components.StatusProps{ Code: 404, Title: "Not Found", Description: "There seems to be nothing here.", ReturnButtonLabel: "Go back home", ReturnButtonAttrs: templ.Attributes{ "hx-get": "/", "hx-target": "main", "hx-swap": "innerHTML", "hx-push-url": "true", }, }) } } templ Forbidden(message string) { @SideNavLayout(nil) { @components.Status(components.StatusProps{ Code: 403, Title: "Forbidden", Description: "Invalid permissions to acces this page.", ReturnButtonLabel: "Go back home", ReturnButtonAttrs: templ.Attributes{ "hx-get": "/", "hx-target": "main", "hx-swap": "innerHTML", "hx-push-url": "true", }, }) } } templ Unauthorized() { @SideNavLayout(nil) { @components.Status( components.StatusProps{ Code: 401, Title: "Unauthorized", Description: "This page is for authenticated users only.", ReturnButtonLabel: "Go back home", ReturnButtonAttrs: templ.Attributes{ "hx-get": "/", "hx-target": "main", "hx-swap": "innerHTML", "hx-push-url": "true", }, }, ) } } ================================================ FILE: internal/views/pages/component.templ ================================================ package pages import ( "github.com/haatos/goshipit/internal/markdown" "github.com/haatos/goshipit/internal/model" "github.com/haatos/goshipit/internal/views/components" "github.com/haatos/goshipit/internal/views/scripts" ) templ ComponentPage(cc model.ComponentCode, examples []templ.Component) { @SideNavLayout(nil) { @ComponentMain(cc, examples) } @scripts.CodeCopyButtonScript() } templ ComponentMain(cc model.ComponentCode, examples []templ.Component) {

    { cc.Label } if cc.DaisyUIURL != "" { @components.Anchor(components.AnchorProps{ Label: "@DaisyUI", Href: cc.DaisyUIURL, Class: "text-sm link link-primary", Attrs: templ.Attributes{"target": "_blank"}, RightIcon: externalLinkIcon(), }) }

    @templ.Raw(markdown.GetHTMLFromMarkdown([]byte(cc.Description)))
    if cc.Code != "" {

    Code

    @templ.Raw(markdown.GetHTMLFromMarkdown([]byte(cc.Code)))
    } if len(examples) > 0 {

    Examples

    }
    for _, example := range examples { @example }
    @scripts.HXCodeCopyButtonScript() } templ externalLinkIcon() { } templ ComponentExampleCode(code string) {
    @templ.Raw(markdown.GetHTMLFromMarkdown([]byte(code)))
    @scripts.HXCodeCopyButtonScript() } templ RawHTML(html string) { @templ.Raw(html) } templ ComponentExampleTabs(title, description string, tabs templ.Component) { if title != "" {

    { title }

    } @templ.Raw(markdown.GetHTMLFromMarkdown([]byte(description))) @tabs } templ ComponentTabs(name string, tabs []components.TabProps) { @components.Tabs( components.TabsProps{ Name: name, Class: "tabs-border", ContentClass: "bg-base-100 border-base-300 rounded-box p-4 overflow-x-auto", Tabs: tabs, }, ) } ================================================ FILE: internal/views/pages/index.templ ================================================ package pages import ( "github.com/haatos/goshipit/internal/views/components" "github.com/haatos/goshipit/internal/views/scripts" ) templ IndexPage() { @SideNavLayout(nil) { @IndexPageContent() } } templ IndexPageContent() { @openSourceToast()
    Go Ship .it

      The component library to

      give your Go applications

      a running start

    Now updated to DaisyUI5

    @components.Features( components.FeaturesProps{ Features: []components.FeatureProps{ { Icon: goIcon(), Title: "Golang", Description: "simple and efficient programming language ideal for building scalable, high-performance applications", URL: "https://go.dev", }, { Icon: templIcon(), Title: "Templ", Description: "fast, type-safe templating engine for Go to create HTML templates using Go code", URL: "https://templ.guide", }, { Icon: htmxIcon(), Title: "HTMX", Description: "lightweight JavaScript library to simplify building dynamic, interactive web applications", URL: "https://htmx.org", }, { Icon: tailwindcssIcon(), Title: "TailwindCSS", Description: "utility-first CSS framework to create flexible and efficient modern web interfaces", URL: "https://tailwindcss.com", }, { Icon: daisyuiIcon(), Title: "DaisyUI", Description: "component library to create attractive, responsive user interfaces with minimal custom styling", URL: "https://daisyui.com", }, { Icon: modelIcon(), Title: "Types", Description: "optional component arguments to simplify usage and reduce boilerplate code", URL: "https://goship.it/types", }, }, }, )

    Develop with the stack you love and go ship it

    goship.it is a Golang + Templ + HTMX (+ TailwindCSS + DaisyUI) component library that enables you to quickly develop an application using the GOTH stack, and put it in the hands of potential customers as quickly as possible.

    The library contains DaisyUI components translated into Templ components that can be easily customized using both TailwindCSS and DaisyUI.

    Examples here use Echo as the router. For usage with other routers or frameworks, refer to the integration guides found at templ.guide to see examples of rendering in different frameworks/libraries.

    } templ openSourceToast() {

    goship.it is now open source,
    see the repository here

    } templ goIcon() { } templ templIcon() {
    templ
    } templ htmxIcon() {
    { "<" }/{ ">" }
    } templ tailwindcssIcon() {
    Tailwind CSS icon
    } templ daisyuiIcon() {
    } templ modelIcon() {
    } templ AboutPage() { @SideNavLayout(nil) { @AboutPageMain() } } templ AboutPageMain() {

    Hi, my name is Tomi

    I'm a software developer from Finland. I'm a creative problem solver who enjoys creating quality driven software.

    Many of my hobby projects are available on Github, mostly written in Go and Python, with some Javascript (React & Nextjs) sprinkled in.

    My website haatos.com contains articles on learning programming (using Go), and a few more advanced tutorials on how to create specific functionality for your web applications.

    You can reach me on LinkedIn or by emailing me at tomi@haatos.com.

    } templ GettingStartedPage(html string) { @SideNavLayout(nil) { @GettingStartedPageMain(html) } @scripts.CodeCopyButtonScript() } templ GettingStartedPageMain(html string) {
    @templ.Raw(html)
    @scripts.HXCodeCopyButtonScript() } templ TypesPage(html string) { @SideNavLayout(nil) { @TypesPageMain(html) } @scripts.CodeCopyButtonScript() } templ TypesPageMain(html string) {
    @templ.Raw(html)
    @scripts.HXCodeCopyButtonScript() } templ PrivacyPage(domain, contactEmail string) { @SideNavLayout(nil) { @PrivacyMain(domain, contactEmail) } } templ PrivacyMain(domain, contactEmail string) {

    Privacy Policy for { domain }

    At { domain }, the privacy of our visitors is of utmost importance to us. This Privacy Policy document outlines the types of personal information that is received and collected by { domain } and how it is used.

    Information We Collect

    Personal Information: When you visit { domain }, we may collect personal information that you voluntarily provide to us, such as your name, email address, and any other information you choose to provide when contacting us or signing up for our services.

    Log Files: Like many other websites, { domain } makes use of log files. The information inside the log files includes internet protocol (IP) addresses, type of browser, Internet Service Provider (ISP), date/time stamp, referring/exit pages, and number of clicks to analyze trends, administer the site, track user's movement around the site, and gather demographic information. IP addresses and other such information are not linked to any information that is personally identifiable.

    Cookies and Web Beacons: { domain } uses cookies to store information about visitors' preferences, to record user-specific information on which pages the user accesses or visits, and to customize web page content based on visitors' browser type or other information that the visitor sends via their browser.

    How We Use Your Information

    • We may use the information we collect from you to personalize your experience and to provide you with better service.
    • Your information helps us to more effectively respond to your customer service requests and support needs.
    • We may periodically send promotional emails about new products, special offers, or other information which we think you may find interesting using the email address which you have provided.
    • We may use the information to improve our products and services.

    Disclosure of Information

    { domain } does not sell, trade, or otherwise transfer your personally identifiable information to outside parties. This does not include trusted third parties who assist us in operating our website, conducting our business, or servicing you, so long as those parties agree to keep this information confidential. We may also release your information when we believe release is appropriate to comply with the law, enforce our site policies, or protect ours or others' rights, property, or safety.

    Consent

    By using our website, you hereby consent to our Privacy Policy and agree to its terms.

    Updates

    This Privacy Policy may change from time to time, and all updates will be posted on this page.

    If you require any more information or have any questions about our Privacy Policy, please feel free to contact us through email at { contactEmail }.

    } templ TermsOfService(domain, contactEmail string) { @SideNavLayout(nil) { @TermsOfServiceMain(domain, contactEmail) } } templ TermsOfServiceMain(domain, contactEmail string) {

    Terms of Service for { domain }

    These Terms of Service govern your use of { domain }. By accessing or using { domain }, you agree to be bound by these Terms of Service.

    Use of Content

    The content provided on { domain } is for your general information and personal use only. It is subject to change without notice.

    Reproduction of any material from { domain } is prohibited unless it is in accordance with the copyright notice, which forms part of these terms and conditions.

    All trademarks reproduced on { domain }, which are not the property of, or licensed to the operator, are acknowledged on the website.

    User Conduct

    You agree not to use { domain }for any unlawful purpose or in any way that could damage, disable, overburden, or impair the site or interfere with any other party's use and enjoyment of { domain }.

    You must not attempt to gain unauthorized access to { domain }, the server on which it is stored, or any server, computer, or database connected to { domain }.

    Links to Other Websites

    { domain } may contain links to other websites. These links are provided for your convenience to provide further information. They do not signify that we endorse the website(s). We have no responsibility for the content of the linked website(s).

    Limitation of Liability

    In no event shall { domain } or its owners, operators, or affiliates be liable for any direct, indirect, punitive, incidental, special, or consequential damages arising out of, or in any way connected with, the use of { domain } or with the delay or inability to use { domain }.

    Indemnity

    You agree to indemnify and hold { domain } and its owners, operators, and affiliates harmless from any claim or demand, including reasonable attorneys' fees, made by any third party due to or arising out of your breach of these Terms of Service or your violation of any law or the rights of a third party.

    Changes to Terms of Service

    { domain } reserves the right to revise these Terms of Service at any time without notice. By using { domain }, you are agreeing to be bound by the then-current version of these Terms of Service.

    If you have any questions about these Terms of Service, please contact us at { contactEmail }.

    } ================================================ FILE: internal/views/pages/server_error.templ ================================================ package pages import ( "fmt" "github.com/haatos/goshipit/internal" "github.com/haatos/goshipit/internal/views/components" ) templ InternalServerError() { @SideNavLayout(nil) { @SideNavLayout(nil) { @components.Status( components.StatusProps{ Code: 500, Title: "Internal Server Error", Description: fmt.Sprintf("Something went terribly wrong! Please contact us at %s if the problem persists.", internal.Settings.ContactEmail), ReturnButtonLabel: "Go back home", ReturnButtonAttrs: templ.Attributes{ "hx-get": "/", "hx-target": "main", "hx-swap": "innerHTML", "hx-push-url": "true", }, }, ) } } } ================================================ FILE: internal/views/scripts/copy_button.templ ================================================ package scripts templ CodeCopyButtonScript() { } templ HXCodeCopyButtonScript() { } ================================================ FILE: package.json ================================================ { "devDependencies": { "@tailwindcss/cli": "^4.1.18", "@tailwindcss/typography": "^0.5.19", "daisyui": "^5.5.19", "tailwindcss": "^4.1.18" }, "scripts": { "build:css": "npx @tailwindcss/cli -i input.css -o ./public/static/css/tw.css --watch" } } ================================================ FILE: readme.md ================================================ # GoShip.it Golang + Templ + HTMX (+ TailwindCSS + DaisyUI) component library to enhance developing an application using the GOTH stack. The library contains DaisyUI components translated into Templ components that can be easily customized using both TailwindCSS and DaisyUI. Updated to support DaisyUI 5. If you're looking for DaisyUI 4 compatible components, take a look [here](https://old.goship.it) ## Getting started Install node dependencies: `npm i -D` Build TailwindCSS: `make tw` Generate component code/json, generate templates and run the server: `make dev` ## Code/data generation `cmd/generate/main.go` is used to generate JSON, markdown and Go code from source code. JSON is used to store and load component and example component source code to be displayed in HTML. The generator also generates a markdown file that contains up-to-date types for components (from `internal/model/components.go`), and .go file containing a mapping of example names to _templ_ components. ## Contributing Components are placed in individual .templ files in `internal/views/components/`. The name of the file is used as the name of the component (converted from snake_case to Capitalized Component Name). The .templ file starts with a category name as a comment, e.g. `// data_display`. For example `internal/views/components/accordion.templ` ```go // data_display package components type AccordionRowProps struct { Label string Type string Name string } templ AccordionRow(props AccordionRowProps) {
    { label }
    { children... }
    } ``` Each component also has an examples file with a corresponding name in `internal/views/examples/`. The file can contain multiple examples, each starting with a comment `// example` with any lines below this line belonging to the example up to the next `// example` line or EOF. E.g.: `internal/views/examples/textarea.templ` ```go package examples import "github.com/haatos/goshipit/internal/views/components" // example templ BasicTextarea() {
    @components.Textarea( components.TextareaProps{ Label: "Description", Name: "description", }, )
    } // example templ BasicTextareaWithError() {
    @components.Textarea( components.TextareaProps{ Label: "Description", Name: "description", Err: "Description cannot be empty", }, )
    } ``` Some examples have corresponding handler functions to provide dummy data for the component to display its usage. E.g. `internal/handler/components.go` contains the handler for lazy-loading example: ```go // LazyLoadExample func GetLazyLoadExample(c echo.Context) error { time.Sleep(2 * time.Second) return render(c, http.StatusOK, examples.LazyLoadResult()) } // LazyLoadExample ``` The handler must be enclosed with the name of the example component as comment on both sides of the handler's function(s). ## CLI To install the goship.it CLI, run `go install github.com/haatos/goshipit/cmd/gsi`, or clone the repository and build it yourself: `go build -o gsi cmd/gsi/main.go`. ### Usage Add components by running `gsi add button`. Components are added to _internal/views/components_ folder. To see a list of all (and installed components) run `gsi list`. To remove a component, run `gsi remove button`. ================================================ FILE: tailwind.config.js ================================================ /* to disable code block CSS from tailwind/typography, we use another code highlighter */ const disabledCss = { "code::before": false, "code::after": false, "blockquote p:first-of-type::before": false, "blockquote p:last-of-type::after": false, pre: false, code: false, "pre code": false, "code::before": false, "code::after": false, }; module.exports = { content: ["internal/views/**/*.templ"], theme: { extend: { /* disable code block CSS */ typography: { DEFAULT: { css: disabledCss }, sm: { css: disabledCss }, lg: { css: disabledCss }, xl: { css: disabledCss }, "2xl": { css: disabledCss }, }, }, }, };