platform/packages/kanban/src/components/KanbanRow.svelte
Andrey Sobolev 964978b783
TSK-1405: Fix hover selection (#3109)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
2023-04-28 16:40:48 +07:00

178 lines
5.4 KiB
Svelte

<!--
// 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 { CategoryType, Doc, Ref } from '@hcengineering/core'
import ui, { Button, IconMoreH, mouseAttractor } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte'
import { slide } from 'svelte/transition'
import { CardDragEvent, DocWithRank, Item } from '../types'
import Spinner from '@hcengineering/ui/src/components/Spinner.svelte'
export let stateObjects: Item[]
export let isDragging: boolean
export let dragCard: Item | undefined
export let objects: Item[]
export let groupByDocs: Record<string | number, Item[]>
export let selection: number | undefined = undefined
export let checkedSet: Set<Ref<Doc>>
export let state: CategoryType
export let index: number
export let cardDragOver: (evt: CardDragEvent, object: Item) => void
export let cardDrop: (evt: CardDragEvent, object: Item) => void
export let onDragStart: (object: Item, state: CategoryType) => void
export let showMenu: (evt: MouseEvent, object: Item) => void
const dispatch = createEventDispatcher()
function toAny (object: any): any {
return object
}
const slideD = (node: any, args: any) => (args.isDragging ? slide(node, args) : {})
const stateRefs: HTMLElement[] = []
$: stateRefs.length = stateObjects.length
export function scroll (item: Item): void {
const pos = stateObjects.findIndex((it) => it._id === item._id)
if (pos >= 0) {
stateRefs[pos]?.scrollIntoView({ behavior: 'auto', block: 'nearest' })
}
}
let limit = 50
let limitedObjects: DocWithRank[] = []
let loading = false
let loadingTimeout: any | undefined = undefined
function update (stateObjects: Item[], limit: number | undefined, index: number): void {
clearTimeout(loadingTimeout)
if (limitedObjects.length > 0 || index * 2 === 0) {
limitedObjects = stateObjects.slice(0, limit)
} else {
loading = true
loadingTimeout = setTimeout(() => {
limitedObjects = stateObjects.slice(0, limit)
loading = false
}, index * 2)
}
}
$: update(stateObjects, limit, index)
</script>
{#each limitedObjects as object, i (object._id)}
{@const dragged = isDragging && object._id === dragCard?._id}
<div
bind:this={stateRefs[i]}
transition:slideD|local={{ isDragging }}
class="step-tb75"
on:dragover|preventDefault={(evt) => cardDragOver(evt, object)}
on:drop|preventDefault={(evt) => cardDrop(evt, object)}
>
<div
class="card-container"
class:selection={selection !== undefined ? objects[selection]?._id === object._id : false}
class:checked={checkedSet.has(object._id)}
on:mouseover={mouseAttractor(() => dispatch('obj-focus', object))}
on:mouseenter={mouseAttractor(() => dispatch('obj-focus', object))}
on:focus={() => {}}
on:contextmenu={(evt) => showMenu(evt, object)}
draggable={true}
class:draggable={true}
on:dragstart
on:dragend
class:dragged
on:dragstart={() => onDragStart(object, state)}
on:dragend={() => {
isDragging = false
}}
>
<slot name="card" object={toAny(object)} {dragged} />
</div>
</div>
{/each}
{#if stateObjects.length > limitedObjects.length}
<div class="step-tb75">
{#if loading}
<Spinner />
{:else}
<div class="card-container h-18 flex-row-center flex-between p-4">
<span class="p-1">
{limitedObjects.length}/{stateObjects.length}
</span>
<Button
size={'small'}
icon={IconMoreH}
label={ui.string.ShowMore}
on:click={() => {
limit = limit + 20
}}
/>
</div>
{/if}
</div>
{/if}
<style lang="scss">
.card-container {
background-color: var(--theme-kanban-card-bg-color);
border: 1px solid var(--theme-kanban-card-border);
border-radius: 0.25rem;
// transition: box-shadow .15s ease-in-out;
// &:hover {
// background-color: var(--board-card-bg-hover);
// }
&.checked {
background-color: var(--highlight-select);
box-shadow: inset 0 0 1px 1px var(--highlight-select-border);
&:hover {
background-color: var(--highlight-select-hover);
}
}
&.selection,
&.checked.selection {
box-shadow: inset 0 0 1px 1px var(--primary-button-enabled);
animation: anim-border 1s ease-in-out;
&:hover {
background-color: var(--highlight-hover);
}
}
&.checked.selection:hover {
background-color: var(--highlight-select-hover);
}
&.draggable {
cursor: grab;
}
&.dragged {
background-color: var(--board-bg-color);
}
}
@keyframes anim-border {
from {
box-shadow: inset 0 0 1px 1px var(--primary-edit-border-color);
}
to {
box-shadow: inset 0 0 1px 1px var(--primary-bg-color);
}
}
</style>