mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-14 20:39:03 +00:00
Calendar: fixed the display of the past days (events) (#3527)
Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
parent
bb83ece433
commit
ecce199b4e
@ -101,20 +101,20 @@
|
||||
let maxHeightAD: number = 0
|
||||
let shownHeightAD: number = 0
|
||||
let shownAD: boolean = false
|
||||
let shortAlldays: { id: string; day: number }[] = []
|
||||
let shortAlldays: { id: string; day: number; fixRow?: boolean }[] = []
|
||||
let moreCounts: number[] = Array<number>(displayedDaysCount)
|
||||
|
||||
$: if (newEvents !== events) {
|
||||
newEvents = events
|
||||
grid = new Array<CalendarGrid>(displayedDaysCount)
|
||||
alldaysGrid = new Array<CalendarADGrid>(displayedDaysCount)
|
||||
newEvents = events
|
||||
alldays = []
|
||||
shortAlldays = []
|
||||
moreCounts = new Array<number>(displayedDaysCount)
|
||||
prepareAllDays()
|
||||
if (shownAD && adMaxRow <= maxAD) shownAD = false
|
||||
prepareAllDays()
|
||||
}
|
||||
$: minimizedAD = maxHeightAD === minHeightAD
|
||||
$: minimizedAD = maxHeightAD === minHeightAD && maxHeightAD !== 0
|
||||
$: newEvents
|
||||
.filter((ev) => !ev.allDay)
|
||||
.forEach((event, i, arr) => {
|
||||
@ -232,8 +232,13 @@
|
||||
}
|
||||
} else if (alldaysGrid[d].alldays[r] === null) {
|
||||
tempEventID = ''
|
||||
if (filtered.length > 0) moreCounts[d] = filtered.length
|
||||
else moreCounts[d] = 0
|
||||
if (filtered.length > 1) moreCounts[d] = filtered.length
|
||||
else {
|
||||
if (filtered.length === 1 && filtered[0] !== null) {
|
||||
shortAlldays.push({ id: filtered[0], day: d, fixRow: true })
|
||||
}
|
||||
moreCounts[d] = 0
|
||||
}
|
||||
} else if (alldaysGrid[d].alldays[r] === tempEventID) {
|
||||
if (filtered.length > 0) {
|
||||
tempEventID = ''
|
||||
@ -263,14 +268,17 @@
|
||||
|
||||
const getRect = (
|
||||
event: CalendarItem
|
||||
): { top: number; bottom: number; left: number; right: number; width: number } => {
|
||||
const result = { top: 0, bottom: 0, left: 0, right: 0, width: 0 }
|
||||
): { top: number; bottom: number; left: number; right: number; width: number; visibility: number } => {
|
||||
const result = { top: 0, bottom: 0, left: 0, right: 0, width: 0, visibility: 1 }
|
||||
const checkDate = new Date(currentDate.getTime() + MILLISECONDS_IN_DAY * event.day)
|
||||
const startDay = checkDate.setHours(startHour, 0, 0)
|
||||
const endDay = checkDate.setHours(displayedHours - 1, 59, 59)
|
||||
const startDay = checkDate.setHours(startHour, 0, 0, 0)
|
||||
const endDay = checkDate.setHours(displayedHours - 1, 59, 59, 999)
|
||||
const startTime = event.date < startDay ? { hours: 0, mins: 0 } : convertToTime(event.date)
|
||||
const endTime =
|
||||
event.dueDate > endDay ? { hours: displayedHours - startHour, mins: 0 } : convertToTime(event.dueDate)
|
||||
if (getDay(weekMonday, event.day).setHours(endTime.hours, endTime.mins, 0, 0) <= todayDate.getTime()) {
|
||||
result.visibility = 0
|
||||
}
|
||||
result.top =
|
||||
rem(3.5) +
|
||||
styleAD +
|
||||
@ -301,13 +309,30 @@
|
||||
(cols - index - 1) * rem(0.125)
|
||||
return result
|
||||
}
|
||||
const getADRect = (id: string, day?: number): { top: number; left: number; width: number; fit: boolean } => {
|
||||
const result = { top: 0, left: 0, width: 0, fit: true }
|
||||
const getADRect = (
|
||||
id: string,
|
||||
day?: number,
|
||||
fixRow?: boolean
|
||||
): { top: number; left: number; width: number; height: number; fit: boolean; visibility: number } => {
|
||||
const result = { top: 0, left: 0, width: 0, height: rem(heightAD), fit: true, visibility: 1 }
|
||||
const index = adRows.findIndex((ev) => ev.id === id)
|
||||
|
||||
const checkTime = new Date().setHours(0, 0, 0, 0)
|
||||
const startEvent = getDay(weekMonday, typeof day !== 'undefined' ? day : adRows[index].startCol).getTime()
|
||||
const endEvent = getDay(weekMonday, adRows[index].endCol).setHours(24)
|
||||
if (endEvent <= checkTime) result.visibility = 0
|
||||
else if (startEvent <= checkTime && endEvent > checkTime) {
|
||||
const eventTime = endEvent - startEvent
|
||||
const remained = endEvent - checkTime
|
||||
const proc = remained / eventTime
|
||||
result.visibility = proc
|
||||
}
|
||||
|
||||
const shown = minimizedAD ? minAD : maxAD
|
||||
const lastRow = adRows[index].row === shown - 1
|
||||
const row = fixRow ? shown - 1 : adRows[index].row
|
||||
const lastRow = row === shown - 1
|
||||
const fitting = !shownAD && lastRow && typeof day !== 'undefined'
|
||||
result.top = rem(0.125 + adRows[index].row * (heightAD + 0.125))
|
||||
result.top = rem(0.125 + row * (heightAD + 0.125))
|
||||
if (fitting) {
|
||||
result.left = rem(0.125) + day * (colWidth + 0.125)
|
||||
if (day < adRows[index].endCol) {
|
||||
@ -318,7 +343,7 @@
|
||||
} else if (d === adRows[index].endCol) result.width = colWidth + colWidth * (d - day) - rem(0.25)
|
||||
}
|
||||
} else result.width = colWidth - rem(0.25)
|
||||
} else if (fitting && (adRows[index].row > shown - 1 || (lastRow && moreCounts[day] > 0))) {
|
||||
} else if (fitting && (row > shown - 1 || (lastRow && moreCounts[day] > 0))) {
|
||||
result.fit = false
|
||||
} else {
|
||||
result.left = rem(0.125) + adRows[index].startCol * (colWidth + 0.125)
|
||||
@ -327,6 +352,11 @@
|
||||
}
|
||||
return result
|
||||
}
|
||||
const getMask = (visibility: number): string => {
|
||||
return visibility === 0 || visibility === 1
|
||||
? 'none'
|
||||
: `linear-gradient(-90deg, rgba(0, 0, 0, 1) ${visibility * 100}%, rgba(0, 0, 0, .4) ${visibility * 100}%)`
|
||||
}
|
||||
const getMore = (day: number): { top: number; left: number; width: number } => {
|
||||
const result = { top: 0, left: 0, width: 0 }
|
||||
const lastRow = (minimizedAD ? minAD : maxAD) - 1
|
||||
@ -398,78 +428,85 @@
|
||||
<div class="sticky-header allday-container" style:grid-column={`col-start 1 / span ${displayedDaysCount}`}>
|
||||
{#if shownHeightAD > maxHeightAD && shownAD}
|
||||
<Scroller noFade={false}>
|
||||
{#key calendarWidth}{#key displayedDaysCount}
|
||||
<div style:min-height={`${shownHeightAD - cellBorder * 2}px`} />
|
||||
{#key styleAD}{#key calendarWidth}{#key displayedDaysCount}
|
||||
<div style:min-height={`${shownHeightAD - cellBorder * 2}px`} />
|
||||
{#each alldays as event, i}
|
||||
{@const rect = getADRect(event.eventId)}
|
||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||
<div
|
||||
class="calendar-element"
|
||||
style:top={`${rect.top}px`}
|
||||
style:height={`${rect.height}px`}
|
||||
style:left={`${rect.left}px`}
|
||||
style:width={`${rect.width}px`}
|
||||
style:opacity={rect.visibility === 0 ? 0.4 : 1}
|
||||
style:--mask-image={getMask(rect.visibility)}
|
||||
tabindex={500 + i}
|
||||
>
|
||||
<slot name="event" id={event.eventId} size={{ width: rect.width, height: rect.height }} />
|
||||
</div>
|
||||
{/each}
|
||||
{/key}{/key}{/key}
|
||||
</Scroller>
|
||||
{:else if shownAD}
|
||||
{#key styleAD}{#key calendarWidth}{#key displayedDaysCount}
|
||||
{#each alldays as event, i}
|
||||
{@const rect = getADRect(event.eventId)}
|
||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||
<div
|
||||
class="calendar-element"
|
||||
style:top={`${rect.top}px`}
|
||||
style:height={`${heightAD}rem`}
|
||||
style:height={`${rect.height}px`}
|
||||
style:left={`${rect.left}px`}
|
||||
style:width={`${rect.width}px`}
|
||||
style:opacity={rect.visibility === 0 ? 0.4 : 1}
|
||||
style:--mask-image={getMask(rect.visibility)}
|
||||
tabindex={500 + i}
|
||||
>
|
||||
<slot name="allday" id={event.eventId} width={rect.width} />
|
||||
<slot name="event" id={event.eventId} size={{ width: rect.width, height: rect.height }} />
|
||||
</div>
|
||||
{/each}
|
||||
{/key}{/key}
|
||||
</Scroller>
|
||||
{:else if shownAD}
|
||||
{#key calendarWidth}{#key displayedDaysCount}
|
||||
{#each alldays as event, i}
|
||||
{@const rect = getADRect(event.eventId)}
|
||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||
<div
|
||||
class="calendar-element"
|
||||
style:top={`${rect.top}px`}
|
||||
style:height={`${heightAD}rem`}
|
||||
style:left={`${rect.left}px`}
|
||||
style:width={`${rect.width}px`}
|
||||
tabindex={500 + i}
|
||||
>
|
||||
<slot name="allday" id={event.eventId} width={rect.width} />
|
||||
</div>
|
||||
{/each}
|
||||
{/key}{/key}
|
||||
{/key}{/key}{/key}
|
||||
{:else}
|
||||
{#key calendarWidth}{#key displayedDaysCount}
|
||||
{#each shortAlldays as event, i}
|
||||
{@const rect = getADRect(event.id, event.day)}
|
||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||
<div
|
||||
class="calendar-element"
|
||||
style:top={`${rect.top}px`}
|
||||
style:height={`${heightAD}rem`}
|
||||
style:left={`${rect.left}px`}
|
||||
style:width={`${rect.width}px`}
|
||||
tabindex={500 + i}
|
||||
>
|
||||
<slot name="allday" id={event.id} width={rect.width} />
|
||||
</div>
|
||||
{/each}
|
||||
{#each moreCounts as more, day}
|
||||
{@const addon = shortAlldays.length}
|
||||
{#if more !== 0}
|
||||
{@const rect = getMore(day)}
|
||||
{#key styleAD}{#key calendarWidth}{#key displayedDaysCount}
|
||||
{#each shortAlldays as event, i}
|
||||
{@const rect = getADRect(event.id, event.day, event.fixRow)}
|
||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="calendar-element antiButton ghost medium accent cursor-pointer"
|
||||
class="calendar-element"
|
||||
style:top={`${rect.top}px`}
|
||||
style:height={`${heightAD}rem`}
|
||||
style:height={`${rect.height}px`}
|
||||
style:left={`${rect.left}px`}
|
||||
style:width={`${rect.width}px`}
|
||||
style:padding-left={'1.25rem'}
|
||||
tabindex={500 + addon + day}
|
||||
on:click={() => (shownAD = true)}
|
||||
style:opacity={rect.visibility === 0 ? 0.4 : 1}
|
||||
style:--mask-image={getMask(rect.visibility)}
|
||||
tabindex={500 + i}
|
||||
>
|
||||
<Label label={ui.string.MoreCount} params={{ count: more }} />
|
||||
<slot name="event" id={event.id} size={{ width: rect.width, height: rect.height }} />
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
{/key}{/key}
|
||||
{/each}
|
||||
{#each moreCounts as more, day}
|
||||
{@const addon = shortAlldays.length}
|
||||
{#if more !== 0}
|
||||
{@const rect = getMore(day)}
|
||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="calendar-element antiButton ghost medium accent cursor-pointer"
|
||||
style:top={`${rect.top}px`}
|
||||
style:height={`${heightAD}rem`}
|
||||
style:left={`${rect.left}px`}
|
||||
style:width={`${rect.width}px`}
|
||||
style:padding-left={'1.25rem'}
|
||||
style:--mask-image={'none'}
|
||||
tabindex={500 + addon + day}
|
||||
on:click={() => (shownAD = true)}
|
||||
>
|
||||
<Label label={ui.string.MoreCount} params={{ count: more }} />
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
{/key}{/key}{/key}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@ -514,9 +551,18 @@
|
||||
style:bottom={`${rect.bottom}px`}
|
||||
style:left={`${rect.left}px`}
|
||||
style:right={`${rect.right}px`}
|
||||
style:opacity={rect.visibility === 0 ? 0.4 : 1}
|
||||
style:--mask-image={'none'}
|
||||
tabindex={1000 + i}
|
||||
>
|
||||
<slot name="event" id={event.eventId} width={rect.width} />
|
||||
<slot
|
||||
name="event"
|
||||
id={event.eventId}
|
||||
size={{
|
||||
width: rect.width,
|
||||
height: (calendarRect?.height ?? rect.top + rect.bottom) - rect.top - rect.bottom
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
{/key}{/key}{/key}
|
||||
@ -539,6 +585,8 @@
|
||||
}
|
||||
.calendar-element {
|
||||
position: absolute;
|
||||
mask-image: var(--mask-image, none);
|
||||
--webkit-mask-image: var(--mask-image, none);
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
.sticky-header {
|
||||
|
@ -380,29 +380,10 @@
|
||||
bind:currentDate
|
||||
on:create={(e) => showCreateDialog(e.detail.date, e.detail.withTime)}
|
||||
>
|
||||
<svelte:fragment slot="allday" let:id let:width>
|
||||
<svelte:fragment slot="event" let:id let:size>
|
||||
{@const event = objects.find((event) => event.eventId === id)}
|
||||
{#if event}
|
||||
<EventElement
|
||||
{event}
|
||||
{width}
|
||||
allday
|
||||
on:create={(e) => {
|
||||
showCreateDialog(e.detail.date, e.detail.withTime)
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="event" let:id let:width>
|
||||
{@const event = objects.find((event) => event.eventId === id)}
|
||||
{#if event}
|
||||
<EventElement
|
||||
{event}
|
||||
{width}
|
||||
on:create={(e) => {
|
||||
showCreateDialog(e.detail.date, e.detail.withTime)
|
||||
}}
|
||||
/>
|
||||
<EventElement {event} {size} />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</DayCalendar>
|
||||
@ -417,29 +398,10 @@
|
||||
bind:currentDate
|
||||
on:create={(e) => showCreateDialog(e.detail.date, e.detail.withTime)}
|
||||
>
|
||||
<svelte:fragment slot="allday" let:id let:width>
|
||||
<svelte:fragment slot="event" let:id let:size>
|
||||
{@const event = objects.find((event) => event.eventId === id)}
|
||||
{#if event}
|
||||
<EventElement
|
||||
{event}
|
||||
{width}
|
||||
allday
|
||||
on:create={(e) => {
|
||||
showCreateDialog(e.detail.date, e.detail.withTime)
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="event" let:id let:width>
|
||||
{@const event = objects.find((event) => event.eventId === id)}
|
||||
{#if event}
|
||||
<EventElement
|
||||
{event}
|
||||
{width}
|
||||
on:create={(e) => {
|
||||
showCreateDialog(e.detail.date, e.detail.withTime)
|
||||
}}
|
||||
/>
|
||||
<EventElement {event} {size} />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</DayCalendar>
|
||||
|
@ -16,11 +16,12 @@
|
||||
import { Calendar, generateEventId } from '@hcengineering/calendar'
|
||||
import { Employee, EmployeeAccount } from '@hcengineering/contact'
|
||||
import { UserBoxList } from '@hcengineering/contact-resources'
|
||||
import { Class, DateRangeMode, Doc, Ref, Timestamp, getCurrentAccount } from '@hcengineering/core'
|
||||
import { Class, DateRangeMode, Doc, Ref, getCurrentAccount } from '@hcengineering/core'
|
||||
import { Card, getClient } from '@hcengineering/presentation'
|
||||
import ui, { DateRangePresenter, EditBox, ToggleWithLabel } from '@hcengineering/ui'
|
||||
import { createEventDispatcher, tick } from 'svelte'
|
||||
import calendar from '../plugin'
|
||||
import { saveUTC } from '../utils'
|
||||
|
||||
export let attachedTo: Ref<Doc> = calendar.ids.NoAttached
|
||||
export let attachedToClass: Ref<Class<Doc>> = calendar.class.Event
|
||||
@ -49,19 +50,6 @@
|
||||
return title !== undefined && title.trim().length === 0 && participants.length === 0
|
||||
}
|
||||
|
||||
const saveUTC = (date: Timestamp): Timestamp => {
|
||||
const utcdate = new Date(date)
|
||||
return Date.UTC(
|
||||
utcdate.getFullYear(),
|
||||
utcdate.getMonth(),
|
||||
utcdate.getDate(),
|
||||
utcdate.getHours(),
|
||||
utcdate.getMinutes(),
|
||||
utcdate.getSeconds(),
|
||||
utcdate.getMilliseconds()
|
||||
)
|
||||
}
|
||||
|
||||
async function saveEvent () {
|
||||
let date: number | undefined
|
||||
if (startDate != null) date = startDate
|
||||
|
@ -33,6 +33,7 @@
|
||||
import { ContextMenu, ObjectPresenter, getObjectPreview } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher, tick } from 'svelte'
|
||||
import calendar from '../plugin'
|
||||
import { saveUTC } from '../utils'
|
||||
|
||||
export let object: Event
|
||||
|
||||
@ -60,26 +61,22 @@
|
||||
|
||||
$: mode = object.allDay ? DateRangeMode.DATE : DateRangeMode.DATETIME
|
||||
|
||||
const defaultDuration = 30 * 60 * 1000
|
||||
const allDayDuration = 24 * 60 * 60 * 1000
|
||||
const defaultDuration = 60 * 60 * 1000
|
||||
const allDayDuration = 24 * 60 * 60 * 1000 - 1
|
||||
let duration = object.dueDate - object.date
|
||||
|
||||
async function updateDate () {
|
||||
await client.update(object, {
|
||||
date: object.date,
|
||||
dueDate: object.dueDate,
|
||||
date: object.allDay ? saveUTC(object.date) : object.date,
|
||||
dueDate: object.allDay ? saveUTC(object.dueDate) : object.dueDate,
|
||||
allDay: object.allDay
|
||||
})
|
||||
}
|
||||
|
||||
async function handleNewStartDate (newStartDate: number | null) {
|
||||
if (newStartDate !== null) {
|
||||
object.date = newStartDate
|
||||
object.date = object.allDay ? new Date(newStartDate).setHours(0, 0, 0, 0) : newStartDate
|
||||
object.dueDate = object.date + (object.allDay ? allDayDuration : duration)
|
||||
if (object.allDay) {
|
||||
object.date = new Date(object.date).setUTCHours(0, 0, 0, 0)
|
||||
object.dueDate = new Date(object.dueDate).setUTCHours(0, 0, 0, 0)
|
||||
}
|
||||
await tick()
|
||||
dueDateRef.adaptValue()
|
||||
await updateDate()
|
||||
@ -90,15 +87,11 @@
|
||||
if (newDueDate !== null) {
|
||||
const diff = newDueDate - object.date
|
||||
if (diff > 0) {
|
||||
object.dueDate = newDueDate
|
||||
duration = diff
|
||||
object.dueDate = object.allDay ? new Date(newDueDate).setHours(23, 59, 59, 999) : newDueDate
|
||||
duration = object.dueDate - object.date
|
||||
} else {
|
||||
object.dueDate = object.date + (object.allDay ? allDayDuration : duration)
|
||||
}
|
||||
if (object.allDay) {
|
||||
object.date = new Date(object.date).setUTCHours(0, 0, 0, 0)
|
||||
object.dueDate = new Date(object.dueDate).setUTCHours(0, 0, 0, 0)
|
||||
}
|
||||
await tick()
|
||||
dueDateRef.adaptValue()
|
||||
await updateDate()
|
||||
@ -107,8 +100,9 @@
|
||||
|
||||
async function allDayChangeHandler () {
|
||||
if (object.allDay) {
|
||||
object.date = new Date(object.date).setUTCHours(0, 0, 0, 0)
|
||||
object.dueDate = new Date(object.dueDate).setUTCHours(0, 0, 0, 0)
|
||||
object.date = new Date(object.date).setHours(0, 0, 0, 0)
|
||||
if (object.dueDate - object.date < allDayDuration) object.dueDate = allDayDuration + object.date
|
||||
else object.dueDate = new Date(object.dueDate).setHours(23, 59, 59, 999)
|
||||
} else {
|
||||
object.dueDate = object.date + defaultDuration
|
||||
}
|
||||
|
@ -19,14 +19,13 @@
|
||||
import EventPresenter from './EventPresenter.svelte'
|
||||
|
||||
export let event: Event
|
||||
export let allday: boolean = false
|
||||
export let width: number = 0
|
||||
export let size: { width: number; height: number }
|
||||
|
||||
$: startDate = new Date(event.date)
|
||||
$: endDate = new Date(event.dueDate)
|
||||
$: oneRow = event.dueDate - event.date <= MILLISECONDS_IN_MINUTE * 30 || allday
|
||||
$: oneRow = size.height < 42 || event.allDay
|
||||
$: narrow = event.dueDate - event.date < MILLISECONDS_IN_MINUTE * 25
|
||||
$: empty = width < 44
|
||||
$: empty = size.width < 44
|
||||
|
||||
const getTime = (date: Date): string => {
|
||||
return `${addZero(date.getHours())}:${addZero(date.getMinutes())}`
|
||||
|
14
plugins/calendar-resources/src/utils.ts
Normal file
14
plugins/calendar-resources/src/utils.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { Timestamp } from '@hcengineering/core'
|
||||
|
||||
export function saveUTC (date: Timestamp): Timestamp {
|
||||
const utcdate = new Date(date)
|
||||
return Date.UTC(
|
||||
utcdate.getFullYear(),
|
||||
utcdate.getMonth(),
|
||||
utcdate.getDate(),
|
||||
utcdate.getHours(),
|
||||
utcdate.getMinutes(),
|
||||
utcdate.getSeconds(),
|
||||
utcdate.getMilliseconds()
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue
Block a user