diff --git a/CHANGELOG.md b/CHANGELOG.md index 3863c3f6..97295de2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,14 +7,14 @@ - Show a confirmation notice when you exit edit mode by pressing ESC in the markdown inputs. - Add the tribe button to link stories from tree.taiga.io with gigs in tribe.taiga.io. - Errors (not found, server error, permissions and blocked project) don't change the current url. -- Attachments image slider -- New admin area to edit the tag colors used in your project +- Neew Attachments image slider in preview mode. +- New admin area to edit the tag colors used in your project. - Display the current user (me) at first in assignment lightbox (thanks to [@mikaoelitiana](https://github.com/mikaoelitiana)) - Add a new permissions to allow add comments instead of use the existent modify permission for this purpose. -- Ability to edit comments, view edition history and redesign comments module UI +- Ability to edit comments, view edition history and redesign comments module UI. - Upvote and downvote issues from the issues list. -- Divide dashboard in two columns in large screens - +- Divide dashboard in two columns in large screens, +- Drag & Drop ordering for wiki links. ### Misc - Lots of small and not so small bugfixes. diff --git a/app/coffee/modules/wiki/main.coffee b/app/coffee/modules/wiki/main.coffee index 7d237ea2..e2bf7749 100644 --- a/app/coffee/modules/wiki/main.coffee +++ b/app/coffee/modules/wiki/main.coffee @@ -57,6 +57,7 @@ class WikiDetailController extends mixOf(taiga.Controller, taiga.PageMixin) constructor: (@scope, @rootscope, @repo, @model, @confirm, @rs, @params, @q, @location, @filter, @log, @appMetaService, @navUrls, @analytics, @translate, @errorHandlingService) -> + @scope.$on("wiki:links:move", @.moveLink) @scope.projectSlug = @params.pslug @scope.wikiSlug = @params.slug @scope.wikiTitle = @scope.wikiSlug @@ -156,6 +157,16 @@ class WikiDetailController extends mixOf(taiga.Controller, taiga.PageMixin) @repo.remove(@scope.wiki).then onSuccess, onError + moveLink: (ctx, item, itemIndex) => + values = @scope.wikiLinks + r = values.indexOf(item) + values.splice(r, 1) + values.splice(itemIndex, 0, item) + _.each values, (value, index) -> + value.order = index + + @repo.saveAll(values) + module.controller("WikiDetailController", WikiDetailController) diff --git a/app/coffee/modules/wiki/nav.coffee b/app/coffee/modules/wiki/nav.coffee index 31ecf4ab..ad61c141 100644 --- a/app/coffee/modules/wiki/nav.coffee +++ b/app/coffee/modules/wiki/nav.coffee @@ -38,7 +38,40 @@ module = angular.module("taigaWiki") WikiNavDirective = ($tgrepo, $log, $location, $confirm, $analytics, $loading, $template, $compile, $translate) -> template = $template.get("wiki/wiki-nav.html", true) - link = ($scope, $el, $attrs) -> + + linkDragAndDrop = ($scope, $el, $attrs) -> + oldParentScope = null + newParentScope = null + itemEl = null + tdom = $el.find(".sortable") + + drake = dragula([tdom[0]], { + direction: 'vertical', + copySortSource: false, + copy: false, + mirrorContainer: tdom[0], + moves: (item) -> return $(item).is('li') + }) + + drake.on 'dragend', (item) -> + itemEl = $(item) + item = itemEl.scope().link + itemIndex = itemEl.index() + $scope.$emit("wiki:links:move", item, itemIndex) + + scroll = autoScroll(window, { + margin: 20, + pixels: 30, + scrollWhenOutside: true, + autoScroll: () -> + return this.down && drake.dragging; + }) + + $scope.$on "$destroy", -> + $el.off() + drake.destroy() + + linkWikiLinks = ($scope, $el, $attrs) -> $ctrl = $el.controller() if not $attrs.ngModel? @@ -130,9 +163,15 @@ WikiNavDirective = ($tgrepo, $log, $location, $confirm, $analytics, $loading, $t $el.find(".new input").val('') $el.find(".add-button").show() - bindOnce($scope, $attrs.ngModel, render) + link = ($scope, $el, $attrs) -> + linkWikiLinks($scope, $el, $attrs) + linkDragAndDrop($scope, $el, $attrs) + + $scope.$on "$destroy", -> + $el.off() + return {link:link} module.directive("tgWikiNav", ["$tgRepo", "$log", "$tgLocation", "$tgConfirm", "$tgAnalytics", diff --git a/app/partials/wiki/wiki-nav.jade b/app/partials/wiki/wiki-nav.jade index f1686f03..258afc42 100644 --- a/app/partials/wiki/wiki-nav.jade +++ b/app/partials/wiki/wiki-nav.jade @@ -2,10 +2,10 @@ header h1(translate="WIKI.NAVIGATION.SECTION_NAME") nav - ul - <% _.each(wikiLinks, function(link, index) { %> - li.wiki-link(data-id!="<%- index %>") - a.link-title(title!="<%- link.title %>", href!="<%- link.url %>")<%- link.title %> + ul.sortable + li.wiki-link(ng-repeat="link in wikiLinks", data-id!="{{ $index }}", tg-bind-scope) + tg-svg.dragger(svg-icon="icon-drag") + a.link-title(title!="{{ link.title }}", href!="{{ link.url }}") {{ link.title }} <% if (deleteWikiLinkPermission) { %> a.js-delete-link.remove-wiki-page(title!="{{'WIKI.DELETE_LINK_TITLE' | translate}}") @@ -15,9 +15,8 @@ nav input.hidden( type="text" placeholder="{{'COMMON.FIELDS.NAME' | translate}}" - value!="<%- link.title %>" + value!="{{ link.title }}" ) - <% }) %> li.new.hidden input( diff --git a/app/styles/modules/wiki/wiki-nav.scss b/app/styles/modules/wiki/wiki-nav.scss index ef09edb9..c19ee691 100644 --- a/app/styles/modules/wiki/wiki-nav.scss +++ b/app/styles/modules/wiki/wiki-nav.scss @@ -2,9 +2,10 @@ @include font-type(text); align-items: center; border-bottom: 1px solid $gray-light; + cursor: move; display: flex; justify-content: space-between; - padding: 1rem 0 1rem 1rem; + padding: 1rem 0; text-transform: uppercase; &:hover { .remove-wiki-page { @@ -13,6 +14,20 @@ transition: opacity .2s linear; transition-delay: .2s; } + .dragger { + cursor: move; + fill: $gray-light; + opacity: 1; + transition: opacity .2s linear; + transition-delay: .2s; + } + } + .dragger { + margin-right: .5rem; + opacity: 0; + svg { + @include svg-size(.9rem); + } } .remove-wiki-page { opacity: 0; @@ -24,6 +39,7 @@ } .link-title { cursor: pointer; + flex-grow: 1; } .icon-trash { fill: $gray-light; diff --git a/e2e/helpers/wiki-helper.js b/e2e/helpers/wiki-helper.js index 214bf9b6..cca4a598 100644 --- a/e2e/helpers/wiki-helper.js +++ b/e2e/helpers/wiki-helper.js @@ -17,10 +17,17 @@ helper.links = function() { return newLink; }, - get: function() { + get: function(index) { + if(index !== null && index !== undefined) + return el.$$(".wiki-link a.link-title").get(index) return el.$$(".wiki-link a.link-title"); }, + getNameOf: async function(index) { + let item = await obj.get(index) + return item.getText() + }, + deleteLink: async function(link){ link.click(); await utils.lightbox.confirm.ok(); @@ -32,6 +39,12 @@ helper.links = function() { return obj; }; +helper.dragAndDropLinks = async function(indexFrom, indexTo) { + let selectedLink = helper.links().get(indexFrom); + let newPosition = helper.links().get(indexTo); + return utils.common.drag(selectedLink, newPosition); +}; + helper.editor = function(){ let el = $('.main.wiki'); diff --git a/e2e/suites/wiki.e2e.js b/e2e/suites/wiki.e2e.js index 0429e0be..8c91ef74 100644 --- a/e2e/suites/wiki.e2e.js +++ b/e2e/suites/wiki.e2e.js @@ -20,6 +20,17 @@ describe('wiki', function() { await utils.common.takeScreenshot("wiki", "empty"); }); + it("drag & drop links", async function() { + let nameOld = await wikiHelper.links().getNameOf(0); + + await wikiHelper.dragAndDropLinks(0, 1); + + let nameNew = await wikiHelper.links().getNameOf(4); + + expect(nameNew).to.be.equal(nameOld); + + }); + it('add link', async function(){ let timestamp = new Date().getTime(); currentWiki.slug = "test-link" + timestamp;