feat: upgrade to packages for vue 3
- vue3-notification - vue-advanced-cropper 2 - vuedraggable 4 - vue-shortkey -> moved in repo
This commit is contained in:
parent
7c3c2945f8
commit
e779681905
10 changed files with 405 additions and 142 deletions
11
package.json
11
package.json
|
@ -14,6 +14,7 @@
|
|||
"test:frontend": "cypress run"
|
||||
},
|
||||
"dependencies": {
|
||||
"@kyvg/vue3-notification": "2.3.3",
|
||||
"@vue/compat": "3.2.14",
|
||||
"browserslist": "4.17.1",
|
||||
"bulma": "0.9.3",
|
||||
|
@ -29,14 +30,13 @@
|
|||
"snake-case": "3.0.4",
|
||||
"ufo": "0.7.9",
|
||||
"vue": "3.2.14",
|
||||
"vue-advanced-cropper": "1.8.2",
|
||||
"vue-drag-resize": "1.5.4",
|
||||
"vue-advanced-cropper": "^2.6.3",
|
||||
"vue-drag-resize": "^2.0.3",
|
||||
"vue-easymde": "1.4.0",
|
||||
"vue-flatpickr-component": "9.0.4",
|
||||
"vue-flatpickr-component": "9.0.5",
|
||||
"vue-i18n": "9.2.0-beta.6",
|
||||
"vue-router": "4.0.11",
|
||||
"vue-shortkey": "3.1.7",
|
||||
"vuedraggable": "2.24.3",
|
||||
"vuedraggable": "4.0.1",
|
||||
"vuex": "4.0.2",
|
||||
"workbox-precaching": "6.3.0"
|
||||
},
|
||||
|
@ -70,7 +70,6 @@
|
|||
"typescript": "4.4.3",
|
||||
"vite": "2.6.1",
|
||||
"vite-plugin-pwa": "0.11.2",
|
||||
"vue-notification": "1.3.20",
|
||||
"wait-on": "6.0.0",
|
||||
"workbox-cli": "6.3.0"
|
||||
},
|
||||
|
|
|
@ -91,17 +91,20 @@
|
|||
@end="e => saveListPosition(e, nk)"
|
||||
handle=".handle"
|
||||
:disabled="n.id < 0"
|
||||
:class="{'dragging-disabled': n.id < 0}"
|
||||
tag="transition-group"
|
||||
item-key="id"
|
||||
:component-data="{
|
||||
type: 'transition',
|
||||
tag: 'ul',
|
||||
name: !drag ? 'flip-list' : null,
|
||||
class: [
|
||||
'menu-list can-be-hidden',
|
||||
{ 'dragging-disabled': n.id < 0 }
|
||||
]
|
||||
}"
|
||||
>
|
||||
<transition-group
|
||||
type="transition"
|
||||
:name="!drag ? 'flip-list' : null"
|
||||
tag="ul"
|
||||
class="menu-list can-be-hidden"
|
||||
>
|
||||
<template #item="{element: l}">
|
||||
<li
|
||||
v-for="l in activeLists[nk]"
|
||||
:key="l.id"
|
||||
class="loader-container"
|
||||
:class="{'is-loading': listUpdating[l.id]}"
|
||||
>
|
||||
|
@ -140,7 +143,7 @@
|
|||
<list-settings-dropdown :list="l" v-if="l.id > 0"/>
|
||||
<span class="list-setting-spacer" v-else></span>
|
||||
</li>
|
||||
</transition-group>
|
||||
</template>
|
||||
</draggable>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -86,9 +86,8 @@
|
|||
:w="t.durationDays * dayWidth"
|
||||
:x="t.offsetDays * dayWidth - 6"
|
||||
:y="0"
|
||||
@clicked="setTaskDragged(t)"
|
||||
@dragstop="resizeTask"
|
||||
@resizestop="resizeTask"
|
||||
@dragstop="(e) => resizeTask(t, e)"
|
||||
@resizestop="(e) => resizeTask(t, e)"
|
||||
axis="x"
|
||||
class="task"
|
||||
>
|
||||
|
@ -136,9 +135,8 @@
|
|||
:sticks="['mr', 'ml']"
|
||||
:x="dayOffsetUntilToday * dayWidth - 6"
|
||||
:y="0"
|
||||
@clicked="setTaskDragged(t)"
|
||||
@dragstop="resizeTask"
|
||||
@resizestop="resizeTask"
|
||||
@dragstop="(e) => resizeTask(t, e)"
|
||||
@resizestop="(e) => resizeTask(t, e)"
|
||||
axis="x"
|
||||
class="task nodate"
|
||||
v-tooltip="$t('list.gantt.noDates')"
|
||||
|
@ -233,7 +231,6 @@ export default {
|
|||
theTasks: [], // Pretty much a copy of the prop, since we cant mutate the prop directly
|
||||
tasksWithoutDates: [],
|
||||
taskService: new TaskService(),
|
||||
taskDragged: null, // Saves to currently dragged task to be able to update it
|
||||
fullWidth: 0,
|
||||
now: new Date(),
|
||||
dayOffsetUntilToday: 0,
|
||||
|
@ -361,15 +358,14 @@ export default {
|
|||
t.offsetDays = Math.floor((t.startDate - this.startDate) / 1000 / 60 / 60 / 24)
|
||||
return t
|
||||
},
|
||||
setTaskDragged(t) {
|
||||
this.taskDragged = t
|
||||
},
|
||||
resizeTask(newRect) {
|
||||
resizeTask(taskDragged, newRect) {
|
||||
if (this.isTaskEdit) {
|
||||
return
|
||||
}
|
||||
|
||||
const didntHaveDates = this.taskDragged.startDate === null ? true : false
|
||||
let newTask = { ...taskDragged }
|
||||
|
||||
const didntHaveDates = newTask.startDate === null ? true : false
|
||||
|
||||
let startDate = new Date(this.startDate)
|
||||
startDate.setDate(
|
||||
|
@ -379,32 +375,32 @@ export default {
|
|||
startDate.setUTCMinutes(0)
|
||||
startDate.setUTCSeconds(0)
|
||||
startDate.setUTCMilliseconds(0)
|
||||
this.taskDragged.startDate = startDate
|
||||
newTask.startDate = startDate
|
||||
let endDate = new Date(startDate)
|
||||
endDate.setDate(
|
||||
startDate.getDate() + newRect.width / this.dayWidth,
|
||||
)
|
||||
this.taskDragged.startDate = startDate
|
||||
this.taskDragged.endDate = endDate
|
||||
newTask.startDate = startDate
|
||||
newTask.endDate = endDate
|
||||
|
||||
// We take the task from the overall tasks array because the one in it has bad data after it was updated once.
|
||||
// FIXME: This is a workaround. We should use a better mechanism to get the task or, even better,
|
||||
// prevent it from containing outdated Data in the first place.
|
||||
for (const tt in this.theTasks) {
|
||||
if (this.theTasks[tt].id === this.taskDragged.id) {
|
||||
this.taskDragged = this.theTasks[tt]
|
||||
if (this.theTasks[tt].id === newTask.id) {
|
||||
newTask = this.theTasks[tt]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const ganttData = {
|
||||
endDate: this.taskDragged.endDate,
|
||||
durationDays: this.taskDragged.durationDays,
|
||||
offsetDays: this.taskDragged.offsetDays,
|
||||
endDate: newTask.endDate,
|
||||
durationDays: newTask.durationDays,
|
||||
offsetDays: newTask.offsetDays,
|
||||
}
|
||||
|
||||
this.taskService
|
||||
.update(this.taskDragged)
|
||||
.update(newTask)
|
||||
.then(r => {
|
||||
r.endDate = ganttData.endDate
|
||||
r.durationDays = ganttData.durationDays
|
||||
|
|
36
src/main.ts
36
src/main.ts
|
@ -1,4 +1,11 @@
|
|||
import { createApp } from 'vue'
|
||||
import { createApp, configureCompat } from 'vue'
|
||||
|
||||
configureCompat({
|
||||
COMPONENT_V_MODEL: false,
|
||||
COMPONENT_ASYNC: false,
|
||||
RENDER_FUNCTION: false,
|
||||
WATCH_ARRAY: false, // TODO: check this again; this might lead to some problemes
|
||||
})
|
||||
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
@ -18,13 +25,13 @@ import {VERSION} from './version.json'
|
|||
// Add CSS
|
||||
import './styles/vikunja.scss'
|
||||
// Notifications
|
||||
import Notifications from 'vue-notification'
|
||||
import Notifications from '@kyvg/vue3-notification'
|
||||
|
||||
// PWA
|
||||
import './registerServiceWorker'
|
||||
|
||||
// Shortcuts
|
||||
// @ts-ignore - no types available
|
||||
import vueShortkey from 'vue-shortkey'
|
||||
import shortkey from '@/plugins/shortkey'
|
||||
// Vuex
|
||||
import {store} from './store'
|
||||
// i18n
|
||||
|
@ -45,19 +52,11 @@ if (window.API_URL.substr(window.API_URL.length - 1, window.API_URL.length) ===
|
|||
|
||||
const app = createApp(App)
|
||||
|
||||
Vue.use(Notifications)
|
||||
app.use(Notifications)
|
||||
|
||||
|
||||
Vue.use(vueShortkey, {prevent: ['input', 'textarea', '.input', '[contenteditable]']})
|
||||
|
||||
app.config.globalProperties.$message = {
|
||||
error(e, actions = []) {
|
||||
return error(e, Vue.prototype, actions)
|
||||
},
|
||||
success(s, actions = []) {
|
||||
return success(s, Vue.prototype, actions)
|
||||
},
|
||||
}
|
||||
app.use(shortkey, {prevent: ['input', 'textarea', '.input', '[contenteditable]']})
|
||||
|
||||
// directives
|
||||
import focus from './directives/focus'
|
||||
|
@ -92,6 +91,15 @@ app.mixin({
|
|||
},
|
||||
})
|
||||
|
||||
app.config.errorHandler = (err, vm, info) => {
|
||||
error(err)
|
||||
}
|
||||
|
||||
app.config.globalProperties.$message = {
|
||||
error,
|
||||
success,
|
||||
}
|
||||
|
||||
app.use(router)
|
||||
app.use(store)
|
||||
app.use(i18n)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import {i18n} from '@/i18n'
|
||||
import { notify } from '@kyvg/vue3-notification'
|
||||
|
||||
export const getErrorText = (r) => {
|
||||
|
||||
|
@ -27,8 +28,8 @@ export const getErrorText = (r) => {
|
|||
return [r.message]
|
||||
}
|
||||
|
||||
export function error(e, context, actions = []) {
|
||||
context.$notify({
|
||||
export function error(e, actions = []) {
|
||||
notify({
|
||||
type: 'error',
|
||||
title: i18n.global.t('error.error'),
|
||||
text: getErrorText(e),
|
||||
|
@ -37,8 +38,8 @@ export function error(e, context, actions = []) {
|
|||
console.error(e, actions)
|
||||
}
|
||||
|
||||
export function success(e, context, actions = []) {
|
||||
context.$notify({
|
||||
export function success(e, actions = []) {
|
||||
notify({
|
||||
type: 'success',
|
||||
title: i18n.global.t('error.success'),
|
||||
text: getErrorText(e),
|
||||
|
|
78
src/plugins/shortkey/helpers.js
Normal file
78
src/plugins/shortkey/helpers.js
Normal file
|
@ -0,0 +1,78 @@
|
|||
function capitalizeFirstLetter(string) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1)
|
||||
}
|
||||
|
||||
const MODIFIER_KEYS = ['shift', 'ctrl', 'meta', 'alt']
|
||||
|
||||
const SHORT_CUT_INDEX = [
|
||||
{ key: 'ArrowUp', value: 'arrowup' },
|
||||
{ key: 'ArrowLeft', value: 'arrowlef' },
|
||||
{ key: 'ArrowRight', value: 'arrowright' },
|
||||
{ key: 'ArrowDown', value: 'arrowdown' },
|
||||
{ key: 'AltGraph', value: 'altgraph' },
|
||||
{ key: 'Escape', value: 'esc' },
|
||||
{ key: 'Enter', value: 'enter' },
|
||||
{ key: 'Tab', value: 'tab' },
|
||||
{ key: ' ', value: 'space' },
|
||||
{ key: 'PageUp', value: 'pagup' },
|
||||
{ key: 'PageDown', value: 'pagedow' },
|
||||
{ key: 'Home', value: 'home' },
|
||||
{ key: 'End', value: 'end' },
|
||||
{ key: 'Delete', value: 'del' },
|
||||
{ key: 'Backspace', value: 'bacspace' },
|
||||
{ key: 'Insert', value: 'insert' },
|
||||
{ key: 'NumLock', value: 'numlock' },
|
||||
{ key: 'CapsLock', value: 'capslock' },
|
||||
{ key: 'Pause', value: 'pause' },
|
||||
{ key: 'ContextMenu', value: 'cotextmenu' },
|
||||
{ key: 'ScrollLock', value: 'scrolllock' },
|
||||
{ key: 'BrowserHome', value: 'browserhome' },
|
||||
{ key: 'MediaSelect', value: 'mediaselect' },
|
||||
]
|
||||
|
||||
export function encodeKey(pKey) {
|
||||
const shortKey = {}
|
||||
|
||||
MODIFIER_KEYS.forEach((key) => {
|
||||
shortKey[`${key}Key`] = pKey.includes(key)
|
||||
})
|
||||
|
||||
let indexedKeys = createShortcutIndex(shortKey)
|
||||
const vKey = pKey.filter(
|
||||
(item) => !MODIFIER_KEYS.includes(item),
|
||||
)
|
||||
indexedKeys += vKey.join('')
|
||||
return indexedKeys
|
||||
}
|
||||
|
||||
function createShortcutIndex(pKey) {
|
||||
let k = ''
|
||||
|
||||
MODIFIER_KEYS.forEach((key) => {
|
||||
if (pKey.key === capitalizeFirstLetter(key) || pKey[`${key}Key`]) {
|
||||
k += key
|
||||
}
|
||||
})
|
||||
|
||||
SHORT_CUT_INDEX.forEach(({ key, value }) => {
|
||||
if (pKey.key === key) {
|
||||
k += value
|
||||
}
|
||||
})
|
||||
|
||||
if (
|
||||
(pKey.key && pKey.key !== ' ' && pKey.key.length === 1) ||
|
||||
/F\d{1,2}|\//g.test(pKey.key)
|
||||
) {
|
||||
k += pKey.key.toLowerCase()
|
||||
}
|
||||
|
||||
return k
|
||||
}
|
||||
export { createShortcutIndex as decodeKey }
|
||||
|
||||
export function parseValue(value) {
|
||||
value = typeof value === 'string' ? JSON.parse(value.replace(/'/gi, '"')) : value
|
||||
|
||||
return value instanceof Array ? { '': value } : value
|
||||
}
|
186
src/plugins/shortkey/index.js
Normal file
186
src/plugins/shortkey/index.js
Normal file
|
@ -0,0 +1,186 @@
|
|||
import { parseValue, decodeKey, encodeKey } from './helpers'
|
||||
|
||||
let mapFunctions = {}
|
||||
let objAvoided = []
|
||||
let elementAvoided = []
|
||||
let keyPressed = false
|
||||
|
||||
function dispatchShortkeyEvent(pKey) {
|
||||
const e = new CustomEvent('shortkey', { bubbles: false })
|
||||
|
||||
if (mapFunctions[pKey].key) {
|
||||
e.srcKey = mapFunctions[pKey].key
|
||||
}
|
||||
|
||||
const elm = mapFunctions[pKey].el
|
||||
|
||||
if (!mapFunctions[pKey].propagte) {
|
||||
elm[elm.length - 1].dispatchEvent(e)
|
||||
} else {
|
||||
elm.forEach((elmItem) => elmItem.dispatchEvent(e))
|
||||
}
|
||||
}
|
||||
|
||||
function keyDown(pKey) {
|
||||
if (
|
||||
(!mapFunctions[pKey].once && !mapFunctions[pKey].push) ||
|
||||
(mapFunctions[pKey].push && !keyPressed)
|
||||
) {
|
||||
dispatchShortkeyEvent(pKey)
|
||||
}
|
||||
}
|
||||
|
||||
function fillMappingFunctions(
|
||||
mappingFunctions,
|
||||
{ b, push, once, focus, propagte, el },
|
||||
) {
|
||||
for (let key in b) {
|
||||
const k = encodeKey(b[key])
|
||||
const propagated = mappingFunctions[k] && mappingFunctions[k].propagte
|
||||
const elm =
|
||||
mappingFunctions[k] && mappingFunctions[k].el
|
||||
? mappingFunctions[k].el
|
||||
: []
|
||||
|
||||
elm.push(el)
|
||||
|
||||
mappingFunctions[k] = {
|
||||
push,
|
||||
once,
|
||||
focus,
|
||||
key,
|
||||
propagte: propagated || propagte,
|
||||
el: elm,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function bindValue(value, el, binding, vnode) {
|
||||
const { modifiers } = binding
|
||||
const push = !!modifiers.push
|
||||
const avoid = !!modifiers.avoid
|
||||
const focus = !modifiers.focus
|
||||
const once = !!modifiers.once
|
||||
const propagte = !!modifiers.propagte
|
||||
|
||||
if (avoid) {
|
||||
objAvoided = objAvoided.filter((itm) => !itm === el)
|
||||
objAvoided.push(el)
|
||||
} else {
|
||||
fillMappingFunctions(mapFunctions, {
|
||||
b: value,
|
||||
push,
|
||||
once,
|
||||
focus,
|
||||
propagte,
|
||||
el: vnode.el,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function unbindValue(value, el) {
|
||||
for (let key in value) {
|
||||
const k = encodeKey(value[key])
|
||||
const idxElm = mapFunctions[k].el.indexOf(el)
|
||||
|
||||
if (mapFunctions[k].el.length > 1 && idxElm > -1) {
|
||||
mapFunctions[k].el.splice(idxElm, 1)
|
||||
} else {
|
||||
delete mapFunctions[k]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function availableElement(decodedKey) {
|
||||
const objectIsAvoided = !!objAvoided.find(
|
||||
(r) => r === document.activeElement,
|
||||
)
|
||||
const filterAvoided = !!elementAvoided.find(
|
||||
(selector) =>
|
||||
document.activeElement && document.activeElement.matches(selector),
|
||||
)
|
||||
return !!mapFunctions[decodedKey] && !(objectIsAvoided || filterAvoided)
|
||||
}
|
||||
|
||||
function keyDownListener(pKey) {
|
||||
const decodedKey = decodeKey(pKey)
|
||||
|
||||
// Check avoidable elements
|
||||
if (!availableElement(decodedKey)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!mapFunctions[decodedKey].propagte) {
|
||||
pKey.preventDefault()
|
||||
pKey.stopPropagation()
|
||||
}
|
||||
|
||||
if (mapFunctions[decodedKey].focus) {
|
||||
keyDown(decodedKey)
|
||||
keyPressed = true
|
||||
} else if (!keyPressed) {
|
||||
const elm = mapFunctions[decodedKey].el
|
||||
elm[elm.length - 1].focus()
|
||||
keyPressed = true
|
||||
}
|
||||
}
|
||||
|
||||
function keyUpListener(pKey) {
|
||||
const decodedKey = decodeKey(pKey)
|
||||
|
||||
if (!availableElement(decodedKey)) {
|
||||
keyPressed = false
|
||||
return
|
||||
}
|
||||
|
||||
if (!mapFunctions[decodedKey].propagte) {
|
||||
pKey.preventDefault()
|
||||
pKey.stopPropagation()
|
||||
}
|
||||
|
||||
if (mapFunctions[decodedKey].once || mapFunctions[decodedKey].push) {
|
||||
dispatchShortkeyEvent(decodedKey)
|
||||
}
|
||||
|
||||
keyPressed = false
|
||||
}
|
||||
|
||||
// register key presses that happen before mounting of directive
|
||||
// if (process?.env?.NODE_ENV !== 'test') {
|
||||
// (() => {
|
||||
document.addEventListener('keydown', keyDownListener, true)
|
||||
document.addEventListener('keyup', keyUpListener, true)
|
||||
// })()
|
||||
// }
|
||||
|
||||
function install(app, options) {
|
||||
elementAvoided = [...(options && options.prevent ? options.prevent : [])]
|
||||
|
||||
app.directive('shortkey', {
|
||||
beforeMount(el, binding, vnode) {
|
||||
// Mapping the commands
|
||||
const value = parseValue(binding.value)
|
||||
bindValue(value, el, binding, vnode)
|
||||
},
|
||||
|
||||
updated(el, binding, vnode) {
|
||||
const oldValue = parseValue(binding.oldValue)
|
||||
unbindValue(oldValue, el)
|
||||
|
||||
const newValue = parseValue(binding.value)
|
||||
bindValue(newValue, el, binding, vnode)
|
||||
},
|
||||
|
||||
unmounted(el, binding) {
|
||||
const value = parseValue(binding.value)
|
||||
unbindValue(value, el)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
install,
|
||||
encodeKey,
|
||||
decodeKey,
|
||||
keyDown,
|
||||
}
|
|
@ -27,17 +27,14 @@
|
|||
group="buckets"
|
||||
:disabled="!canWrite"
|
||||
:class="{'dragging-disabled': !canWrite}"
|
||||
tag="transition-group"
|
||||
:item-key="({id}) => `bucket${id}`"
|
||||
:component-data="bucketDraggableComponentData"
|
||||
>
|
||||
<transition-group
|
||||
type="transition"
|
||||
:name="!dragBucket ? 'move-bucket': null"
|
||||
tag="div"
|
||||
class="kanban-bucket-container">
|
||||
<template #item="{element: bucket, index: bucketIndex }">
|
||||
<div
|
||||
:key="`bucket${bucket.id}`"
|
||||
class="bucket"
|
||||
:class="{'is-collapsed': collapsedBuckets[bucket.id]}"
|
||||
v-for="(bucket, k) in buckets"
|
||||
>
|
||||
<div class="bucket-header" @click="() => unCollapseBucket(bucket)">
|
||||
<span
|
||||
|
@ -136,16 +133,15 @@
|
|||
:group="{name: 'tasks', put: shouldAcceptDrop(bucket) && !dragBucket}"
|
||||
:disabled="!canWrite"
|
||||
:class="{'dragging-disabled': !canWrite}"
|
||||
:data-bucket-index="k"
|
||||
:data-bucket-index="bucketIndex"
|
||||
class="dropper"
|
||||
tag="transition-group"
|
||||
:item-key="(task) => `bucket${bucket.id}-task${task.id}`"
|
||||
:component-data="taskDraggableTaskComponentData"
|
||||
>
|
||||
<transition-group type="transition" :name="!drag ? 'move-card': null" tag="div">
|
||||
<kanban-card
|
||||
:key="`bucket${bucket.id}-task${task.id}`"
|
||||
v-for="task in bucket.tasks"
|
||||
:task="task"
|
||||
/>
|
||||
</transition-group>
|
||||
<template #item="{element: task}">
|
||||
<kanban-card :task="task" />
|
||||
</template>
|
||||
</draggable>
|
||||
</div>
|
||||
<div class="bucket-footer" v-if="canWrite">
|
||||
|
@ -181,7 +177,7 @@
|
|||
</x-button>
|
||||
</div>
|
||||
</div>
|
||||
</transition-group>
|
||||
</template>
|
||||
</draggable>
|
||||
|
||||
<div class="bucket new-bucket" v-if="canWrite && !loading && buckets.length > 0">
|
||||
|
@ -251,6 +247,15 @@ import {getCollapsedBucketState, saveCollapsedBucketState} from '@/helpers/saveC
|
|||
import {calculateItemPosition} from '../../../helpers/calculateItemPosition'
|
||||
import KanbanCard from '../../../components/tasks/partials/kanban-card'
|
||||
|
||||
const DRAG_OPTIONS = {
|
||||
// sortable options
|
||||
animation: 150,
|
||||
ghostClass: 'ghost',
|
||||
dragClass: 'task-dragging',
|
||||
delayOnTouchOnly: true,
|
||||
delay: 150,
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'Kanban',
|
||||
components: {
|
||||
|
@ -261,6 +266,8 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
dragOptions: DRAG_OPTIONS,
|
||||
|
||||
drag: false,
|
||||
dragBucket: false,
|
||||
sourceBucket: 0,
|
||||
|
@ -305,6 +312,21 @@ export default {
|
|||
'$route.params.listId': 'loadBuckets',
|
||||
},
|
||||
computed: {
|
||||
bucketDraggableComponentData() {
|
||||
return {
|
||||
type: 'transition',
|
||||
tag: 'div',
|
||||
name: !this.dragBucket ? 'move-bucket': null,
|
||||
class: 'kanban-bucket-container',
|
||||
}
|
||||
},
|
||||
taskDraggableTaskComponentData() {
|
||||
return {
|
||||
type: 'transition',
|
||||
tag: 'div',
|
||||
name: !this.drag ? 'move-card': null,
|
||||
}
|
||||
},
|
||||
buckets: {
|
||||
get() {
|
||||
return this.$store.state.kanban.buckets
|
||||
|
@ -313,17 +335,6 @@ export default {
|
|||
this.$store.commit('kanban/setBuckets', value)
|
||||
},
|
||||
},
|
||||
dragOptions() {
|
||||
const options = {
|
||||
animation: 150,
|
||||
ghostClass: 'ghost',
|
||||
dragClass: 'task-dragging',
|
||||
delay: 150,
|
||||
delayOnTouchOnly: true,
|
||||
}
|
||||
|
||||
return options
|
||||
},
|
||||
...mapState({
|
||||
loadedListId: state => state.kanban.listId,
|
||||
loading: state => state[LOADING] && state[LOADING_MODULE] === 'kanban',
|
||||
|
|
|
@ -89,27 +89,28 @@
|
|||
handle=".handle"
|
||||
:disabled="!canWrite"
|
||||
:class="{'dragging-disabled': !canWrite}"
|
||||
item-key="id"
|
||||
>
|
||||
<single-task-in-list
|
||||
:show-list-color="false"
|
||||
:disabled="!canWrite"
|
||||
:key="t.id"
|
||||
:the-task="t"
|
||||
@taskUpdated="updateTasks"
|
||||
task-detail-route="task.detail"
|
||||
v-for="t in tasks"
|
||||
>
|
||||
<span class="icon handle">
|
||||
<icon icon="grip-lines"/>
|
||||
</span>
|
||||
<div
|
||||
@click="editTask(t.id)"
|
||||
class="icon settings"
|
||||
v-if="!list.isArchived && canWrite"
|
||||
<template #item="{element: t}">
|
||||
<single-task-in-list
|
||||
:show-list-color="false"
|
||||
:disabled="!canWrite"
|
||||
:the-task="t"
|
||||
@taskUpdated="updateTasks"
|
||||
task-detail-route="task.detail"
|
||||
>
|
||||
<icon icon="pencil-alt"/>
|
||||
</div>
|
||||
</single-task-in-list>
|
||||
<span class="icon handle">
|
||||
<icon icon="grip-lines"/>
|
||||
</span>
|
||||
<div
|
||||
@click="editTask(t.id)"
|
||||
class="icon settings"
|
||||
v-if="!list.isArchived && canWrite"
|
||||
>
|
||||
<icon icon="pencil-alt"/>
|
||||
</div>
|
||||
</single-task-in-list>
|
||||
</template>
|
||||
</draggable>
|
||||
</div>
|
||||
<card
|
||||
|
|
62
yarn.lock
62
yarn.lock
|
@ -1293,6 +1293,11 @@
|
|||
"@types/yargs" "^16.0.0"
|
||||
chalk "^4.0.0"
|
||||
|
||||
"@kyvg/vue3-notification@2.3.3":
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/@kyvg/vue3-notification/-/vue3-notification-2.3.3.tgz#7de890cbf000cf063da47bb49caa83bfb0350108"
|
||||
integrity sha512-NxFejSs7FlaUzsPQ4jJyrB+UCFfsBUqICcQH70wHId52Hc9G9i0rBp2Wj3UsNj21H/HitP3r92rfz4ly2ImyMA==
|
||||
|
||||
"@nodelib/fs.scandir@2.1.5":
|
||||
version "2.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
|
||||
|
@ -2807,11 +2812,6 @@ csstype@^2.6.8:
|
|||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.17.tgz#4cf30eb87e1d1a005d8b6510f95292413f6a1c0e"
|
||||
integrity sha512-u1wmTI1jJGzCJzWndZo8mk4wnPTZd1eOIYTYvuEyOQGfmDl3TrabCCfKnOC86FZwW/9djqTl933UF/cS425i9A==
|
||||
|
||||
custom-event-polyfill@^1.0.7:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/custom-event-polyfill/-/custom-event-polyfill-1.0.7.tgz#9bc993ddda937c1a30ccd335614c6c58c4f87aee"
|
||||
integrity sha512-TDDkd5DkaZxZFM8p+1I3yAlvM3rSr1wbrOliG4yJiwinMZN8z/iGL7BTlDkrJcYTmgUSb4ywVCc3ZaUtOtC76w==
|
||||
|
||||
cypress-file-upload@5.0.8:
|
||||
version "5.0.8"
|
||||
resolved "https://registry.yarnpkg.com/cypress-file-upload/-/cypress-file-upload-5.0.8.tgz#d8824cbeaab798e44be8009769f9a6c9daa1b4a1"
|
||||
|
@ -3096,11 +3096,6 @@ electron-to-chromium@^1.3.846:
|
|||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.846.tgz#a55fd59613dbcaed609e965e3e88f42b08c401d3"
|
||||
integrity sha512-2jtSwgyiRzybHRxrc2nKI+39wH3AwQgn+sogQ+q814gv8hIFwrcZbV07Ea9f8AmK0ufPVZUvvAG1uZJ+obV4Jw==
|
||||
|
||||
element-matches@^0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/element-matches/-/element-matches-0.1.2.tgz#7345cb71e965bd2b12f725e524591c102198361a"
|
||||
integrity sha512-yWh1otcs3OKUWDvu/IxyI36ZI3WNaRZlI0uG/DK6fu0pap0VYZ0J5pEGTk1zakme+hT0OKHwhlHc0N5TJhY6yQ==
|
||||
|
||||
emittery@^0.8.1:
|
||||
version "0.8.1"
|
||||
resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860"
|
||||
|
@ -6907,21 +6902,19 @@ vite@2.6.1:
|
|||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
vue-advanced-cropper@1.8.2:
|
||||
version "1.8.2"
|
||||
resolved "https://registry.yarnpkg.com/vue-advanced-cropper/-/vue-advanced-cropper-1.8.2.tgz#c163e54584cd568e9fb2b63bc0fe08a58c060421"
|
||||
integrity sha512-dngjbph+QbqLpk4DWCoCajWcThyy08NorFhMcmrnGFJvdxTFiuJ+XjOtrXhNpDdl7U6E/csrkx6i6E8V+BwmoQ==
|
||||
vue-advanced-cropper@^2.6.3:
|
||||
version "2.6.3"
|
||||
resolved "https://registry.yarnpkg.com/vue-advanced-cropper/-/vue-advanced-cropper-2.6.3.tgz#3ec29584a53954f915a86e0f28069b79d7c29719"
|
||||
integrity sha512-EWZ4dXnP7NpTNsCOlgGjUvBYtPtrWX+wMdW0w3PjwOk4UtQfvKz2IPYtIBzZqvxwUq0OIUq1K5y3VnCvs1DBDA==
|
||||
dependencies:
|
||||
classnames "^2.2.6"
|
||||
debounce "^1.2.0"
|
||||
easy-bem "^1.0.2"
|
||||
|
||||
vue-drag-resize@1.5.4, vue-drag-resize@^1.5.0-rc3:
|
||||
version "1.5.4"
|
||||
resolved "https://registry.yarnpkg.com/vue-drag-resize/-/vue-drag-resize-1.5.4.tgz#f583f40f356e5792aa89109b3d13ba4407c25198"
|
||||
integrity sha512-SR3U7n6TAZEBgP7zw7bR9mjtAlYBjqIoaWTDPz5HXN/nYhOxKSA31aD7p71fmq1jtyt9reAnCx62valNL9ZAcg==
|
||||
dependencies:
|
||||
vue-drag-resize "^1.5.0-rc3"
|
||||
vue-drag-resize@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/vue-drag-resize/-/vue-drag-resize-2.0.3.tgz#1faf0813f43304205bb355fbb3dacc548dd9398a"
|
||||
integrity sha512-5q03tZ/LyvQsg1iHRcqs+wI2OKNbNIWl9+7V8rVL6MxJhZLCIYSSgbAUaDE38LhD6dFd5aJhdgNmES61AxjXuw==
|
||||
|
||||
vue-easymde@1.4.0:
|
||||
version "1.4.0"
|
||||
|
@ -6944,10 +6937,10 @@ vue-eslint-parser@^7.0.0, vue-eslint-parser@^7.10.0:
|
|||
lodash "^4.17.21"
|
||||
semver "^6.3.0"
|
||||
|
||||
vue-flatpickr-component@9.0.4:
|
||||
version "9.0.4"
|
||||
resolved "https://registry.yarnpkg.com/vue-flatpickr-component/-/vue-flatpickr-component-9.0.4.tgz#b064fffb11b0e351213434d2f6b2a291d753438c"
|
||||
integrity sha512-E8XfzLhrPsQBtZluWYEn3m21VHn7PArYnel3QPYL3auBrVMc07WaK6b20e04OK8LUCq9V+OKNZe4MoI0znY/Hw==
|
||||
vue-flatpickr-component@9.0.5:
|
||||
version "9.0.5"
|
||||
resolved "https://registry.yarnpkg.com/vue-flatpickr-component/-/vue-flatpickr-component-9.0.5.tgz#350ec73b7f3d7f80b050c170c088234f9c3bd14c"
|
||||
integrity sha512-fKuz/D4ePQKi+jPo4xjYRgBCLTWrTsCoKbx8nam63x4kTDtSqvFOjNwLvy0QgwC0lC+aFpUWa1dNYTH0hgUcCA==
|
||||
dependencies:
|
||||
flatpickr "^4.6.9"
|
||||
|
||||
|
@ -6961,11 +6954,6 @@ vue-i18n@9.2.0-beta.6:
|
|||
"@intlify/vue-devtools" "9.2.0-beta.6"
|
||||
"@vue/devtools-api" "^6.0.0-beta.13"
|
||||
|
||||
vue-notification@1.3.20:
|
||||
version "1.3.20"
|
||||
resolved "https://registry.yarnpkg.com/vue-notification/-/vue-notification-1.3.20.tgz#d85618127763b46f3e25b8962b857947d5a97cbe"
|
||||
integrity sha512-vPj67Ah72p8xvtyVE8emfadqVWguOScAjt6OJDEUdcW5hW189NsqvfkOrctxHUUO9UYl9cTbIkzAEcPnHu+zBQ==
|
||||
|
||||
vue-router@4.0.11:
|
||||
version "4.0.11"
|
||||
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.0.11.tgz#cd649a0941c635281763a20965b599643ddc68ed"
|
||||
|
@ -6973,14 +6961,6 @@ vue-router@4.0.11:
|
|||
dependencies:
|
||||
"@vue/devtools-api" "^6.0.0-beta.14"
|
||||
|
||||
vue-shortkey@3.1.7:
|
||||
version "3.1.7"
|
||||
resolved "https://registry.yarnpkg.com/vue-shortkey/-/vue-shortkey-3.1.7.tgz#31c09a99ed597331a6a49a45eeff894a78a19eef"
|
||||
integrity sha512-Wm/vPXXS+4Wl/LoYpD+cZc0J0HIoVlY8Ep0JLIqqswmAya3XUBtsqKbhzEf9sXo+3rZ5p1YsUyZfXas8XD7YjQ==
|
||||
dependencies:
|
||||
custom-event-polyfill "^1.0.7"
|
||||
element-matches "^0.1.2"
|
||||
|
||||
vue@3.2.14:
|
||||
version "3.2.14"
|
||||
resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.14.tgz#da577433fb74f328ed58255c728a6a1bf196d337"
|
||||
|
@ -6992,10 +6972,10 @@ vue@3.2.14:
|
|||
"@vue/server-renderer" "3.2.14"
|
||||
"@vue/shared" "3.2.14"
|
||||
|
||||
vuedraggable@2.24.3:
|
||||
version "2.24.3"
|
||||
resolved "https://registry.yarnpkg.com/vuedraggable/-/vuedraggable-2.24.3.tgz#43c93849b746a24ce503e123d5b259c701ba0d19"
|
||||
integrity sha512-6/HDXi92GzB+Hcs9fC6PAAozK1RLt1ewPTLjK0anTYguXLAeySDmcnqE8IC0xa7shvSzRjQXq3/+dsZ7ETGF3g==
|
||||
vuedraggable@4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vuedraggable/-/vuedraggable-4.0.1.tgz#3bcaab0808b7944030b7d9a29f9a63d59dfa12c5"
|
||||
integrity sha512-7qN5jhB1SLfx5P+HCm3JUW+pvgA1bSLgYLSVOeLWBDH9z+zbaEH0OlyZBVMLOxFR+JUHJjwDD0oy7T4r9TEgDA==
|
||||
dependencies:
|
||||
sortablejs "1.10.2"
|
||||
|
||||
|
|
Loading…
Reference in a new issue