mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-23 05:53:09 +03:00
Board: Checklist item dnd support (#1873)
Signed-off-by: Anna No <anna.no@xored.com>
This commit is contained in:
parent
c25c5cfdad
commit
bbcbb7722a
@ -125,7 +125,7 @@ export class TTask extends TAttachedDoc implements Task {
|
||||
todoItems!: number
|
||||
}
|
||||
|
||||
@Model(task.class.TodoItem, core.class.AttachedDoc, DOMAIN_TASK)
|
||||
@Model(task.class.TodoItem, core.class.AttachedDoc, DOMAIN_TASK, [task.interface.DocWithRank])
|
||||
@UX(task.string.Todo)
|
||||
export class TTodoItem extends TAttachedDoc implements TodoItem {
|
||||
@Prop(TypeMarkup(), task.string.TodoName, task.icon.Task)
|
||||
@ -143,6 +143,8 @@ export class TTodoItem extends TAttachedDoc implements TodoItem {
|
||||
|
||||
@Prop(Collection(task.class.TodoItem), task.string.Todos)
|
||||
items!: number
|
||||
|
||||
declare rank: string
|
||||
}
|
||||
|
||||
@Model(task.class.SpaceWithStates, core.class.Space)
|
||||
|
@ -16,7 +16,7 @@
|
||||
import { Ref } from '@anticrm/core'
|
||||
import { createQuery, getClient, UserBox, MessageBox } from '@anticrm/presentation'
|
||||
import type { TodoItem } from '@anticrm/task'
|
||||
import task from '@anticrm/task'
|
||||
import task, { calcRank } from '@anticrm/task'
|
||||
import {
|
||||
Button,
|
||||
CheckBox,
|
||||
@ -45,7 +45,9 @@
|
||||
let hideDoneItems: boolean = false
|
||||
let newItemName = ''
|
||||
let editingItemId: Ref<TodoItem> | undefined = undefined
|
||||
let hovered: Ref<TodoItem> | undefined
|
||||
let hovered: Ref<TodoItem> | undefined = undefined
|
||||
let dragItem: TodoItem | undefined = undefined
|
||||
let dragOverItem: TodoItem | undefined = undefined
|
||||
|
||||
function deleteChecklist () {
|
||||
if (!value) {
|
||||
@ -79,11 +81,13 @@
|
||||
|
||||
async function addItem (event: CustomEvent<string>) {
|
||||
newItemName = ''
|
||||
const prev = checklistItems && checklistItems.length > 0 ? checklistItems[checklistItems.length - 1] : undefined
|
||||
const item = {
|
||||
name: event.detail ?? '',
|
||||
assignee: null,
|
||||
dueTo: null,
|
||||
done: false
|
||||
done: false,
|
||||
rank: calcRank(prev, undefined)
|
||||
}
|
||||
if (item.name.length <= 0) {
|
||||
return
|
||||
@ -121,16 +125,49 @@
|
||||
showPopup(ContextMenu, { object: item }, getEventPopupPositionElement(e))
|
||||
}
|
||||
|
||||
$: checklistItemsQuery.query(task.class.TodoItem, { space: value.space, attachedTo: value._id }, (result) => {
|
||||
checklistItems = result
|
||||
done = checklistItems.reduce((result: number, current: TodoItem) => {
|
||||
return current.done ? result + 1 : result
|
||||
}, 0)
|
||||
})
|
||||
function itemDrop (): void {
|
||||
if (dragItem === undefined || dragOverItem === undefined || dragItem._id === dragOverItem._id) {
|
||||
return
|
||||
}
|
||||
const index = checklistItems.findIndex((item) => item._id === dragOverItem?._id)
|
||||
const dragIndex = checklistItems.findIndex((item) => item._id === dragItem?._id)
|
||||
if (dragIndex > index) {
|
||||
const prev = index - 1 >= 0 ? checklistItems[index - 1] : undefined
|
||||
dragItem.rank = calcRank(prev, dragOverItem)
|
||||
client.update(dragItem, { rank: dragItem.rank })
|
||||
} else {
|
||||
const next = index + 1 < checklistItems.length ? checklistItems[index + 1] : undefined
|
||||
dragItem.rank = calcRank(dragOverItem, next)
|
||||
client.update(dragItem, { rank: dragItem.rank })
|
||||
}
|
||||
}
|
||||
|
||||
$: checklistItemsQuery.query(
|
||||
task.class.TodoItem,
|
||||
{ space: value.space, attachedTo: value._id },
|
||||
(result) => {
|
||||
checklistItems = result
|
||||
done = checklistItems.reduce((result: number, current: TodoItem) => {
|
||||
return current.done ? result + 1 : result
|
||||
}, 0)
|
||||
},
|
||||
{
|
||||
sort: {
|
||||
rank: 1
|
||||
}
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
{#if value !== undefined}
|
||||
<div class="flex-col w-full">
|
||||
<div
|
||||
class="flex-col w-full"
|
||||
on:drop|preventDefault={(ev) => {
|
||||
itemDrop()
|
||||
}}
|
||||
on:dragover|preventDefault
|
||||
on:dragleave
|
||||
>
|
||||
<div class="flex-row-stretch mt-4 mb-2">
|
||||
{#if isEditingName}
|
||||
<div class="flex-grow">
|
||||
@ -178,6 +215,17 @@
|
||||
<div
|
||||
class="flex-row-stretch mb-1 mt-1 pl-1 min-h-7 border-radius-1"
|
||||
class:background-button-noborder-bg-hover={hovered === item._id && editingItemId !== item._id}
|
||||
draggable={true}
|
||||
on:dragstart={() => {
|
||||
dragItem = item
|
||||
}}
|
||||
on:dragend={() => {
|
||||
dragItem = undefined
|
||||
dragOverItem = undefined
|
||||
}}
|
||||
on:dragover={() => {
|
||||
dragOverItem = item
|
||||
}}
|
||||
on:mouseover={() => {
|
||||
hovered = item._id
|
||||
}}
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
import { Card } from '@anticrm/board'
|
||||
import { WithLookup } from '@anticrm/core'
|
||||
import task, { TodoItem } from '@anticrm/task'
|
||||
import task, { calcRank, TodoItem } from '@anticrm/task'
|
||||
import { translate } from '@anticrm/platform'
|
||||
import presentation, { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { Label, Button, Dropdown, EditBox, IconClose } from '@anticrm/ui'
|
||||
@ -18,6 +18,7 @@
|
||||
label: ''
|
||||
}
|
||||
|
||||
let lastCardList: TodoItem | undefined = undefined
|
||||
let name: string | undefined
|
||||
let selectedTemplate: ListItem | undefined = undefined
|
||||
let templateListItems: ListItem[] = [noneListItem]
|
||||
@ -51,7 +52,8 @@
|
||||
name,
|
||||
done: false,
|
||||
dueTo: null,
|
||||
assignee: null
|
||||
assignee: null,
|
||||
rank: calcRank(lastCardList)
|
||||
}
|
||||
)
|
||||
if (items.length > 0) {
|
||||
@ -61,7 +63,8 @@
|
||||
name: item.name,
|
||||
dueTo: item.dueTo,
|
||||
done: item.done,
|
||||
assignee: item.assignee
|
||||
assignee: item.assignee,
|
||||
rank: item.rank
|
||||
})
|
||||
)
|
||||
)
|
||||
@ -75,13 +78,17 @@
|
||||
(result: WithLookup<Card>[]) => {
|
||||
templateListItems = [noneListItem]
|
||||
templatesMap = new Map()
|
||||
lastCardList = undefined
|
||||
|
||||
for (const card of result) {
|
||||
const todoItems = card.$lookup?.todoItems as TodoItem[]
|
||||
if (!todoItems) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (card._id === value?._id && todoItems.length > 0) {
|
||||
todoItems.sort((a, b) => a.rank?.localeCompare(b.rank))
|
||||
lastCardList = todoItems[todoItems.length - 1]
|
||||
}
|
||||
templateListItems.push({
|
||||
_id: card._id,
|
||||
label: card.title,
|
||||
|
@ -14,9 +14,9 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Class, Ref, Space } from '@anticrm/core'
|
||||
import { Card, getClient } from '@anticrm/presentation'
|
||||
import type { Task } from '@anticrm/task'
|
||||
import task from '@anticrm/task'
|
||||
import { Card, createQuery, getClient } from '@anticrm/presentation'
|
||||
import type { Task, TodoItem } from '@anticrm/task'
|
||||
import task, { calcRank } from '@anticrm/task'
|
||||
import { DatePicker, EditBox, Grid } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import plugin from '../../plugin'
|
||||
@ -28,24 +28,43 @@
|
||||
let name: string
|
||||
const done = false
|
||||
let dueTo: number | null = null
|
||||
let latestItem: TodoItem | undefined = undefined
|
||||
|
||||
$: _space = space
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
const todoItemsQuery = createQuery()
|
||||
|
||||
export function canClose (): boolean {
|
||||
return objectId === undefined
|
||||
}
|
||||
|
||||
async function createTodo () {
|
||||
await client.addCollection(task.class.TodoItem, space, objectId, _class, 'todos', {
|
||||
await client.addCollection(task.class.TodoItem, space, objectId, _class, 'todoItems', {
|
||||
name,
|
||||
assignee: null,
|
||||
done,
|
||||
dueTo: dueTo ?? null
|
||||
dueTo: dueTo ?? null,
|
||||
rank: calcRank(latestItem)
|
||||
})
|
||||
}
|
||||
|
||||
$: todoItemsQuery.query(
|
||||
task.class.TodoItem,
|
||||
{ attachedTo: objectId },
|
||||
(result) => {
|
||||
latestItem = undefined
|
||||
if (result && result.length > 0) {
|
||||
latestItem = result[result.length - 1]
|
||||
}
|
||||
},
|
||||
{
|
||||
sort: {
|
||||
rank: 1
|
||||
}
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<Card
|
||||
|
@ -53,8 +53,9 @@
|
||||
{ key: 'done', presenter: plugin.component.TodoStatePresenter, label: plugin.string.TodoState }
|
||||
]}
|
||||
options={{
|
||||
// lookup: {
|
||||
// }
|
||||
sort: {
|
||||
rank: 1
|
||||
}
|
||||
}}
|
||||
query={{ attachedTo: objectId }}
|
||||
/>
|
||||
|
@ -89,7 +89,7 @@ export interface Task extends AttachedDoc, DocWithRank {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface TodoItem extends AttachedDoc {
|
||||
export interface TodoItem extends AttachedDoc, DocWithRank {
|
||||
name: Markup
|
||||
assignee: Ref<Employee> | null
|
||||
done: boolean
|
||||
|
Loading…
Reference in New Issue
Block a user