platform/packages/ui/src/components/calendar/Month.svelte
Alexander Platov 28329f74bc
Update DatePicker (#1337)
Signed-off-by: Alexander Platov <sas_lord@mail.ru>
2022-04-09 01:17:04 +07:00

207 lines
5.9 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 { TCellStyle, ICell } from './internal/DateUtils'
import { firstDay, day, getWeekDayName, areDatesEqual, getMonthName, daysInMonth } from './internal/DateUtils'
export let currentDate: Date | null
export let mondayStart: boolean = true
export let hideNavigator: boolean = false
const dispatch = createEventDispatcher()
let monthYear: string
const today: Date = new Date(Date.now())
let viewDate: Date = new Date(currentDate ?? today)
$: firstDayOfCurrentMonth = firstDay(viewDate, mondayStart)
const isToday = (n: number): boolean => {
if (areDatesEqual(today, new Date(viewDate.getFullYear(), viewDate.getMonth(), n))) return true
return false
}
const capitalizeFirstLetter = (str: string): string => str.charAt(0).toUpperCase() + str.slice(1)
let days: Array<ICell> = []
const getDateStyle = (date: Date): TCellStyle => {
if (currentDate != undefined && areDatesEqual(currentDate, date)) return 'selected'
return 'not-selected'
}
const renderCellStyles = (): void => {
days = []
for (let i = 1; i <= daysInMonth(viewDate); i++) {
const tempDate = new Date(viewDate.getFullYear(), viewDate.getMonth(), i)
days.push({
dayOfWeek: (tempDate.getDay() === 0) ? 7 : tempDate.getDay(),
style: getDateStyle(tempDate)
})
}
days = days
monthYear = capitalizeFirstLetter(getMonthName(viewDate)) + ' ' + viewDate.getFullYear()
}
afterUpdate(() => {
if (viewDate) renderCellStyles()
})
</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={() => {
viewDate.setMonth(viewDate.getMonth() - 1)
renderCellStyles()
}}>
<div class="icon-btn"><Icon icon={IconNavPrev} size={'full'} /></div>
</div>
<div class="btn" on:click={() => {
viewDate.setMonth(viewDate.getMonth() + 1)
renderCellStyles()
}}>
<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 days as day, i}
<div
class="day {day.style}"
class:today={isToday(i)}
class:day-off={day.dayOfWeek > 5}
style="grid-column: {day.dayOfWeek}/{day.dayOfWeek + 1};"
on:click|stopPropagation={() => {
viewDate.setDate(i + 1)
dispatch('update', viewDate)
}}
>
{i + 1}
</div>
{/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;
&.day-off { color: var(--content-color); }
&.today {
font-weight: 500;
color: var(--caption-color);
background-color: var(--button-bg-color);
border-color: var(--dark-color);
}
&.focused { box-shadow: 0 0 0 3px var(--primary-button-outline); }
&.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>