diff --git a/app/coffee/modules/backlog/main.coffee b/app/coffee/modules/backlog/main.coffee index a6c7133c..802a1357 100644 --- a/app/coffee/modules/backlog/main.coffee +++ b/app/coffee/modules/backlog/main.coffee @@ -173,7 +173,7 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F @scope.projectId = data.project return data - return promise.then(=> @loadProject()) + return promise.then(=> @.loadProject()) .then(=> @.loadUsersAndRoles()) .then(=> @.loadBacklog()) diff --git a/app/coffee/modules/base/tags.coffee b/app/coffee/modules/base/tags.coffee index 06c81847..63e4b636 100644 --- a/app/coffee/modules/base/tags.coffee +++ b/app/coffee/modules/base/tags.coffee @@ -20,6 +20,7 @@ ### taiga = @.taiga +trim = @.taiga.trim module = angular.module("taigaBase") @@ -88,3 +89,117 @@ ColorizeTagsDirective = -> return {link: link} module.directive("tgColorizeTags", ColorizeTagsDirective) + +############################################################################# +## TagLine (possible should be moved as generic directive) +############################################################################# + +TagLineDirective = ($log, $rs) -> + # Main directive template (rendered by angular) + template = """ +
+ + """ + + # Tags template (rendered manually using lodash) + templateTags = _.template(""" + <% _.each(tags, function(tag) { %> + + <% }); %>""") + + renderTags = ($el, tags, editable, tagsColors) -> + ctx = { + tags: _.map(tags, (t) -> {name: t, color: tagsColors[t]}) + editable: editable + } + html = templateTags(ctx) + $el.find("div.tags-container").html(html) + + normalizeTags = (tags) -> + tags = _.map(tags, trim) + tags = _.map(tags, (x) -> x.toLowerCase()) + return _.uniq(tags) + + link = ($scope, $el, $attrs, $model) -> + editable = if $attrs.editable == "true" then true else false + $el.addClass('tags-block') + + addValue = (value) -> + value = trim(value) + return if value.length <= 0 + + tags = _.clone($model.$modelValue, false) + tags = [] if not tags? + tags.push(value) + + $scope.$apply -> + $model.$setViewValue(normalizeTags(tags)) + + + $scope.$watch $attrs.ngModel, (val) -> + return if not val + renderTags($el, val, editable, $scope.project.tags_colors) + + $scope.$watch "projectId", (val) -> + return if not val? + positioningFunction = (position, elements) -> + menu = elements.element.element + menu.css 'width', elements.target.width + menu.css 'top', position.top + menu.css 'left', position.left + + promise = $rs.projects.tags($scope.projectId) + promise.then (data) -> + if editable + $el.find("input").autocomplete({ + source: data, + position: + my: "left top", + using: positioningFunction + select: (event, ui) -> + addValue(ui.item.value) + ui.item.value = "" + }) + + $el.find("input").remove() if not editable + + $el.on "keypress", "input", (event) -> + return if event.keyCode != 13 + event.preventDefault() + + $el.on "keyup", "input", (event) -> + return if event.keyCode != 13 + event.preventDefault() + + target = angular.element(event.currentTarget) + addValue(target.val()) + target.val("") + $el.find("input").autocomplete("close") + + + $el.on "click", ".icon-delete", (event) -> + event.preventDefault() + target = angular.element(event.currentTarget) + value = trim(target.siblings(".tag-name").text()) + + if value.length <= 0 + return + + tags = _.clone($model.$modelValue, false) + tags = _.pull(tags, value) + + $scope.$apply -> + $model.$setViewValue(normalizeTags(tags)) + + return { + link:link, + require:"ngModel" + template: template + } + +module.directive("tgTagLine", ["$log", "$tgResources", TagLineDirective]) diff --git a/app/coffee/modules/common.coffee b/app/coffee/modules/common.coffee index d02c9881..51853f07 100644 --- a/app/coffee/modules/common.coffee +++ b/app/coffee/modules/common.coffee @@ -27,89 +27,6 @@ typeIsArray = @.taiga.typeIsArray module = angular.module("taigaCommon", []) -############################################################################# -## TagLine (possible should be moved as generic directive) -############################################################################# - -TagLineDirective = ($log) -> - # Main directive template (rendered by angular) - template = """ - - - """ - - # Tags template (rendered manually using lodash) - templateTags = _.template(""" - <% _.each(tags, function(tag) { %> - - <% }); %>""") - - renderTags = ($el, tags, editable, tagsColors) -> - ctx = { - tags: _.map(tags, (t) -> {name: t, color: tagsColors[t]}) - editable: editable - } - html = templateTags(ctx) - $el.find("div.tags-container").html(html) - - normalizeTags = (tags) -> - tags = _.map(tags, trim) - tags = _.map(tags, (x) -> x.toLowerCase()) - return _.uniq(tags) - - link = ($scope, $el, $attrs, $model) -> - editable = if $attrs.editable == "true" then true else false - - $scope.$watch $attrs.ngModel, (val) -> - return if not val - renderTags($el, val, editable, $scope.project.tags_colors) - - $el.find("input").remove() if not editable - - $el.on "keyup", "input", (event) -> - return if event.keyCode != 13 - target = angular.element(event.currentTarget) - value = trim(target.val()) - - if value.length <= 0 - return - - tags = _.clone($model.$modelValue, false) - tags = [] if not tags? - tags.push(value) - - target.val("") - - $scope.$apply -> - $model.$setViewValue(normalizeTags(tags)) - - $el.on "click", ".icon-delete", (event) -> - event.preventDefault() - target = angular.element(event.currentTarget) - value = trim(target.siblings(".tag-name").text()) - - if value.length <= 0 - return - - tags = _.clone($model.$modelValue, false) - tags = _.pull(tags, value) - - $scope.$apply -> - $model.$setViewValue(normalizeTags(tags)) - - return { - link:link, - require:"ngModel" - template: template - } - -module.directive("tgTagLine", ["$log", TagLineDirective]) - ############################################################################# ## Change (comment and history mode) directive ############################################################################# diff --git a/app/coffee/modules/resources/projects.coffee b/app/coffee/modules/resources/projects.coffee index f3e1ab51..19437881 100644 --- a/app/coffee/modules/resources/projects.coffee +++ b/app/coffee/modules/resources/projects.coffee @@ -45,6 +45,9 @@ resourceProvider = ($repo) -> service.stats = (projectId) -> return $repo.queryOneRaw("projects", "#{projectId}/stats") + service.tags = (projectId) -> + return $repo.queryOneRaw("projects", "#{projectId}/tags") + service.tagsColors = (id) -> return $repo.queryOne("projects", "#{id}/tags_colors") diff --git a/app/partials/backlog.jade b/app/partials/backlog.jade index f0235085..7505ae09 100644 --- a/app/partials/backlog.jade +++ b/app/partials/backlog.jade @@ -35,11 +35,11 @@ block content sidebar.menu-secondary.sidebar include views/modules/sprints - div.lightbox.lightbox-generic-form(tg-lb-create-edit-userstory) - include views/modules/lightbox-us-create-edit + div.lightbox.lightbox-generic-form(tg-lb-create-edit-userstory) + include views/modules/lightbox-us-create-edit - div.lightbox.lightbox-generic-bulk(tg-lb-create-bulk-userstories) - include views/modules/lightbox-us-bulk + div.lightbox.lightbox-generic-bulk(tg-lb-create-bulk-userstories) + include views/modules/lightbox-us-bulk - div.lightbox.lightbox-sprint-add-edit(tg-lb-create-edit-sprint) - include views/modules/lightbox-sprint-add-edit + div.lightbox.lightbox-sprint-add-edit(tg-lb-create-edit-sprint) + include views/modules/lightbox-sprint-add-edit diff --git a/app/partials/issues-detail-edit.jade b/app/partials/issues-detail-edit.jade index 964eb445..39d1f067 100644 --- a/app/partials/issues-detail-edit.jade +++ b/app/partials/issues-detail-edit.jade @@ -23,7 +23,7 @@ block content span.block-description(tg-bind-html="issue.blocked_note || 'This issue is blocked'") a.unblock(ng-click="ctrl.unblock()", href="", title="Unblock issue") Unblock - div.user-story-tags(tg-tag-line, editable="true", ng-model="issue.tags") + div(tg-tag-line, editable="true", ng-model="issue.tags") section.us-content textarea(placeholder="Write a description of your issue", ng-model="issue.description", tg-markitup) diff --git a/app/partials/issues-detail.jade b/app/partials/issues-detail.jade index 1c2c32b4..1c25f4fe 100644 --- a/app/partials/issues-detail.jade +++ b/app/partials/issues-detail.jade @@ -24,7 +24,7 @@ block content a.icon.icon-arrow-left(ng-show="previousUrl",href="{{ previousUrl }}", title="previous issue") a.icon.icon-arrow-right(ng-show="nextUrl", href="{{ nextUrl }}", title="next issue") - div.user-story-tags(tg-tag-line, ng-model="issue.tags", ng-show="issue.tags") + div(tg-tag-line, ng-model="issue.tags", ng-show="issue.tags") section.us-content.wysiwyg(tg-bind-html="issue.description_html") diff --git a/app/partials/task-detail-edit.jade b/app/partials/task-detail-edit.jade index 5789f94b..d55ce906 100644 --- a/app/partials/task-detail-edit.jade +++ b/app/partials/task-detail-edit.jade @@ -23,7 +23,7 @@ block content span.block-description(tg-bind-html="task.blocked_note || 'This task is blocked'") a.unblock(ng-click="ctrl.unblock()", href="", title="Unblock task") Unblock - div.user-story-tags(tg-tag-line, editable="true", ng-model="task.tags") + div(tg-tag-line, editable="true", ng-model="task.tags") section.us-content textarea(placeholder="Write a description of your task", ng-model="task.description", tg-markitup) diff --git a/app/partials/task-detail.jade b/app/partials/task-detail.jade index f8b6dc5a..fcbcc5cb 100644 --- a/app/partials/task-detail.jade +++ b/app/partials/task-detail.jade @@ -24,7 +24,7 @@ block content a.icon.icon-arrow-left(ng-show="previousUrl",href="{{ previousUrl }}", title="previous task") a.icon.icon-arrow-right(ng-show="nextUrl", href="{{ nextUrl }}", title="next task") - div.user-story-tags(tg-tag-line, ng-model="task.tags", ng-show="task.tags") + div(tg-tag-line, ng-model="task.tags", ng-show="task.tags") section.us-content.wysiwyg(tg-bind-html="task.description_html") diff --git a/app/partials/us-detail-edit.jade b/app/partials/us-detail-edit.jade index a2ad2800..e1ea9592 100644 --- a/app/partials/us-detail-edit.jade +++ b/app/partials/us-detail-edit.jade @@ -23,7 +23,7 @@ block content span.block-description(tg-bind-html="us.blocked_note || 'This US is blocked'") a.unblock(ng-click="ctrl.unblock()", href="", title="Unblock US") Unblock - div.user-story-tags(tg-tag-line, editable="true", ng-model="us.tags") + div(tg-tag-line, editable="true", ng-model="us.tags") section.us-content textarea(placeholder="Write a description of your user story", ng-model="us.description", tg-markitup) diff --git a/app/partials/us-detail.jade b/app/partials/us-detail.jade index 94924a27..cfe263e3 100644 --- a/app/partials/us-detail.jade +++ b/app/partials/us-detail.jade @@ -26,7 +26,7 @@ block content title="previous user story") a.icon.icon-arrow-right(ng-show="nextUrl", href="{{ nextUrl }}", title="next user story") - div.user-story-tags(tg-tag-line, ng-model="us.tags", ng-show="us.tags") + div(tg-tag-line, ng-model="us.tags", ng-show="us.tags") section.us-content.wysiwyg(tg-bind-html="us.description_html") diff --git a/app/partials/views/components/backlog-row.jade b/app/partials/views/components/backlog-row.jade index 3a48f45c..66d36fc4 100644 --- a/app/partials/views/components/backlog-row.jade +++ b/app/partials/views/components/backlog-row.jade @@ -1,6 +1,6 @@ div.row.us-item-row(ng-repeat="us in visibleUserstories|orderBy:order track by us.id", tg-draggable, ng-class="{blocked: us.is_blocked}") div.user-stories - div.user-story-tags(tg-colorize-tags="us.tags", tg-colorize-tags-type="backlog") + div.tags-block(tg-colorize-tags="us.tags", tg-colorize-tags-type="backlog") div.user-story-name input(tg-check-permission, permission="modify_us", type="checkbox", name="") a.clickable(tg-nav="project-userstories-detail:project=project.slug,ref=us.ref", diff --git a/app/partials/views/modules/lightbox-create-issue.jade b/app/partials/views/modules/lightbox-create-issue.jade index 91df62c8..1a7142de 100644 --- a/app/partials/views/modules/lightbox-create-issue.jade +++ b/app/partials/views/modules/lightbox-create-issue.jade @@ -14,7 +14,7 @@ form select.severity(ng-model="issue.severity", ng-options="s.id as s.name for s in severityList") fieldset - input(type="text", placeholder="Tags", tg-tags, ng-model="issue.tags") + div(tg-tag-line, editable="true", ng-model="issue.tags") fieldset textarea.description(placeholder="Description", ng-model="issue.description") diff --git a/app/partials/views/modules/lightbox-task-create-edit.jade b/app/partials/views/modules/lightbox-task-create-edit.jade index 093a60dc..499b3776 100644 --- a/app/partials/views/modules/lightbox-task-create-edit.jade +++ b/app/partials/views/modules/lightbox-task-create-edit.jade @@ -16,7 +16,7 @@ form option(value="") Unassigned fieldset - input(type="text", placeholder="Tags", tg-tags, ng-model="task.tags") + div(tg-tag-line, editable="true", ng-model="task.tags") fieldset textarea.description(placeholder="Type a short description", ng-model="task.description") diff --git a/app/partials/views/modules/lightbox-us-create-edit.jade b/app/partials/views/modules/lightbox-us-create-edit.jade index 56781ba9..ab8024fa 100644 --- a/app/partials/views/modules/lightbox-us-create-edit.jade +++ b/app/partials/views/modules/lightbox-us-create-edit.jade @@ -9,7 +9,7 @@ form select(name="status", ng-model="us.status", ng-options="s.id as s.name for s in usStatusList", tg-i18n="placeholder:common.status") fieldset - input(type="text", name="tags", placeholder="Tags", tg-tags, ng-model="us.tags") + div(tg-tag-line, editable="true", ng-model="us.tags") fieldset textarea.description(name="description", ng-model="us.description", diff --git a/app/styles/components/tag.scss b/app/styles/components/tag.scss index b0b39513..bfab0391 100644 --- a/app/styles/components/tag.scss +++ b/app/styles/components/tag.scss @@ -13,3 +13,33 @@ } } } + +.ui-autocomplete { + z-index: 99910; + background: white; + border: 1px solid $gray-light; + .ui-state-focus { + background: $fresh-taiga; + } +} + +.ui-helper-hidden-accessible { + display: none; +} + +.tags-block { + .tags-container { + display: inline-block; + vertical-align: middle; + } + input { + display: inline-block; + padding: .4rem; + width: 14rem; + } + .tag { + @extend %small; + margin: 0 .5rem .5rem 0; + padding: .5rem; + } +} diff --git a/app/styles/layout/us-detail.scss b/app/styles/layout/us-detail.scss index 4c78bdbc..2b29554e 100644 --- a/app/styles/layout/us-detail.scss +++ b/app/styles/layout/us-detail.scss @@ -114,23 +114,6 @@ } } -.user-story-tags { - .tags-container { - display: inline-block; - vertical-align: middle; - } - input { - display: inline-block; - padding: .4rem; - width: 14rem; - } - .tag { - @extend %small; - margin: 0 .5rem .5rem 0; - padding: .5rem; - } -} - .us-activity-tabs { @extend %title; border-bottom: 3px solid $gray-light; diff --git a/app/styles/modules/backlog/backlog-table.scss b/app/styles/modules/backlog/backlog-table.scss index a84eb509..68150de8 100644 --- a/app/styles/modules/backlog/backlog-table.scss +++ b/app/styles/modules/backlog/backlog-table.scss @@ -3,7 +3,7 @@ @include table-flex(); width: 100%; &.show-tags { - .user-story-tags { + .tags-block { display: block; } } @@ -156,7 +156,7 @@ white-space: nowrap; } } - .user-story-tags { + .tags-block { display: none; margin-bottom: .3rem; .tag { diff --git a/app/styles/modules/backlog/sprints.scss b/app/styles/modules/backlog/sprints.scss index 2af029a5..b36c4a83 100644 --- a/app/styles/modules/backlog/sprints.scss +++ b/app/styles/modules/backlog/sprints.scss @@ -165,7 +165,7 @@ text-align: center; } .us-item-row { - .user-story-tags, + .tags-block, .us-settings, .status, .icon-drag-v,