DatePicker rework (#737)

* fix(date-picker): append calendar to date picker element #345

Fixes #345

* fix(date-picker): do not import rangePlugin from esm folder

* fix(date-picker): correctly type change event for single/range types

* feat(date-picker): add valueFrom, valueTo for range datepicker

* docs(date-picker): add range type example

* docs(date-picker): extract range example to iframe

* docs(date-picker): extract single type to iframe
This commit is contained in:
Eric Liu 2021-07-08 16:33:03 -07:00 committed by GitHub
commit edefd6429b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 139 additions and 56 deletions

View file

@ -1047,18 +1047,19 @@ None.
### Props
| Prop name | Kind | Reactive | Type | Default value | Description |
| :------------- | :--------------- | :------- | :--------------------------------------------------- | ------------------------------------------------ | --------------------------------------------- |
| value | <code>let</code> | Yes | <code>number &#124; string</code> | <code>""</code> | Specify the date picker input value |
| datePickerType | <code>let</code> | No | <code>"simple" &#124; "single" &#124; "range"</code> | <code>"simple"</code> | Specify the date picker type |
| appendTo | <code>let</code> | No | <code>HTMLElement</code> | -- | Specify the element to append the calendar to |
| dateFormat | <code>let</code> | No | <code>string</code> | <code>"m/d/Y"</code> | Specify the date format |
| maxDate | <code>let</code> | No | <code>null &#124; string &#124; Date</code> | <code>null</code> | Specify the maximum date |
| minDate | <code>let</code> | No | <code>null &#124; string &#124; Date</code> | <code>null</code> | Specify the minimum date |
| locale | <code>let</code> | No | <code>string</code> | <code>"en"</code> | Specify the locale |
| short | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to use the short variant |
| light | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to enable the light variant |
| id | <code>let</code> | No | <code>string</code> | <code>"ccs-" + Math.random().toString(36)</code> | Set an id for the date picker element |
| Prop name | Kind | Reactive | Type | Default value | Description |
| :------------- | :--------------- | :------- | :--------------------------------------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------- |
| valueTo | <code>let</code> | Yes | <code>string</code> | <code>""</code> | Specify the date picker end date value (to)<br />Only works with the "range" date picker type |
| valueFrom | <code>let</code> | Yes | <code>string</code> | <code>""</code> | Specify the date picker start date value (from)<br />Only works with the "range" date picker type |
| value | <code>let</code> | Yes | <code>number &#124; string</code> | <code>""</code> | Specify the date picker input value |
| datePickerType | <code>let</code> | No | <code>"simple" &#124; "single" &#124; "range"</code> | <code>"simple"</code> | Specify the date picker type |
| dateFormat | <code>let</code> | No | <code>string</code> | <code>"m/d/Y"</code> | Specify the date format |
| maxDate | <code>let</code> | No | <code>null &#124; string &#124; Date</code> | <code>null</code> | Specify the maximum date |
| minDate | <code>let</code> | No | <code>null &#124; string &#124; Date</code> | <code>null</code> | Specify the minimum date |
| locale | <code>let</code> | No | <code>string</code> | <code>"en"</code> | Specify the locale |
| short | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to use the short variant |
| light | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to enable the light variant |
| id | <code>let</code> | No | <code>string</code> | <code>"ccs-" + Math.random().toString(36)</code> | Set an id for the date picker element |
### Slots
@ -1068,13 +1069,13 @@ None.
### Events
| Event name | Type | Detail |
| :--------- | :--------- | :----- |
| click | forwarded | -- |
| mouseover | forwarded | -- |
| mouseenter | forwarded | -- |
| mouseleave | forwarded | -- |
| change | dispatched | -- |
| Event name | Type | Detail |
| :--------- | :--------- | :---------------------------------------------------------------------------------------------------------------------------------- |
| change | dispatched | <code>string &#124; { selectedDates: [dateFrom: Date, dateTo?: Date]; dateStr: string &#124; { from: string; to: string; } }</code> |
| click | forwarded | -- |
| mouseover | forwarded | -- |
| mouseenter | forwarded | -- |
| mouseleave | forwarded | -- |
## `DatePickerInput`

View file

@ -2393,13 +2393,24 @@
"reactive": true
},
{
"name": "appendTo",
"name": "valueFrom",
"kind": "let",
"description": "Specify the element to append the calendar to",
"type": "HTMLElement",
"description": "Specify the date picker start date value (from)\nOnly works with the \"range\" date picker type",
"type": "string",
"value": "\"\"",
"isFunction": false,
"constant": false,
"reactive": false
"reactive": true
},
{
"name": "valueTo",
"kind": "let",
"description": "Specify the date picker end date value (to)\nOnly works with the \"range\" date picker type",
"type": "string",
"value": "\"\"",
"isFunction": false,
"constant": false,
"reactive": true
},
{
"name": "dateFormat",
@ -2474,11 +2485,15 @@
],
"slots": [{ "name": "__default__", "default": true, "slot_props": "{}" }],
"events": [
{
"type": "dispatched",
"name": "change",
"detail": "string | { selectedDates: [dateFrom: Date, dateTo?: Date]; dateStr: string | { from: string; to: string; } }"
},
{ "type": "forwarded", "name": "click", "element": "div" },
{ "type": "forwarded", "name": "mouseover", "element": "div" },
{ "type": "forwarded", "name": "mouseenter", "element": "div" },
{ "type": "forwarded", "name": "mouseleave", "element": "div" },
{ "type": "dispatched", "name": "change" }
{ "type": "forwarded", "name": "mouseleave", "element": "div" }
],
"typedefs": [],
"rest_props": { "type": "Element", "name": "div" }

