mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-11 10:47:24 +00:00
202 lines
6.2 KiB
Svelte
202 lines
6.2 KiB
Svelte
<!--
|
|
// Copyright © 2020 Anticrm Platform Contributors.
|
|
//
|
|
// 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 { afterUpdate, createEventDispatcher } from 'svelte'
|
|
import { IconNavPrev, IconNavNext, Icon } from '../..'
|
|
import { firstDay, day, getWeekDayName, areDatesEqual, getMonthName, weekday, isWeekend } from './internal/DateUtils'
|
|
|
|
export let currentDate: Date | null
|
|
export let viewDate: Date
|
|
export let mondayStart: boolean = true
|
|
export let hideNavigator: boolean = false
|
|
export let viewUpdate: boolean = true
|
|
export let displayedWeeksCount = 6
|
|
|
|
const dispatch = createEventDispatcher()
|
|
|
|
$: firstDayOfCurrentMonth = firstDay(viewDate, mondayStart)
|
|
let monthYear: string
|
|
const today: Date = new Date(Date.now())
|
|
const capitalizeFirstLetter = (str: string): string => str.charAt(0).toUpperCase() + str.slice(1)
|
|
|
|
if (viewDate == undefined) viewDate = currentDate ?? today
|
|
afterUpdate(() => {
|
|
if (currentDate && viewUpdate) viewDate = currentDate
|
|
if (viewDate) {
|
|
monthYear = capitalizeFirstLetter(getMonthName(viewDate)) + ' ' + viewDate.getFullYear()
|
|
firstDayOfCurrentMonth = firstDay(viewDate, mondayStart)
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<div class="month-container">
|
|
<div class="header">
|
|
{#if viewDate}
|
|
<div class="monthYear">{monthYear}</div>
|
|
<div class="group" class:hideNavigator>
|
|
<div class="btn" on:click={() => {
|
|
if (viewUpdate) viewDate.setMonth(viewDate.getMonth() - 1)
|
|
dispatch('navigation', '-m')
|
|
}}>
|
|
<div class="icon-btn"><Icon icon={IconNavPrev} size={'full'} /></div>
|
|
</div>
|
|
<div class="btn" on:click={() => {
|
|
if (viewUpdate) viewDate.setMonth(viewDate.getMonth() + 1)
|
|
dispatch('navigation', '+m')
|
|
}}>
|
|
<div class="icon-btn"><Icon icon={IconNavNext} size={'full'} /></div>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
|
|
{#if viewDate}
|
|
<div class="calendar">
|
|
{#each [...Array(7).keys()] as dayOfWeek}
|
|
<span class="caption">{capitalizeFirstLetter(getWeekDayName(day(firstDayOfCurrentMonth, dayOfWeek), 'short'))}</span>
|
|
{/each}
|
|
|
|
{#each [...Array(displayedWeeksCount).keys()] as weekIndex}
|
|
{#each [...Array(7).keys()] as dayOfWeek}
|
|
<div
|
|
class="day"
|
|
class:weekend={isWeekend(weekday(firstDayOfCurrentMonth, weekIndex, dayOfWeek))}
|
|
class:today={areDatesEqual(today, weekday(firstDayOfCurrentMonth, weekIndex, dayOfWeek))}
|
|
class:selected={currentDate && weekday(firstDayOfCurrentMonth, weekIndex, dayOfWeek).getMonth() ===
|
|
currentDate.getMonth() && areDatesEqual(currentDate, weekday(firstDayOfCurrentMonth, weekIndex, dayOfWeek))}
|
|
class:wrongMonth={weekday(firstDayOfCurrentMonth, weekIndex, dayOfWeek).getMonth() !==
|
|
viewDate.getMonth()}
|
|
style={`grid-column-start: ${dayOfWeek + 1}; grid-row-start: ${weekIndex + 2};`}
|
|
on:click|stopPropagation={() => {
|
|
viewDate = weekday(firstDayOfCurrentMonth, weekIndex, dayOfWeek)
|
|
if (currentDate) {
|
|
viewDate.setHours(currentDate.getHours())
|
|
viewDate.setMinutes(currentDate.getMinutes())
|
|
}
|
|
dispatch('update', viewDate)
|
|
}}
|
|
>
|
|
{weekday(firstDayOfCurrentMonth, weekIndex, dayOfWeek).getDate()}
|
|
</div>
|
|
{/each}
|
|
{/each}
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
|
|
<style lang="scss">
|
|
.month-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-height: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
color: var(--theme-caption-color);
|
|
|
|
.header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 1rem 1rem .75rem;
|
|
color: var(--caption-color);
|
|
|
|
.monthYear {
|
|
font-weight: 500;
|
|
font-size: 1rem;
|
|
&::first-letter { text-transform: capitalize; }
|
|
}
|
|
.group {
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
&.hideNavigator { visibility: hidden; }
|
|
.btn {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
width: 1.25rem;
|
|
height: 1.75rem;
|
|
color: var(--dark-color);
|
|
cursor: pointer;
|
|
|
|
.icon-btn { height: .75rem; }
|
|
&:hover { color: var(--accent-color); }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.calendar {
|
|
position: relative;
|
|
display: grid;
|
|
grid-template-columns: repeat(7, 1fr);
|
|
gap: .5rem;
|
|
padding: 0 1rem 1rem;
|
|
|
|
.caption, .day {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
width: 1.625rem;
|
|
height: 1.625rem;
|
|
font-size: 1rem;
|
|
color: var(--content-color);
|
|
}
|
|
.caption {
|
|
align-items: start;
|
|
height: 2rem;
|
|
color: var(--dark-color);
|
|
&::first-letter { text-transform: capitalize; }
|
|
}
|
|
.day {
|
|
position: relative;
|
|
color: var(--accent-color);
|
|
background-color: rgba(var(--accent-color), .05);
|
|
border: 1px solid transparent;
|
|
border-radius: 50%;
|
|
cursor: pointer;
|
|
|
|
&.weekend { color: var(--content-color); }
|
|
&.wrongMonth { color: var(--dark-color); }
|
|
&.today {
|
|
font-weight: 500;
|
|
color: var(--caption-color);
|
|
background-color: var(--button-bg-color);
|
|
border-color: var(--dark-color);
|
|
}
|
|
&.selected, &:hover {
|
|
color: var(--caption-color);
|
|
background-color: var(--primary-bg-color);
|
|
}
|
|
|
|
&:before {
|
|
content: '';
|
|
position: absolute;
|
|
inset: -.625rem;
|
|
}
|
|
}
|
|
|
|
&::before {
|
|
position: absolute;
|
|
content: '';
|
|
top: 2rem;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 1px;
|
|
background-color: var(--button-bg-color);
|
|
}
|
|
}
|
|
</style>
|