budibase/packages/materialdesign-components/src/DatePicker/DatePicker.svelte

180 lines
4.3 KiB
Svelte

<script>
import { onMount } from "svelte"
import {
startOfMonth,
endOfMonth,
getDate,
getMonth,
getYear,
addMonths,
subMonths,
format,
} from "date-fns"
import { MDCMenu } from "@material/menu"
import { Textfield } from "../Textfield"
import Icon from "../Common/Icon.svelte"
import ripple from "../Common/Ripple.js"
import { Body1, Body2, Caption } from "../Typography"
import { IconButton } from "../IconButton"
let menu
let instance
let textfieldValue = ""
let daysArr = []
let navDate = new Date()
const weekdayMap = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]
export let _bb
export let date = new Date()
export let label = ""
export let onSelect = selectedDate => {}
onMount(() => {
if (!!menu) {
instance = new MDCMenu(menu)
instance.open = false
instance.setFixedPostion = true
}
})
function selectDate(dayOfMonth) {
let month = getMonth(navDate)
let year = getYear(navDate)
date = new Date(year, month, dayOfMonth)
onSelect(date)
}
function addMonth() {
navDate = addMonths(navDate, 1)
}
function subtractMonth() {
navDate = subMonths(navDate, 1)
}
function openCalendar(isOpen) {
instance.open = isOpen === undefined ? !instance.open : isOpen
}
function textFieldChange(value) {
const isDate = /^\d{1,2}\/\d{1,2}\/\d{4}$/
if (isDate.test(value)) {
const [year, month, day] = value.split("/").reverse()
if (month > 0 && month <= 12 && (day > 0 && day <= 31)) {
date = new Date(year, month - 1, day)
navDate = date
openCalendar(true)
onSelect(date)
}
}
}
$: dateMonthEnds = endOfMonth(navDate).getDate()
$: dateMonthBegins = startOfMonth(navDate).getDay()
$: dayStart = dateMonthBegins + 1 //1 = sunday
$: monthAndYear = format(navDate, "MMMM y")
$: selectedDate = format(date, "dd/MM/yyyy")
$: dayOfSelectedDate = getDate(date)
$: for (let d = 1; d <= dateMonthEnds; d++) {
if (d === 1) {
daysArr = [d]
} else {
daysArr = [...daysArr, d]
}
}
$: rowRepeater =
dateMonthBegins > 5 && daysArr[daysArr.length - 1] > 30 ? 6 : 5
$: sameMonthAndYear =
getMonth(date) === getMonth(navDate) && getYear(date) === getYear(navDate)
</script>
<div class="mdc-menu-surface--anchor">
<Textfield
{label}
onChange={textFieldChange}
value={selectedDate}
trailingIcon={true}
useIconButton={true}
iconButtonClick={openCalendar}
icon="calendar_today" />
<div
bind:this={menu}
class="mdc-menu mdc-menu-surface bbmd-menu"
style={`margin-top: 70px`}>
<div class="calendar-container">
<div class="month-picker">
<div>
<IconButton icon="chevron_left" {_bb} onClick={subtractMonth} />
</div>
<div class="centreText">
<Body1 text={monthAndYear} />
</div>
<div>
<IconButton icon="chevron_right" {_bb} onClick={addMonth} />
</div>
</div>
<div class="week-days">
{#each weekdayMap as day, i}
<div class="centreText">
<Caption text={day} />
</div>
{/each}
</div>
<div
class="day-picker"
style={`grid-template-rows: repeat(${rowRepeater}, 40px)`}>
{#each daysArr as day, i}
<div
use:ripple
style={i === 0 ? `grid-column-start: ${dayStart}` : ``}
on:click={() => selectDate(day)}
class={`bbmd-day ${dayOfSelectedDate === day && sameMonthAndYear ? 'selected' : ''}`}>
<Body2 text={day} />
</div>
{/each}
</div>
</div>
<!-- Superfluous but necessary to keep the menu instance sweet -->
<ul class="mdc-list" role="menu" />
</div>
</div>
<style>
.bbmd-menu {
width: 330px;
height: auto;
padding: 5px;
}
.month-picker {
display: grid;
grid-template-columns: 40px 1fr 40px;
justify-content: center;
align-items: center;
}
.calendar-container {
display: grid;
height: 100%;
grid-template-rows: repeat(3, auto);
grid-gap: 5px;
}
.week-days {
display: grid;
grid-template-columns: repeat(7, 1fr);
}
.day-picker {
display: grid;
grid-template-columns: repeat(7, 1fr);
}
.centreText {
text-align: center;
}
</style>