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:
@ -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
|
||||
|
Reference in New Issue
Block a user