diff --git a/app/coffee/modules/common/tags.coffee b/app/coffee/modules/common/tags.coffee index dc003cd7..7b75df89 100644 --- a/app/coffee/modules/common/tags.coffee +++ b/app/coffee/modules/common/tags.coffee @@ -93,89 +93,274 @@ ColorizeTagsDirective = -> module.directive("tgColorizeTags", ColorizeTagsDirective) + ############################################################################# -## TagLine (possible should be moved as generic directive) +## TagLine Directive (for Lightboxes) ############################################################################# -TagLineDirective = ($rootscope, $log, $rs, $tgrepo, $confirm) -> - # Main directive template (rendered by angular) +LbTagLineDirective = ($rs) -> + ENTER_KEY = 13 + template = """
- - - Add tag - - - """ + + """ # TODO: i18n # Tags template (rendered manually using lodash) templateTags = _.template(""" <% _.each(tags, function(tag) { %> <%- tag.name %> - <% if (editable) { %> - <% } %> - <% }); %>""") + <% }); %> + """) # TODO: i18n link = ($scope, $el, $attrs, $model) -> - editable = false - - $el.addClass("tags-block") - $el.find("input").hide() - - renderTags = ($el, tags, editable, tagsColors) -> + renderTags = (tags, 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) + showSaveButton = -> $el.find(".save").removeClass("hidden") + hideSaveButton = -> $el.find(".save").addClass("hidden") + + resetInput = -> + $el.find("input").val("") + $el.find("input").autocomplete("close") addValue = (value) -> - value = trim(value) - return if value.length <= 0 + value = trim(value.toLowerCase()) + return if value.length == 0 tags = _.clone($model.$modelValue, false) tags = [] if not tags? - tags.push(value) + tags.push(value) if value not in tags $scope.$apply -> - $model.$setViewValue(normalizeTags(tags)) - autosaveModel = $scope.$eval($attrs.autosaveModel) - if autosaveModel - promise = $tgrepo.save(autosaveModel) - promise.then -> - $rootscope.$broadcast("history:reload") - promise.then null, -> - $confirm.notify("error") + $model.$setViewValue(tags) saveInputTag = () -> - input = $el.find('input') + value = $el.find("input").val() - addValue(input.val()) - input.val("") - input.autocomplete("close") - $el.find('.save').hide() + addValue(value) + resetInput() + hideSaveButton() + + $el.on "keypress", "input", (event) -> + return if event.keyCode != ENTER_KEY + event.preventDefault() + + $el.on "keyup", "input", (event) -> + target = angular.element(event.currentTarget) + + if event.keyCode == ENTER_KEY + saveInputTag() + else + if target.val().length + showSaveButton() + else + hideSaveButton() + + $el.on "click", ".save", (event) -> + event.preventDefault() + saveInputTag + + $el.on "click", ".icon-delete", (event) -> + event.preventDefault() + target = angular.element(event.currentTarget) + + value = target.siblings(".tag-name").text() + tags = _.clone($model.$modelValue, false) + tags = _.pull(tags, value) + + $scope.$apply -> + $model.$setViewValue(tags) bindOnce $scope, "project", (project) -> - # If not editable, no tags preloading is needed. - editable = project.my_permissions.indexOf($attrs.requiredPerm) != -1 + positioningFunction = (position, elements) -> + menu = elements.element.element + menu.css("width", elements.target.width) + menu.css("top", position.top) + menu.css("left", position.left) - if not $scope.$eval($attrs.autosaveModel)? - $el.find("a.save").remove() + $rs.projects.tags(project.id).then (data) -> + $el.find("input").autocomplete({ + source: data + position: { + my: "left top", + using: positioningFunction + } + select: (event, ui) -> + addValue(ui.item.value) + ui.item.value = "" + }) - if not editable - $el.find("input").remove() + $scope.$watch $attrs.ngModel, (tags) -> + tagsColors = $scope.project?.tags_colors or [] + renderTags(tags, tagsColors) + + $scope.$on "$destroy", -> + $el.off() + + return { + link:link, + require:"ngModel" + template: template + } + +module.directive("tgLbTagLine", ["$tgResources", LbTagLineDirective]) + + +############################################################################# +## TagLine Directive (for detail pages) +############################################################################# + +TagLineDirective = ($rootScope, $repo, $rs, $confirm) -> + ENTER_KEY = 13 + ESC_KEY = 27 + + template = """ +
+ + + + """ # TODO: i18n + + # Tags template (rendered manually using lodash) + templateTags = _.template(""" + <% _.each(tags, function(tag) { %> + + <%- tag.name %> + <% if (isEditable) { %> + + <% } %> + + <% }); %> + """) # TODO: i18n + + link = ($scope, $el, $attrs, $model) -> + isEditable = -> + return $scope.project.my_permissions.indexOf($attrs.requiredPerm) != -1 + + renderTags = (tags, tagsColors) -> + ctx = { + tags: _.map(tags, (t) -> {name: t, color: tagsColors[t]}) + isEditable: isEditable() + } + html = templateTags(ctx) + $el.find("div.tags-container").html(html) + + renderInReadModeOnly = -> + $el.find(".add-tag").remove() + $el.find("input").remove() + $el.find(".save").remove() + + showAddTagButton = -> $el.find(".add-tag").removeClass("hidden") + hideAddTagButton = -> $el.find(".add-tag").addClass("hidden") + + showAddTagButtonText = -> $el.find(".add-tag-text").removeClass("hidden") + hideAddTagButtonText = -> $el.find(".add-tag-text").addClass("hidden") + + showSaveButton = -> $el.find(".save").removeClass("hidden") + hideSaveButton = -> $el.find(".save").addClass("hidden") + + showInput = -> $el.find("input").removeClass("hidden") + hideInput = -> $el.find("input").addClass("hidden") + resetInput = -> + $el.find("input").val("") + $el.find("input").autocomplete("close") + + addValue = (value) -> + value = trim(value.toLowerCase()) + return if value.length == 0 + + tags = _.clone($model.$modelValue, false) + tags = [] if not tags? + tags.push(value) if value not in tags + + $scope.$apply -> + $model.$setViewValue(tags) + + autosaveModel = $scope.$eval($attrs.autosaveModel) + if autosaveModel + onSuccess = -> + $rootScope.$broadcast("history:reload") + onError = -> + $confirm.notify("error") + $scope.$apply -> + autosaveModel.revert() + $repo.save(autosaveModel).then(onSuccess, onError) + + saveInputTag = () -> + value = $el.find("input").val() + + addValue(value) + resetInput() + hideSaveButton() + + $el.on "keypress", "input", (event) -> + return if event.keyCode not in [ENTER_KEY, ESC_KEY] + event.preventDefault() + + $el.on "keyup", "input", (event) -> + target = angular.element(event.currentTarget) + + if event.keyCode == ENTER_KEY + saveInputTag() + else if event.keyCode == ESC_KEY + resetInput() + hideInput() + hideSaveButton() + showAddTagButton() + else + if target.val().length + showSaveButton() + else + hideSaveButton() + + $el.on "click", ".save", (event) -> + event.preventDefault() + saveInputTag() + + $el.on "click", ".add-tag", (event) -> + event.preventDefault() + hideAddTagButton() + showInput() + + $el.on "click", ".icon-delete", (event) -> + event.preventDefault() + target = angular.element(event.currentTarget) + + value = target.siblings(".tag-name").text() + tags = _.clone($model.$modelValue, false) + tags = _.pull(tags, value) + + $scope.$apply -> + $model.$setViewValue(tags) + + autosaveModel = $scope.$eval($attrs.autosaveModel) + if autosaveModel + onSuccess = -> + $rootScope.$broadcast("history:reload") + onError = -> + $confirm.notify("error") + $scope.$apply -> + autosaveModel.revert() + $repo.save(autosaveModel).then(onSuccess, onError) + + bindOnce $scope, "project", (project) -> + if not isEditable() + renderInReadModeOnly() return + showAddTagButton() positioningFunction = (position, elements) -> menu = elements.element.element @@ -195,62 +380,17 @@ TagLineDirective = ($rootscope, $log, $rs, $tgrepo, $confirm) -> ui.item.value = "" }) - $el.on "keypress", "input", (event) -> - return if event.keyCode not in [13, 27] - event.preventDefault() + $scope.$watch $attrs.ngModel, (tags) -> + return if not tags - $el.on "keyup", "input", (event) -> - target = angular.element(event.currentTarget) - - if event.keyCode == 13 - saveInputTag() - else if event.keyCode == 27 - $el.find('.save').hide() - $el.find("input").hide() - $el.find("input").val('') - $el.find('.add-tag').show() - else if target.val().length - $el.find('.save').show() + if tags.length + hideAddTagButtonText() else - $el.find('.save').hide() + showAddTagButtonText() - $el.on "click", ".save", saveInputTag + tagsColors = $scope.project?.tags_colors or [] + renderTags(tags, tagsColors) - $el.on "click", ".add-tag", (event) -> - event.preventDefault() - target = angular.element(event.currentTarget) - target.hide() - target.siblings('input').show() - - $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)) - autosaveModel = $scope.$eval($attrs.autosaveModel) - if autosaveModel - promise = $tgrepo.save(autosaveModel) - promise.then -> - $rootscope.$broadcast("history:reload") - promise.then null, -> - $confirm.notify("error") - - $scope.$watch $attrs.ngModel, (val) -> - tags_colors = if $scope.project?.tags_colors? then $scope.project.tags_colors else [] - renderTags($el, val, editable, tags_colors) - - if val? and val.length > 0 - $el.find("span.add-tag-text").hide() - else - $el.find("span.add-tag-text").show() $scope.$on "$destroy", -> $el.off() @@ -261,4 +401,4 @@ TagLineDirective = ($rootscope, $log, $rs, $tgrepo, $confirm) -> template: template } -module.directive("tgTagLine", ["$rootScope", "$log", "$tgResources", "$tgRepo", "$tgConfirm", TagLineDirective]) +module.directive("tgTagLine", ["$rootScope", "$tgRepo", "$tgResources", "$tgConfirm", TagLineDirective]) diff --git a/app/partials/views/modules/lightbox-create-issue.jade b/app/partials/views/modules/lightbox-create-issue.jade index 512e238b..1dbe545d 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 - div(tg-tag-line, ng-model="issue.tags", required-perm="add_issue") + div.tags-block(tg-lb-tag-line, 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 a8796491..96772bd8 100644 --- a/app/partials/views/modules/lightbox-task-create-edit.jade +++ b/app/partials/views/modules/lightbox-task-create-edit.jade @@ -15,9 +15,8 @@ form placeholder="Assigned to") option(value="") Unassigned - fieldset(ng-switch="isNew") - div(ng-switch-when="true", tg-tag-line, ng-model="task.tags", required-perm="add_task") - div(ng-switch-default, tg-tag-line, ng-model="task.tags", required-perm="modify_task") + fieldset + div.tags-block(tg-lb-tag-line, 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 70ec6f81..15808251 100644 --- a/app/partials/views/modules/lightbox-us-create-edit.jade +++ b/app/partials/views/modules/lightbox-us-create-edit.jade @@ -13,9 +13,8 @@ form select(name="status", ng-model="us.status", ng-options="s.id as s.name for s in usStatusList", tr="placeholder:common.status") - fieldset(ng-switch="isNew") - div(ng-switch-when="true", tg-tag-line, ng-model="us.tags", required-perm="add_us") - div(ng-switch-default, tg-tag-line, ng-model="us.tags", required-perm="modify_us") + fieldset + div.tags-block(tg-lb-tag-line, 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 7c52dd35..af550f85 100644 --- a/app/styles/components/tag.scss +++ b/app/styles/components/tag.scss @@ -54,9 +54,6 @@ .add-tag-text { @extend %small; } - .save { - display: none; - } }