mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-30 21:45:01 +02:00
339 lines
9.1 KiB
Svelte
339 lines
9.1 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 { Event } from '@hcengineering/calendar'
|
|
import { Class, Doc, DocumentQuery, FindOptions, Ref, SortingOrder, Space } from '@hcengineering/core'
|
|
import { createQuery } from '@hcengineering/presentation'
|
|
import {
|
|
AnyComponent,
|
|
areDatesEqual,
|
|
Button,
|
|
IconBack,
|
|
IconForward,
|
|
MonthCalendar,
|
|
showPopup,
|
|
WeekCalendar,
|
|
YearCalendar
|
|
} from '@hcengineering/ui'
|
|
import Scroller from '@hcengineering/ui/src/components/Scroller.svelte'
|
|
import { BuildModelKey } from '@hcengineering/view'
|
|
import { CalendarMode } from '../index'
|
|
import calendar from '../plugin'
|
|
import Day from './Day.svelte'
|
|
import Hour from './Hour.svelte'
|
|
|
|
export let _class: Ref<Class<Doc>>
|
|
export let space: Ref<Space> | undefined = undefined
|
|
export let query: DocumentQuery<Event> = {}
|
|
export let options: FindOptions<Event> | undefined = undefined
|
|
export let baseMenuClass: Ref<Class<Event>> | undefined = undefined
|
|
export let config: (string | BuildModelKey)[]
|
|
export let createComponent: AnyComponent | undefined = undefined
|
|
|
|
const mondayStart = true
|
|
|
|
// Current selected day
|
|
let currentDate: Date = new Date()
|
|
let selectedDate: Date = new Date()
|
|
|
|
let objects: Event[] = []
|
|
|
|
const q = createQuery()
|
|
|
|
async function update (_class: Ref<Class<Event>>, query: DocumentQuery<Event>, options?: FindOptions<Event>) {
|
|
q.query<Event>(
|
|
_class,
|
|
query,
|
|
(result) => {
|
|
objects = result
|
|
},
|
|
{ sort: { date: SortingOrder.Ascending }, ...options }
|
|
)
|
|
}
|
|
$: update(_class, query, options)
|
|
|
|
function areDatesLess (firstDate: Date, secondDate: Date): boolean {
|
|
return (
|
|
firstDate.getFullYear() <= secondDate.getFullYear() &&
|
|
firstDate.getMonth() <= secondDate.getMonth() &&
|
|
firstDate.getDate() <= secondDate.getDate()
|
|
)
|
|
}
|
|
|
|
function findEvents (events: Event[], date: Date, minutes = false): Event[] {
|
|
return events.filter((it) => {
|
|
const d1 = new Date(it.date)
|
|
const d2 = new Date(it.dueDate ?? it.date)
|
|
const inDays = areDatesLess(d1, date) && areDatesLess(date, d2)
|
|
|
|
if (minutes) {
|
|
if (areDatesEqual(d1, date)) {
|
|
return (
|
|
(date.getTime() <= d1.getTime() && d1.getTime() < date.getTime() + 60 * 60 * 1000) ||
|
|
(d1.getTime() <= date.getTime() && date.getTime() < d2.getTime())
|
|
)
|
|
}
|
|
|
|
if (areDatesEqual(d2, date)) {
|
|
return (
|
|
(date.getTime() < d2.getTime() && d2.getTime() < date.getTime() + 60 * 60 * 1000) ||
|
|
(d1.getTime() <= date.getTime() && date.getTime() < d2.getTime())
|
|
)
|
|
}
|
|
|
|
// Somethere in middle
|
|
return inDays
|
|
}
|
|
return inDays
|
|
})
|
|
}
|
|
|
|
function inc (val: number): void {
|
|
if (val === 0) {
|
|
currentDate = new Date()
|
|
return
|
|
}
|
|
switch (mode) {
|
|
case CalendarMode.Day: {
|
|
currentDate.setDate(currentDate.getDate() + val)
|
|
break
|
|
}
|
|
case CalendarMode.Week: {
|
|
currentDate.setDate(currentDate.getDate() + val * 7)
|
|
break
|
|
}
|
|
case CalendarMode.Month: {
|
|
currentDate.setMonth(currentDate.getMonth() + val)
|
|
break
|
|
}
|
|
case CalendarMode.Year: {
|
|
currentDate.setFullYear(currentDate.getFullYear() + val)
|
|
break
|
|
}
|
|
}
|
|
currentDate = currentDate
|
|
}
|
|
function getMonthName (date: Date): string {
|
|
return new Intl.DateTimeFormat('default', {
|
|
month: 'long'
|
|
}).format(date)
|
|
}
|
|
function getWeekName (date: Date): string {
|
|
const onejan = new Date(date.getFullYear(), 0, 1)
|
|
const week = Math.ceil(((date.getTime() - onejan.getTime()) / 86400000 + onejan.getDay() + 1) / 7)
|
|
|
|
return `W${week}`
|
|
}
|
|
|
|
let mode: CalendarMode = CalendarMode.Year
|
|
|
|
function label (date: Date, mode: CalendarMode): string {
|
|
switch (mode) {
|
|
case CalendarMode.Day: {
|
|
return `${date.getDate()} ${getMonthName(date)} ${date.getFullYear()}`
|
|
}
|
|
case CalendarMode.Week: {
|
|
return `${getWeekName(date)} ${getMonthName(date)} ${date.getFullYear()}`
|
|
}
|
|
case CalendarMode.Month: {
|
|
return `${getMonthName(date)} ${date.getFullYear()}`
|
|
}
|
|
case CalendarMode.Year: {
|
|
return `${date.getFullYear()}`
|
|
}
|
|
}
|
|
}
|
|
|
|
function showCreateDialog (date: Date, withTime: boolean) {
|
|
if (createComponent === undefined) {
|
|
return
|
|
}
|
|
showPopup(createComponent, { date, withTime }, 'top')
|
|
}
|
|
|
|
let indexes = new Map<Ref<Event>, number>()
|
|
</script>
|
|
|
|
<div class="fs-title ml-10 mb-2 flex-row-center">
|
|
{label(currentDate, mode)}
|
|
</div>
|
|
|
|
<div class="flex gap-2 mb-4 ml-10">
|
|
<Button
|
|
size={'small'}
|
|
label={calendar.string.ModeDay}
|
|
on:click={() => {
|
|
mode = CalendarMode.Day
|
|
}}
|
|
/>
|
|
<Button
|
|
size={'small'}
|
|
label={calendar.string.ModeWeek}
|
|
on:click={() => {
|
|
mode = CalendarMode.Week
|
|
}}
|
|
/>
|
|
<Button
|
|
size={'small'}
|
|
label={calendar.string.ModeMonth}
|
|
on:click={() => {
|
|
mode = CalendarMode.Month
|
|
}}
|
|
/>
|
|
<Button
|
|
size={'small'}
|
|
label={calendar.string.ModeYear}
|
|
on:click={() => {
|
|
mode = CalendarMode.Year
|
|
}}
|
|
/>
|
|
<div class="flex ml-4 gap-2">
|
|
<Button
|
|
icon={IconBack}
|
|
size={'small'}
|
|
on:click={() => {
|
|
inc(-1)
|
|
}}
|
|
/>
|
|
<Button
|
|
size={'small'}
|
|
label={calendar.string.Today}
|
|
on:click={() => {
|
|
inc(0)
|
|
}}
|
|
/>
|
|
<Button
|
|
icon={IconForward}
|
|
size={'small'}
|
|
on:click={() => {
|
|
inc(1)
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ml-10 mr-6 h-full clear-mins">
|
|
{#if mode === CalendarMode.Year}
|
|
<Scroller>
|
|
<YearCalendar
|
|
{mondayStart}
|
|
cellHeight={'2.5rem'}
|
|
bind:selectedDate
|
|
bind:currentDate
|
|
on:change={(e) => {
|
|
currentDate = e.detail
|
|
if (areDatesEqual(selectedDate, currentDate)) {
|
|
mode = CalendarMode.Month
|
|
}
|
|
selectedDate = e.detail
|
|
}}
|
|
>
|
|
<svelte:fragment slot="cell" let:date let:today let:selected let:wrongMonth>
|
|
<Day
|
|
events={findEvents(objects, date)}
|
|
{date}
|
|
{_class}
|
|
{baseMenuClass}
|
|
{options}
|
|
{config}
|
|
{today}
|
|
{selected}
|
|
{wrongMonth}
|
|
{query}
|
|
/>
|
|
</svelte:fragment>
|
|
</YearCalendar>
|
|
</Scroller>
|
|
{:else if mode === CalendarMode.Month}
|
|
<div class="flex flex-grow">
|
|
<MonthCalendar {mondayStart} cellHeight={'8.5rem'} bind:selectedDate bind:currentDate>
|
|
<svelte:fragment slot="cell" let:date let:today let:selected let:wrongMonth>
|
|
<Day
|
|
events={findEvents(objects, date)}
|
|
{date}
|
|
size={'huge'}
|
|
{_class}
|
|
{baseMenuClass}
|
|
{options}
|
|
{config}
|
|
{today}
|
|
{selected}
|
|
{wrongMonth}
|
|
{query}
|
|
on:select={(e) => {
|
|
currentDate = e.detail
|
|
if (areDatesEqual(selectedDate, currentDate)) {
|
|
mode = CalendarMode.Day
|
|
}
|
|
selectedDate = e.detail
|
|
}}
|
|
on:create={(e) => {
|
|
showCreateDialog(e.detail, false)
|
|
}}
|
|
/>
|
|
</svelte:fragment>
|
|
</MonthCalendar>
|
|
</div>
|
|
{:else if mode === CalendarMode.Week}
|
|
<Scroller>
|
|
<WeekCalendar
|
|
{mondayStart}
|
|
cellHeight={'4.5rem'}
|
|
bind:selectedDate
|
|
bind:currentDate
|
|
on:select={(e) => {
|
|
currentDate = e.detail
|
|
selectedDate = e.detail
|
|
mode = CalendarMode.Day
|
|
}}
|
|
>
|
|
<svelte:fragment slot="cell" let:date>
|
|
<Hour
|
|
events={findEvents(objects, date, true)}
|
|
{date}
|
|
bind:indexes
|
|
on:create={(e) => {
|
|
showCreateDialog(e.detail, true)
|
|
}}
|
|
/>
|
|
</svelte:fragment>
|
|
</WeekCalendar>
|
|
</Scroller>
|
|
{:else if mode === CalendarMode.Day}
|
|
<Scroller>
|
|
<WeekCalendar
|
|
{mondayStart}
|
|
displayedDaysCount={1}
|
|
startFromWeekStart={false}
|
|
cellHeight={'4.5rem'}
|
|
bind:selectedDate
|
|
bind:currentDate
|
|
>
|
|
<svelte:fragment slot="cell" let:date>
|
|
<Hour
|
|
events={findEvents(objects, date, true)}
|
|
{date}
|
|
bind:indexes
|
|
wide
|
|
on:create={(e) => {
|
|
showCreateDialog(e.detail, true)
|
|
}}
|
|
/>
|
|
</svelte:fragment>
|
|
</WeekCalendar>
|
|
</Scroller>
|
|
{/if}
|
|
</div>
|