View file

@ -57,9 +57,11 @@ components: ["DatePicker", "DatePickerInput", "DatePickerSkeleton"]
### Single
<DatePicker datePickerType="single">
<DatePickerInput labelText="Schedule a meeting" placeholder="mm/dd/yyyy" />
</DatePicker>
<FileSource src="/framed/DatePicker/DatePickerSingle" />
### Range
<FileSource src="/framed/DatePicker/DatePickerRange" />
### Skeleton

View file

@ -0,0 +1,8 @@
<script>
import { DatePicker, DatePickerInput } from "carbon-components-svelte";
</script>
<DatePicker datePickerType="range" on:change>
<DatePickerInput labelText="Start date" placeholder="mm/dd/yyyy" />
<DatePickerInput labelText="End date" placeholder="mm/dd/yyyy" />
</DatePicker>

View file

@ -0,0 +1,7 @@
<script>
import { DatePicker, DatePickerInput } from "carbon-components-svelte";
</script>
<DatePicker datePickerType="single" on:change>
<DatePickerInput labelText="Meeting date" placeholder="mm/dd/yyyy" />
</DatePicker>

View file

@ -1,6 +1,6 @@
module.exports = {
optimizeDeps: {
include: ["clipboard-copy"],
include: ["clipboard-copy", "flatpickr/dist/plugins/rangePlugin"],
exclude: ["@sveltech/routify"],
},
};

View file

