DatePicker
DatePicker
These are widgets for selecting dates and date ranges. The DatePicker component returns a JavaScript Date object via the onChange function, while the DateRangePicker returns a JavaScript Date array.
import { Flex } from '@semcore/ui/base-components';
import { DatePicker, DateRangePicker } from '@semcore/ui/date-picker';
import { Text } from '@semcore/ui/typography';
import React from 'react';
const Demo = () => {
const [value, setValue] = React.useState(new Date('06/29/2020'));
const [valueRange, setValueRange] = React.useState([]);
return (
<Flex gap={5} flexWrap>
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='simple-date-picker'>
Simple date picker
</Text>
<DatePicker value={value} onChange={(date: any) => setValue(date)}>
<DatePicker.Trigger mt={2} id='simple-date-picker' />
<DatePicker.Popper />
</DatePicker>
</Flex>
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='simple-date-range-picker'>
Date range picker
</Text>
<DateRangePicker value={valueRange} onChange={(date: any) => setValueRange(date)}>
<DateRangePicker.Trigger mt={2} id='simple-date-range-picker' />
<DateRangePicker.Popper />
</DateRangePicker>
</Flex>
</Flex>
);
};
export default Demo;
MonthRangePicker
These are widgets for selecting a single month and a range of months, respectively. The API is similar to that of the DatePicker and DateRangePicker components.
import { Flex } from '@semcore/ui/base-components';
import { MonthPicker, MonthRangePicker } from '@semcore/ui/date-picker';
import { Text } from '@semcore/ui/typography';
import React from 'react';
const Demo = () => {
const [value, setValue] = React.useState(new Date());
const [valueRange, setValueRange] = React.useState([]);
return (
<Flex gap={5} flexWrap>
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='simple-month-picker'>
Simple month picker
</Text>
<MonthPicker value={value} onChange={(date: any) => setValue(date)}>
<MonthPicker.Trigger mt={2} id='simple-month-picker' />
<MonthPicker.Popper />
</MonthPicker>
</Flex>
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='simple-month-range-picker'>
Month range picker
</Text>
<MonthRangePicker value={valueRange} onChange={(date: any) => setValueRange(date)}>
<MonthRangePicker.Trigger mt={2} id='simple-month-range-picker' />
<MonthRangePicker.Popper />
</MonthRangePicker>
</Flex>
</Flex>
);
};
export default Demo;
Trigger and Popper
To access the internal components, you must expand the component. The Trigger and Popper components are wrapped by Dropdown.Trigger and Dropdown.Popper, respectively. All the properties and examples available in Dropdown also work in DatePicker.
import { Flex } from '@semcore/ui/base-components';
import { DatePicker } from '@semcore/ui/date-picker';
import { Text } from '@semcore/ui/typography';
import React from 'react';
const Demo = () => {
return (
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='trigger-and-popper-example-picker'>
Date picker
</Text>
<DatePicker>
<DatePicker.Trigger mt={2}>
<DatePicker.Trigger.SingleDateInput>
<DatePicker.Trigger.SingleDateInput.Indicator />
<DatePicker.Trigger.SingleDateInput.MaskedInput id='trigger-and-popper-example-picker' />
</DatePicker.Trigger.SingleDateInput>
</DatePicker.Trigger>
<DatePicker.Popper />
</DatePicker>
</Flex>
);
};
export default Demo;
Custom header
You can change the header layout by expanding the component further.
import { Flex } from '@semcore/ui/base-components';
import { DatePicker } from '@semcore/ui/date-picker';
import { Text } from '@semcore/ui/typography';
import React from 'react';
const Demo = () => {
return (
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='custom-header-example-picker'>
Date picker
</Text>
<DatePicker>
<DatePicker.Trigger mt={2} id='custom-header-example-picker' />
<DatePicker.Popper>
<DatePicker.Header>
<DatePicker.Prev />
<DatePicker.Title>
{({ displayedPeriod }) =>
typeof displayedPeriod === 'string'
? displayedPeriod
: new Intl.DateTimeFormat('en-US', {
month: 'short',
year: 'numeric',
}).format(displayedPeriod)}
</DatePicker.Title>
<DatePicker.Next />
</DatePicker.Header>
<DatePicker.Calendar />
</DatePicker.Popper>
</DatePicker>
</Flex>
);
};
export default Demo;
Custom day
Calendar days can have metrics, and you can change the units by passing a function to the Calendar component.
import { Flex } from '@semcore/ui/base-components';
import { DatePicker } from '@semcore/ui/date-picker';
import ProgressBar from '@semcore/ui/progress-bar';
import { Text } from '@semcore/ui/typography';
import React from 'react';
const stableRandom = (randomIndex: number) => {
return Math.abs(Math.sin(Math.PI * randomIndex * Math.cos(100 - randomIndex++)));
};
const Demo = () => {
return (
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='custom-day-example-picker'>
Date picker
</Text>
<div>
<DatePicker>
<DatePicker.Trigger mt={2} id='custom-day-example-picker' />
<DatePicker.Popper>
<DatePicker.Header />
<DatePicker.Calendar>
{({ days }) =>
days.map((data, i) => {
const progress = stableRandom(i) * 100;
return (
<DatePicker.Calendar.Unit {...data} key={i}>
<Flex direction='column' p={1} w='100%' alignItems='center'>
{data.children}
<ProgressBar
size='s'
duration={0}
value={progress}
theme='dark'
mt={1}
aria-label={`Progress is ${progress.toFixed(2)}%`}
/>
</Flex>
</DatePicker.Calendar.Unit>
);
})}
</DatePicker.Calendar>
</DatePicker.Popper>
</DatePicker>
</div>
</Flex>
);
};
export default Demo;
Disabled dates
You can prevent selection of certain dates or a range of dates using the disabled property (and imagine yourself as a superhero 🕺🏻). The property takes an array of dates or an array with two dates to specify a range, or a crontab format for selecting dates periodically.
import { Flex } from '@semcore/ui/base-components';
import { DatePicker } from '@semcore/ui/date-picker';
import { Text } from '@semcore/ui/typography';
import React from 'react';
const Demo = () => {
const today = new Date();
return (
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='disabled-dates-example-picker'>
Date picker
</Text>
<DatePicker
disabled={[new Date(today.getFullYear(), 0, 1), [today, false], '* * 6,7']}
disabledErrorText='January 1 of this year is off-limits, only dates before today work, and Saturdays are a no-go.'
>
<DatePicker.Trigger mt={2} id='disabled-dates-example-picker' />
<DatePicker.Popper />
</DatePicker>
</Flex>
);
};
export default Demo;
Custom date ranges
Date ranges may be customized or switched off at all, by transferring periods={[]}.
import { Flex } from '@semcore/ui/base-components';
import { DateRangePicker } from '@semcore/ui/date-picker';
import { Text } from '@semcore/ui/typography';
import React from 'react';
const Demo = () => {
const pastYear = new Date();
pastYear.setFullYear(pastYear.getFullYear() - 1);
const past6days = new Date();
past6days.setDate(past6days.getDate() - 6);
const past13days = new Date();
past13days.setDate(past13days.getDate() - 13);
const periods = [
{ children: 'Last 7 days', value: [past6days, new Date()] },
{ children: 'Last 14 days', value: [past13days, new Date()] },
{ children: 'Last Year', value: [pastYear, new Date()] },
];
return (
<Flex gap={5} flexWrap>
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='customized-periods'>
Customized periods
</Text>
<DateRangePicker periods={periods}>
<DateRangePicker.Trigger mt={2} id='customized-periods' />
<DateRangePicker.Popper />
</DateRangePicker>
</Flex>
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='normal-periods'>
Normal periods
</Text>
<DateRangePicker>
<DateRangePicker.Trigger mt={2} id='normal-periods' />
<DateRangePicker.Popper />
</DateRangePicker>
</Flex>
</Flex>
);
};
export default Demo;
Week picker
You can manually select a custom period, if needed (for example, a week) by taking all the control in manual mode. For example, you can create the WeekPeaker using DateRangePicker.
import { Flex } from '@semcore/ui/base-components';
import { DateRangePicker } from '@semcore/ui/date-picker';
import { Text } from '@semcore/ui/typography';
import React from 'react';
function dateToClosestWeek(date: Date): [Date, Date] {
const startWeek = new Date(date);
const endWeek = new Date(date);
startWeek.setDate(startWeek.getDate() + 1 - (startWeek.getDay() || 7));
endWeek.setDate(endWeek.getDate() + 7 - (endWeek.getDay() || 7));
return [startWeek, endWeek];
}
const Demo = () => {
const [visible, setVisible] = React.useState(false);
const [value, setValue] = React.useState<Date[]>([]);
const [highlighted, setHighlighted] = React.useState<Date[]>([]);
const [preselected, setPreselected] = React.useState<Date[]>([]);
const handleHighlightedChange = React.useCallback((highlighted: Date[]) => {
if (highlighted.length === 0) {
setHighlighted([]);
setPreselected([]);
return;
}
const week = dateToClosestWeek(highlighted[0]);
setHighlighted(week);
setPreselected(week);
}, []);
const handleChange = React.useCallback((value: Date[]) => {
if (value.length === 0) {
setValue([]);
setHighlighted([]);
setPreselected([]);
return;
}
const week = dateToClosestWeek(value[0]);
setValue(week);
setHighlighted(week);
setPreselected(week);
setVisible(false);
}, []);
return (
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='week-picker'>
Week picker
</Text>
<DateRangePicker
visible={visible}
onVisibleChange={setVisible}
value={value}
onChange={handleChange}
highlighted={highlighted}
preselectedValue={preselected}
onPreselectedValueChange={handleChange}
onHighlightedChange={handleHighlightedChange}
>
<DateRangePicker.Trigger mt={2}>
<DateRangePicker.Trigger.DateRange>
<DateRangePicker.Trigger.DateRange.Indicator />
<DateRangePicker.Trigger.DateRange.FromMaskedInput id='week-picker' />
<DateRangePicker.Trigger.DateRange.RangeSep />
<DateRangePicker.Trigger.DateRange.ToMaskedInput disabled />
</DateRangePicker.Trigger.DateRange>
</DateRangePicker.Trigger>
<DateRangePicker.Popper>
<DateRangePicker.Header />
<DateRangePicker.Calendar renderOutdated />
</DateRangePicker.Popper>
</DateRangePicker>
</Flex>
);
};
export default Demo;
DateRangeComparator
DateRangeComparator allows user to compare two date ranges. Additional date range may be controlled with compare and onCompareChange props.
import { Flex } from '@semcore/ui/base-components';
import { DateRangeComparator, MonthDateRangeComparator } from '@semcore/ui/date-picker';
import React from 'react';
const Demo = () => {
return (
<Flex gap={4} flexWrap>
<DateRangeComparator />
<MonthDateRangeComparator />
</Flex>
);
};
export default Demo;
DateRangeComparator advanced usage
DateRangeComparator internal structure maybe deeply redefined for flexible customization.
import { Flex } from '@semcore/ui/base-components';
import { DateRangeComparator, MonthDateRangeComparator } from '@semcore/ui/date-picker';
import WarningIcon from '@semcore/ui/icon/Warning/m';
import Notice from '@semcore/ui/notice';
import { Text } from '@semcore/ui/typography';
import React from 'react';
const Demo = () => {
return (
<Flex flexWrap>
<DateRangeComparator>
<DateRangeComparator.Trigger />
<DateRangeComparator.Popper>
<DateRangeComparator.Header>
<DateRangeComparator.ValueDateRange />
<DateRangeComparator.CompareToggle />
<DateRangeComparator.CompareDateRange />
</DateRangeComparator.Header>
<DateRangeComparator.Body>
<DateRangeComparator.RangeCalendar>
<Flex direction='column'>
<DateRangeComparator.CalendarHeader tag={Flex}>
<DateRangeComparator.Prev />
<DateRangeComparator.Title />
</DateRangeComparator.CalendarHeader>
<DateRangeComparator.Calendar />
</Flex>
<Flex direction='column'>
<DateRangeComparator.CalendarHeader tag={Flex}>
<DateRangeComparator.Title />
<DateRangeComparator.Next />
</DateRangeComparator.CalendarHeader>
<DateRangeComparator.Calendar />
</Flex>
</DateRangeComparator.RangeCalendar>
<DateRangeComparator.Periods>
<DateRangeComparator.Periods.Divider />
<DateRangeComparator.Periods.Column>
<DateRangeComparator.Periods.Options />
<DateRangeComparator.Periods.Controls>
<DateRangeComparator.Apply />
<DateRangeComparator.Reset />
</DateRangeComparator.Periods.Controls>
</DateRangeComparator.Periods.Column>
</DateRangeComparator.Periods>
</DateRangeComparator.Body>
<DateRangeComparator.Footer>
Place for a hint, useful message or controls.
</DateRangeComparator.Footer>
<Notice
aria-labelledby='comparator-notice-title'
theme='warning'
style={{ borderTopLeftRadius: 0, borderTopRightRadius: 0 }}
>
<Notice.Content>
<Flex mb={1}>
<Notice.Label>
<WarningIcon />
</Notice.Label>
<Text bold id='comparator-notice-title'>
Warning
</Text>
</Flex>
If you change your location, all previously collected data for this article will be
lost.
</Notice.Content>
</Notice>
</DateRangeComparator.Popper>
</DateRangeComparator>
</Flex>
);
};
export default Demo;
MonthRangeComparator advanced usage
MonthDateRangeComparator internal structure maybe deeply redefined for flexible customization.
import { Flex } from '@semcore/ui/base-components';
import { MonthDateRangeComparator } from '@semcore/ui/date-picker';
import WarningIcon from '@semcore/ui/icon/Warning/m';
import Notice from '@semcore/ui/notice';
import { Text } from '@semcore/ui/typography';
import React from 'react';
const Demo = () => {
return (
<Flex flexWrap>
<MonthDateRangeComparator>
<MonthDateRangeComparator.Trigger />
<MonthDateRangeComparator.Popper>
<MonthDateRangeComparator.Header>
<MonthDateRangeComparator.ValueDateRange />
<MonthDateRangeComparator.CompareToggle />
<MonthDateRangeComparator.CompareDateRange />
</MonthDateRangeComparator.Header>
<MonthDateRangeComparator.Body>
<MonthDateRangeComparator.RangeCalendar>
<Flex direction='column'>
<MonthDateRangeComparator.CalendarHeader tag={Flex}>
<MonthDateRangeComparator.Prev />
<MonthDateRangeComparator.Title />
</MonthDateRangeComparator.CalendarHeader>
<MonthDateRangeComparator.Calendar />
</Flex>
<Flex direction='column'>
<MonthDateRangeComparator.CalendarHeader tag={Flex}>
<MonthDateRangeComparator.Title />
<MonthDateRangeComparator.Next />
</MonthDateRangeComparator.CalendarHeader>
<MonthDateRangeComparator.Calendar />
</Flex>
</MonthDateRangeComparator.RangeCalendar>
<MonthDateRangeComparator.Periods>
<MonthDateRangeComparator.Periods.Divider />
<MonthDateRangeComparator.Periods.Column>
<MonthDateRangeComparator.Periods.Options />
<MonthDateRangeComparator.Periods.Controls>
<MonthDateRangeComparator.Apply />
<MonthDateRangeComparator.Reset />
</MonthDateRangeComparator.Periods.Controls>
</MonthDateRangeComparator.Periods.Column>
</MonthDateRangeComparator.Periods>
</MonthDateRangeComparator.Body>
<MonthDateRangeComparator.Footer>
Place for a hint, useful message or controls.
</MonthDateRangeComparator.Footer>
<Notice
aria-labelledby='month-notice-title'
theme='warning'
style={{ borderTopLeftRadius: 0, borderTopRightRadius: 0 }}
>
<Notice.Content>
<Flex mb={1}>
<Notice.Label>
<WarningIcon />
</Notice.Label>
<Text bold id='month-notice-title'>
Warning
</Text>
</Flex>
If you change your location, all previously collected data for this article will be
lost.
</Notice.Content>
</Notice>
</MonthDateRangeComparator.Popper>
</MonthDateRangeComparator>
</Flex>
);
};
export default Demo;