Skip to content

Commit

Permalink
Re-do file structure
Browse files Browse the repository at this point in the history
  • Loading branch information
t-lock committed Aug 20, 2024
1 parent 9848001 commit a11c351
Show file tree
Hide file tree
Showing 4 changed files with 282 additions and 282 deletions.
275 changes: 242 additions & 33 deletions src/components/DatePicker.svelte
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>
39 changes: 39 additions & 0 deletions src/components/DatePickerAsyncWrapper.svelte
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}
Loading

0 comments on commit a11c351

Please sign in to comment.