@ -1,6 +1,6 @@
<script>
/**
* @dispatch {string} change
* @event {string | { selectedDates: [dateFrom: Date, dateTo?: Date]; dateStr: string | { from: string; to: string; } }} change
*/
/**
@ -16,10 +16,18 @@
export let value = "";
/**
* Specify the element to append the calendar to
* @type {HTMLElement}
* Specify the date picker start date value (from)
* Only works with the "range" date picker type
* @type {string}
*/
export let appendTo = document.body;
export let valueFrom = "";
/**
* Specify the date picker end date value (to)
* Only works with the "range" date picker type
* @type {string}
*/
export let valueTo = "";
/** Specify the date format */
export let dateFormat = "m/d/Y";
@ -65,18 +73,23 @@
(_) => _.filter(({ labelText }) => !!labelText).length === 0
);
const inputValue = writable(value);
const inputValueFrom = writable(valueFrom);
const inputValueTo = writable(valueTo);
const mode = writable(datePickerType);
const range = derived(mode, (_) => _ === "range");
const hasCalendar = derived(mode, (_) => _ === "single" || _ === "range");
let calendar = undefined;
let datePickerRef = undefined;
let inputRef = undefined;
let inputRefTo = undefined;
let calendar = null;
let datePickerRef = null;
let inputRef = null;
let inputRefTo = null;
setContext("DatePicker", {
range,
inputValue,
inputValueFrom,
inputValueTo,
inputIds,
hasCalendar,
add: (data) => {
inputs.update((_) => [..._, data]);
@ -119,7 +132,7 @@
if ($hasCalendar && !calendar) {
calendar = createCalendar({
options: {
appendTo,
appendTo: datePickerRef,
dateFormat,
defaultDate: $inputValue,
locale,
@ -133,10 +146,16 @@
const detail = { selectedDates: calendar.selectedDates };
if ($range) {
const from = inputRef.value;
const to = inputRefTo.value;
detail.dateStr = {
from: inputRef.value,
to: inputRefTo.value,
};
valueFrom = from;
valueTo = to;
} else {
detail.dateStr = inputRef.value;
}
@ -146,8 +165,15 @@
});
}
if (calendar && !$range) {
calendar.setDate($inputValue);
if (calendar) {
if ($range) {
calendar.setDate([$inputValueFrom, $inputValueTo]);
// workaround to remove the default range plugin separator "to"
inputRef.value = $inputValueFrom;
} else {
calendar.setDate($inputValue);
}
}
});
@ -159,19 +185,17 @@
$: inputValue.set(value);
$: value = $inputValue;
$: inputValueFrom.set(valueFrom);
$: valueFrom = $inputValueFrom;
$: inputValueTo.set(valueTo);
$: valueTo = $inputValueTo;
</script>
<svelte:body
on:click="{({ target }) => {
if (!calendar || !calendar.isOpen) {
return;
}
if (datePickerRef && datePickerRef.contains(target)) {
return;
}
if (!calendar.calendarContainer.contains(target)) {
calendar.close();
}
if (!calendar || !calendar.isOpen) return;
if (datePickerRef && datePickerRef.contains(target)) return;
if (!calendar.calendarContainer.contains(target)) calendar.close();
}}" />
<div

View file

@ -60,11 +60,14 @@
add,
hasCalendar,
declareRef,
inputIds,
updateValue,
blurInput,
openCalendar,
focusCalendar,
inputValue,
inputValueFrom,
inputValueTo,
} = getContext("DatePicker");
add({ id, labelText });
@ -105,7 +108,11 @@
pattern="{pattern}"
disabled="{disabled}"
{...$$restProps}
value="{!$range ? $inputValue : undefined}"
value="{$range
? $inputIds.indexOf(id) === 0
? $inputValueFrom
: $inputValueTo
: $inputValue}"
class:bx--date-picker__input="{true}"
class:bx--date-picker__input--invalid="{invalid}"
class="{size && `bx--date-picker__input--${size}`}"

View file

@ -1,5 +1,5 @@
import flatpickr from "flatpickr";
import rangePlugin from "flatpickr/dist/esm/plugins/rangePlugin";
import rangePlugin from "flatpickr/dist/plugins/rangePlugin";
let l10n;

View file

@ -2,7 +2,11 @@
import { DatePicker, DatePickerSkeleton, DatePickerInput } from "../types";
</script>
<DatePicker>
<DatePicker
on:change="{(e) => {
console.log(e.detail);
}}"
>
<DatePickerInput labelText="Date of birth" placeholder="mm/dd/yyyy" />
</DatePicker>
@ -51,7 +55,7 @@
/>
</DatePicker>
<DatePicker datePickerType="single">
<DatePicker valueFrom="" valueTo="" datePickerType="single">
<DatePickerInput labelText="Schedule a meeting" placeholder="mm/dd/yyyy" />
</DatePicker>

View file

@ -16,9 +16,18 @@ export interface DatePickerProps
value?: number | string;
/**
* Specify the element to append the calendar to
* Specify the date picker start date value (from)
* Only works with the "range" date picker type
* @default ""
*/
appendTo?: HTMLElement;
valueFrom?: string;
/**
* Specify the date picker end date value (to)
* Only works with the "range" date picker type
* @default ""
*/
valueTo?: string;
/**
* Specify the date format
@ -66,11 +75,17 @@ export interface DatePickerProps
export default class DatePicker extends SvelteComponentTyped<
DatePickerProps,
{
change: CustomEvent<
| string
| {
selectedDates: [dateFrom: Date, dateTo?: Date];
dateStr: string | { from: string; to: string };
}
>;
click: WindowEventMap["click"];
mouseover: WindowEventMap["mouseover"];
mouseenter: WindowEventMap["mouseenter"];
mouseleave: WindowEventMap["mouseleave"];
change: CustomEvent<any>;
},
{ default: {} }
> {}