Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kanban colored boards #16647

Merged
merged 34 commits into from
Sep 29, 2021
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
d7ba3cf
#11160 Add colors column for ProjectBoard
romdum Aug 6, 2021
b2c13e0
#11160 Add color input in edit project board form
romdum Aug 6, 2021
19bc8a7
Add label for color
romdum Aug 6, 2021
bfbbbda
Add color picker
romdum Aug 7, 2021
1d4d8e7
Add color input on new board form
romdum Aug 7, 2021
1d0d134
Linter fix
romdum Aug 7, 2021
ad2dc84
Add migration for color project board column
romdum Aug 8, 2021
85b9346
validate project board column
romdum Aug 8, 2021
ca4481a
Correct BoardColorPattern comment
romdum Aug 8, 2021
b182243
Create a generic "color" translation key
romdum Aug 8, 2021
77ed1e3
Add color validation on new project board form
romdum Aug 8, 2021
e64812b
Apply suggestions from code review
6543 Aug 8, 2021
404ab33
Merge branch 'main' into feature/board_color
6543 Aug 8, 2021
3ce6a4c
Merge branch 'main' into feature/board_color
6543 Aug 21, 2021
ec952cd
Change board label color according to background
romdum Aug 23, 2021
153a7fe
Possibility to remove board color
romdum Aug 23, 2021
9f86b27
Merge branch 'main' into feature/board_color
romdum Aug 23, 2021
ff47888
Merge branch main to fix conflicts
romdum Aug 25, 2021
592ac5a
Merge branch 'main' into feature/board_color
6543 Aug 28, 2021
e1ff66a
Use less variable for project board label color
romdum Aug 31, 2021
7918bb7
Add button to remove board colors
romdum Sep 7, 2021
20b4dd6
Add check before update board color
romdum Sep 7, 2021
6a62f90
Linter fix
romdum Sep 7, 2021
1c98c83
Code review
romdum Sep 9, 2021
cc4a33a
Merge branch 'main' into feature/board_color
romdum Sep 9, 2021
037cd56
Merge branch 'main' into feature/board_color
romdum Sep 9, 2021
a86ee3c
Remove btn which remove board color and add precolor
romdum Sep 9, 2021
5e335dc
linter fix
romdum Sep 9, 2021
c39ca47
Add precolor on new project board form
romdum Sep 9, 2021
694bc41
linter fix
romdum Sep 9, 2021
d769196
Board color - Code review
romdum Sep 24, 2021
dc4e4c4
Merge branch 'main'
romdum Sep 24, 2021
3b12e74
Feature Board colored - Code review
romdum Sep 27, 2021
95d7555
Merge branch 'main' into feature/board_color
6543 Sep 29, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions models/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,8 @@ var migrations = []Migration{
NewMigration("RecreateIssueResourceIndexTable to have a primary key instead of an unique index", recreateIssueResourceIndexTable),
// v193 -> v194
NewMigration("Add repo id column for attachment table", addRepoIDForAttachment),
// v194 -> v195
NewMigration("Add Color to ProjectBoard table", addColorColToProjectBoard),
}

// GetCurrentDBVersion returns the current db version
Expand Down
22 changes: 22 additions & 0 deletions models/migrations/v194.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package migrations

import (
"fmt"

"xorm.io/xorm"
)

func addColorColToProjectBoard(x *xorm.Engine) error {
type ProjectBoard struct {
Color string `xorm:"VARCHAR(7)"`
}

if err := x.Sync2(new(ProjectBoard)); err != nil {
return fmt.Errorf("Sync2: %v", err)
}
return nil
}
24 changes: 22 additions & 2 deletions models/project_board.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
package models

