From dbd9d8dd54fd3b0ff7b02ae7b7da8369654e3725 Mon Sep 17 00:00:00 2001 From: jaqra <48099350+jaqra@users.noreply.github.com> Date: Wed, 23 Oct 2019 19:29:14 +0300 Subject: [PATCH] Add 'Alt + click' feature to exclude labels (#8199) Add 'Alt + click' and 'Alt +enter' feature to exclude particular labels on searching for issues. --- models/issue.go | 8 ++++++-- models/issue_label.go | 6 +++++- options/locale/locale_en-US.ini | 1 + public/css/index.css | 2 ++ public/js/index.js | 35 +++++++++++++++++++++++++++++++-- public/less/_repository.less | 17 ++++++++++++++++ templates/repo/issue/list.tmpl | 5 +++-- 7 files changed, 67 insertions(+), 7 deletions(-) diff --git a/models/issue.go b/models/issue.go index 688a412d8c..f006917891 100644 --- a/models/issue.go +++ b/models/issue.go @@ -1248,8 +1248,12 @@ func (opts *IssuesOptions) setupSession(sess *xorm.Session) { if opts.LabelIDs != nil { for i, labelID := range opts.LabelIDs { - sess.Join("INNER", fmt.Sprintf("issue_label il%d", i), - fmt.Sprintf("issue.id = il%[1]d.issue_id AND il%[1]d.label_id = %[2]d", i, labelID)) + if labelID > 0 { + sess.Join("INNER", fmt.Sprintf("issue_label il%d", i), + fmt.Sprintf("issue.id = il%[1]d.issue_id AND il%[1]d.label_id = %[2]d", i, labelID)) + } else { + sess.Where("issue.id not in (select issue_id from issue_label where label_id = ?)", -labelID) + } } } } diff --git a/models/issue_label.go b/models/issue_label.go index 9efc7fd51f..1fc873cfd4 100644 --- a/models/issue_label.go +++ b/models/issue_label.go @@ -72,6 +72,7 @@ type Label struct { IsChecked bool `xorm:"-"` QueryString string `xorm:"-"` IsSelected bool `xorm:"-"` + IsExcluded bool `xorm:"-"` } // APIFormat converts a Label to the api.Label format @@ -97,7 +98,10 @@ func (label *Label) LoadSelectedLabelsAfterClick(currentSelectedLabels []int64) for _, s := range currentSelectedLabels { if s == label.ID { labelSelected = true - } else if s > 0 { + } else if -s == label.ID { + labelSelected = true + label.IsExcluded = true + } else if s != 0 { labelQuerySlice = append(labelQuerySlice, strconv.FormatInt(s, 10)) } } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index eb38a777c8..60acab0178 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -802,6 +802,7 @@ issues.delete_branch_at = `deleted branch %s %s` issues.open_tab = %d Open issues.close_tab = %d Closed issues.filter_label = Label +issues.filter_label_exclude = `Use alt + click/enter to exclude labels` issues.filter_label_no_select = All labels issues.filter_milestone = Milestone issues.filter_milestone_no_select = All milestones diff --git a/public/css/index.css b/public/css/index.css index 9292604422..e404c1fec6 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -458,6 +458,8 @@ i.icon.centerlock{top:1.5em} .repository .filter.menu .label.color{border-radius:3px;margin-left:15px;padding:0 8px} .repository .filter.menu .octicon{float:left;margin:5px -7px 0 -5px;width:16px} .repository .filter.menu.labels .octicon{margin:-2px -7px 0 -5px} +.repository .filter.menu.labels .label-filter .menu .info{display:inline-block;padding:9px 7px 7px 7px;text-align:center;border-bottom:1px solid #ccc;font-size:12px} +.repository .filter.menu.labels .label-filter .menu .info code{border:1px solid #ccc;border-radius:3px;padding:3px 2px 1px 2px;font-size:11px} .repository .filter.menu .text{margin-left:.9em} .repository .filter.menu .menu{max-height:300px;overflow-x:auto;right:0!important;left:auto!important} .repository .filter.menu .dropdown.item{margin:1px;padding-right:0} diff --git a/public/js/index.js b/public/js/index.js index 90819677a5..cf19bf71a0 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -3278,8 +3278,39 @@ function initIssueList() { }, fullTextSearch: true - }) - ; + }); + + $(".menu a.label-filter-item").each(function() { + $(this).click(function(e) { + if (e.altKey) { + const href = $(this).attr("href"); + const id = $(this).data("label-id"); + + const regStr = "labels=(-?[0-9]+%2c)*(" + id + ")(%2c-?[0-9]+)*&"; + const newStr = "labels=$1-$2$3&"; + + window.location = href.replace(new RegExp(regStr), newStr); + } + }); + }); + + $(".menu .ui.dropdown.label-filter").keydown(function(e) { + if (e.altKey && e.keyCode == 13) { + const selectedItems = $(".menu .ui.dropdown.label-filter .menu .item.selected"); + + if (selectedItems.length > 0) { + const item = $(selectedItems[0]); + + const href = item.attr("href"); + const id = item.data("label-id"); + + const regStr = "labels=(-?[0-9]+%2c)*(" + id + ")(%2c-?[0-9]+)*&"; + const newStr = "labels=$1-$2$3&"; + + window.location = href.replace(new RegExp(regStr), newStr); + } + } + }); } function cancelCodeComment(btn) { const form = $(btn).closest("form"); diff --git a/public/less/_repository.less b/public/less/_repository.less index 33ee5761c4..48a1214c07 100644 --- a/public/less/_repository.less +++ b/public/less/_repository.less @@ -158,6 +158,23 @@ margin: -2px -7px 0 -5px; } + &.labels { + .label-filter .menu .info { + display: inline-block; + padding: 9px 7px 7px 7px; + text-align: center; + border-bottom: 1px solid #cccccc; + font-size: 12px; + + code { + border: 1px solid #cccccc; + border-radius: 3px; + padding: 3px 2px 1px 2px; + font-size: 11px; + } + } + } + .text { margin-left: 0.9em; } diff --git a/templates/repo/issue/list.tmpl b/templates/repo/issue/list.tmpl index e64cef2724..9b354a6800 100644 --- a/templates/repo/issue/list.tmpl +++ b/templates/repo/issue/list.tmpl @@ -42,15 +42,16 @@