mirror of
https://github.com/go-gitea/gitea.git
synced 2025-06-28 12:09:53 +00:00
Improve labels-list
rendering (#34846)
Make labels list use consistent gap --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
parent
aa9d86745a
commit
1e50cec0b3
19 changed files with 185 additions and 115 deletions
|
@ -42,6 +42,8 @@ func HTMLFormat(s template.HTML, rawArgs ...any) template.HTML {
|
||||||
// for most basic types (including template.HTML which is safe), just do nothing and use it
|
// for most basic types (including template.HTML which is safe), just do nothing and use it
|
||||||
case string:
|
case string:
|
||||||
args[i] = template.HTMLEscapeString(v)
|
args[i] = template.HTMLEscapeString(v)
|
||||||
|
case template.URL:
|
||||||
|
args[i] = template.HTMLEscapeString(string(v))
|
||||||
case fmt.Stringer:
|
case fmt.Stringer:
|
||||||
args[i] = template.HTMLEscapeString(v.String())
|
args[i] = template.HTMLEscapeString(v.String())
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -10,6 +10,15 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type testStringer struct{}
|
||||||
|
|
||||||
|
func (t testStringer) String() string {
|
||||||
|
return "&StringMethod"
|
||||||
|
}
|
||||||
|
|
||||||
func TestHTMLFormat(t *testing.T) {
|
func TestHTMLFormat(t *testing.T) {
|
||||||
assert.Equal(t, template.HTML("<a>< < 1</a>"), HTMLFormat("<a>%s %s %d</a>", "<", template.HTML("<"), 1))
|
assert.Equal(t, template.HTML("<a>< < 1</a>"), HTMLFormat("<a>%s %s %d</a>", "<", template.HTML("<"), 1))
|
||||||
|
assert.Equal(t, template.HTML("%!s(<nil>)"), HTMLFormat("%s", nil))
|
||||||
|
assert.Equal(t, template.HTML("<>"), HTMLFormat("%s", template.URL("<>")))
|
||||||
|
assert.Equal(t, template.HTML("&StringMethod &StringMethod"), HTMLFormat("%s %s", testStringer{}, &testStringer{}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,8 +122,23 @@ func (ut *RenderUtils) RenderIssueSimpleTitle(text string) template.HTML {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// RenderLabel renders a label
|
func (ut *RenderUtils) RenderLabelWithLink(label *issues_model.Label, link any) template.HTML {
|
||||||
|
var attrHref template.HTML
|
||||||
|
switch link.(type) {
|
||||||
|
case template.URL, string:
|
||||||
|
attrHref = htmlutil.HTMLFormat(`href="%s"`, link)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unexpected type %T for link", link))
|
||||||
|
}
|
||||||
|
return ut.renderLabelWithTag(label, "a", attrHref)
|
||||||
|
}
|
||||||
|
|
||||||
func (ut *RenderUtils) RenderLabel(label *issues_model.Label) template.HTML {
|
func (ut *RenderUtils) RenderLabel(label *issues_model.Label) template.HTML {
|
||||||
|
return ut.renderLabelWithTag(label, "span", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderLabel renders a label
|
||||||
|
func (ut *RenderUtils) renderLabelWithTag(label *issues_model.Label, tagName, tagAttrs template.HTML) template.HTML {
|
||||||
locale := ut.ctx.Value(translation.ContextKey).(translation.Locale)
|
locale := ut.ctx.Value(translation.ContextKey).(translation.Locale)
|
||||||
var extraCSSClasses string
|
var extraCSSClasses string
|
||||||
textColor := util.ContrastColor(label.Color)
|
textColor := util.ContrastColor(label.Color)
|
||||||
|
@ -137,8 +152,8 @@ func (ut *RenderUtils) RenderLabel(label *issues_model.Label) template.HTML {
|
||||||
|
|
||||||
if labelScope == "" {
|
if labelScope == "" {
|
||||||
// Regular label
|
// Regular label
|
||||||
return htmlutil.HTMLFormat(`<div class="ui label %s" style="color: %s !important; background-color: %s !important;" data-tooltip-content title="%s">%s</div>`,
|
return htmlutil.HTMLFormat(`<%s %s class="ui label %s" style="color: %s !important; background-color: %s !important;" data-tooltip-content title="%s"><span class="gt-ellipsis">%s</span></%s>`,
|
||||||
extraCSSClasses, textColor, label.Color, descriptionText, ut.RenderEmoji(label.Name))
|
tagName, tagAttrs, extraCSSClasses, textColor, label.Color, descriptionText, ut.RenderEmoji(label.Name), tagName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scoped label
|
// Scoped label
|
||||||
|
@ -152,7 +167,7 @@ func (ut *RenderUtils) RenderLabel(label *issues_model.Label) template.HTML {
|
||||||
// Ensure we add the same amount of contrast also near 0 and 1.
|
// Ensure we add the same amount of contrast also near 0 and 1.
|
||||||
darken := contrast + math.Max(luminance+contrast-1.0, 0.0)
|
darken := contrast + math.Max(luminance+contrast-1.0, 0.0)
|
||||||
lighten := contrast + math.Max(contrast-luminance, 0.0)
|
lighten := contrast + math.Max(contrast-luminance, 0.0)
|
||||||
// Compute factor to keep RGB values proportional.
|
// Compute the factor to keep RGB values proportional.
|
||||||
darkenFactor := math.Max(luminance-darken, 0.0) / math.Max(luminance, 1.0/255.0)
|
darkenFactor := math.Max(luminance-darken, 0.0) / math.Max(luminance, 1.0/255.0)
|
||||||
lightenFactor := math.Min(luminance+lighten, 1.0) / math.Max(luminance, 1.0/255.0)
|
lightenFactor := math.Min(luminance+lighten, 1.0) / math.Max(luminance, 1.0/255.0)
|
||||||
|
|
||||||
|
@ -173,26 +188,29 @@ func (ut *RenderUtils) RenderLabel(label *issues_model.Label) template.HTML {
|
||||||
|
|
||||||
if label.ExclusiveOrder > 0 {
|
if label.ExclusiveOrder > 0 {
|
||||||
// <scope> | <label> | <order>
|
// <scope> | <label> | <order>
|
||||||
return htmlutil.HTMLFormat(`<span class="ui label %s scope-parent" data-tooltip-content title="%s">`+
|
return htmlutil.HTMLFormat(`<%s %s class="ui label %s scope-parent" data-tooltip-content title="%s">`+
|
||||||
`<div class="ui label scope-left" style="color: %s !important; background-color: %s !important">%s</div>`+
|
`<div class="ui label scope-left" style="color: %s !important; background-color: %s !important">%s</div>`+
|
||||||
`<div class="ui label scope-middle" style="color: %s !important; background-color: %s !important">%s</div>`+
|
`<div class="ui label scope-middle" style="color: %s !important; background-color: %s !important">%s</div>`+
|
||||||
`<div class="ui label scope-right">%d</div>`+
|
`<div class="ui label scope-right">%d</div>`+
|
||||||
`</span>`,
|
`</%s>`,
|
||||||
|
tagName, tagAttrs,
|
||||||
extraCSSClasses, descriptionText,
|
extraCSSClasses, descriptionText,
|
||||||
textColor, scopeColor, scopeHTML,
|
textColor, scopeColor, scopeHTML,
|
||||||
textColor, itemColor, itemHTML,
|
textColor, itemColor, itemHTML,
|
||||||
label.ExclusiveOrder)
|
label.ExclusiveOrder,
|
||||||
|
tagName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// <scope> | <label>
|
// <scope> | <label>
|
||||||
return htmlutil.HTMLFormat(`<span class="ui label %s scope-parent" data-tooltip-content title="%s">`+
|
return htmlutil.HTMLFormat(`<%s %s class="ui label %s scope-parent" data-tooltip-content title="%s">`+
|
||||||
`<div class="ui label scope-left" style="color: %s !important; background-color: %s !important">%s</div>`+
|
`<div class="ui label scope-left" style="color: %s !important; background-color: %s !important">%s</div>`+
|
||||||
`<div class="ui label scope-right" style="color: %s !important; background-color: %s !important">%s</div>`+
|
`<div class="ui label scope-right" style="color: %s !important; background-color: %s !important">%s</div>`+
|
||||||
`</span>`,
|
`</%s>`,
|
||||||
|
tagName, tagAttrs,
|
||||||
extraCSSClasses, descriptionText,
|
extraCSSClasses, descriptionText,
|
||||||
textColor, scopeColor, scopeHTML,
|
textColor, scopeColor, scopeHTML,
|
||||||
textColor, itemColor, itemHTML,
|
textColor, itemColor, itemHTML,
|
||||||
)
|
tagName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RenderEmoji renders html text with emoji post processors
|
// RenderEmoji renders html text with emoji post processors
|
||||||
|
@ -235,7 +253,8 @@ func (ut *RenderUtils) RenderLabels(labels []*issues_model.Label, repoLink strin
|
||||||
if label == nil {
|
if label == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
htmlCode += fmt.Sprintf(`<a href="%s?labels=%d">%s</a>`, baseLink, label.ID, ut.RenderLabel(label))
|
link := fmt.Sprintf("%s?labels=%d", baseLink, label.ID)
|
||||||
|
htmlCode += string(ut.RenderLabelWithLink(label, template.URL(link)))
|
||||||
}
|
}
|
||||||
htmlCode += "</span>"
|
htmlCode += "</span>"
|
||||||
return template.HTML(htmlCode)
|
return template.HTML(htmlCode)
|
||||||
|
|
|
@ -205,6 +205,17 @@ func TestRenderLabels(t *testing.T) {
|
||||||
issue = &issues.Issue{IsPull: true}
|
issue = &issues.Issue{IsPull: true}
|
||||||
expected = `/owner/repo/pulls?labels=123`
|
expected = `/owner/repo/pulls?labels=123`
|
||||||
assert.Contains(t, ut.RenderLabels([]*issues.Label{label}, "/owner/repo", issue), expected)
|
assert.Contains(t, ut.RenderLabels([]*issues.Label{label}, "/owner/repo", issue), expected)
|
||||||
|
|
||||||
|
expectedLabel := `<a href="<>" class="ui label " style="color: #fff !important; background-color: label-color !important;" data-tooltip-content title=""><span class="gt-ellipsis">label-name</span></a>`
|
||||||
|
assert.Equal(t, expectedLabel, string(ut.RenderLabelWithLink(label, "<>")))
|
||||||
|
assert.Equal(t, expectedLabel, string(ut.RenderLabelWithLink(label, template.URL("<>"))))
|
||||||
|
|
||||||
|
label = &issues.Label{ID: 123, Name: "</>", Exclusive: true}
|
||||||
|
expectedLabel = `<a href="" class="ui label scope-parent" data-tooltip-content title=""><div class="ui label scope-left" style="color: #fff !important; background-color: #000000 !important"><</div><div class="ui label scope-right" style="color: #fff !important; background-color: #000000 !important">></div></a>`
|
||||||
|
assert.Equal(t, expectedLabel, string(ut.RenderLabelWithLink(label, "")))
|
||||||
|
label = &issues.Label{ID: 123, Name: "</>", Exclusive: true, ExclusiveOrder: 1}
|
||||||
|
expectedLabel = `<a href="" class="ui label scope-parent" data-tooltip-content title=""><div class="ui label scope-left" style="color: #fff !important; background-color: #000000 !important"><</div><div class="ui label scope-middle" style="color: #fff !important; background-color: #000000 !important">></div><div class="ui label scope-right">1</div></a>`
|
||||||
|
assert.Equal(t, expectedLabel, string(ut.RenderLabelWithLink(label, "")))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUserMention(t *testing.T) {
|
func TestUserMention(t *testing.T) {
|
||||||
|
|
|
@ -10,8 +10,8 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-item">
|
<div class="flex-item">
|
||||||
<div class="flex-item-icon">
|
<div class="flex-item-leading">
|
||||||
{{svg "octicon-tag" 16}}
|
<div class="tw-mt-1">{{svg "octicon-tag" 16}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-item-main">
|
<div class="flex-item-main">
|
||||||
<div class="flex-item-header">
|
<div class="flex-item-header">
|
||||||
|
|
|
@ -63,16 +63,21 @@
|
||||||
|
|
||||||
{{if or .Labels .Assignees}}
|
{{if or .Labels .Assignees}}
|
||||||
<div class="issue-card-bottom">
|
<div class="issue-card-bottom">
|
||||||
<div class="labels-list">
|
{{/* the labels container must always be present, to help the assignees list right-aligned */}}
|
||||||
{{range .Labels}}
|
<div class="issue-card-bottom-part labels-list">
|
||||||
<a target="_blank" href="{{$.Issue.Repo.Link}}/issues?labels={{.ID}}">{{ctx.RenderUtils.RenderLabel .}}</a>
|
{{range $label := .Labels}}
|
||||||
|
{{$link := print $.Issue.Repo.Link "/issues"}}
|
||||||
|
{{$link = QueryBuild $link "labels" $label.ID}}
|
||||||
|
{{ctx.RenderUtils.RenderLabelWithLink $label $link}}
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div class="issue-card-assignees">
|
{{if .Assignees}}
|
||||||
|
<div class="issue-card-bottom-part tw-justify-end">
|
||||||
{{range .Assignees}}
|
{{range .Assignees}}
|
||||||
<a target="_blank" href="{{.HomeLink}}" data-tooltip-content="{{ctx.Locale.Tr "repo.projects.column.assigned_to"}} {{.Name}}">{{ctx.AvatarUtils.Avatar . 28}}</a>
|
<a target="_blank" href="{{.HomeLink}}" data-tooltip-content="{{ctx.Locale.Tr "repo.projects.column.assigned_to"}} {{.Name}}">{{ctx.AvatarUtils.Avatar . 24}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<ul class="issue-label-list">
|
<ul class="issue-label-list muted-links">
|
||||||
{{$canEditLabel := and (not $.PageIsOrgSettingsLabels) (not $.Repository.IsArchived) (or $.CanWriteIssues $.CanWritePulls)}}
|
{{$canEditLabel := and (not $.PageIsOrgSettingsLabels) (not $.Repository.IsArchived) (or $.CanWriteIssues $.CanWritePulls)}}
|
||||||
{{$canEditLabel = or $canEditLabel $.PageIsOrgSettingsLabels}}
|
{{$canEditLabel = or $canEditLabel $.PageIsOrgSettingsLabels}}
|
||||||
{{range .Labels}}
|
{{range .Labels}}
|
||||||
|
@ -42,9 +42,8 @@
|
||||||
<a class="open-issues" href="{{$.RepoLink}}/issues?labels={{.ID}}">{{svg "octicon-issue-opened"}} {{ctx.Locale.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a>
|
<a class="open-issues" href="{{$.RepoLink}}/issues?labels={{.ID}}">{{svg "octicon-issue-opened"}} {{ctx.Locale.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div class="label-operation tw-flex">
|
<div class="label-operation">
|
||||||
{{template "repo/issue/labels/label_archived" .}}
|
{{template "repo/issue/labels/label_archived" .}}
|
||||||
<div class="tw-flex tw-ml-auto">
|
|
||||||
{{if $canEditLabel}}
|
{{if $canEditLabel}}
|
||||||
<a class="edit-label-button" href="#"
|
<a class="edit-label-button" href="#"
|
||||||
data-label-id="{{.ID}}" data-label-name="{{.Name}}" data-label-color="{{.Color}}"
|
data-label-id="{{.ID}}" data-label-name="{{.Name}}" data-label-color="{{.Color}}"
|
||||||
|
@ -57,7 +56,6 @@
|
||||||
data-modal-confirm-content="{{ctx.Locale.Tr "repo.issues.label_deletion_desc"}}"
|
data-modal-confirm-content="{{ctx.Locale.Tr "repo.issues.label_deletion_desc"}}"
|
||||||
>{{svg "octicon-trash"}} {{ctx.Locale.Tr "repo.issues.label_delete"}}</a>
|
>{{svg "octicon-trash"}} {{ctx.Locale.Tr "repo.issues.label_delete"}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui list muted-links flex-items-block tw-flex tw-flex-col tw-gap-2">
|
<div class="ui relaxed list muted-links flex-items-block">
|
||||||
<span class="item empty-list {{if $issueAssignees}}tw-hidden{{end}}">{{ctx.Locale.Tr "repo.issues.new.no_assignees"}}</span>
|
<span class="item empty-list {{if $issueAssignees}}tw-hidden{{end}}">{{ctx.Locale.Tr "repo.issues.new.no_assignees"}}</span>
|
||||||
{{range $issueAssignees}}
|
{{range $issueAssignees}}
|
||||||
<a class="item" href="{{$pageMeta.RepoLink}}/{{if $pageMeta.IsPullRequest}}pulls{{else}}issues{{end}}?assignee={{.ID}}">
|
<a class="item" href="{{$pageMeta.RepoLink}}/{{if $pageMeta.IsPullRequest}}pulls{{else}}issues{{end}}?assignee={{.ID}}">
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ui list labels-list tw-my-2 tw-flex tw-gap-2">
|
<div class="ui list labels-list">
|
||||||
<span class="item empty-list {{if $data.SelectedLabelIDs}}tw-hidden{{end}}">{{ctx.Locale.Tr "repo.issues.new.no_label"}}</span>
|
<span class="item empty-list {{if $data.SelectedLabelIDs}}tw-hidden{{end}}">{{ctx.Locale.Tr "repo.issues.new.no_label"}}</span>
|
||||||
{{range $data.AllLabels}}
|
{{range $data.AllLabels}}
|
||||||
{{if .IsChecked}}
|
{{if .IsChecked}}
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ui relaxed list flex-items-block tw-my-4">
|
<div class="ui relaxed list flex-items-block">
|
||||||
<span class="item empty-list {{if or $data.OriginalReviews $data.CurrentPullReviewers}}tw-hidden{{end}}">
|
<span class="item empty-list {{if or $data.OriginalReviews $data.CurrentPullReviewers}}tw-hidden{{end}}">
|
||||||
{{ctx.Locale.Tr "repo.issues.new.no_reviewers"}}
|
{{ctx.Locale.Tr "repo.issues.new.no_reviewers"}}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -169,7 +169,7 @@
|
||||||
</div>
|
</div>
|
||||||
{{else if eq .Type 7}}
|
{{else if eq .Type 7}}
|
||||||
{{if or .AddedLabels .RemovedLabels}}
|
{{if or .AddedLabels .RemovedLabels}}
|
||||||
<div class="timeline-item event" id="{{.HashTag}}">
|
<div class="timeline-item event with-labels-list-inline" id="{{.HashTag}}">
|
||||||
<span class="badge">{{svg "octicon-tag"}}</span>
|
<span class="badge">{{svg "octicon-tag"}}</span>
|
||||||
{{template "shared/user/avatarlink" dict "user" .Poster}}
|
{{template "shared/user/avatarlink" dict "user" .Poster}}
|
||||||
<span class="text grey muted-links">
|
<span class="text grey muted-links">
|
||||||
|
|
|
@ -3,11 +3,14 @@
|
||||||
{{range .Issues}}
|
{{range .Issues}}
|
||||||
<div class="flex-item">
|
<div class="flex-item">
|
||||||
|
|
||||||
<div class="flex-item-icon">
|
<div class="flex-item-leading">
|
||||||
{{if $.CanWriteIssuesOrPulls}}
|
{{/* using some tw helpers is the only way to align the checkbox */}}
|
||||||
<input type="checkbox" autocomplete="off" class="issue-checkbox tw-mr-4" data-issue-id={{.ID}} aria-label="{{ctx.Locale.Tr "repo.issues.action_check"}} "{{.Title}}"">
|
<div class="flex-text-inline tw-mt-[2px]">
|
||||||
{{end}}
|
{{if $.CanWriteIssuesOrPulls}}
|
||||||
{{template "shared/issueicon" .}}
|
<input type="checkbox" autocomplete="off" class="issue-checkbox tw-mr-[14px]" data-issue-id={{.ID}} aria-label="{{ctx.Locale.Tr "repo.issues.action_check"}} "{{.Title}}"">
|
||||||
|
{{end}}
|
||||||
|
{{template "shared/issueicon" .}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-item-main">
|
<div class="flex-item-main">
|
||||||
|
@ -19,7 +22,7 @@
|
||||||
{{template "repo/commit_statuses" dict "Status" (index $.CommitLastStatus .PullRequest.ID) "Statuses" (index $.CommitStatuses .PullRequest.ID)}}
|
{{template "repo/commit_statuses" dict "Status" (index $.CommitLastStatus .PullRequest.ID) "Statuses" (index $.CommitStatuses .PullRequest.ID)}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
<span class="labels-list tw-ml-1">
|
<span class="labels-list">
|
||||||
{{range .Labels}}
|
{{range .Labels}}
|
||||||
<a href="?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}{{if ne $.listType "milestone"}}&milestone={{$.MilestoneID}}{{end}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.RenderUtils.RenderLabel .}}</a>
|
<a href="?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}{{if ne $.listType "milestone"}}&milestone={{$.MilestoneID}}{{end}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.RenderUtils.RenderLabel .}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -32,7 +35,7 @@
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-item-body">
|
<div class="flex-item-body tw-mt-1">
|
||||||
<a class="index" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}">
|
<a class="index" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}">
|
||||||
{{if eq $.listType "dashboard"}}
|
{{if eq $.listType "dashboard"}}
|
||||||
{{.Repo.FullName}}#{{.Index}}
|
{{.Repo.FullName}}#{{.Index}}
|
||||||
|
|
|
@ -34,6 +34,11 @@
|
||||||
/* z-index */
|
/* z-index */
|
||||||
--z-index-modal: 1001; /* modal dialog, hard-coded from Fomantic modal.css */
|
--z-index-modal: 1001; /* modal dialog, hard-coded from Fomantic modal.css */
|
||||||
--z-index-toast: 1002; /* should be larger than modal */
|
--z-index-toast: 1002; /* should be larger than modal */
|
||||||
|
|
||||||
|
--font-size-label: 12px; /* font size of individual labels */
|
||||||
|
|
||||||
|
--gap-inline: 0.25rem; /* gap for inline texts and elements, for example: the spaces for sentence with labels, button text, etc */
|
||||||
|
--gap-block: 0.25rem; /* gap for element blocks, for example: spaces between buttons, menu image & title, header icon & title etc */
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) and (max-width: 1200px) {
|
@media (min-width: 768px) and (max-width: 1200px) {
|
||||||
|
@ -1093,7 +1098,7 @@ table th[data-sortt-desc] .svg {
|
||||||
.flex-text-inline {
|
.flex-text-inline {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: .25rem;
|
gap: var(--gap-inline);
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
min-width: 0; /* make ellipsis work */
|
min-width: 0; /* make ellipsis work */
|
||||||
}
|
}
|
||||||
|
@ -1121,7 +1126,7 @@ table th[data-sortt-desc] .svg {
|
||||||
.flex-text-block {
|
.flex-text-block {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: .5rem;
|
gap: var(--gap-block);
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1136,7 +1141,7 @@ the "!important" is necessary to override Fomantic UI menu item styles, meanwhil
|
||||||
.ui.dropdown .menu.flex-items-menu > .item:not(.hidden, .filtered, .tw-hidden) {
|
.ui.dropdown .menu.flex-items-menu > .item:not(.hidden, .filtered, .tw-hidden) {
|
||||||
display: flex !important;
|
display: flex !important;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: .5rem;
|
gap: var(--gap-block);
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
.ui.dropdown .menu.flex-items-menu > .item img,
|
.ui.dropdown .menu.flex-items-menu > .item img,
|
||||||
|
|
|
@ -4,25 +4,19 @@
|
||||||
.ui.label {
|
.ui.label {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: .25rem;
|
gap: var(--gap-inline);
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
vertical-align: middle;
|
max-width: 100%;
|
||||||
line-height: 1;
|
|
||||||
background: var(--color-label-bg);
|
background: var(--color-label-bg);
|
||||||
color: var(--color-label-text);
|
color: var(--color-label-text);
|
||||||
padding: 0.3em 0.5em;
|
padding: 2px 6px;
|
||||||
font-size: 0.85714286rem;
|
font-size: var(--font-size-label);
|
||||||
font-weight: var(--font-weight-medium);
|
font-weight: var(--font-weight-medium);
|
||||||
border: 0 solid transparent;
|
border: 0 solid transparent;
|
||||||
border-radius: 0.28571429rem;
|
border-radius: var(--border-radius);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
.ui.label:first-child {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
.ui.label:last-child {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a.ui.label {
|
a.ui.label {
|
||||||
|
@ -292,3 +286,58 @@ a.ui.ui.ui.basic.grey.label:hover {
|
||||||
.ui.large.label {
|
.ui.large.label {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* To let labels break up and wrap across multiple lines (issue title, comment event), use "display: contents here" to apply parent layout.
|
||||||
|
If the labels-list itself needs some layouts, use extra classes or "tw" helpers. */
|
||||||
|
.labels-list {
|
||||||
|
display: contents;
|
||||||
|
font-size: var(--font-size-label); /* it must match the label font size, otherwise the height mismatches */
|
||||||
|
}
|
||||||
|
|
||||||
|
.labels-list a {
|
||||||
|
max-width: 100%; /* for ellipsis */
|
||||||
|
}
|
||||||
|
|
||||||
|
.labels-list .ui.label {
|
||||||
|
min-height: 20px;
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.with-labels-list-inline .labels-list .ui.label + .ui.label {
|
||||||
|
margin-left: var(--gap-inline);
|
||||||
|
}
|
||||||
|
|
||||||
|
.with-labels-list-inline .labels-list .ui.label {
|
||||||
|
line-height: var(--line-height-default);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scoped labels with different colors on left and right */
|
||||||
|
.ui.label.scope-parent {
|
||||||
|
background: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
gap: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui.label.scope-parent > .ui.label {
|
||||||
|
margin: 0 !important; /* scoped label's margin is handled by the parent */
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui.label.scope-left {
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui.label.scope-middle {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui.label.scope-right {
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui.label.archived-label {
|
||||||
|
filter: grayscale(0.5);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
margin: 1em 0;
|
margin: 1em 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-size: 1em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui.list:first-child {
|
.ui.list:first-child {
|
||||||
|
|
|
@ -73,10 +73,21 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.issue-content-right .ui.list {
|
||||||
|
margin: 0.5em 0;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.issue-sidebar-combo > .ui.dropdown .item:not(.checked) .item-check-mark {
|
.issue-sidebar-combo > .ui.dropdown .item:not(.checked) .item-check-mark {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.issue-content-right .ui.list.labels-list {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--gap-inline);
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 767.98px) {
|
@media (max-width: 767.98px) {
|
||||||
.issue-content-left,
|
.issue-content-left,
|
||||||
.issue-content-right {
|
.issue-content-right {
|
||||||
|
@ -1544,49 +1555,6 @@ td .commit-summary {
|
||||||
border-bottom-right-radius: 4px;
|
border-bottom-right-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.labels-list {
|
|
||||||
display: inline-flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 2.5px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.labels-list .label, .scope-parent > .label {
|
|
||||||
padding: 0 6px;
|
|
||||||
min-height: 20px;
|
|
||||||
line-height: 1.3; /* there is a `font-size: 1.25em` for inside emoji, so here the line-height needs to be larger slightly */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Scoped labels with different colors on left and right */
|
|
||||||
.ui.label.scope-parent {
|
|
||||||
background: none !important;
|
|
||||||
padding: 0 !important;
|
|
||||||
gap: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.archived-label {
|
|
||||||
filter: grayscale(0.5);
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ui.label.scope-left {
|
|
||||||
border-bottom-right-radius: 0;
|
|
||||||
border-top-right-radius: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ui.label.scope-middle {
|
|
||||||
border-radius: 0;
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ui.label.scope-right {
|
|
||||||
border-bottom-left-radius: 0;
|
|
||||||
border-top-left-radius: 0;
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.repo-button-row {
|
.repo-button-row {
|
||||||
margin: 8px 0;
|
margin: 8px 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -29,13 +29,16 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 0.25em;
|
gap: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.issue-card-assignees {
|
.issue-card-bottom-part {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.25em;
|
gap: 0.25em;
|
||||||
justify-content: end;
|
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
overflow: hidden;
|
||||||
|
max-width: fit-content;
|
||||||
|
max-height: fit-content;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,41 +4,46 @@
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.issue-label-list .item {
|
.issue-label-list > .item {
|
||||||
border-bottom: 1px solid var(--color-secondary);
|
border-bottom: 1px solid var(--color-secondary);
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 1em 0;
|
padding: 1em 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.issue-label-list .item:first-child {
|
.issue-label-list > .item:first-child {
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.issue-label-list .item:last-child {
|
.issue-label-list > .item:last-child {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.issue-label-list .item .label-title {
|
.issue-label-list > .item .label-title {
|
||||||
width: 33%;
|
width: 33%;
|
||||||
|
padding-right: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.issue-label-list .item .label-issues {
|
.issue-label-list > .item .label-issues {
|
||||||
width: 33%;
|
width: 33%;
|
||||||
|
padding-right: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.issue-label-list .item .label-operation {
|
.issue-label-list > .item .label-operation {
|
||||||
width: 33%;
|
width: 33%;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5em;
|
||||||
|
justify-content: end;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.issue-label-list .item a {
|
.issue-label-list > .item .label-operation a {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
padding-right: 10px;
|
|
||||||
color: var(--color-text-light);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.issue-label-list .item.org-label {
|
.issue-label-list > .item.org-label {
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,14 +33,6 @@
|
||||||
color: var(--color-primary) !important;
|
color: var(--color-primary) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flex-item .flex-item-icon {
|
|
||||||
align-self: baseline; /* mainly used by the issue list, to align the leading icon with the title */
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-item .flex-item-icon + .flex-item-main {
|
|
||||||
align-self: baseline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-item .flex-item-trailing {
|
.flex-item .flex-item-trailing {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
|
@ -54,7 +46,9 @@
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: .25rem;
|
/* labels are under effect of this gap here because they are display:contents. Ideally we should make wrapping
|
||||||
|
of labels work without display: contents and set this to a static value again. */
|
||||||
|
gap: var(--gap-inline);
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue