mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-14 04:08:19 +00:00
Make Kanban a bit more smooth (#1283)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
3a19d46590
commit
78d984b684
@ -15,18 +15,18 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { AttachmentsPresenter } from '@anticrm/attachment-resources'
|
||||
import { CommentsPresenter } from '@anticrm/chunter-resources'
|
||||
import { ContactPresenter } from '@anticrm/contact-resources'
|
||||
import type { WithLookup } from '@anticrm/core'
|
||||
import type { Card } from '@anticrm/board'
|
||||
import { CommentsPresenter } from '@anticrm/chunter-resources'
|
||||
import type { WithLookup } from '@anticrm/core'
|
||||
import notification from '@anticrm/notification'
|
||||
import { ActionIcon, Component, IconMoreH, showPanel, showPopup } from '@anticrm/ui'
|
||||
import view from '@anticrm/view'
|
||||
import { ContextMenu } from '@anticrm/view-resources'
|
||||
import board from '../plugin'
|
||||
import notification from '@anticrm/notification'
|
||||
|
||||
export let object: WithLookup<Card>
|
||||
export let draggable: boolean
|
||||
export let dragged: boolean
|
||||
|
||||
function showMenu (ev?: Event): void {
|
||||
showPopup(ContextMenu, { object }, (ev as MouseEvent).target as HTMLElement)
|
||||
@ -37,7 +37,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="card-container" {draggable} class:draggable on:dragstart on:dragend>
|
||||
<div class="card-container" {draggable} class:draggable on:dragstart on:dragend class:dragged={dragged}>
|
||||
<div class="flex-between mb-4">
|
||||
<div class="flex-col">
|
||||
<div class="fs-title cursor-pointer" on:click={showLead}>{object.title}</div>
|
||||
@ -72,14 +72,21 @@
|
||||
.card-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1rem 1.25rem;
|
||||
background-color: rgba(222, 222, 240, 0.06);
|
||||
border-radius: 0.75rem;
|
||||
padding: .5rem 1rem;
|
||||
background-color: var(--board-card-bg-color);
|
||||
border: 1px solid var(--board-card-bg-color);
|
||||
border-radius: .25rem;
|
||||
user-select: none;
|
||||
backdrop-filter: blur(10px);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--board-card-bg-hover);
|
||||
}
|
||||
&.draggable {
|
||||
cursor: grab;
|
||||
}
|
||||
&.dragged {
|
||||
padding: 1rem;
|
||||
background-color: var(--board-bg-color);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
export let object: WithLookup<Lead>
|
||||
export let draggable: boolean
|
||||
export let dragged: boolean
|
||||
|
||||
function showMenu (ev?: Event): void {
|
||||
showPopup(ContextMenu, { object }, (ev as MouseEvent).target as HTMLElement)
|
||||
@ -37,7 +38,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="card-container" {draggable} class:draggable on:dragstart on:dragend>
|
||||
<div class="card-container" {draggable} class:draggable on:dragstart on:dragend class:dragged={dragged}>
|
||||
<div class="flex-between mb-4">
|
||||
<div class="flex-col">
|
||||
<div class="fs-title cursor-pointer" on:click={showLead}>{object.title}</div>
|
||||
@ -81,9 +82,15 @@
|
||||
border-radius: .25rem;
|
||||
user-select: none;
|
||||
|
||||
&:hover { background-color: var(--board-card-bg-hover); }
|
||||
&:hover {
|
||||
background-color: var(--board-card-bg-hover);
|
||||
}
|
||||
&.draggable {
|
||||
cursor: grab;
|
||||
}
|
||||
&.dragged {
|
||||
padding: 1rem;
|
||||
background-color: var(--board-bg-color);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
export let object: WithLookup<Applicant>
|
||||
export let draggable: boolean
|
||||
export let dragged: boolean
|
||||
|
||||
function showCandidate () {
|
||||
showPanel(view.component.EditDoc, object.attachedTo, object.attachedToClass, 'full')
|
||||
@ -36,7 +37,7 @@
|
||||
$: doneTasks = todoItems.filter((it) => it.done)
|
||||
</script>
|
||||
|
||||
<div class="card-container" {draggable} class:draggable on:dragstart on:dragend>
|
||||
<div class="card-container" {draggable} class:draggable on:dragstart on:dragend class:dragged={dragged}>
|
||||
<div class="flex-between mb-3">
|
||||
<div class="flex-row-center">
|
||||
<Avatar avatar={object.$lookup?.attachedTo?.avatar} size={'medium'} />
|
||||
@ -48,10 +49,12 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="tool mr-1 flex-row-center">
|
||||
{#if !dragged}
|
||||
<div class="mr-2">
|
||||
<Component is={notification.component.NotificationPresenter} props={{ value: object }} />
|
||||
</div>
|
||||
<ActionIcon label={undefined} icon={IconMoreH} size={'small'} />
|
||||
<ActionIcon label={undefined} icon={IconMoreH} size={'small'} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-between">
|
||||
@ -87,10 +90,17 @@
|
||||
border-radius: .25rem;
|
||||
user-select: none;
|
||||
|
||||
&:hover { background-color: var(--board-card-bg-hover); }
|
||||
&:hover {
|
||||
background-color: var(--board-card-bg-hover);
|
||||
}
|
||||
&.draggable {
|
||||
cursor: grab;
|
||||
}
|
||||
&.dragged {
|
||||
padding: 1rem;
|
||||
background-color: var(--board-bg-color);
|
||||
border: 1px solid var(--board-bg-color);
|
||||
}
|
||||
}
|
||||
.tool {
|
||||
align-self: start;
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
export let object: WithLookup<Issue>
|
||||
export let draggable: boolean
|
||||
export let dragged: boolean
|
||||
|
||||
const showMenu = (ev?: Event): void => {
|
||||
showPopup(ContextMenu, { object }, ev ? (ev.target as HTMLElement) : null)
|
||||
@ -35,7 +36,7 @@
|
||||
$: doneTasks = todoItems.filter((it) => it.done)
|
||||
</script>
|
||||
|
||||
<div class="card-container" {draggable} class:draggable on:dragstart on:dragend>
|
||||
<div class="card-container" {draggable} class:draggable on:dragstart on:dragend class:dragged={dragged}>
|
||||
<div class="flex-between mb-2">
|
||||
<div class="flex">
|
||||
<TaskPresenter value={object} />
|
||||
@ -80,13 +81,21 @@
|
||||
.card-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1rem 1.25rem;
|
||||
background-color: rgba(222, 222, 240, 0.06);
|
||||
border-radius: 0.75rem;
|
||||
padding: .5rem 1rem;
|
||||
background-color: var(--board-card-bg-color);
|
||||
border: 1px solid var(--board-card-bg-color);
|
||||
border-radius: .25rem;
|
||||
user-select: none;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--board-card-bg-hover);
|
||||
}
|
||||
&.draggable {
|
||||
cursor: grab;
|
||||
}
|
||||
&.dragged {
|
||||
padding: 1rem;
|
||||
background-color: var(--board-bg-color);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
233
plugins/task-resources/src/components/kanban/Kanban.svelte
Normal file
233
plugins/task-resources/src/components/kanban/Kanban.svelte
Normal file
@ -0,0 +1,233 @@
|
||||
<!--
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import core, {
|
||||
AttachedDoc,
|
||||
Class,
|
||||
Doc,
|
||||
DocumentQuery,
|
||||
DocumentUpdate,
|
||||
FindOptions, Ref, Space
|
||||
} from '@anticrm/core'
|
||||
import { getResource } from '@anticrm/platform'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import task, { calcRank, DocWithRank } from '@anticrm/task'
|
||||
import { AnySvelteComponent, getPlatformColor, Loading, ScrollBox } from '@anticrm/ui'
|
||||
import { slide } from 'svelte/transition'
|
||||
import KanbanPanel from './KanbanPanel.svelte'
|
||||
|
||||
type StateType = any
|
||||
type Item = DocWithRank & { state: StateType; doneState: StateType | null }
|
||||
|
||||
export let _class: Ref<Class<Item>>
|
||||
export let space: Ref<Space>
|
||||
// export let open: AnyComponent
|
||||
export let search: string
|
||||
export let options: FindOptions<Item> | undefined
|
||||
// export let config: string[]
|
||||
|
||||
type TypeState = { _id: StateType; title: string; color: number }
|
||||
|
||||
export let states: TypeState[] = []
|
||||
|
||||
export let stateQuery: DocumentQuery<Item> = {
|
||||
/// doneState: null
|
||||
}
|
||||
|
||||
let objects: Item[] = []
|
||||
|
||||
const objsQ = createQuery()
|
||||
$: objsQ.query(
|
||||
_class,
|
||||
{
|
||||
space,
|
||||
...stateQuery,
|
||||
...(search !== '' ? { $search: search } : {})
|
||||
},
|
||||
(result) => {
|
||||
objects = result
|
||||
},
|
||||
{
|
||||
...options
|
||||
}
|
||||
)
|
||||
|
||||
function getStateObjects (objects: Item[], state: TypeState, dragItem?: Item): {it:Item, prev?:Item, next?: Item}[] {
|
||||
const stateCards = objects.filter(it => it.state === state._id)
|
||||
stateCards.sort((a, b) => a.rank.localeCompare(b.rank))
|
||||
return stateCards.map((it, idx, arr) => ({ it, prev: arr[idx - 1], next: arr[idx + 1] }))
|
||||
}
|
||||
|
||||
async function updateItem (item: Item, update: DocumentUpdate<Item>) {
|
||||
if (client.getHierarchy().isDerived(_class, core.class.AttachedDoc)) {
|
||||
const adoc: AttachedDoc = item as Doc as AttachedDoc
|
||||
await client.updateCollection(
|
||||
_class,
|
||||
space,
|
||||
adoc._id as Ref<Doc> as Ref<AttachedDoc>,
|
||||
adoc.attachedTo,
|
||||
adoc.attachedToClass,
|
||||
adoc.collection,
|
||||
update
|
||||
)
|
||||
} else {
|
||||
await client.updateDoc(item._class, item.space, item._id, update)
|
||||
}
|
||||
}
|
||||
|
||||
async function move (state: StateType) {
|
||||
if (dragCard === undefined) {
|
||||
return
|
||||
}
|
||||
let updates: DocumentUpdate<Item> = {}
|
||||
|
||||
if (dragCardInitialState !== state) {
|
||||
updates = {
|
||||
...updates,
|
||||
state
|
||||
}
|
||||
}
|
||||
|
||||
if (dragCardInitialRank !== dragCard.rank) {
|
||||
updates = {
|
||||
...updates,
|
||||
rank: dragCard.rank
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(updates).length > 0) {
|
||||
await updateItem(dragCard, updates)
|
||||
}
|
||||
dragCard = undefined
|
||||
}
|
||||
|
||||
const client = getClient()
|
||||
|
||||
let dragCard: Item | undefined
|
||||
let dragCardInitialRank: string
|
||||
let dragCardInitialState: StateType
|
||||
|
||||
async function cardPresenter (_class: Ref<Class<Doc>>): Promise<AnySvelteComponent> {
|
||||
const clazz = client.getHierarchy().getClass(_class)
|
||||
const presenterMixin = client.getHierarchy().as(clazz, task.mixin.KanbanCard)
|
||||
return await getResource(presenterMixin.card)
|
||||
}
|
||||
|
||||
let isDragging = false
|
||||
|
||||
async function updateDone (query: DocumentUpdate<Item>): Promise<void> {
|
||||
isDragging = false
|
||||
if (dragCard === undefined) {
|
||||
return
|
||||
}
|
||||
await updateItem(dragCard, query)
|
||||
}
|
||||
function doCalcRank (object: {prev?: Item, it: Item, next?: Item}, event: DragEvent & { currentTarget: EventTarget & HTMLDivElement}): string {
|
||||
const rect = event.currentTarget.getBoundingClientRect()
|
||||
|
||||
if (event.clientY < rect.top + rect.height * 2 / 3) {
|
||||
return calcRank(object.prev, object.it)
|
||||
} else {
|
||||
return calcRank(object.it, object.next)
|
||||
}
|
||||
}
|
||||
const slideD = (node: any, args:any) => args.isDragging ? slide(node, args) : {}
|
||||
</script>
|
||||
|
||||
{#await cardPresenter(_class)}
|
||||
<Loading />
|
||||
{:then presenter}
|
||||
<div class="flex-col kanban-container">
|
||||
<div class="scrollable">
|
||||
<ScrollBox>
|
||||
<div class="kanban-content">
|
||||
{#each states as state}
|
||||
<KanbanPanel
|
||||
label={state.title}
|
||||
color={getPlatformColor(state.color)}
|
||||
on:dragover={(event) => {
|
||||
event.preventDefault()
|
||||
if (dragCard !== undefined && dragCard.state !== state._id) {
|
||||
dragCard.state = state._id
|
||||
const objs = getStateObjects(objects, state)
|
||||
dragCard.rank = calcRank(objs[objs.length - 1]?.it, undefined)
|
||||
}
|
||||
}}
|
||||
on:drop={() => {
|
||||
move(state._id)
|
||||
isDragging = false
|
||||
}}
|
||||
>
|
||||
<!-- <KanbanCardEmpty label={'Create new application'} /> -->
|
||||
{#each getStateObjects(objects, state, dragCard) as object}
|
||||
<div transition:slideD={{ isDragging }}
|
||||
class="step-tb75"
|
||||
on:dragover|preventDefault={(evt) => {
|
||||
if (dragCard !== undefined) {
|
||||
dragCard.rank = doCalcRank(object, evt)
|
||||
}
|
||||
}}
|
||||
on:drop|preventDefault={(evt) => {
|
||||
if (dragCard !== undefined) {
|
||||
dragCard.rank = doCalcRank(object, evt)
|
||||
}
|
||||
isDragging = false
|
||||
}}
|
||||
>
|
||||
<svelte:component
|
||||
this={presenter}
|
||||
object={object.it}
|
||||
dragged={isDragging && object.it._id === dragCard?._id}
|
||||
draggable={true}
|
||||
on:dragstart={() => {
|
||||
dragCardInitialState = state._id
|
||||
dragCardInitialRank = object.it.rank
|
||||
dragCard = object.it
|
||||
isDragging = true
|
||||
}}
|
||||
on:dragend={() => {
|
||||
isDragging = false
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
</KanbanPanel>
|
||||
{/each}
|
||||
<!-- <KanbanPanelEmpty label={'Add new column'} /> -->
|
||||
</div>
|
||||
</ScrollBox>
|
||||
</div>
|
||||
{#if isDragging}
|
||||
<slot name="doneBar" onDone={updateDone} />
|
||||
{/if}
|
||||
</div>
|
||||
{/await}
|
||||
|
||||
<style lang="scss">
|
||||
.kanban-container {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
background: var(--board-bg-color);
|
||||
}
|
||||
.kanban-content {
|
||||
display: flex;
|
||||
margin: 1.5rem 2rem;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.scrollable {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
@ -14,15 +14,10 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { AttachedDoc, Class, Doc, DocumentUpdate, FindOptions, Ref, SortingOrder } from '@anticrm/core'
|
||||
import core from '@anticrm/core'
|
||||
import { getResource } from '@anticrm/platform'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import type { Kanban, SpaceWithStates, State } from '@anticrm/task'
|
||||
import task, { DoneState, LostState, WonState, DocWithRank, calcRank } from '@anticrm/task'
|
||||
import { AnySvelteComponent, getPlatformColor, Grid } from '@anticrm/ui'
|
||||
import { Loading, ScrollBox } from '@anticrm/ui'
|
||||
import KanbanPanel from './KanbanPanel.svelte'
|
||||
import { Ref } from '@anticrm/core'
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
import type { Kanban } from '@anticrm/task'
|
||||
import task, { DoneState, LostState, WonState } from '@anticrm/task'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let kanban: Kanban
|
||||
|
@ -15,15 +15,12 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import core,{ AttachedDoc,Class,Doc,DocumentUpdate,FindOptions,Ref,SortingOrder } from '@anticrm/core'
|
||||
import { getResource } from '@anticrm/platform'
|
||||
import { createQuery,getClient } from '@anticrm/presentation'
|
||||
import type { Kanban,SpaceWithStates,State } from '@anticrm/task'
|
||||
import task,{ calcRank,DocWithRank,DoneState } from '@anticrm/task'
|
||||
import { AnySvelteComponent,getPlatformColor,Loading,ScrollBox } from '@anticrm/ui'
|
||||
import { Class, FindOptions, Ref, SortingOrder } from '@anticrm/core'
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
import type { Kanban, SpaceWithStates, State } from '@anticrm/task'
|
||||
import task, { DocWithRank, DoneState } from '@anticrm/task'
|
||||
import KanbanUI from './Kanban.svelte'
|
||||
import KanbanDragDone from './KanbanDragDone.svelte'
|
||||
import KanbanPanel from './KanbanPanel.svelte'
|
||||
// import KanbanPanelEmpty from './KanbanPanelEmpty.svelte'
|
||||
|
||||
type Item = DocWithRank & { state: Ref<State>, doneState: Ref<DoneState> | null }
|
||||
|
||||
@ -37,8 +34,6 @@
|
||||
let kanban: Kanban
|
||||
let states: State[] = []
|
||||
|
||||
let objects: Item[] = []
|
||||
|
||||
const kanbanQuery = createQuery()
|
||||
$: kanbanQuery.query(task.class.Kanban, { attachedTo: space }, result => { kanban = result[0] })
|
||||
|
||||
@ -50,186 +45,14 @@
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const objsQ = createQuery()
|
||||
$: objsQ.query(
|
||||
_class,
|
||||
{
|
||||
space,
|
||||
doneState: null,
|
||||
...search !== '' ? {$search: search} : {}
|
||||
},
|
||||
result => { objects = result },
|
||||
{
|
||||
...options,
|
||||
sort: {
|
||||
rank: SortingOrder.Ascending
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const filteredObjsQ = createQuery()
|
||||
|
||||
// Undefined means no filtering
|
||||
let target: Set<Ref<Doc>> | undefined
|
||||
$: if (search === '') {
|
||||
filteredObjsQ.unsubscribe()
|
||||
target = undefined
|
||||
} else {
|
||||
filteredObjsQ.query(
|
||||
_class,
|
||||
{
|
||||
space,
|
||||
doneState: null,
|
||||
...search !== '' ? {$search: search} : {}
|
||||
},
|
||||
result => { target = new Set(result.map(x => x._id)) },
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
function dragover (object: Item) {
|
||||
if (dragCard !== object) {
|
||||
const dragover = objects.indexOf(object)
|
||||
objects = objects.filter(x => x !== dragCard)
|
||||
objects = [...objects.slice(0, dragover), dragCard, ...objects.slice(dragover)]
|
||||
}
|
||||
}
|
||||
|
||||
async function updateItem (item: Item, update: DocumentUpdate<Item>) {
|
||||
if (client.getHierarchy().isDerived(_class, core.class.AttachedDoc)) {
|
||||
const adoc: AttachedDoc = item as Doc as AttachedDoc
|
||||
await client.updateCollection(
|
||||
_class,
|
||||
space,
|
||||
adoc._id as Ref<Doc> as Ref<AttachedDoc>,
|
||||
adoc.attachedTo,
|
||||
adoc.attachedToClass,
|
||||
adoc.collection,
|
||||
update
|
||||
)
|
||||
} else {
|
||||
await client.updateDoc(item._class, item.space, item._id, update)
|
||||
}
|
||||
}
|
||||
|
||||
async function move (state: Ref<State>) {
|
||||
let updates: DocumentUpdate<Item> = {}
|
||||
|
||||
if (dragCardInitialState !== state) {
|
||||
updates = {
|
||||
...updates,
|
||||
state
|
||||
}
|
||||
}
|
||||
|
||||
if (dragCardInitialPosition !== dragCardEndPosition) {
|
||||
const [prev, next] = [objects[dragCardEndPosition - 1], objects[dragCardEndPosition + 1]]
|
||||
|
||||
updates = {
|
||||
...updates,
|
||||
rank: calcRank(prev, next)
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(updates).length > 0) {
|
||||
await updateItem(dragCard, updates)
|
||||
}
|
||||
}
|
||||
|
||||
const client = getClient()
|
||||
|
||||
let dragCard: Item
|
||||
let dragCardInitialPosition: number
|
||||
let dragCardEndPosition: number
|
||||
let dragCardInitialState: Ref<State>
|
||||
|
||||
async function cardPresenter (_class: Ref<Class<Doc>>): Promise<AnySvelteComponent> {
|
||||
const clazz = client.getHierarchy().getClass(_class)
|
||||
const presenterMixin = client.getHierarchy().as(clazz, task.mixin.KanbanCard)
|
||||
return await getResource(presenterMixin.card)
|
||||
}
|
||||
|
||||
async function onDone (state: DoneState): Promise<void> {
|
||||
isDragging = false
|
||||
await updateItem(dragCard, { doneState: state._id })
|
||||
}
|
||||
|
||||
let isDragging = false
|
||||
</script>
|
||||
|
||||
{#await cardPresenter(_class)}
|
||||
<Loading/>
|
||||
{:then presenter}
|
||||
<div class="flex-col kanban-container">
|
||||
<div class="scrollable">
|
||||
<ScrollBox>
|
||||
<div class="kanban-content">
|
||||
{#each states as state (state)}
|
||||
<KanbanPanel label={state.title} color={getPlatformColor(state.color)}
|
||||
on:dragover={(event) => {
|
||||
event.preventDefault()
|
||||
if (dragCard.state !== state._id) {
|
||||
dragCard.state = state._id
|
||||
}
|
||||
}}
|
||||
on:drop={() => {
|
||||
move(state._id)
|
||||
isDragging = false
|
||||
}}>
|
||||
<!-- <KanbanCardEmpty label={'Create new application'} /> -->
|
||||
{#each objects as object, j (object)}
|
||||
{#if object.state === state._id && (target === undefined || target.has(object._id))}
|
||||
<div
|
||||
class="step-tb75"
|
||||
on:dragover|preventDefault={() => {
|
||||
dragover(object)
|
||||
dragCardEndPosition = j
|
||||
}}
|
||||
on:drop|preventDefault={() => {
|
||||
dragCardEndPosition = j
|
||||
isDragging = false
|
||||
}}
|
||||
>
|
||||
<svelte:component this={presenter} {object} draggable={true}
|
||||
on:dragstart={() => {
|
||||
dragCardInitialState = state._id
|
||||
dragCardInitialPosition = j
|
||||
dragCardEndPosition = j
|
||||
dragCard = object
|
||||
isDragging = true
|
||||
}}
|
||||
on:dragend={() => {
|
||||
isDragging = false
|
||||
}}/>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</KanbanPanel>
|
||||
{/each}
|
||||
<!-- <KanbanPanelEmpty label={'Add new column'} /> -->
|
||||
</div>
|
||||
</ScrollBox>
|
||||
</div>
|
||||
{#if isDragging}
|
||||
<KanbanDragDone {kanban} on:done={(e) => { onDone(e.detail) }} />
|
||||
{/if}
|
||||
</div>
|
||||
{/await}
|
||||
|
||||
<style lang="scss">
|
||||
.kanban-container {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
background: var(--board-bg-color);
|
||||
}
|
||||
.kanban-content {
|
||||
display: flex;
|
||||
margin: 1.5rem 2rem;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.scrollable {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
<KanbanUI {_class} {space} {search} {options} stateQuery={{ doneState: null }} states={states}>
|
||||
// eslint-disable-next-line no-undef
|
||||
<svelte:fragment slot='doneBar' let:onDone={onDone}>
|
||||
<KanbanDragDone {kanban} on:done={(e) => {
|
||||
// eslint-disable-next-line no-undef
|
||||
onDone({ doneState: e.detail._id })
|
||||
}} />
|
||||
</svelte:fragment>
|
||||
</KanbanUI>
|
||||
|
Loading…
Reference in New Issue
Block a user