-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
282 additions
and
282 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,39 +1,248 @@ | ||
<script> | ||
import { onMount } from 'svelte' | ||
import DatePickerInner from './DatePickerInner.svelte' | ||
import { setLoadingCursor } from './lib/context' | ||
import { createEventDispatcher, getContext, setContext } from 'svelte' | ||
import { CalendarStyle } from '../calendar-style' | ||
import { contextKey, setup } from './lib/context' | ||
import { dayjs } from './lib/date-utils' | ||
import { createViewContext } from './lib/view-context' | ||
import Popover from './Popover.svelte' | ||
import Toolbar from './Toolbar.svelte' | ||
import View from './view/View.svelte' | ||
let ready = false | ||
export let range = false | ||
export let defaultRange = [ 1, 'month' ] | ||
export let placeholder = 'Choose Date' | ||
export let format = 'DD / MM / YYYY' | ||
export let start = dayjs().subtract(1, 'year') | ||
export let end = dayjs().add(1, 'year') | ||
export let trigger = null | ||
export let selectableCallback = null | ||
export let styling = new CalendarStyle() | ||
export let selected | ||
export let closeOnFocusLoss = true | ||
export let time = false | ||
export let morning = 7 | ||
export let night = 19 | ||
export let minuteStep = 5 | ||
export let continueText = 'Continue' | ||
onMount(async () => { | ||
await setLoadingCursor() | ||
ready = true | ||
}) | ||
const dispatch = createEventDispatcher() | ||
const startContextKey = {} | ||
const endContextKey = {} | ||
const config = { | ||
start: dayjs(start), | ||
end: dayjs(end), | ||
isRangePicker: range, | ||
defaultRange, | ||
isTimePicker: time, | ||
closeOnFocusLoss, | ||
format, | ||
morning, | ||
night, | ||
selectableCallback, | ||
minuteStep: parseInt(minuteStep) | ||
} | ||
setContext(contextKey, setup(selected, config)) | ||
const { | ||
selectedStartDate, | ||
selectedEndDate, | ||
isOpen, | ||
isClosing, | ||
highlighted, | ||
formatter, | ||
isDateChosen, | ||
isSelectingFirstDate | ||
} = getContext(contextKey) | ||
setContext(startContextKey, createViewContext(true, getContext(contextKey))) | ||
if (config.isRangePicker) { | ||
setContext(endContextKey, createViewContext(false, getContext(contextKey))) | ||
} | ||
let popover | ||
function initialisePicker () { | ||
highlighted.set($selectedStartDate) | ||
dispatch('open') | ||
} | ||
function setRangeValue () { | ||
selected = [ $selectedStartDate, $selectedEndDate ] | ||
dispatch('range-selected', { | ||
from: $selectedStartDate.toDate(), | ||
to: $selectedEndDate.toDate() | ||
}) | ||
} | ||
function setDateValue () { | ||
selected = $selectedStartDate.toDate() | ||
dispatch('date-selected', { | ||
date: $selectedStartDate.toDate() | ||
}) | ||
} | ||
function swapDatesIfRequired () { | ||
if (!config.isRangePicker) { return } | ||
const from = $selectedStartDate | ||
const to = $selectedEndDate | ||
if (to.isBefore(from)) { | ||
selectedStartDate.set(to) | ||
selectedEndDate.set(from) | ||
} | ||
} | ||
function addDate (e) { | ||
const { date } = e.detail | ||
if ($isSelectingFirstDate) { | ||
selectedStartDate.set(date) | ||
} else { | ||
selectedEndDate.set(date) | ||
} | ||
swapDatesIfRequired() | ||
config.isRangePicker && isSelectingFirstDate.update(v => !v) | ||
} | ||
function close () { | ||
swapDatesIfRequired() | ||
popover.close() | ||
} | ||
$: { | ||
if ($isDateChosen) { | ||
config.isRangePicker ? setRangeValue() : setDateValue() | ||
dispatch('change') | ||
} | ||
} | ||
/** | ||
* Allow external sources to update dates by binding to selected prop | ||
* and updating with JS Date objects | ||
*/ | ||
$: { | ||
if (config.isRangePicker && selected) { | ||
if (selected[0] instanceof Date) { | ||
selectedStartDate.set(dayjs(selected[0])) | ||
} | ||
if (selected[1] instanceof Date) { | ||
selectedEndDate.set(dayjs(selected[1])) | ||
} | ||
} | ||
} | ||
/** | ||
* Allow external sources to react to internal selections via event forwarding | ||
*/ | ||
let selectedStart = null | ||
$: { | ||
if ($selectedStartDate) { | ||
selectedStart = $selectedStartDate.toDate() | ||
} else { | ||
selectedStart = null | ||
} | ||
} | ||
let selectedEnd = null | ||
$: { | ||
if ($selectedEndDate) { | ||
selectedEnd = $selectedEndDate.toDate() | ||
} else { | ||
selectedEnd = null | ||
} | ||
} | ||
</script> | ||
|
||
{#if ready} | ||
{#if $$slots.default} | ||
<DatePickerInner {...$$restProps} | ||
on:open | ||
on:range-selected | ||
on:date-selected | ||
on:change | ||
on:close | ||
let:selectedStart | ||
let:selectedEnd | ||
> | ||
<slot {selectedStart} {selectedEnd} /> | ||
<div slot="beforeContents"> | ||
<slot name="beforeContents" {selectedStart} {selectedEnd} /> | ||
<style> | ||
.datepicker { | ||
display: inline-block; | ||
text-align: center; | ||
overflow: visible; | ||
width: var(--datepicker-width); | ||
} | ||
.calendar-button { | ||
padding: 10px 20px; | ||
border: 1px solid var(--button-border-color); | ||
display: block; | ||
text-align: center; | ||
width: var(--button-width); | ||
text-decoration: none; | ||
cursor: pointer; | ||
background: var(--button-background-color); | ||
color: var(--button-text-color); | ||
border-radius: 7px; | ||
box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.1); | ||
} | ||
*, | ||
*:before, | ||
*:after { | ||
box-sizing: inherit; | ||
} | ||
.contents { | ||
min-width: 320px; | ||
width: 100%; | ||
display: flex; | ||
flex-direction: column; | ||
background: var(--content-background); | ||
} | ||
.view { | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
} | ||
@media (min-width: 680px) { | ||
.view { | ||
flex-direction: row; | ||
justify-content: center; | ||
} | ||
} | ||
</style> | ||
|
||
<div | ||
class="datepicker" | ||
class:open={$isOpen} | ||
class:closing={$isClosing} | ||
style={styling.toWrapperStyle()}> | ||
<Popover | ||
{trigger} | ||
bind:this={popover} | ||
on:opened={initialisePicker} | ||
on:closed={() => dispatch('close')}> | ||
<div slot="trigger"> | ||
<slot formatted={$formatter} {selectedStart} {selectedEnd}> | ||
{#if !trigger} | ||
<button class="calendar-button" type="button"> | ||
{#if $isDateChosen} | ||
{$formatter.formattedCombined} | ||
{:else} | ||
{placeholder} | ||
{/if} | ||
</button> | ||
{/if} | ||
</slot> | ||
</div> | ||
<div slot="beforeContents"> | ||
<slot name="beforeContents" {selectedStart} {selectedEnd} /> | ||
</div> | ||
<div class="contents" slot="contents" class:is-range-picker={config.isRangePicker}> | ||
<div class="view"> | ||
<View | ||
viewContextKey={startContextKey} | ||
on:chosen={addDate} | ||
/> | ||
{#if config.isRangePicker} | ||
<View | ||
viewContextKey={endContextKey} | ||
on:chosen={addDate} | ||
/> | ||
{/if} | ||
</div> | ||
</DatePickerInner> | ||
{:else} | ||
<DatePickerInner {...$$restProps} | ||
on:open | ||
on:range-selected | ||
on:date-selected | ||
on:change | ||
on:close | ||
/> | ||
{/if} | ||
{/if} | ||
<Toolbar continueText={continueText} on:close={close} /> | ||
</div> | ||
</Popover> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<script> | ||
import { onMount } from 'svelte' | ||
import DatePickerInner from './DatePicker.svelte' | ||
import { setLoadingCursor } from './lib/context' | ||
let ready = false | ||
onMount(async () => { | ||
await setLoadingCursor() | ||
ready = true | ||
}) | ||
</script> | ||
|
||
{#if ready} | ||
{#if $$slots.default} | ||
<DatePickerInner {...$$restProps} | ||
on:open | ||
on:range-selected | ||
on:date-selected | ||
on:change | ||
on:close | ||
let:selectedStart | ||
let:selectedEnd | ||
> | ||
<slot {selectedStart} {selectedEnd} /> | ||
<div slot="beforeContents"> | ||
<slot name="beforeContents" {selectedStart} {selectedEnd} /> | ||
</div> | ||
</DatePickerInner> | ||
{:else} | ||
<DatePickerInner {...$$restProps} | ||
on:open | ||
on:range-selected | ||
on:date-selected | ||
on:change | ||
on:close | ||
/> | ||
{/if} | ||
{/if} |
Oops, something went wrong.