Calendar: fixed the display of the past days (events) (#3527)

Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
Alexander Platov 2023-07-28 06:46:26 +03:00 committed by GitHub
parent bb83ece433
commit ecce199b4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 150 additions and 145 deletions

View File

@ -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 {

View File

@ -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>

View File

@ -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

View File

@ -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
}

View File

@ -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())}`

View 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()
)
}