import (
"fmt"
"regexp"

"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"

Expand All @@ -31,12 +34,16 @@ const (
ProjectBoardTypeBugTriage
)

// BoardColorPattern is a regexp witch can validate BoardColor
var BoardColorPattern = regexp.MustCompile("^#[0-9a-fA-F]{6}$")

// ProjectBoard is used to represent boards on a project
type ProjectBoard struct {
ID int64 `xorm:"pk autoincr"`
Title string
Default bool `xorm:"NOT NULL DEFAULT false"` // issues not assigned to a specific board will be assigned to this board
Sorting int8 `xorm:"NOT NULL DEFAULT 0"`
Default bool `xorm:"NOT NULL DEFAULT false"` // issues not assigned to a specific board will be assigned to this board
Sorting int8 `xorm:"NOT NULL DEFAULT 0"`
Color string `xorm:"VARCHAR(7)"`

ProjectID int64 `xorm:"INDEX NOT NULL"`
CreatorID int64 `xorm:"NOT NULL"`
Expand Down Expand Up @@ -95,6 +102,12 @@ func createBoardsForProjectsType(sess *xorm.Session, project *Project) error {

// NewProjectBoard adds a new project board to a given project
func NewProjectBoard(board *ProjectBoard) error {
if board.Color != "" {
6543 marked this conversation as resolved.
Show resolved Hide resolved
lafriks marked this conversation as resolved.
Show resolved Hide resolved
if !BoardColorPattern.MatchString(board.Color) {
lafriks marked this conversation as resolved.
Show resolved Hide resolved
return fmt.Errorf("bad color code: %s", board.Color)
}
}
6543 marked this conversation as resolved.
Show resolved Hide resolved

_, err := x.Insert(board)
return err
}
Expand Down Expand Up @@ -173,6 +186,13 @@ func updateProjectBoard(e Engine, board *ProjectBoard) error {
fieldToUpdate = append(fieldToUpdate, "title")
}

if len(board.Color) != 0 {
lafriks marked this conversation as resolved.
Show resolved Hide resolved
if !BoardColorPattern.MatchString(board.Color) {
return fmt.Errorf("bad color code: %s", board.Color)
}
}
fieldToUpdate = append(fieldToUpdate, "color")

_, err := e.ID(board.ID).Cols(fieldToUpdate...).Update(board)

return err
Expand Down
3 changes: 1 addition & 2 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ error = Error
error404 = The page you are trying to reach either <strong>does not exist</strong> or <strong>you are not authorized</strong> to view it.

never = Never
color = Color

[error]
occurred = An error has occurred
Expand Down Expand Up @@ -977,7 +978,6 @@ commit_graph = Commit Graph
commit_graph.select = Select branches
commit_graph.hide_pr_refs = Hide Pull Requests
commit_graph.monochrome = Mono
commit_graph.color = Color
blame = Blame
normal_view = Normal View
line = line
Expand Down Expand Up @@ -1791,7 +1791,6 @@ settings.slack_username = Username
settings.slack_icon_url = Icon URL
settings.discord_username = Username
settings.discord_icon_url = Icon URL
settings.slack_color = Color
settings.event_desc = Trigger On:
settings.event_push_only = Push Events
settings.event_send_everything = All Events
Expand Down
7 changes: 7 additions & 0 deletions routers/web/repo/projects.go
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,7 @@ func AddBoardToProjectPost(ctx *context.Context) {
if err := models.NewProjectBoard(&models.ProjectBoard{
ProjectID: project.ID,
Title: form.Title,
Color: form.Color,
CreatorID: ctx.User.ID,
}); err != nil {
ctx.ServerError("NewProjectBoard", err)
Expand Down Expand Up @@ -513,6 +514,12 @@ func EditProjectBoard(ctx *context.Context) {
board.Title = form.Title
}

if len(form.Color) != 0 {
lafriks marked this conversation as resolved.
Show resolved Hide resolved
board.Color = form.Color
} else {
board.Color = ""
}

if form.Sorting != 0 {
board.Sorting = form.Sorting
}
Expand Down
1 change: 1 addition & 0 deletions services/forms/repo_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,7 @@ type UserCreateProjectForm struct {
type EditProjectBoardForm struct {
Title string `binding:"Required;MaxSize(100)"`
Sorting int8
Color string `binding:"MaxSize(7)"`
6543 marked this conversation as resolved.
Show resolved Hide resolved
}

// _____ .__.__ __
Expand Down
2 changes: 1 addition & 1 deletion templates/repo/graph.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
</div>
</div>
<button id="flow-color-monochrome" class="ui labelled icon button{{if eq .Mode "monochrome"}} active{{end}}" title="{{.i18n.Tr "repo.commit_graph.monochrome"}}">{{svg "material-invert-colors" 16 "mr-2"}}{{.i18n.Tr "repo.commit_graph.monochrome"}}</button>
<button id="flow-color-colored" class="ui labelled icon button{{if ne .Mode "monochrome"}} active{{end}}" title="{{.i18n.Tr "repo.commit_graph.color"}}">{{svg "material-palette" 16 "mr-2"}}{{.i18n.Tr "repo.commit_graph.color"}}</button>
<button id="flow-color-colored" class="ui labelled icon button{{if ne .Mode "monochrome"}} active{{end}}" title="{{.i18n.Tr "color"}}">{{svg "material-palette" 16 "mr-2"}}{{.i18n.Tr "color"}}</button>
</div>
</h2>
<div class="ui dividing"></div>
Expand Down
24 changes: 22 additions & 2 deletions templates/repo/projects/view.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
{{if and .CanWriteProjects (not .Repository.IsArchived) .PageIsProjects}}
<a class="ui green button show-modal item" data-modal="#new-board-item">{{.i18n.Tr "new_project_board"}}</a>
{{end}}
<div class="ui small modal" id="new-board-item">
<div class="ui small modal new-board-modal" id="new-board-item">
<div class="header">
{{$.i18n.Tr "repo.projects.board.new"}}
</div>
Expand All @@ -21,6 +21,16 @@
<input class="new-board" id="new_board" name="title" required>
</div>

<div class="field color-field">
<label for="new_board_color">{{$.i18n.Tr "color"}}</label>
<div class="color picker column">
<input class="color-picker" maxlength="7" placeholder="#c320f6" id="new_board_color_picker" name="color">
<div class="column precolors">
{{template "repo/issue/label_precolors"}}
</div>
</div>
</div>

<div class="text right actions">
<div class="ui cancel button">{{$.i18n.Tr "settings.cancel"}}</div>
<button data-url="{{$.RepoLink}}/projects/{{$.Project.ID}}" class="ui green button" id="new_board_submit">{{$.i18n.Tr "repo.projects.board.new_submit"}}</button>
Expand Down Expand Up @@ -69,7 +79,7 @@
<div class="board">
{{ range $board := .Boards }}

<div class="ui segment board-column" data-id="{{.ID}}" data-sorting="{{.Sorting}}" data-url="{{$.RepoLink}}/projects/{{$.Project.ID}}/{{.ID}}">
<div class="ui segment board-column" style="background: {{.Color}}!important;" data-id="{{.ID}}" data-sorting="{{.Sorting}}" data-url="{{$.RepoLink}}/projects/{{$.Project.ID}}/{{.ID}}">
<div class="board-column-header df ac sb">
<div class="ui large label board-label py-2">{{.Title}}</div>
{{if and $.CanWriteProjects (not $.Repository.IsArchived) $.PageIsProjects (ne .ID 0)}}
Expand Down Expand Up @@ -104,6 +114,16 @@
<input class="project-board-title" id="new_board_title" name="title" value="{{.Title}}" required>
</div>

<div class="field color-field">
<label for="new_board_color">{{$.i18n.Tr "color"}}</label>
<div class="color picker column">
<input class="color-picker" maxlength="7" placeholder="#c320f6" id="new_board_color" name="color" value="{{.Color}}">
<div class="column precolors">
{{template "repo/issue/label_precolors"}}
</div>
</div>
</div>

<div class="text right actions">
<div class="ui cancel button">{{$.i18n.Tr "settings.cancel"}}</div>
<button data-url="{{$.RepoLink}}/projects/{{$.Project.ID}}/{{.ID}}" class="ui red button">{{$.i18n.Tr "repo.projects.board.edit"}}</button>
Expand Down
2 changes: 1 addition & 1 deletion templates/repo/settings/webhook/slack.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<input id="icon_url" name="icon_url" value="{{.SlackHook.IconURL}}" placeholder="e.g. https://example.com/img/favicon.png">
</div>
<div class="field">
<label for="color">{{.i18n.Tr "repo.settings.slack_color"}}</label>
<label for="color">{{.i18n.Tr "color"}}</label>
<input id="color" name="color" value="{{.SlackHook.Color}}" placeholder="e.g. #dd4b39, good, warning, danger">
</div>
{{template "repo/settings/webhook/settings" .}}
Expand Down
51 changes: 47 additions & 4 deletions web_src/js/features/projects.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default async function initProject() {
if (parseInt($(column).data('sorting')) !== i) {
$.ajax({
url: $(column).data('url'),
data: JSON.stringify({sorting: i}),
data: JSON.stringify({sorting: i, color: rgbToHex($(column).css('backgroundColor'))}),
headers: {
'X-Csrf-Token': csrf,
'X-Remote': true,
Expand Down Expand Up @@ -62,10 +62,17 @@ export default async function initProject() {
}

$('.edit-project-board').each(function () {
const projectTitleLabel = $(this).closest('.board-column-header').find('.board-label');
const projectHeader = $(this).closest('.board-column-header');
const projectTitleLabel = projectHeader.find('.board-label');
const projectTitleInput = $(this).find(
'.content > .form > .field > .project-board-title',
);
const projectColorInput = $(this).find('.content > .form > .field #new_board_color');
const boardColumn = $(this).closest('.board-column');

if (boardColumn.css('backgroundColor')) {
setLabelColor(projectHeader, rgbToHex(boardColumn.css('backgroundColor')));
}

$(this)
.find('.content > .form > .actions > .red')
Expand All @@ -74,7 +81,7 @@ export default async function initProject() {

$.ajax({
url: $(this).data('url'),
data: JSON.stringify({title: projectTitleInput.val()}),
data: JSON.stringify({title: projectTitleInput.val(), color: projectColorInput.val()}),
headers: {
'X-Csrf-Token': csrf,
'X-Remote': true,
Expand All @@ -84,6 +91,10 @@ export default async function initProject() {
}).done(() => {
projectTitleLabel.text(projectTitleInput.val());
projectTitleInput.closest('form').removeClass('dirty');
if (projectColorInput.val()) {
setLabelColor(projectHeader, projectColorInput.val());
}
boardColumn.attr('style', `background: ${projectColorInput.val()}!important`);
$('.ui.modal').modal('hide');
});
});
Expand Down Expand Up @@ -127,10 +138,11 @@ export default async function initProject() {
e.preventDefault();

const boardTitle = $('#new_board');
const projectColorInput = $('#new_board_color_picker');

$.ajax({
url: $(this).data('url'),
data: JSON.stringify({title: boardTitle.val()}),
data: JSON.stringify({title: boardTitle.val(), color: projectColorInput.val()}),
headers: {
'X-Csrf-Token': csrf,
'X-Remote': true,
Expand All @@ -143,3 +155,34 @@ export default async function initProject() {
});
});
}

function setLabelColor(label, color) {
const red = getRelativeColor(parseInt(color.substr(1, 2), 16));
const green = getRelativeColor(parseInt(color.substr(3, 2), 16));
const blue = getRelativeColor(parseInt(color.substr(5, 2), 16));
const luminance = 0.2126 * red + 0.7152 * green + 0.0722 * blue;

if (luminance > 0.179) {
label.removeClass('light-label').addClass('dark-label');
} else {
label.removeClass('dark-label').addClass('light-label');
}
}

/**
* Inspired by W3C recommandation https://www.w3.org/TR/WCAG20/#relativeluminancedef
*/
function getRelativeColor(color) {
color /= 255;
return color <= 0.03928 ? color / 12.92 : ((color + 0.055) / 1.055) ** 2.4;
}

function rgbToHex(rgb) {
rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
return `#${hex(rgb[1])}${hex(rgb[2])}${hex(rgb[3])}`;
}

function hex(x) {
const hexDigits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
return Number.isNaN(x) ? '00' : hexDigits[(x - x % 16) / 16] + hexDigits[x % 16];
}
21 changes: 15 additions & 6 deletions web_src/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,13 +159,8 @@ function initLabelEdit() {
$newLabelPanel.hide();
});

createColorPicker($('.color-picker'));
initColorPicker();

$('.precolors .color').on('click', function () {
const color_hex = $(this).data('color-hex');
$('.color-picker').val(color_hex);
$('.minicolors-swatch-color').css('background-color', color_hex);
});
$('.edit-label-button').on('click', function () {
$('.edit-label .color-picker').minicolors('value', $(this).data('color'));
$('#label-modal-id').val($(this).data('id'));
Expand All @@ -182,6 +177,16 @@ function initLabelEdit() {
});
}

function initColorPicker() {
createColorPicker($('.color-picker'));

$('.precolors .color').on('click', function () {
const color_hex = $(this).data('color-hex');
$('.color-picker').val(color_hex);
$('.minicolors-swatch-color').css('background-color', color_hex);
});
}

function updateIssuesMeta(url, action, issueIds, elementId) {
return new Promise(((resolve) => {
$.ajax({
Expand Down Expand Up @@ -2752,6 +2757,10 @@ $(document).ready(async () => {
});
$('.show-modal.button').on('click', function () {
$($(this).data('modal')).modal('show');
const colorPickers = $($(this).data('modal')).find('.color-picker');
if (colorPickers.length > 0) {
initColorPicker();
}
});
$('.delete-post.button').on('click', function () {
const $this = $(this);
Expand Down
15 changes: 15 additions & 0 deletions web_src/less/_base.less
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@
--color-placeholder-text: #aaa;
--color-editor-line-highlight: var(--color-primary-light-6);
--color-project-board-bg: var(--color-secondary-light-4);
--color-project-board-dark-label: #555555;
--color-project-board-light-label: #a6aab5;
--color-caret: var(--color-text-dark);
--color-reaction-bg: #0000000a;
--color-reaction-active-bg: var(--color-primary-alpha-20);
Expand Down Expand Up @@ -2084,3 +2086,16 @@ table th[data-sortt-desc] {
margin-top: -.5em;
margin-bottom: -.5em;
}

.precolors {
padding-left: 0;
padding-right: 0;
margin: 3px 10px auto;
width: 120px;

.color {
float: left;
width: 15px;
height: 15px;
}
}
13 changes: 0 additions & 13 deletions web_src/less/_repository.less
Original file line number Diff line number Diff line change
Expand Up @@ -2692,19 +2692,6 @@
width: 15px;
height: 15px;
}

.precolors {
padding-left: 0;
padding-right: 0;
margin: 3px 10px auto;
width: 120px;

.color {
float: left;
width: 15px;
height: 15px;
}
}
}
}

Expand Down
Loading