From 91726756fda78e98b179b24642de27c7f988d471 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 24 Jan 2023 05:42:16 +0100 Subject: [PATCH] Label display improvements for scopes and consistency * Show scoped label as "Scope > Name", with scope prefix slightly dimmed * Show label with background color also in assignment and filter menus for consistency, in particular for scoped label rendering. * Tweak light/dark color text detection slight, to make it white on some background colors that I found otherwise hard to read. * Don't use exactly black/white text colors to look a bit nicer * Show label in labels editing list the same size as elsewhere, and without tag icon. To give a better preview of how it will actually look. * Increase height of menus to show more labels (and projects, milestones, ..). Showing only 3-4 labels as before leads to a lot of scrolling. * Refactor code so label rendering is done in a single helper function. --- models/issues/label.go | 51 +++++-------------- models/issues/label_test.go | 7 ++- modules/templates/helper.go | 31 ++++++++++- templates/projects/view.tmpl | 2 +- templates/repo/issue/labels/label.tmpl | 6 +-- templates/repo/issue/labels/label_list.tmpl | 4 +- templates/repo/issue/list.tmpl | 4 +- templates/repo/issue/milestone_issues.tmpl | 4 +- templates/repo/issue/new_form.tmpl | 4 +- .../repo/issue/view_content/sidebar.tmpl | 4 +- templates/repo/projects/view.tmpl | 2 +- templates/shared/issuelist.tmpl | 2 +- web_src/less/_base.less | 6 +-- web_src/less/_repository.less | 14 ++--- 14 files changed, 64 insertions(+), 77 deletions(-) diff --git a/models/issues/label.go b/models/issues/label.go index be279227f7a2..097e183780e1 100644 --- a/models/issues/label.go +++ b/models/issues/label.go @@ -7,8 +7,6 @@ package issues import ( "context" "fmt" - "html/template" - "math" "regexp" "strconv" "strings" @@ -163,49 +161,24 @@ func (label *Label) BelongsToRepo() bool { return label.RepoID > 0 } -// SrgbToLinear converts a component of an sRGB color to its linear intensity -// See: https://en.wikipedia.org/wiki/SRGB#The_reverse_transformation_(sRGB_to_CIE_XYZ) -func SrgbToLinear(color uint8) float64 { - flt := float64(color) / 255 - if flt <= 0.04045 { - return flt / 12.92 - } - return math.Pow((flt+0.055)/1.055, 2.4) -} - -// Luminance returns the luminance of an sRGB color -func Luminance(color uint32) float64 { - r := SrgbToLinear(uint8(0xFF & (color >> 16))) - g := SrgbToLinear(uint8(0xFF & (color >> 8))) - b := SrgbToLinear(uint8(0xFF & color)) - - // luminance ratios for sRGB - return 0.2126*r + 0.7152*g + 0.0722*b -} - -// LuminanceThreshold is the luminance at which white and black appear to have the same contrast -// i.e. x such that 1.05 / (x + 0.05) = (x + 0.05) / 0.05 -// i.e. math.Sqrt(1.05*0.05) - 0.05 -const LuminanceThreshold float64 = 0.179 - -// ForegroundColor calculates the text color for labels based -// on their background color. -func (label *Label) ForegroundColor() template.CSS { +// Determine if label text should be light or dark to be readable on +// background color. +func (label *Label) UseLightTextColor() bool { if strings.HasPrefix(label.Color, "#") { if color, err := strconv.ParseUint(label.Color[1:], 16, 64); err == nil { - // NOTE: see web_src/js/components/ContextPopup.vue for similar implementation - luminance := Luminance(uint32(color)) + // sRGB color space luminance + r := float64(uint8(0xFF & (uint32(color) >> 16))) + g := float64(uint8(0xFF & (uint32(color) >> 8))) + b := float64(uint8(0xFF & uint32(color))) - // prefer white or black based upon contrast - if luminance < LuminanceThreshold { - return template.CSS("#fff") - } - return template.CSS("#000") + luminance := (0.299*r + 0.587*g + 0.114*b) / 255 + + // NOTE: see web_src/js/components/ContextPopup.vue for similar implementation + return luminance < 0.5 } } - // default to black - return template.CSS("#000") + return false } // Return scope substring of label name, or empty string if none exists. diff --git a/models/issues/label_test.go b/models/issues/label_test.go index e7264f8907db..d25bc8ffad9f 100644 --- a/models/issues/label_test.go +++ b/models/issues/label_test.go @@ -4,7 +4,6 @@ package issues_test import ( - "html/template" "testing" "code.gitea.io/gitea/models/db" @@ -25,13 +24,13 @@ func TestLabel_CalOpenIssues(t *testing.T) { assert.EqualValues(t, 2, label.NumOpenIssues) } -func TestLabel_ForegroundColor(t *testing.T) { +func TestLabel_TextColor(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}) - assert.Equal(t, template.CSS("#000"), label.ForegroundColor()) + assert.False(t, label.UseLightTextColor()) label = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2}) - assert.Equal(t, template.CSS("#fff"), label.ForegroundColor()) + assert.True(t, label.UseLightTextColor()) } func TestLabel_Scope(t *testing.T) { diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 7b997b49d9e7..eadffb38615a 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -379,6 +379,9 @@ func NewFuncMap() []template.FuncMap { // the table is NOT sorted with this header return "" }, + "RenderLabel": func(label *issues_model.Label) template.HTML { + return template.HTML(RenderLabel(label)) + }, "RenderLabels": func(labels []*issues_model.Label, repoLink string) template.HTML { htmlCode := `` for _, label := range labels { @@ -386,8 +389,8 @@ func NewFuncMap() []template.FuncMap { if label == nil { continue } - htmlCode += fmt.Sprintf("%s ", - repoLink, label.ID, label.ForegroundColor(), label.Color, html.EscapeString(label.Description), RenderEmoji(label.Name)) + htmlCode += fmt.Sprintf("%s ", + repoLink, label.ID, RenderLabel(label)) } htmlCode += "" return template.HTML(htmlCode) @@ -795,6 +798,30 @@ func RenderIssueTitle(ctx context.Context, text, urlPrefix string, metas map[str return template.HTML(renderedText) } +// RenderLabel renders a label +func RenderLabel(label *issues_model.Label) string { + labelScope := label.Scope() + + textColor := "#000" + scopeTextColor := "#444" + if label.UseLightTextColor() { + textColor = "#ddd" + scopeTextColor = "#bbb" + } + + var text string + if labelScope == "" { + text = string(RenderEmoji(label.Name)) + } else { + scopeText := strings.ReplaceAll(labelScope, "::", " \u203A ") + itemText := label.Name[len(labelScope):] + text = fmt.Sprintf("%s %s", scopeTextColor, RenderEmoji(scopeText), RenderEmoji(itemText)) + } + + return fmt.Sprintf("
%s
", + textColor, label.Color, emoji.ReplaceAliases(label.Description), text) +} + // RenderEmoji renders html text with emoji post processors func RenderEmoji(text string) template.HTML { renderedText, err := markup.RenderEmoji(template.HTMLEscapeString(text)) diff --git a/templates/projects/view.tmpl b/templates/projects/view.tmpl index ac72acb82b6a..ae81e6618e70 100644 --- a/templates/projects/view.tmpl +++ b/templates/projects/view.tmpl @@ -234,7 +234,7 @@ {{if or .Labels .Assignees}}
{{range .Labels}} - {{.Name | RenderEmoji}} + {{RenderLabel .}} {{end}}
{{range .Assignees}} diff --git a/templates/repo/issue/labels/label.tmpl b/templates/repo/issue/labels/label.tmpl index 0afe5cb6e7b1..87d8f0c41c2c 100644 --- a/templates/repo/issue/labels/label.tmpl +++ b/templates/repo/issue/labels/label.tmpl @@ -1,9 +1,7 @@ - {{.label.Name | RenderEmoji}} + {{RenderLabel .label}} diff --git a/templates/repo/issue/labels/label_list.tmpl b/templates/repo/issue/labels/label_list.tmpl index 464c9fe208d5..d45285212d11 100644 --- a/templates/repo/issue/labels/label_list.tmpl +++ b/templates/repo/issue/labels/label_list.tmpl @@ -31,7 +31,7 @@
  • -
    {{svg "octicon-tag"}} {{.Name | RenderEmoji}}
    + {{RenderLabel .}}
    @@ -74,7 +74,7 @@
  • -
    {{svg "octicon-tag"}} {{.Name | RenderEmoji}}
    + {{RenderLabel .}}
    diff --git a/templates/repo/issue/list.tmpl b/templates/repo/issue/list.tmpl index b0538c4fd4fe..cc2601190daa 100644 --- a/templates/repo/issue/list.tmpl +++ b/templates/repo/issue/list.tmpl @@ -58,7 +58,7 @@
    {{end}} {{$previousScope = $scope}} - {{if .IsExcluded}}{{svg "octicon-circle-slash"}}{{else if .IsSelected}}{{if $scope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}{{end}} {{.Name | RenderEmoji}} + {{if .IsExcluded}}{{svg "octicon-circle-slash"}}{{else if .IsSelected}}{{if $scope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}{{end}} {{RenderLabel .}} {{end}}
    @@ -192,7 +192,7 @@ {{end}} {{$previousScope = $scope}}
    - {{if contain $.SelLabelIDs .ID}}{{if $scope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}{{end}} {{.Name | RenderEmoji}} + {{if contain $.SelLabelIDs .ID}}{{if $scope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}{{end}} {{RenderLabel .}}
    {{end}}
    diff --git a/templates/repo/issue/milestone_issues.tmpl b/templates/repo/issue/milestone_issues.tmpl index f3e28208adb9..a5c2ad1e34cd 100644 --- a/templates/repo/issue/milestone_issues.tmpl +++ b/templates/repo/issue/milestone_issues.tmpl @@ -58,7 +58,7 @@ {{.locale.Tr "repo.issues.filter_label_exclude" | Safe}} {{.locale.Tr "repo.issues.filter_label_no_select"}} {{range .Labels}} - {{if .IsExcluded}}{{svg "octicon-circle-slash"}}{{else if contain $.SelLabelIDs .ID}}{{svg "octicon-check"}}{{end}} {{.Name | RenderEmoji}} + {{if .IsExcluded}}{{svg "octicon-circle-slash"}}{{else if contain $.SelLabelIDs .ID}}{{svg "octicon-check"}}{{end}} {{RenderLabel .}} {{end}}
  • @@ -161,7 +161,7 @@ diff --git a/templates/repo/issue/new_form.tmpl b/templates/repo/issue/new_form.tmpl index 6195b991bddd..6e5bf6e883aa 100644 --- a/templates/repo/issue/new_form.tmpl +++ b/templates/repo/issue/new_form.tmpl @@ -60,7 +60,7 @@
    {{end}} {{$previousScope = $scope}} - {{if $scope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}} {{.Name | RenderEmoji}} + {{if $scope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}  {{RenderLabel .}} {{if .Description}}
    {{.Description | RenderEmoji}}{{end}}
    {{end}} @@ -72,7 +72,7 @@
    {{end}} {{$previousScope = $scope}} - {{if $scope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}} {{.Name | RenderEmoji}} + {{if $scope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}  {{RenderLabel .}} {{if .Description}}
    {{.Description | RenderEmoji}}{{end}}
    {{end}} {{else}} diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl index f1de047a9208..0182edf187f0 100644 --- a/templates/repo/issue/view_content/sidebar.tmpl +++ b/templates/repo/issue/view_content/sidebar.tmpl @@ -130,7 +130,7 @@
    {{end}} {{$previousScope = $scope}} - {{if $scope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}} {{.Name | RenderEmoji}} + {{if $scope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}  {{RenderLabel .}} {{if .Description}}
    {{.Description | RenderEmoji}}{{end}}
    {{end}}
    @@ -141,7 +141,7 @@
    {{end}} {{$previousScope = $scope}} - {{if $scope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}} {{.Name | RenderEmoji}} + {{if $scope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}  {{RenderLabel .}} {{if .Description}}
    {{.Description | RenderEmoji}}{{end}}
    {{end}} {{else}} diff --git a/templates/repo/projects/view.tmpl b/templates/repo/projects/view.tmpl index 262efd2e0e80..60c0187bb8a3 100644 --- a/templates/repo/projects/view.tmpl +++ b/templates/repo/projects/view.tmpl @@ -238,7 +238,7 @@ {{if or .Labels .Assignees}}
    {{range .Labels}} - {{.Name | RenderEmoji}} + {{RenderLabel .}} {{end}}
    {{range .Assignees}} diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl index f3aa2610bb1a..4b60296d990a 100644 --- a/templates/shared/issuelist.tmpl +++ b/templates/shared/issuelist.tmpl @@ -43,7 +43,7 @@ {{end}} {{range .Labels}} - {{.Name | RenderEmoji}} + {{RenderLabel .}} {{end}}
    diff --git a/web_src/less/_base.less b/web_src/less/_base.less index 697bc0ee7459..82e286a24802 100644 --- a/web_src/less/_base.less +++ b/web_src/less/_base.less @@ -2529,7 +2529,7 @@ table th[data-sortt-desc] { border-top: none; a { - font-size: 15px; + font-size: 12px; padding-top: 5px; padding-right: 10px; color: var(--color-text-light); @@ -2542,10 +2542,6 @@ table th[data-sortt-desc] { margin-right: 30px; } } - - .ui.label { - font-size: 1em; - } } .item:last-child { diff --git a/web_src/less/_repository.less b/web_src/less/_repository.less index 7aa42b1f0718..c5dd080a5748 100644 --- a/web_src/less/_repository.less +++ b/web_src/less/_repository.less @@ -92,7 +92,7 @@ .metas { .menu { overflow-x: auto; - max-height: 300px; + max-height: 500px; } .ui.list { @@ -155,12 +155,6 @@ } .filter.menu { - .label.color { - border-radius: 3px; - margin-left: 15px; - padding: 0 8px; - } - &.labels { .label-filter .menu .info { display: inline-block; @@ -181,7 +175,7 @@ } .menu { - max-height: 300px; + max-height: 500px; overflow-x: auto; right: 0 !important; left: auto !important; @@ -190,7 +184,7 @@ .select-label { .desc { - padding-left: 16px; + padding-left: 23px; } } @@ -607,7 +601,7 @@ min-width: 220px; .filter.menu { - max-height: 300px; + max-height: 500px; overflow-x: auto; } }