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