Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to control selected dates from parent component, and react to internal selections in parent components #68

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
33 changes: 28 additions & 5 deletions src/App.svelte
Original file line number Diff line number Diff line change
@@ -59,13 +59,13 @@
<div class="row">
<aside>
<div class="menu-box">
<h4>Navigation</h4>
<h4>Navigation</h4>
<nav class="side-nav">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/date-picker">Date Picker</a></li>
<li><a href="/range-picker">Range Picker</a></li>
</ul>
</ul>
</nav>
</div>
</aside>
@@ -272,7 +272,9 @@
format='ddd, DD MMM YYYY'
range={true}
time={true}
closeOnFocusLoss={false}
on:range-selected={e => {
console.log(e)
firedEvents = [
...firedEvents,
`Picked range ${e.detail.from} to ${e.detail.to}`
@@ -284,7 +286,18 @@
'Change fired'
]
}}
/>
let:selectedStart
let:selectedEnd
forceClose={forcedClose}
>
<div style="border: 1px solid black; padding: 10px; border-radius: 4px;">
<p>{selectedStart}</p>
<p>{selectedEnd}</p>
</div>
<div slot="beforeContents">
<h1>Before Contents Slot</h1>
</div>
</DatePicker>
</div>
<ul>
{#each firedEvents as fired}
@@ -293,6 +306,9 @@
<li>Pick date to see events</li>
{/each}
</ul>
<div style="position: fixed; top: 0; right: 0;">
<button on:click={forceClose}>click to force closed</button>
</div>
</Route>
</Route>
</Route>
@@ -314,7 +330,7 @@
<div class="container">
<div class="row">
<div class="col-lg-12 center">

</div>
</div>
</div>
@@ -335,6 +351,13 @@
let firedEvents = []
let firedEventsValue = null
let customSelected = null

let forcedClose = false
async function forceClose () {
forcedClose = true
await new Promise((resolve) => setTimeout(resolve, 100))
forcedClose = false
}
</script>

<style>
@@ -446,4 +469,4 @@
background-position: 100%;
}
}
</style>
</style>
64 changes: 57 additions & 7 deletions src/components/DatePicker.svelte
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
<script>
import Popover from './Popover.svelte'
import { dayjs } from './lib/date-utils'
import { createEventDispatcher, getContext, setContext } from 'svelte'
import { CalendarStyle } from '../calendar-style'
import { contextKey, setup } from './lib/context'
import { createEventDispatcher, setContext, getContext } from 'svelte'
import { CalendarStyle } from '../calendar-style.js'
import { createViewContext } from './lib/view-context.js'
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'

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')
@@ -23,6 +24,7 @@
export let night = 19
export let minuteStep = 5
export let continueText = 'Continue'
export let forceClose = false

const dispatch = createEventDispatcher()

@@ -33,6 +35,7 @@
start: dayjs(start),
end: dayjs(end),
isRangePicker: range,
defaultRange,
isTimePicker: time,
closeOnFocusLoss,
format,
@@ -66,7 +69,7 @@
highlighted.set($selectedStartDate)
dispatch('open')
}

function setRangeValue () {
selected = [ $selectedStartDate, $selectedEndDate ]
dispatch('range-selected', {
@@ -114,6 +117,50 @@
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
}
}

/**
* Allow forceful closing from outside
*/
$: {
if ($isOpen && forceClose) {
close()
}
}
</script>

<style>
@@ -177,7 +224,7 @@
on:opened={initialisePicker}
on:closed={() => dispatch('close')}>
<div slot="trigger">
<slot formatted={$formatter}>
<slot formatted={$formatter} {selectedStart} {selectedEnd}>
{#if !trigger}
<button class="calendar-button" type="button">
{#if $isDateChosen}
@@ -189,6 +236,9 @@
{/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
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}
71 changes: 36 additions & 35 deletions src/components/Popover.svelte
Original file line number Diff line number Diff line change
@@ -70,34 +70,35 @@
<slot name="trigger">
</slot>
</div>
<div
class="contents-wrapper"
<div
class="contents-wrapper"
class:visible={$isOpen}
class:shrink={$isClosing}
class:is-fullscreen={isFullscreen}
style="top: {translateY}px; left: {translateX}px"
style="top: {translateY}px; left: {translateX}px"
bind:this={contentsWrapper}>
<div class="wrapper" bind:this={contentsAnimated}>
<div class="contents-inner">
<slot name="contents"></slot>
<slot name="beforeContents" />
<slot name="contents" />
</div>
</div>
</div>
</div>

<style>
.sc-popover {
.sc-popover {
position: relative;
}

.contents-wrapper {
.contents-wrapper {
position: fixed;
transition: none;
z-index: 2;
display: none;
}

.contents-wrapper.visible {
.contents-wrapper.visible {
display: block;
}

@@ -109,65 +110,65 @@
overflow: scroll;
}

.contents-wrapper.visible .wrapper {
opacity: 1;
.contents-wrapper.visible .wrapper {
opacity: 1;
transform: scale(1);
display: block;
}
.contents-wrapper.shrink .wrapper {

.contents-wrapper.shrink .wrapper {
animation: shrink 150ms forwards cubic-bezier(.92,.09,.18,1.05);
}

.wrapper {
.wrapper {
background: #fff;
box-shadow: 0px 10px 26px rgba(0,0,0,0.4) ;
opacity: .8;
opacity: .8;
padding-top: 0;
display: none;
animation: grow 200ms forwards cubic-bezier(.92,.09,.18,1.05);
}

.contents-inner {
.contents-inner {
animation: fadeIn 400ms forwards;
}

@keyframes grow {
0% {
transform: scale(.9,.1);
opacity: 0;
@keyframes grow {
0% {
transform: scale(.9,.1);
opacity: 0;
}
30% {
opacity: 1;
30% {
opacity: 1;
}
100% {
100% {
transform: scale(1);
}
}

@keyframes shrink {
0% {
transform: scale(1);
opacity: 1;
@keyframes shrink {
0% {
transform: scale(1);
opacity: 1;
}
70% {
opacity: 1;
70% {
opacity: 1;
}
100% {
opacity: 0;
100% {
opacity: 0;
transform: scale(.9,.1);
}
}

@keyframes fadeIn {
0% {
opacity: 0;
@keyframes fadeIn {
0% {
opacity: 0;
}
50% {
50% {
opacity: 0;
}
100% {
opacity: 1;
100% {
opacity: 1;
}
}
</style>
Loading