mirror of
https://github.com/carbon-design-system/carbon-components-svelte.git
synced 2025-09-14 18:01:06 +00:00
* docs: update number of available Carbon icons * docs: add svelte:head example for loading CDN styles * chore(deps-dev): upgrade svelte to 3.40.1 * fix(a11y): disable a11y-mouse-events-have-key-events warning #760 * fix(deps): upgrade carbon-icons-svelte to v10.36.0 to avoid a11y warnings #760 * refactor(overflow-menu): remove formatStyle utility * docs: links in paragraphs should be inline * docs: add note on using all styles for dynamic theming
226 lines
5.3 KiB
Svelte
226 lines
5.3 KiB
Svelte
<script>
|
|
/**
|
|
* @event {string | { selectedDates: [dateFrom: Date, dateTo?: Date]; dateStr: string | { from: string; to: string; } }} change
|
|
*/
|
|
|
|
/**
|
|
* Specify the date picker type
|
|
* @type {"simple" | "single" | "range"}
|
|
*/
|
|
export let datePickerType = "simple";
|
|
|
|
/**
|
|
* Specify the date picker input value
|
|
* @type {number | string}
|
|
*/
|
|
export let value = "";
|
|
|
|
/**
|
|
* Specify the date picker start date value (from)
|
|
* Only works with the "range" date picker type
|
|
* @type {string}
|
|
*/
|
|
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";
|
|
|
|
/**
|
|
* Specify the maximum date
|
|
* @type {null | string | Date}
|
|
*/
|
|
export let maxDate = null;
|
|
|
|
/**
|
|
* Specify the minimum date
|
|
* @type {null | string | Date}
|
|
*/
|
|
export let minDate = null;
|
|
|
|
/** Specify the locale */
|
|
export let locale = "en";
|
|
|
|
/** Set to `true` to use the short variant */
|
|
export let short = false;
|
|
|
|
/** Set to `true` to enable the light variant */
|
|
export let light = false;
|
|
|
|
/** Set an id for the date picker element */
|
|
export let id = "ccs-" + Math.random().toString(36);
|
|
|
|
import {
|
|
createEventDispatcher,
|
|
setContext,
|
|
afterUpdate,
|
|
onDestroy,
|
|
} from "svelte";
|
|
import { writable, derived } from "svelte/store";
|
|
import { createCalendar } from "./createCalendar";
|
|
|
|
const dispatch = createEventDispatcher();
|
|
const inputs = writable([]);
|
|
const inputIds = derived(inputs, (_) => _.map(({ id }) => id));
|
|
const labelTextEmpty = derived(
|
|
inputs,
|
|
(_) => _.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 = null;
|
|
let datePickerRef = null;
|
|
let inputRef = null;
|
|
let inputRefTo = null;
|
|
|
|
setContext("DatePicker", {
|
|
range,
|
|
inputValue,
|
|
inputValueFrom,
|
|
inputValueTo,
|
|
inputIds,
|
|
hasCalendar,
|
|
add: (data) => {
|
|
inputs.update((_) => [..._, data]);
|
|
},
|
|
declareRef: ({ id, ref }) => {
|
|
if ($inputIds.indexOf(id) === 0) {
|
|
inputRef = ref;
|
|
} else {
|
|
inputRefTo = ref;
|
|
}
|
|
},
|
|
updateValue: ({ type, value }) => {
|
|
if ((!calendar && type === "input") || type === "change") {
|
|
inputValue.set(value);
|
|
}
|
|
|
|
if (!calendar && type === "change") {
|
|
dispatch("change", value);
|
|
}
|
|
},
|
|
blurInput: (relatedTarget) => {
|
|
if (calendar && !calendar.calendarContainer.contains(relatedTarget)) {
|
|
calendar.close();
|
|
}
|
|
},
|
|
openCalendar: () => {
|
|
calendar.open();
|
|
},
|
|
focusCalendar: () => {
|
|
(
|
|
calendar.selectedDateElem ||
|
|
calendar.todayDateElem ||
|
|
calendar.calendarContainer.querySelector(".flatpickr-day[tabindex]") ||
|
|
calendar.calendarContainer
|
|
).focus();
|
|
},
|
|
});
|
|
|
|
async function initCalendar() {
|
|
calendar = await createCalendar({
|
|
options: {
|
|
appendTo: datePickerRef,
|
|
dateFormat,
|
|
defaultDate: $inputValue,
|
|
locale,
|
|
maxDate,
|
|
minDate,
|
|
mode: $mode,
|
|
},
|
|
base: inputRef,
|
|
input: inputRefTo,
|
|
dispatch: (event) => {
|
|
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;
|
|
}
|
|
|
|
return dispatch(event, detail);
|
|
},
|
|
});
|
|
}
|
|
|
|
afterUpdate(() => {
|
|
if (calendar) {
|
|
if ($range) {
|
|
calendar.setDate([$inputValueFrom, $inputValueTo]);
|
|
|
|
// workaround to remove the default range plugin separator "to"
|
|
inputRef.value = $inputValueFrom;
|
|
} else {
|
|
calendar.setDate($inputValue);
|
|
}
|
|
}
|
|
});
|
|
|
|
onDestroy(() => {
|
|
if (calendar) {
|
|
calendar.destroy();
|
|
}
|
|
});
|
|
|
|
$: inputValue.set(value);
|
|
$: value = $inputValue;
|
|
$: inputValueFrom.set(valueFrom);
|
|
$: valueFrom = $inputValueFrom;
|
|
$: inputValueTo.set(valueTo);
|
|
$: valueTo = $inputValueTo;
|
|
$: if ($hasCalendar && !calendar && inputRef) initCalendar();
|
|
</script>
|
|
|
|
<svelte:window
|
|
on:click="{({ target }) => {
|
|
if (!calendar || !calendar.isOpen) return;
|
|
if (datePickerRef && datePickerRef.contains(target)) return;
|
|
if (!calendar.calendarContainer.contains(target)) calendar.close();
|
|
}}"
|
|
/>
|
|
|
|
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
|
|
<div
|
|
class:bx--form-item="{true}"
|
|
{...$$restProps}
|
|
on:click
|
|
on:mouseover
|
|
on:mouseenter
|
|
on:mouseleave
|
|
>
|
|
<div
|
|
bind:this="{datePickerRef}"
|
|
id="{id}"
|
|
class:bx--date-picker="{true}"
|
|
class:bx--date-picker--short="{short}"
|
|
class:bx--date-picker--light="{light}"
|
|
class="{datePickerType &&
|
|
`bx--date-picker--${datePickerType}`}
|
|
{datePickerType === 'range' &&
|
|
$labelTextEmpty &&
|
|
'bx--date-picker--nolabel'}"
|
|
>
|
|
<slot />
|
|
</div>
|
|
</div>
|