diff --git a/modules/svg/svg.go b/modules/svg/svg.go index 234b1f8c13..917df9439a 100644 --- a/modules/svg/svg.go +++ b/modules/svg/svg.go @@ -7,6 +7,7 @@ import ( "fmt" "html/template" "path" + "sort" "strings" "sync" @@ -78,6 +79,16 @@ func MockIcon(icon string) func() { } } +// DiscoveredIconNames returns the sorted list of all discovered SVG icon names +func DiscoveredIconNames() []string { + names := make([]string, 0, len(svgIcons)) + for name := range svgIcons { + names = append(names, name) + } + sort.Strings(names) + return names +} + // RenderHTML renders icons - arguments icon name (string), size (int), class (string) func RenderHTML(icon string, others ...any) template.HTML { result, _ := renderHTML(icon, others...) diff --git a/routers/web/devtest/devtest.go b/routers/web/devtest/devtest.go index af54843016..116d54415d 100644 --- a/routers/web/devtest/devtest.go +++ b/routers/web/devtest/devtest.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/modules/badge" "code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/svg" "code.gitea.io/gitea/modules/indexer/code" "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/util" @@ -182,6 +183,21 @@ func prepareMockDataRelativeTime(ctx *context.Context) { ctx.Data["TimeFuture1y"] = now.Add(366 * 24 * time.Hour) } +func prepareMockDataIconGallery(ctx *context.Context) { + allNames := svg.DiscoveredIconNames() + grouped := map[string][]string{} + for _, name := range allNames { + prefix := "other" + if before, _, ok := strings.Cut(name, "-"); ok { + prefix = before + } + grouped[prefix] = append(grouped[prefix], name) + } + ctx.Data["IconGroups"] = grouped + ctx.Data["IconGroupOrder"] = []string{"octicon", "gitea", "fontawesome", "material", "other"} + ctx.Data["IconCount"] = len(allNames) +} + func prepareMockData(ctx *context.Context) { switch ctx.Req.URL.Path { case "/devtest/gitea-ui": @@ -192,6 +208,8 @@ func prepareMockData(ctx *context.Context) { prepareMockDataBadgeActionsSvg(ctx) case "/devtest/relative-time": prepareMockDataRelativeTime(ctx) + case "/devtest/icon-gallery": + prepareMockDataIconGallery(ctx) case "/devtest/toast-and-message": prepareMockDataToastAndMessage(ctx) case "/devtest/unicode-escape": diff --git a/templates/devtest/icon-gallery.tmpl b/templates/devtest/icon-gallery.tmpl new file mode 100644 index 0000000000..77607f9370 --- /dev/null +++ b/templates/devtest/icon-gallery.tmpl @@ -0,0 +1,25 @@ +{{template "devtest/devtest-header"}} +
+

Icon Gallery

+

All {{.IconCount}} SVG icons available in templates.

+

+ + +

+ + {{range $prefix := .IconGroupOrder}} + {{$icons := index $.IconGroups $prefix}} + {{if $icons}} +

{{$prefix}} {{len $icons}}

+
+ {{range $name := $icons}} + + {{end}} +
+ {{end}} + {{end}} +
+{{template "devtest/devtest-footer"}} diff --git a/web_src/css/devtest.css b/web_src/css/devtest.css index c344d99058..18424681f9 100644 --- a/web_src/css/devtest.css +++ b/web_src/css/devtest.css @@ -18,3 +18,20 @@ h1, h2 { .fetch-action-demo-forms .form-fetch-action { border: 1px red dashed; /* show the border for demo purpose */ } + +.icon-gallery-search { + width: 300px; +} + +.icon-gallery-card { + width: 120px; +} + +.icon-gallery-preview { + height: 32px; +} + +.icon-gallery-name { + max-width: 108px; + font-size: 10px; +} diff --git a/web_src/js/modules/devtest.ts b/web_src/js/modules/devtest.ts index 634d16293e..f9f1ff0e07 100644 --- a/web_src/js/modules/devtest.ts +++ b/web_src/js/modules/devtest.ts @@ -9,6 +9,25 @@ import {showGlobalErrorMessage} from './errors.ts'; type LevelMap = Record Toast | null>; function initDevtestPage() { + const iconSearch = document.querySelector('#icon-search'); + const iconSizeToggle = document.querySelector('#icon-size-toggle'); + if (iconSearch && iconSizeToggle) { + iconSearch.addEventListener('input', () => { + const query = iconSearch.value.toLowerCase(); + for (const card of document.querySelectorAll('.icon-card')) { + card.style.display = card.getAttribute('data-name')!.includes(query) ? '' : 'none'; + } + }); + + iconSizeToggle.addEventListener('change', () => { + const size = iconSizeToggle.checked ? '24' : '16'; + for (const icon of document.querySelectorAll('.icon-card svg')) { + icon.setAttribute('width', size); + icon.setAttribute('height', size); + } + }); + } + const toastButtons = document.querySelectorAll('.toast-test-button'); if (toastButtons.length) { const levelMap: LevelMap = {info: showInfoToast, warning: showWarningToast, error: showErrorToast};