platform/plugins/calendar-resources/src/components/EventElement.svelte
Denis Bykhov 30f255fa7c
UBER-731 (#3585)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
2023-08-14 19:35:07 +07:00

197 lines
5.7 KiB
Svelte

<!--
// Copyright © 2023 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 calendar, { CalendarEventPresenter, Event } from '@hcengineering/calendar'
import { Doc, DocumentUpdate } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import {
Component,
MILLISECONDS_IN_MINUTE,
deviceOptionsStore,
getEventPositionElement,
showPopup,
tooltip
} from '@hcengineering/ui'
import view, { ObjectEditor } from '@hcengineering/view'
import { Menu } from '@hcengineering/view-resources'
import { createEventDispatcher } from 'svelte'
import { calendarStore, isReadOnly, isVisible } from '../utils'
import EventPresenter from './EventPresenter.svelte'
export let event: Event
export let hourHeight: number
export let size: { width: number; height: number }
$: oneRow = size.height < 42 || event.allDay
$: narrow = event.dueDate - event.date < MILLISECONDS_IN_MINUTE * 25
$: empty = size.width < 44
function click () {
if (visible) {
const editor = hierarchy.classHierarchyMixin<Doc, ObjectEditor>(event._class, view.mixin.ObjectEditor)
if (editor?.editor !== undefined) {
showPopup(editor.editor, { object: event })
}
}
}
const client = getClient()
const hierarchy = client.getHierarchy()
$: presenter = hierarchy.classHierarchyMixin<Doc, CalendarEventPresenter>(
event._class,
calendar.mixin.CalendarEventPresenter
)
let div: HTMLDivElement
const dispatch = createEventDispatcher()
$: fontSize = $deviceOptionsStore.fontSize
function dragStart (e: DragEvent) {
if (readOnly) return
if (event.allDay) return
originDate = event.date
originDueDate = event.dueDate
const rect = div.getBoundingClientRect()
const topThreshold = rect.y + fontSize / 2
if (e.dataTransfer) {
e.dataTransfer.effectAllowed = 'move'
e.dataTransfer.dropEffect = 'move'
}
dragInitY = e.y
if (e.y < topThreshold) {
dragDirection = 'top'
} else {
const bottomThreshold = rect.y + rect.height - fontSize / 2
if (e.y > bottomThreshold) {
dragDirection = 'bottom'
} else {
dragDirection = 'mid'
}
}
}
let originDate = event.date
let originDueDate = event.dueDate
$: pixelPer15Min = hourHeight / 4
let dragInitY: number | undefined
let dragDirection: 'bottom' | 'mid' | 'top' | undefined
function drag (e: DragEvent) {
if (readOnly) return
if (event.allDay) return
if (dragInitY !== undefined) {
const diff = Math.floor((e.y - dragInitY) / pixelPer15Min)
if (diff) {
if (dragDirection !== 'bottom') {
const newValue = new Date(originDate).setMinutes(new Date(originDate).getMinutes() + 15 * diff)
if (dragDirection === 'top') {
if (newValue < event.dueDate) {
event.date = newValue
dispatch('resize')
}
} else {
const newDue = new Date(originDueDate).setMinutes(new Date(originDueDate).getMinutes() + 15 * diff)
event.date = newValue
event.dueDate = newDue
dispatch('resize')
}
} else {
const newDue = new Date(originDueDate).setMinutes(new Date(originDueDate).getMinutes() + 15 * diff)
if (newDue > event.date) {
event.dueDate = newDue
dispatch('resize')
}
}
}
}
}
async function drop () {
const update: DocumentUpdate<Event> = {}
if (originDate !== event.date) {
update.date = event.date
}
if (originDueDate !== event.dueDate) {
update.dueDate = event.dueDate
}
if (Object.keys(update).length > 0) {
await client.update(event, {
dueDate: event.dueDate,
date: event.date
})
}
}
function showMenu (ev: MouseEvent) {
ev.preventDefault()
showPopup(Menu, { object: event }, getEventPositionElement(ev))
}
$: visible = isVisible(event, $calendarStore)
$: readOnly = isReadOnly(event)
</script>
{#if event}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
bind:this={div}
class="event-container"
class:oneRow
class:empty
draggable={!event.allDay}
use:tooltip={{ component: EventPresenter, props: { value: event, hideDetails: !visible } }}
on:click|stopPropagation={click}
on:contextmenu={showMenu}
on:dragstart={dragStart}
on:drag={drag}
on:dragend={drop}
on:drop
>
{#if !empty && presenter?.presenter}
<Component is={presenter.presenter} props={{ event, narrow, oneRow, hideDetails: !visible }} />
{/if}
</div>
{/if}
<style lang="scss">
.event-container {
pointer-events: auto;
overflow: hidden;
display: flex;
flex-direction: column;
flex-grow: 1;
width: 100%;
height: 100%;
min-width: 0;
min-height: 0;
font-size: 0.8125rem;
background-color: #f3f6fb;
border: 1px solid rgba(43, 81, 144, 0.2);
border-left: 0.25rem solid #2b5190;
border-radius: 0.25rem;
cursor: pointer;
&:not(.oneRow, .empty) {
padding: 0.25rem 0.5rem 0.25rem 1rem;
}
&.oneRow:not(.empty) {
justify-content: center;
padding: 0 0.25rem 0 1rem;
}
}
</style>