Compare commits

..

No commits in common. "a8e13e64da7f9dc6176365e6b9295981af5d81ea" and "023521b016d004ca185f0c4e2d80d30e2722fa81" have entirely different histories.

19 changed files with 158 additions and 100 deletions

View File

@ -23,15 +23,17 @@ For repositories, labels can be created by going to `Issues` and clicking on `La
For organizations, you can define organization-wide labels that are shared with all organization repositories, including both already-existing repositories as well as newly created ones. Organization-wide labels can be created in the organization `Settings`. For organizations, you can define organization-wide labels that are shared with all organization repositories, including both already-existing repositories as well as newly created ones. Organization-wide labels can be created in the organization `Settings`.
Labels have a mandatory name, a mandatory color, an optional description, and must either be exclusive or not (see `Scoped Labels` below). Labels have a mandatory name, a mandatory color, an optional description, and must either be exclusive or not (see `Scoped labels` below).
When you create a repository, you can ensure certain labels exist by using the `Issue Labels` option. This option lists a number of available label sets that are [configured globally on your instance](../customizing-gitea/#labels). Its contained labels will all be created as well while creating the repository. When you create a repository, you can ensure certain labels exist by using the `Issue Labels` option. This option lists a number of available label sets that are [configured globally on your instance](../customizing-gitea/#labels). Its contained labels will all be created as well while creating the repository.
## Scoped Labels ## Scoped Labels
Scoped labels are used to ensure at most a single label with the same scope is assigned to an issue or pull request. For example, if labels `kind/bug` and `kind/enhancement` have the Exclusive option set, an issue can only be classified as a bug or an enhancement. A scoped label is a label that contains `/` in its name (not at either end of the name). For example labels `kind/bug` and `kind/enhancement` both have scope `kind`. Such labels will display the scope with slightly darker color.
A scoped label must contain `/` in its name (not at either end of the name). The scope of a label is determined based on the **last** `/`, so for example the scope of label `scope/subscope/item` is `scope/subscope`. The scope of a label is determined based on the **last** `/`, so for example the scope of label `scope/subscope/item` is `scope/subscope`.
Scoped labels can be marked as exclusive. This ensures at most a single label with the same scope is assigned to an issue or pull request. For example, if `kind/bug` and `kind/enhancement` are marked exclusive, an issue can only be classified as a bug or an enhancement.
## Filtering by Label ## Filtering by Label

View File

@ -834,7 +834,7 @@ func RenderLabel(ctx context.Context, label *issues_model.Label) string {
// Make scope and item background colors slightly darker and lighter respectively. // Make scope and item background colors slightly darker and lighter respectively.
// More contrast needed with higher luminance, empirically tweaked. // More contrast needed with higher luminance, empirically tweaked.
luminance := (0.299*r + 0.587*g + 0.114*b) / 255 luminance := (0.299*r + 0.587*g + 0.114*b) / 255
contrast := 0.01 + luminance*0.03 contrast := 0.01 + luminance*0.06
// 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)
@ -859,10 +859,12 @@ func RenderLabel(ctx context.Context, label *issues_model.Label) string {
return fmt.Sprintf("<span class='ui label scope-parent' title='%s'>"+ return fmt.Sprintf("<span class='ui label scope-parent' 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='background: linear-gradient(-80deg, %s 48%%, %s 52%% 0%%);'>&nbsp;</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>", "</span>",
description, description,
textColor, scopeColor, scopeText, textColor, scopeColor, scopeText,
itemColor, scopeColor,
textColor, itemColor, itemText) textColor, itemColor, itemText)
} }

View File

@ -176,7 +176,7 @@ func CreateIssueAttachment(ctx *context.APIContext) {
filename = query filename = query
} }
attachment, err := attachment.UploadAttachment(file, setting.Attachment.AllowedTypes, header.Size, &repo_model.Attachment{ attachment, err := attachment.UploadAttachment(file, setting.Attachment.AllowedTypes, &repo_model.Attachment{
Name: filename, Name: filename,
UploaderID: ctx.Doer.ID, UploaderID: ctx.Doer.ID,
RepoID: ctx.Repo.Repository.ID, RepoID: ctx.Repo.Repository.ID,

View File

@ -180,7 +180,7 @@ func CreateIssueCommentAttachment(ctx *context.APIContext) {
filename = query filename = query
} }
attachment, err := attachment.UploadAttachment(file, setting.Attachment.AllowedTypes, header.Size, &repo_model.Attachment{ attachment, err := attachment.UploadAttachment(file, setting.Attachment.AllowedTypes, &repo_model.Attachment{
Name: filename, Name: filename,
UploaderID: ctx.Doer.ID, UploaderID: ctx.Doer.ID,
RepoID: ctx.Repo.Repository.ID, RepoID: ctx.Repo.Repository.ID,

View File

@ -194,7 +194,7 @@ func CreateReleaseAttachment(ctx *context.APIContext) {
} }
// Create a new attachment and save the file // Create a new attachment and save the file
attach, err := attachment.UploadAttachment(file, setting.Repository.Release.AllowedTypes, header.Size, &repo_model.Attachment{ attach, err := attachment.UploadAttachment(file, setting.Repository.Release.AllowedTypes, &repo_model.Attachment{
Name: filename, Name: filename,
UploaderID: ctx.Doer.ID, UploaderID: ctx.Doer.ID,
RepoID: release.RepoID, RepoID: release.RepoID,

View File

@ -44,7 +44,7 @@ func uploadAttachment(ctx *context.Context, repoID int64, allowedTypes string) {
} }
defer file.Close() defer file.Close()
attach, err := attachment.UploadAttachment(file, allowedTypes, header.Size, &repo_model.Attachment{ attach, err := attachment.UploadAttachment(file, allowedTypes, &repo_model.Attachment{
Name: header.Filename, Name: header.Filename,
UploaderID: ctx.Doer.ID, UploaderID: ctx.Doer.ID,
RepoID: repoID, RepoID: repoID,

View File

@ -113,7 +113,7 @@ func Projects(ctx *context.Context) {
pager.AddParam(ctx, "state", "State") pager.AddParam(ctx, "state", "State")
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects) ctx.Data["CanWriteProjects"] = true
ctx.Data["IsShowClosed"] = isShowClosed ctx.Data["IsShowClosed"] = isShowClosed
ctx.Data["IsProjectsPage"] = true ctx.Data["IsProjectsPage"] = true
ctx.Data["SortType"] = sortType ctx.Data["SortType"] = sortType

View File

@ -19,14 +19,14 @@ import (
) )
// NewAttachment creates a new attachment object, but do not verify. // NewAttachment creates a new attachment object, but do not verify.
func NewAttachment(attach *repo_model.Attachment, file io.Reader, size int64) (*repo_model.Attachment, error) { func NewAttachment(attach *repo_model.Attachment, file io.Reader) (*repo_model.Attachment, error) {
if attach.RepoID == 0 { if attach.RepoID == 0 {
return nil, fmt.Errorf("attachment %s should belong to a repository", attach.Name) return nil, fmt.Errorf("attachment %s should belong to a repository", attach.Name)
} }
err := db.WithTx(db.DefaultContext, func(ctx context.Context) error { err := db.WithTx(db.DefaultContext, func(ctx context.Context) error {
attach.UUID = uuid.New().String() attach.UUID = uuid.New().String()
size, err := storage.Attachments.Save(attach.RelativePath(), file, size) size, err := storage.Attachments.Save(attach.RelativePath(), file, -1)
if err != nil { if err != nil {
return fmt.Errorf("Create: %w", err) return fmt.Errorf("Create: %w", err)
} }
@ -39,7 +39,7 @@ func NewAttachment(attach *repo_model.Attachment, file io.Reader, size int64) (*
} }
// UploadAttachment upload new attachment into storage and update database // UploadAttachment upload new attachment into storage and update database
func UploadAttachment(file io.Reader, allowedTypes string, fileSize int64, opts *repo_model.Attachment) (*repo_model.Attachment, error) { func UploadAttachment(file io.Reader, allowedTypes string, opts *repo_model.Attachment) (*repo_model.Attachment, error) {
buf := make([]byte, 1024) buf := make([]byte, 1024)
n, _ := util.ReadAtMost(file, buf) n, _ := util.ReadAtMost(file, buf)
buf = buf[:n] buf = buf[:n]
@ -48,5 +48,5 @@ func UploadAttachment(file io.Reader, allowedTypes string, fileSize int64, opts
return nil, err return nil, err
} }
return NewAttachment(opts, io.MultiReader(bytes.NewReader(buf), file), fileSize) return NewAttachment(opts, io.MultiReader(bytes.NewReader(buf), file))
} }

View File

@ -36,7 +36,7 @@ func TestUploadAttachment(t *testing.T) {
RepoID: 1, RepoID: 1,
UploaderID: user.ID, UploaderID: user.ID,
Name: filepath.Base(fPath), Name: filepath.Base(fPath),
}, f, -1) }, f)
assert.NoError(t, err) assert.NoError(t, err)
attachment, err := repo_model.GetAttachmentByUUID(db.DefaultContext, attach.UUID) attachment, err := repo_model.GetAttachmentByUUID(db.DefaultContext, attach.UUID)

View File

@ -87,7 +87,7 @@ func (h *ReplyHandler) Handle(ctx context.Context, content *MailContent, doer *u
attachmentIDs := make([]string, 0, len(content.Attachments)) attachmentIDs := make([]string, 0, len(content.Attachments))
if setting.Attachment.Enabled { if setting.Attachment.Enabled {
for _, attachment := range content.Attachments { for _, attachment := range content.Attachments {
a, err := attachment_service.UploadAttachment(bytes.NewReader(attachment.Content), setting.Attachment.AllowedTypes, int64(len(attachment.Content)), &repo_model.Attachment{ a, err := attachment_service.UploadAttachment(bytes.NewReader(attachment.Content), setting.Attachment.AllowedTypes, &repo_model.Attachment{
Name: attachment.Name, Name: attachment.Name,
UploaderID: doer.ID, UploaderID: doer.ID,
RepoID: issue.Repo.ID, RepoID: issue.Repo.ID,

View File

@ -106,13 +106,11 @@ func TestRelease_Create(t *testing.T) {
IsTag: false, IsTag: false,
}, nil, "")) }, nil, ""))
testPlayload := "testtest"
attach, err := attachment.NewAttachment(&repo_model.Attachment{ attach, err := attachment.NewAttachment(&repo_model.Attachment{
RepoID: repo.ID, RepoID: repo.ID,
UploaderID: user.ID, UploaderID: user.ID,
Name: "test.txt", Name: "test.txt",
}, strings.NewReader(testPlayload), int64(len([]byte(testPlayload)))) }, strings.NewReader("testtest"))
assert.NoError(t, err) assert.NoError(t, err)
release := repo_model.Release{ release := repo_model.Release{
@ -241,12 +239,11 @@ func TestRelease_Update(t *testing.T) {
assert.Equal(t, tagName, release.TagName) assert.Equal(t, tagName, release.TagName)
// Add new attachments // Add new attachments
samplePayload := "testtest"
attach, err := attachment.NewAttachment(&repo_model.Attachment{ attach, err := attachment.NewAttachment(&repo_model.Attachment{
RepoID: repo.ID, RepoID: repo.ID,
UploaderID: user.ID, UploaderID: user.ID,
Name: "test.txt", Name: "test.txt",
}, strings.NewReader(samplePayload), int64(len([]byte(samplePayload)))) }, strings.NewReader("testtest"))
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, UpdateRelease(user, gitRepo, release, []string{attach.UUID}, nil, nil)) assert.NoError(t, UpdateRelease(user, gitRepo, release, []string{attach.UUID}, nil, nil))

View File

@ -51,7 +51,7 @@
{{JsPrettyNumber .NumClosedIssues}}&nbsp;{{$.locale.Tr "repo.issues.closed_title"}} {{JsPrettyNumber .NumClosedIssues}}&nbsp;{{$.locale.Tr "repo.issues.closed_title"}}
</span> </span>
</div> </div>
{{if and $.CanWriteProjects (not $.Repository.IsArchived)}} {{if and (or $.CanWriteIssues $.CanWritePulls) (not $.Repository.IsArchived)}}
<div class="ui right operate"> <div class="ui right operate">
<a href="{{$.Link}}/{{.ID}}/edit" data-id={{.ID}} data-title={{.Title}}>{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}}</a> <a href="{{$.Link}}/{{.ID}}/edit" data-id={{.ID}} data-title={{.Title}}>{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}}</a>
{{if .IsClosed}} {{if .IsClosed}}
@ -59,7 +59,7 @@
{{else}} {{else}}
<a class="link-action" href data-url="{{$.Link}}/{{.ID}}/close">{{svg "octicon-skip"}} {{$.locale.Tr "repo.projects.close"}}</a> <a class="link-action" href data-url="{{$.Link}}/{{.ID}}/close">{{svg "octicon-skip"}} {{$.locale.Tr "repo.projects.close"}}</a>
{{end}} {{end}}
<a class="delete-button" href="#" data-url="{{$.Link}}/{{.ID}}/delete" data-id="{{.ID}}">{{svg "octicon-trash"}} {{$.locale.Tr "repo.issues.label_delete"}}</a> <a class="delete-button" href="#" data-url="{{$.RepoLink}}/projects/{{.ID}}/delete" data-id="{{.ID}}">{{svg "octicon-trash"}} {{$.locale.Tr "repo.issues.label_delete"}}</a>
</div> </div>
{{end}} {{end}}
{{if .Description}} {{if .Description}}
@ -75,7 +75,7 @@
</div> </div>
</div> </div>
{{if $.CanWriteProjects}} {{if or .CanWriteIssues .CanWritePulls}}
<div class="ui small basic delete modal"> <div class="ui small basic delete modal">
<div class="ui icon header"> <div class="ui icon header">
{{svg "octicon-trash"}} {{svg "octicon-trash"}}

View File

@ -43,7 +43,7 @@
<h2 class="project-title">{{$.Project.Title}}</h2> <h2 class="project-title">{{$.Project.Title}}</h2>
<div class="content project-description">{{$.Project.RenderedContent|Str2html}}</div> <div class="content project-description">{{$.Project.RenderedContent|Str2html}}</div>
</div> </div>
{{if $.CanWriteProjects}} {{if or $.CanWriteIssues $.CanWritePulls}}
<div class="column right aligned"> <div class="column right aligned">
<div class="ui compact right small menu"> <div class="ui compact right small menu">
<a class="item" href="{{$.Link}}/edit?redirect=project" data-id={{$.Project.ID}} data-title={{$.Project.Title}}> <a class="item" href="{{$.Link}}/edit?redirect=project" data-id={{$.Project.ID}} data-title={{$.Project.Title}}>
@ -128,7 +128,7 @@
<div class="text right actions"> <div class="text right actions">
<div class="ui cancel button">{{$.locale.Tr "settings.cancel"}}</div> <div class="ui cancel button">{{$.locale.Tr "settings.cancel"}}</div>
<button data-url="{{$.Link}}/{{.ID}}" class="ui primary button edit-column-button">{{$.locale.Tr "repo.projects.column.edit"}}</button> <button data-url="{{$.Link}}/{{.ID}}" class="ui primary button">{{$.locale.Tr "repo.projects.column.edit"}}</button>
</div> </div>
</form> </form>
</div> </div>
@ -256,7 +256,7 @@
</div> </div>
{{if .CanWriteProjects}} {{if or .CanWriteIssues .CanWritePulls}}
<div class="ui small basic delete modal"> <div class="ui small basic delete modal">
<div class="ui icon header"> <div class="ui icon header">
{{svg "octicon-trash"}} {{svg "octicon-trash"}}

View File

@ -1,43 +0,0 @@
<div class="ui {{if or (not .HasIssuesOrPullsWritePermission) .Repository.IsArchived}}disabled{{end}} floating jump select-label dropdown">
<a class="text gt-df gt-ac muted">
<strong>{{.locale.Tr "repo.issues.new.labels"}}</strong>
{{if and .HasIssuesOrPullsWritePermission (not .Repository.IsArchived)}}
{{svg "octicon-gear" 16 "gt-ml-2"}}
{{end}}
</a>
<div class="filter menu" {{if .Issue}}data-action="update" data-issue-id="{{$.Issue.ID}}" data-update-url="{{$.RepoLink}}/issues/labels"{{else}}data-id="#label_ids"{{end}}>
<div class="header" style="text-transform: none;font-size:16px;">{{.locale.Tr "repo.issues.new.add_labels_title"}}</div>
{{if or .Labels .OrgLabels}}
<div class="ui icon search input">
<i class="icon gt-df gt-ac gt-jc">{{svg "octicon-search" 16}}</i>
<input type="text" placeholder="{{.locale.Tr "repo.issues.filter_labels"}}">
</div>
{{end}}
<a class="no-select item" href="#">{{.locale.Tr "repo.issues.new.clear_labels"}}</a>
{{if or .Labels .OrgLabels}}
{{$previousExclusiveScope := "_no_scope"}}
{{range .Labels}}
{{$exclusiveScope := .ExclusiveScope}}
{{if and (ne $previousExclusiveScope "_no_scope") (ne $previousExclusiveScope $exclusiveScope)}}
<div class="ui divider"></div>
{{end}}
{{$previousExclusiveScope = $exclusiveScope}}
<a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}" data-scope="{{$exclusiveScope}}"><span class="octicon-check {{if not .IsChecked}}invisible{{end}}">{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}</span>&nbsp;&nbsp;{{RenderLabel $.Context .}}
{{if .Description}}<br><small class="desc">{{.Description | RenderEmoji $.Context}}</small>{{end}}</a>
{{end}}
<div class="ui divider"></div>
{{$previousExclusiveScope := "_no_scope"}}
{{range .OrgLabels}}
{{$exclusiveScope := .ExclusiveScope}}
{{if and (ne $previousExclusiveScope "_no_scope") (ne $previousExclusiveScope $exclusiveScope)}}
<div class="ui divider"></div>
{{end}}
{{$previousExclusiveScope = $exclusiveScope}}
<a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}" data-scope="{{$exclusiveScope}}"><span class="octicon-check {{if not .IsChecked}}invisible{{end}}">{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}</span>&nbsp;&nbsp;{{RenderLabel $.Context .}}
{{if .Description}}<br><small class="desc">{{.Description | RenderEmoji $.Context}}</small>{{end}}</a>
{{end}}
{{else}}
<div class="header" style="text-transform: none;font-size:14px;">{{.locale.Tr "repo.issues.new.no_items"}}</div>
{{end}}
</div>
</div>

View File

@ -36,7 +36,50 @@
{{template "repo/issue/branch_selector_field" .}} {{template "repo/issue/branch_selector_field" .}}
<input id="label_ids" name="label_ids" type="hidden" value="{{.label_ids}}"> <input id="label_ids" name="label_ids" type="hidden" value="{{.label_ids}}">
{{template "repo/issue/labels/labels_selector_field" .}} <div class="ui {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}} floating jump select-label dropdown">
<span class="text">
<strong>{{.locale.Tr "repo.issues.new.labels"}}</strong>
{{if .HasIssuesOrPullsWritePermission}}
{{svg "octicon-gear"}}
{{end}}
</span>
<div class="filter menu" data-id="#label_ids">
<div class="header" style="text-transform: none;font-size:16px;">{{.locale.Tr "repo.issues.new.add_labels_title"}}</div>
{{if or .Labels .OrgLabels}}
<div class="ui icon search input">
<i class="icon gt-df gt-ac gt-jc">{{svg "octicon-search" 16}}</i>
<input type="text" placeholder="{{.locale.Tr "repo.issues.filter_labels"}}">
</div>
{{end}}
<div class="no-select item">{{.locale.Tr "repo.issues.new.clear_labels"}}</div>
{{if or .Labels .OrgLabels}}
{{$previousExclusiveScope := "_no_scope"}}
{{range .Labels}}
{{$exclusiveScope := .ExclusiveScope}}
{{if and (ne $previousExclusiveScope "_no_scope") (ne $previousExclusiveScope $exclusiveScope)}}
<div class="ui divider"></div>
{{end}}
{{$previousExclusiveScope = $exclusiveScope}}
<a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}" data-scope="{{$exclusiveScope}}"><span class="octicon-check {{if not .IsChecked}}invisible{{end}}">{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}</span>&nbsp;&nbsp;{{RenderLabel $.Context .}}
{{if .Description}}<br><small class="desc">{{.Description | RenderEmoji $.Context}}</small>{{end}}</a>
{{end}}
<div class="ui divider"></div>
{{$previousExclusiveScope := "_no_scope"}}
{{range .OrgLabels}}
{{$exclusiveScope := .ExclusiveScope}}
{{if and (ne $previousExclusiveScope "_no_scope") (ne $previousExclusiveScope $exclusiveScope)}}
<div class="ui divider"></div>
{{end}}
{{$previousExclusiveScope = $exclusiveScope}}
<a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}" data-scope="{{$exclusiveScope}}"><span class="octicon-check {{if not .IsChecked}}invisible{{end}}">{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}</span>&nbsp;&nbsp;{{RenderLabel $.Context .}}
{{if .Description}}<br><small class="desc">{{.Description | RenderEmoji $.Context}}</small>{{end}}</a>
{{end}}
{{else}}
<div class="header" style="text-transform: none;font-size:14px;">{{.locale.Tr "repo.issues.new.no_items"}}</div>
{{end}}
</div>
</div>
{{template "repo/issue/labels/labels_sidebar" dict "root" $}} {{template "repo/issue/labels/labels_sidebar" dict "root" $}}
<div class="ui divider"></div> <div class="ui divider"></div>

View File

@ -106,7 +106,49 @@
<div class="ui divider"></div> <div class="ui divider"></div>
{{end}} {{end}}
{{template "repo/issue/labels/labels_selector_field" .}} <div class="ui {{if or (not .HasIssuesOrPullsWritePermission) .Repository.IsArchived}}disabled{{end}} floating jump select-label dropdown">
<a class="text gt-df gt-ac muted">
<strong>{{.locale.Tr "repo.issues.new.labels"}}</strong>
{{if and .HasIssuesOrPullsWritePermission (not .Repository.IsArchived)}}
{{svg "octicon-gear" 16 "gt-ml-2"}}
{{end}}
</a>
<div class="filter menu" data-action="update" data-issue-id="{{$.Issue.ID}}" data-update-url="{{$.RepoLink}}/issues/labels">
<div class="header" style="text-transform: none;font-size:16px;">{{.locale.Tr "repo.issues.new.add_labels_title"}}</div>
{{if or .Labels .OrgLabels}}
<div class="ui icon search input">
<i class="icon gt-df gt-ac gt-jc">{{svg "octicon-search" 16}}</i>
<input type="text" placeholder="{{.locale.Tr "repo.issues.filter_labels"}}">
</div>
{{end}}
<a class="no-select item" href="#">{{.locale.Tr "repo.issues.new.clear_labels"}}</a>
{{if or .Labels .OrgLabels}}
{{$previousExclusiveScope := "_no_scope"}}
{{range .Labels}}
{{$exclusiveScope := .ExclusiveScope}}
{{if and (ne $previousExclusiveScope "_no_scope") (ne $previousExclusiveScope $exclusiveScope)}}
<div class="ui divider"></div>
{{end}}
{{$previousExclusiveScope = $exclusiveScope}}
<a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}" data-scope="{{$exclusiveScope}}"><span class="octicon-check {{if not .IsChecked}}invisible{{end}}">{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}</span>&nbsp;&nbsp;{{RenderLabel $.Context .}}
{{if .Description}}<br><small class="desc">{{.Description | RenderEmoji $.Context}}</small>{{end}}</a>
{{end}}
<div class="ui divider"></div>
{{$previousExclusiveScope := "_no_scope"}}
{{range .OrgLabels}}
{{$exclusiveScope := .ExclusiveScope}}
{{if and (ne $previousExclusiveScope "_no_scope") (ne $previousExclusiveScope $exclusiveScope)}}
<div class="ui divider"></div>
{{end}}
{{$previousExclusiveScope = $exclusiveScope}}
<a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}" data-scope="{{$exclusiveScope}}"><span class="octicon-check {{if not .IsChecked}}invisible{{end}}">{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}</span>&nbsp;&nbsp;{{RenderLabel $.Context .}}
{{if .Description}}<br><small class="desc">{{.Description | RenderEmoji $.Context}}</small>{{end}}</a>
{{end}}
{{else}}
<div class="header" style="text-transform: none;font-size:14px;">{{.locale.Tr "repo.issues.new.no_items"}}</div>
{{end}}
</div>
</div>
{{template "repo/issue/labels/labels_sidebar" dict "root" $}} {{template "repo/issue/labels/labels_sidebar" dict "root" $}}
<div class="ui divider"></div> <div class="ui divider"></div>

View File

@ -53,15 +53,15 @@
{{JsPrettyNumber .NumClosedIssues}}&nbsp;{{$.locale.Tr "repo.issues.closed_title"}} {{JsPrettyNumber .NumClosedIssues}}&nbsp;{{$.locale.Tr "repo.issues.closed_title"}}
</span> </span>
</div> </div>
{{if and $.CanWriteProjects (not $.Repository.IsArchived)}} {{if and (or $.CanWriteIssues $.CanWritePulls) (not $.Repository.IsArchived)}}
<div class="ui right operate"> <div class="ui right operate">
<a href="{{.Link}}/edit" data-id={{.ID}} data-title={{.Title}}>{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}}</a> <a href="{{$.Link}}/{{.ID}}/edit" data-id={{.ID}} data-title={{.Title}}>{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}}</a>
{{if .IsClosed}} {{if .IsClosed}}
<a class="link-action" href data-url="{{.Link}}/open">{{svg "octicon-check"}} {{$.locale.Tr "repo.projects.open"}}</a> <a class="link-action" href data-url="{{$.Link}}/{{.ID}}/open">{{svg "octicon-check"}} {{$.locale.Tr "repo.projects.open"}}</a>
{{else}} {{else}}
<a class="link-action" href data-url="{{.Link}}/close">{{svg "octicon-skip"}} {{$.locale.Tr "repo.projects.close"}}</a> <a class="link-action" href data-url="{{$.Link}}/{{.ID}}/close">{{svg "octicon-skip"}} {{$.locale.Tr "repo.projects.close"}}</a>
{{end}} {{end}}
<a class="delete-button" href="#" data-url="{{.Link}}/delete" data-id="{{.ID}}">{{svg "octicon-trash"}} {{$.locale.Tr "repo.issues.label_delete"}}</a> <a class="delete-button" href="#" data-url="{{$.RepoLink}}/projects/{{.ID}}/delete" data-id="{{.ID}}">{{svg "octicon-trash"}} {{$.locale.Tr "repo.issues.label_delete"}}</a>
</div> </div>
{{end}} {{end}}
{{if .Description}} {{if .Description}}
@ -77,7 +77,7 @@
</div> </div>
</div> </div>
{{if .CanWriteProjects}} {{if or .CanWriteIssues .CanWritePulls}}
<div class="ui small basic delete modal"> <div class="ui small basic delete modal">
<div class="ui icon header"> <div class="ui icon header">
{{svg "octicon-trash"}} {{svg "octicon-trash"}}

View File

@ -95,15 +95,19 @@ export function initRepoProject() {
$('.edit-project-board').each(function () { $('.edit-project-board').each(function () {
const projectHeader = $(this).closest('.board-column-header'); const projectHeader = $(this).closest('.board-column-header');
const projectTitleLabel = projectHeader.find('.board-label'); const projectTitleLabel = projectHeader.find('.board-label');
const projectTitleInput = $(this).find('.project-board-title'); const projectTitleInput = $(this).find(
const projectColorInput = $(this).find('#new_board_color'); '.content > .form > .field > .project-board-title',
);
const projectColorInput = $(this).find('.content > .form > .field #new_board_color');
const boardColumn = $(this).closest('.board-column'); const boardColumn = $(this).closest('.board-column');
if (boardColumn.css('backgroundColor')) { if (boardColumn.css('backgroundColor')) {
setLabelColor(projectHeader, rgbToHex(boardColumn.css('backgroundColor'))); setLabelColor(projectHeader, rgbToHex(boardColumn.css('backgroundColor')));
} }
$(this).find('.edit-column-button').on('click', function (e) { $(this)
.find('.content > .form > .actions > .red')
.on('click', function (e) {
e.preventDefault(); e.preventDefault();
$.ajax({ $.ajax({

View File

@ -2838,11 +2838,11 @@
.labels-list .label { .labels-list .label {
margin: 2px 0; margin: 2px 0;
display: inline-flex !important; display: inline-block !important;
line-height: 1.3em; // there is a `font-size: 1.25em` for inside emoji, so here the line-height needs to be larger slightly line-height: 1.3em; // 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 // Scoped labels with different colors on left and right, and slanted divider in the middle
.scope-parent { .scope-parent {
background: none !important; background: none !important;
padding: 0 !important; padding: 0 !important;
@ -2851,12 +2851,23 @@
.ui.label.scope-left { .ui.label.scope-left {
border-bottom-right-radius: 0; border-bottom-right-radius: 0;
border-top-right-radius: 0; border-top-right-radius: 0;
padding-right: 0;
margin-right: 0;
}
.ui.label.scope-middle {
width: 12px;
border-radius: 0;
padding-left: 0;
padding-right: 0;
margin-left: 0;
margin-right: 0; margin-right: 0;
} }
.ui.label.scope-right { .ui.label.scope-right {
border-bottom-left-radius: 0; border-bottom-left-radius: 0;
border-top-left-radius: 0; border-top-left-radius: 0;
padding-left: 0;
margin-left: 0; margin-left: 0;
} }