diff --git a/package.json b/package.json index 427eabcd94..69ce98f96f 100644 --- a/package.json +++ b/package.json @@ -122,6 +122,7 @@ "vue-router": "^3.0.0", "vue-style-loader": "^3.0.0", "vue-template-compiler": "^2.5.2", + "vuedraggable": "^2.15.0", "vuejs-datepicker": "git://github.com/habitrpg/vuejs-datepicker#825a866b6a9c52dd8c588a3e8b900880875ce914", "webpack": "^2.2.1", "webpack-merge": "^4.0.0", diff --git a/website/client/components/tasks/column.vue b/website/client/components/tasks/column.vue index 61e67aee90..c70341a76e 100644 --- a/website/client/components/tasks/column.vue +++ b/website/client/components/tasks/column.vue @@ -34,11 +34,10 @@ .svg-icon(v-html="icons[type]", :class="`icon-${type}`", v-once) h3(v-once) {{$t('theseAreYourTasks', {taskType: $t(types[type].label)})}} .small-text {{$t(`${type}sDesc`)}} - .sortable-tasks( + draggable( ref="tasksList", - v-sortable='activeFilters[type].label !== "scheduled"', - @onsort='sorted', - data-sortableId + @update='sorted', + :options='{disabled: activeFilters[type].label === "scheduled"}', ) task( v-for="task in taskList", @@ -234,7 +233,6 @@ import Task from './task'; import sortBy from 'lodash/sortBy'; import throttle from 'lodash/throttle'; -import sortable from 'client/directives/sortable.directive'; import buyMixin from 'client/mixins/buy'; import { mapState, mapActions } from 'client/libs/store'; import shopItem from '../shops/shopItem'; @@ -251,6 +249,7 @@ import habitIcon from 'assets/svg/habit.svg'; import dailyIcon from 'assets/svg/daily.svg'; import todoIcon from 'assets/svg/todo.svg'; import rewardIcon from 'assets/svg/reward.svg'; +import draggable from 'vuedraggable'; export default { mixins: [buyMixin, notifications], @@ -258,9 +257,7 @@ export default { Task, BuyQuestModal, shopItem, - }, - directives: { - sortable, + draggable, }, props: ['type', 'isUser', 'searchText', 'selectedTags', 'taskListOverride', 'group'], // @TODO: maybe we should store the group on state? data () { diff --git a/website/client/components/tasks/taskModal.vue b/website/client/components/tasks/taskModal.vue index 55ecd16010..2ddee0b6f9 100644 --- a/website/client/components/tasks/taskModal.vue +++ b/website/client/components/tasks/taskModal.vue @@ -26,7 +26,11 @@ .option(v-if="checklistEnabled") label(v-once) {{ $t('checklist') }} br - div(v-sortable='true', @onsort='sortedChecklist') + draggable( + v-model="checklist", + :options="{handle: '.grippy', filter: '.task-dropdown'}", + @update="sortedChecklist" + ) .inline-edit-input-group.checklist-group.input-group(v-for="(item, $index) in checklist") span.grippy input.inline-edit-input.checklist-item.form-control(type="text", v-model="item.text") @@ -466,6 +470,7 @@ } &:hover { + cursor: text; .destroy-icon { display: inline-block; color: $gray-200; @@ -518,11 +523,11 @@ import TagsPopup from './tagsPopup'; import { mapGetters, mapActions, mapState } from 'client/libs/store'; import toggleSwitch from 'client/components/ui/toggleSwitch'; -import sortable from 'client/directives/sortable.directive'; import clone from 'lodash/clone'; import Datepicker from 'vuejs-datepicker'; import moment from 'moment'; import uuid from 'uuid'; +import draggable from 'vuedraggable'; import informationIcon from 'assets/svg/information.svg'; import difficultyTrivialIcon from 'assets/svg/difficulty-trivial.svg'; @@ -539,9 +544,7 @@ export default { TagsPopup, Datepicker, toggleSwitch, - }, - directives: { - sortable, + draggable, }, // purpose is either create or edit, task is the task created or edited props: ['task', 'purpose', 'challengeId', 'groupId'], diff --git a/website/client/directives/sortable.directive.js b/website/client/directives/sortable.directive.js deleted file mode 100644 index 24ebabf0aa..0000000000 --- a/website/client/directives/sortable.directive.js +++ /dev/null @@ -1,46 +0,0 @@ -import Sortable from 'sortablejs'; -import uuid from 'uuid'; - -let emit = (vNode, eventName, data) => { - let handlers = vNode.data && vNode.data.on || - vNode.componentOptions && vNode.componentOptions.listeners; - - if (handlers && handlers[eventName]) { - handlers[eventName].fns(data); - } -}; - -let sortableReferences = {}; - -function createSortable (el, vNode) { - let sortableRef = Sortable.create(el, { - filter: '.task-dropdown', // do not make the tasks dropdown draggable or it won't work - onSort: (evt) => { - emit(vNode, 'onsort', { - oldIndex: evt.oldIndex, - newIndex: evt.newIndex, - }); - }, - }); - - let uniqueId = uuid(); - sortableReferences[uniqueId] = sortableRef; - el.dataset.sortableId = uniqueId; -} - -export default { - bind (el, binding, vNode) { - createSortable(el, vNode); - }, - unbind (el) { - if (sortableReferences[el.dataset.sortableId]) sortableReferences[el.dataset.sortableId].destroy(); - }, - update (el, vNode) { - if (sortableReferences[el.dataset.sortableId] && !vNode.value) { - sortableReferences[el.dataset.sortableId].destroy(); - delete sortableReferences[el.dataset.sortableId]; - return; - } - if (!sortableReferences[el.dataset.sortableId]) createSortable(el, vNode); - }, -};