Merge pull request #1040 from taigaio/enhancement/wiki-links-drag-and-drop
Enhancement #4339: Add wiki links drag and drop orderingstable
commit
8dcb79a033
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -7,14 +7,14 @@
|
||||||
- Show a confirmation notice when you exit edit mode by pressing ESC in the markdown inputs.
|
- 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.
|
- 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.
|
- Errors (not found, server error, permissions and blocked project) don't change the current url.
|
||||||
- Attachments image slider
|
- Neew Attachments image slider in preview mode.
|
||||||
- New admin area to edit the tag colors used in your project
|
- 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))
|
- 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.
|
- 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.
|
- 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
|
### Misc
|
||||||
- Lots of small and not so small bugfixes.
|
- Lots of small and not so small bugfixes.
|
||||||
|
|
|
@ -57,6 +57,7 @@ class WikiDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
|
||||||
|
|
||||||
constructor: (@scope, @rootscope, @repo, @model, @confirm, @rs, @params, @q, @location,
|
constructor: (@scope, @rootscope, @repo, @model, @confirm, @rs, @params, @q, @location,
|
||||||
@filter, @log, @appMetaService, @navUrls, @analytics, @translate, @errorHandlingService) ->
|
@filter, @log, @appMetaService, @navUrls, @analytics, @translate, @errorHandlingService) ->
|
||||||
|
@scope.$on("wiki:links:move", @.moveLink)
|
||||||
@scope.projectSlug = @params.pslug
|
@scope.projectSlug = @params.pslug
|
||||||
@scope.wikiSlug = @params.slug
|
@scope.wikiSlug = @params.slug
|
||||||
@scope.wikiTitle = @scope.wikiSlug
|
@scope.wikiTitle = @scope.wikiSlug
|
||||||
|
@ -156,6 +157,16 @@ class WikiDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
|
||||||
|
|
||||||
@repo.remove(@scope.wiki).then onSuccess, onError
|
@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)
|
module.controller("WikiDetailController", WikiDetailController)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,40 @@ module = angular.module("taigaWiki")
|
||||||
WikiNavDirective = ($tgrepo, $log, $location, $confirm, $analytics, $loading, $template,
|
WikiNavDirective = ($tgrepo, $log, $location, $confirm, $analytics, $loading, $template,
|
||||||
$compile, $translate) ->
|
$compile, $translate) ->
|
||||||
template = $template.get("wiki/wiki-nav.html", true)
|
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()
|
$ctrl = $el.controller()
|
||||||
|
|
||||||
if not $attrs.ngModel?
|
if not $attrs.ngModel?
|
||||||
|
@ -130,9 +163,15 @@ WikiNavDirective = ($tgrepo, $log, $location, $confirm, $analytics, $loading, $t
|
||||||
$el.find(".new input").val('')
|
$el.find(".new input").val('')
|
||||||
$el.find(".add-button").show()
|
$el.find(".add-button").show()
|
||||||
|
|
||||||
|
|
||||||
bindOnce($scope, $attrs.ngModel, render)
|
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}
|
return {link:link}
|
||||||
|
|
||||||
module.directive("tgWikiNav", ["$tgRepo", "$log", "$tgLocation", "$tgConfirm", "$tgAnalytics",
|
module.directive("tgWikiNav", ["$tgRepo", "$log", "$tgLocation", "$tgConfirm", "$tgAnalytics",
|
||||||
|
|
|
@ -2,10 +2,10 @@ header
|
||||||
h1(translate="WIKI.NAVIGATION.SECTION_NAME")
|
h1(translate="WIKI.NAVIGATION.SECTION_NAME")
|
||||||
|
|
||||||
nav
|
nav
|
||||||
ul
|
ul.sortable
|
||||||
<% _.each(wikiLinks, function(link, index) { %>
|
li.wiki-link(ng-repeat="link in wikiLinks", data-id!="{{ $index }}", tg-bind-scope)
|
||||||
li.wiki-link(data-id!="<%- index %>")
|
tg-svg.dragger(svg-icon="icon-drag")
|
||||||
a.link-title(title!="<%- link.title %>", href!="<%- link.url %>")<%- link.title %>
|
a.link-title(title!="{{ link.title }}", href!="{{ link.url }}") {{ link.title }}
|
||||||
|
|
||||||
<% if (deleteWikiLinkPermission) { %>
|
<% if (deleteWikiLinkPermission) { %>
|
||||||
a.js-delete-link.remove-wiki-page(title!="{{'WIKI.DELETE_LINK_TITLE' | translate}}")
|
a.js-delete-link.remove-wiki-page(title!="{{'WIKI.DELETE_LINK_TITLE' | translate}}")
|
||||||
|
@ -15,9 +15,8 @@ nav
|
||||||
input.hidden(
|
input.hidden(
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="{{'COMMON.FIELDS.NAME' | translate}}"
|
placeholder="{{'COMMON.FIELDS.NAME' | translate}}"
|
||||||
value!="<%- link.title %>"
|
value!="{{ link.title }}"
|
||||||
)
|
)
|
||||||
<% }) %>
|
|
||||||
|
|
||||||
li.new.hidden
|
li.new.hidden
|
||||||
input(
|
input(
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
@include font-type(text);
|
@include font-type(text);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-bottom: 1px solid $gray-light;
|
border-bottom: 1px solid $gray-light;
|
||||||
|
cursor: move;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 1rem 0 1rem 1rem;
|
padding: 1rem 0;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
&:hover {
|
&:hover {
|
||||||
.remove-wiki-page {
|
.remove-wiki-page {
|
||||||
|
@ -13,6 +14,20 @@
|
||||||
transition: opacity .2s linear;
|
transition: opacity .2s linear;
|
||||||
transition-delay: .2s;
|
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 {
|
.remove-wiki-page {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
@ -24,6 +39,7 @@
|
||||||
}
|
}
|
||||||
.link-title {
|
.link-title {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
.icon-trash {
|
.icon-trash {
|
||||||
fill: $gray-light;
|
fill: $gray-light;
|
||||||
|
|
|
@ -17,10 +17,17 @@ helper.links = function() {
|
||||||
return newLink;
|
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");
|
return el.$$(".wiki-link a.link-title");
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getNameOf: async function(index) {
|
||||||
|
let item = await obj.get(index)
|
||||||
|
return item.getText()
|
||||||
|
},
|
||||||
|
|
||||||
deleteLink: async function(link){
|
deleteLink: async function(link){
|
||||||
link.click();
|
link.click();
|
||||||
await utils.lightbox.confirm.ok();
|
await utils.lightbox.confirm.ok();
|
||||||
|
@ -32,6 +39,12 @@ helper.links = function() {
|
||||||
return obj;
|
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(){
|
helper.editor = function(){
|
||||||
let el = $('.main.wiki');
|
let el = $('.main.wiki');
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,17 @@ describe('wiki', function() {
|
||||||
await utils.common.takeScreenshot("wiki", "empty");
|
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(){
|
it('add link', async function(){
|
||||||
let timestamp = new Date().getTime();
|
let timestamp = new Date().getTime();
|
||||||
currentWiki.slug = "test-link" + timestamp;
|
currentWiki.slug = "test-link" + timestamp;
|
||||||
|
|
Loading…
Reference in New Issue