Skip to content
代码片段 群组 项目
未验证 提交 db9f7778 编辑于 作者: Coung Ngo's avatar Coung Ngo 提交者: GitLab
浏览文件

Merge branch '461844-work-items-drag-and-drop-checklist-items' into 'master'

Adds Drag and drop checklist items to Work Items

See merge request https://gitlab.com/gitlab-org/gitlab/-/merge_requests/157541



Merged-by: default avatarCoung Ngo <cngo@gitlab.com>
Approved-by: default avatarNick Leonard <nleonard@gitlab.com>
Approved-by: default avatarCoung Ngo <cngo@gitlab.com>
Reviewed-by: default avatarCoung Ngo <cngo@gitlab.com>
Reviewed-by: default avatarNick Leonard <nleonard@gitlab.com>
Reviewed-by: default avatarFernanda Toledo <ftoledo@gitlab.com>
Co-authored-by: default avatarFernanda Toledo <ftoledo@gitlab.com>
No related branches found
No related tags found
无相关合并请求
<script> <script>
import { GlButton, GlTooltipDirective } from '@gitlab/ui'; import { GlButton, GlTooltipDirective } from '@gitlab/ui';
import Vue from 'vue'; import Vue from 'vue';
import Sortable from 'sortablejs';
import { renderGFM } from '~/behaviors/markdown/render_gfm'; import { renderGFM } from '~/behaviors/markdown/render_gfm';
import TaskListItemActions from '~/issues/show/components/task_list_item_actions.vue'; import TaskListItemActions from '~/issues/show/components/task_list_item_actions.vue';
import eventHub from '~/issues/show/event_hub'; import eventHub from '~/issues/show/event_hub';
import { deleteTaskListItem, insertNextToTaskListItemText } from '~/issues/show/utils'; import {
import { isDragging } from '~/sortable/utils'; convertDescriptionWithNewSort,
deleteTaskListItem,
insertNextToTaskListItemText,
} from '~/issues/show/utils';
import { getSortableDefaultOptions, isDragging } from '~/sortable/utils';
import SafeHtml from '~/vue_shared/directives/safe_html'; import SafeHtml from '~/vue_shared/directives/safe_html';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
...@@ -79,6 +84,11 @@ export default { ...@@ -79,6 +84,11 @@ export default {
}, },
immediate: true, immediate: true,
}, },
isUpdating: {
handler(isUpdating) {
this.sortable.option('disabled', isUpdating);
},
},
}, },
mounted() { mounted() {
eventHub.$on('delete-task-list-item', this.deleteTaskListItem); eventHub.$on('delete-task-list-item', this.deleteTaskListItem);
...@@ -97,11 +107,48 @@ export default { ...@@ -97,11 +107,48 @@ export default {
if (this.canEdit) { if (this.canEdit) {
this.initCheckboxes(); this.initCheckboxes();
this.removeAllPointerEventListeners(); this.removeAllPointerEventListeners();
this.renderSortableLists();
this.renderTaskListItemActions(); this.renderTaskListItemActions();
} }
this.truncateLongDescription(); this.truncateLongDescription();
}, },
renderSortableLists() {
// We exclude GLFM table of contents which have a `section-nav` class on the root `ul`.
const lists = this.$el.querySelectorAll?.(
'.description ul:not(.section-nav), .description ul:not(.section-nav) ul, .description ol',
);
lists?.forEach((list) => {
if (list.children.length <= 1) {
return;
}
Array.from(list.children).forEach((listItem) => {
listItem.prepend(this.createDragIconElement());
this.addPointerEventListeners(listItem, '.drag-icon');
});
this.sortable = Sortable.create(
list,
getSortableDefaultOptions({
handle: '.drag-icon',
onUpdate: (event) => {
const description = convertDescriptionWithNewSort(this.descriptionText, event.to);
this.$emit('descriptionUpdated', description);
},
}),
);
});
},
createDragIconElement() {
const container = document.createElement('div');
// eslint-disable-next-line no-unsanitized/property
container.innerHTML = `<svg class="drag-icon s14 gl-icon gl-cursor-grab gl-opacity-0" role="img" aria-hidden="true">
<use href="${gon.sprite_icons}#grip"></use>
</svg>`;
return container.firstChild;
},
initCheckboxes() { initCheckboxes() {
this.checkboxes = this.$el.querySelectorAll('.task-list-item-checkbox'); this.checkboxes = this.$el.querySelectorAll('.task-list-item-checkbox');
...@@ -135,9 +182,11 @@ export default { ...@@ -135,9 +182,11 @@ export default {
const pointeroverListener = (event) => { const pointeroverListener = (event) => {
const element = event.target.closest('li').querySelector(elementSelector); const element = event.target.closest('li').querySelector(elementSelector);
if (!element || isDragging() || this.isUpdating) { if (!element || isDragging() || this.isUpdating) {
element.classList.remove('gl-cursor-grab');
return; return;
} }
element.classList.add(FULL_OPACITY); element.classList.add(FULL_OPACITY);
element.classList.add('gl-cursor-grab');
}; };
const pointeroutListener = (event) => { const pointeroutListener = (event) => {
const element = event.target.closest('li').querySelector(elementSelector); const element = event.target.closest('li').querySelector(elementSelector);
......
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册