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 React from 'react';
import { DatePicker, DateRangePicker } from '@semcore/ui/date-picker';
import { Box, Flex } from '@semcore/ui/flex-box';
const Demo = () => {
const [value, setValue] = React.useState(new Date('06/29/2020'));
const [valueRange, setValueRange] = React.useState([]);
return (
<Flex>
<Box>
<DatePicker value={value} onChange={(date) => setValue(date)}>
<DatePicker.Trigger />
<DatePicker.Popper />
</DatePicker>
</Box>
<Box ml={5}>
<DateRangePicker value={valueRange} onChange={(date) => setValueRange(date)}>
<DateRangePicker.Trigger />
<DateRangePicker.Popper />
</DateRangePicker>
</Box>
</Flex>
);
};
import React from 'react';
import { DatePicker, DateRangePicker } from '@semcore/ui/date-picker';
import { Box, Flex } from '@semcore/ui/flex-box';
const Demo = () => {
const [value, setValue] = React.useState(new Date('06/29/2020'));
const [valueRange, setValueRange] = React.useState([]);
return (
<Flex>
<Box>
<DatePicker value={value} onChange={(date) => setValue(date)}>
<DatePicker.Trigger />
<DatePicker.Popper />
</DatePicker>
</Box>
<Box ml={5}>
<DateRangePicker value={valueRange} onChange={(date) => setValueRange(date)}>
<DateRangePicker.Trigger />
<DateRangePicker.Popper />
</DateRangePicker>
</Box>
</Flex>
);
};
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 React from 'react';
import { MonthPicker, MonthRangePicker } from '@semcore/ui/date-picker';
import { Box, Flex } from '@semcore/ui/flex-box';
const Demo = () => {
const [value, setValue] = React.useState(new Date());
const [valueRange, setValueRange] = React.useState([]);
return (
<Flex>
<Box>
<MonthPicker value={value} onChange={(date) => setValue(date)}>
<MonthPicker.Trigger />
<MonthPicker.Popper />
</MonthPicker>
</Box>
<Box ml={5}>
<MonthRangePicker value={valueRange} onChange={(date) => setValueRange(date)}>
<MonthRangePicker.Trigger />
<MonthRangePicker.Popper />
</MonthRangePicker>
</Box>
</Flex>
);
};
import React from 'react';
import { MonthPicker, MonthRangePicker } from '@semcore/ui/date-picker';
import { Box, Flex } from '@semcore/ui/flex-box';
const Demo = () => {
const [value, setValue] = React.useState(new Date());
const [valueRange, setValueRange] = React.useState([]);
return (
<Flex>
<Box>
<MonthPicker value={value} onChange={(date) => setValue(date)}>
<MonthPicker.Trigger />
<MonthPicker.Popper />
</MonthPicker>
</Box>
<Box ml={5}>
<MonthRangePicker value={valueRange} onChange={(date) => setValueRange(date)}>
<MonthRangePicker.Trigger />
<MonthRangePicker.Popper />
</MonthRangePicker>
</Box>
</Flex>
);
};
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 React from 'react';
import { DatePicker } from '@semcore/ui/date-picker';
const Demo = () => {
return (
<DatePicker>
<DatePicker.Trigger>
<DatePicker.Trigger.SingleDateInput>
<DatePicker.Trigger.SingleDateInput.Indicator />
<DatePicker.Trigger.SingleDateInput.MaskedInput />
</DatePicker.Trigger.SingleDateInput>
</DatePicker.Trigger>
<DatePicker.Popper />
</DatePicker>
);
};
import React from 'react';
import { DatePicker } from '@semcore/ui/date-picker';
const Demo = () => {
return (
<DatePicker>
<DatePicker.Trigger>
<DatePicker.Trigger.SingleDateInput>
<DatePicker.Trigger.SingleDateInput.Indicator />
<DatePicker.Trigger.SingleDateInput.MaskedInput />
</DatePicker.Trigger.SingleDateInput>
</DatePicker.Trigger>
<DatePicker.Popper />
</DatePicker>
);
};
Custom header
You can change the header layout by expanding the component further.
import React from 'react';
import { DatePicker } from '@semcore/ui/date-picker';
const Demo = () => {
return (
<DatePicker>
<DatePicker.Trigger />
<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>
);
};
import React from 'react';
import { DatePicker } from '@semcore/ui/date-picker';
const Demo = () => {
return (
<DatePicker>
<DatePicker.Trigger />
<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>
);
};
Custom day
Calendar days can have metrics, and you can change the units by passing a function to the Calendar
component.
import React from 'react';
import { DatePicker } from '@semcore/ui/date-picker';
import ProgressBar from '@semcore/ui/progress-bar';
import { Flex } from '@semcore/ui/flex-box';
function randomInteger(min, max) {
let rand = min - 0.5 + Math.random() * (max - min + 1);
rand = Math.round(rand);
return rand;
}
const Demo = () => {
return (
<DatePicker>
<DatePicker.Trigger />
<DatePicker.Popper>
<DatePicker.Header />
<DatePicker.Calendar>
{({ days }) =>
days.map((data, i) => (
<DatePicker.Calendar.Unit {...data} key={i}>
<Flex direction='column' p={1} w={'100%'} alignItems='center'>
{data.children}
<ProgressBar
size='s'
duration={0}
value={randomInteger(0, 100)}
theme='dark'
mt={1}
/>
</Flex>
</DatePicker.Calendar.Unit>
))
}
</DatePicker.Calendar>
</DatePicker.Popper>
</DatePicker>
);
};
import React from 'react';
import { DatePicker } from '@semcore/ui/date-picker';
import ProgressBar from '@semcore/ui/progress-bar';
import { Flex } from '@semcore/ui/flex-box';
function randomInteger(min, max) {
let rand = min - 0.5 + Math.random() * (max - min + 1);
rand = Math.round(rand);
return rand;
}
const Demo = () => {
return (
<DatePicker>
<DatePicker.Trigger />
<DatePicker.Popper>
<DatePicker.Header />
<DatePicker.Calendar>
{({ days }) =>
days.map((data, i) => (
<DatePicker.Calendar.Unit {...data} key={i}>
<Flex direction='column' p={1} w={'100%'} alignItems='center'>
{data.children}
<ProgressBar
size='s'
duration={0}
value={randomInteger(0, 100)}
theme='dark'
mt={1}
/>
</Flex>
</DatePicker.Calendar.Unit>
))
}
</DatePicker.Calendar>
</DatePicker.Popper>
</DatePicker>
);
};
Disabled
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 React from 'react';
import { DatePicker } from '@semcore/ui/date-picker';
const Demo = () => {
const today = new Date();
return (
<DatePicker disabled={[new Date(today.getFullYear(), 0, 1), [today, false], '* * 6,7']}>
<DatePicker.Trigger />
<DatePicker.Popper />
</DatePicker>
);
};
import React from 'react';
import { DatePicker } from '@semcore/ui/date-picker';
const Demo = () => {
const today = new Date();
return (
<DatePicker disabled={[new Date(today.getFullYear(), 0, 1), [today, false], '* * 6,7']}>
<DatePicker.Trigger />
<DatePicker.Popper />
</DatePicker>
);
};
Custom date ranges
Date ranges may be customized or switched off at all, by transferring periods={[]}
.
import React from 'react';
import { DateRangePicker } from '@semcore/ui/date-picker';
import { Flex, Box } from '@semcore/ui/flex-box';
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>
<Box mr={5} mb={5}>
<DateRangePicker periods={periods}>
<DateRangePicker.Trigger />
<DateRangePicker.Popper />
</DateRangePicker>
</Box>
<Box>
<DateRangePicker>
<DateRangePicker.Trigger />
<DateRangePicker.Popper />
</DateRangePicker>
</Box>
</Flex>
);
};
import React from 'react';
import { DateRangePicker } from '@semcore/ui/date-picker';
import { Flex, Box } from '@semcore/ui/flex-box';
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>
<Box mr={5} mb={5}>
<DateRangePicker periods={periods}>
<DateRangePicker.Trigger />
<DateRangePicker.Popper />
</DateRangePicker>
</Box>
<Box>
<DateRangePicker>
<DateRangePicker.Trigger />
<DateRangePicker.Popper />
</DateRangePicker>
</Box>
</Flex>
);
};
Week picker
You can manually select a custom period, if needed (for example, a week) by taking all the control in manual mode.
import React from 'react';
import { DateRangePicker } from '@semcore/ui/date-picker';
function dateToClosestWeek(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([]);
const [highlighted, setHighlighted] = React.useState([]);
React.useEffect(() => {
if (!value[0]) return;
const week = dateToClosestWeek(value[0]);
if (!value[1] || week[0].getTime() !== value[0].getTime()) {
setValue(week);
}
}, [value[0]?.getTime()]);
return (
<DateRangePicker
visible={visible}
onVisibleChange={(visible) => setVisible(visible)}
value={value}
onChange={setValue}
highlighted={highlighted}
>
<DateRangePicker.Trigger>
<DateRangePicker.Trigger.DateRange>
<DateRangePicker.Trigger.DateRange.Indicator />
<DateRangePicker.Trigger.DateRange.FromMaskedInput />
<DateRangePicker.Trigger.DateRange.RangeSep />
<DateRangePicker.Trigger.DateRange.ToMaskedInput disabled />
</DateRangePicker.Trigger.DateRange>
</DateRangePicker.Trigger>
<DateRangePicker.Popper>
<DateRangePicker.Header />
<DateRangePicker.Calendar
renderOutdated
onHighlightedChange={(date) => {
if (date.length === 1) setHighlighted([]);
}}
>
{({ days }) =>
days.map((data, i) => (
<DateRangePicker.Calendar.Unit
{...data}
key={i}
onMouseEnter={() => {
setHighlighted(dateToClosestWeek(data.date));
}}
onClick={() => {
setValue(dateToClosestWeek(data.date));
setVisible(false);
return false;
}}
/>
))
}
</DateRangePicker.Calendar>
</DateRangePicker.Popper>
</DateRangePicker>
);
};
import React from 'react';
import { DateRangePicker } from '@semcore/ui/date-picker';
function dateToClosestWeek(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([]);
const [highlighted, setHighlighted] = React.useState([]);
React.useEffect(() => {
if (!value[0]) return;
const week = dateToClosestWeek(value[0]);
if (!value[1] || week[0].getTime() !== value[0].getTime()) {
setValue(week);
}
}, [value[0]?.getTime()]);
return (
<DateRangePicker
visible={visible}
onVisibleChange={(visible) => setVisible(visible)}
value={value}
onChange={setValue}
highlighted={highlighted}
>
<DateRangePicker.Trigger>
<DateRangePicker.Trigger.DateRange>
<DateRangePicker.Trigger.DateRange.Indicator />
<DateRangePicker.Trigger.DateRange.FromMaskedInput />
<DateRangePicker.Trigger.DateRange.RangeSep />
<DateRangePicker.Trigger.DateRange.ToMaskedInput disabled />
</DateRangePicker.Trigger.DateRange>
</DateRangePicker.Trigger>
<DateRangePicker.Popper>
<DateRangePicker.Header />
<DateRangePicker.Calendar
renderOutdated
onHighlightedChange={(date) => {
if (date.length === 1) setHighlighted([]);
}}
>
{({ days }) =>
days.map((data, i) => (
<DateRangePicker.Calendar.Unit
{...data}
key={i}
onMouseEnter={() => {
setHighlighted(dateToClosestWeek(data.date));
}}
onClick={() => {
setValue(dateToClosestWeek(data.date));
setVisible(false);
return false;
}}
/>
))
}
</DateRangePicker.Calendar>
</DateRangePicker.Popper>
</DateRangePicker>
);
};
Date range comparator
Date range comparator allows user to compare two date ranges. Additional date range may be controlled with compare
and onCompareChange
props.
import React from 'react';
import { DateRangeComparator, MonthDateRangeComparator } from '@semcore/ui/date-picker';
import { Flex } from '@semcore/ui/flex-box';
const Demo = () => {
return (
<Flex gap={4} flexWrap>
<DateRangeComparator />
<MonthDateRangeComparator />
</Flex>
);
};
import React from 'react';
import { DateRangeComparator, MonthDateRangeComparator } from '@semcore/ui/date-picker';
import { Flex } from '@semcore/ui/flex-box';
const Demo = () => {
return (
<Flex gap={4} flexWrap>
<DateRangeComparator />
<MonthDateRangeComparator />
</Flex>
);
};
Date range comparator advanced use
Both DateRangeComparator
and MonthDateRangeComparator
internal structure maybe deeply redefined for flexible customization.
import React from 'react';
import { DateRangeComparator, MonthDateRangeComparator } from '@semcore/ui/date-picker';
import { Flex } from '@semcore/ui/flex-box';
import Notice from '@semcore/ui/notice';
import WarningIcon from '@semcore/ui/icon/Warning/m';
import { Text } from '@semcore/ui/typography';
const Demo = () => {
return (
<Flex gap={4} flexWrap>
<DateRangeComparator>
<DateRangeComparator.Trigger />
<DateRangeComparator.Popper>
<DateRangeComparator.Header>
<DateRangeComparator.ValueDateRange />
<DateRangeComparator.CompareToggle />
<DateRangeComparator.CompareDateRange />
</DateRangeComparator.Header>
<DateRangeComparator.Body>
<DateRangeComparator.RangeCalendar />
<DateRangeComparator.Periods />
</DateRangeComparator.Body>
<DateRangeComparator.Footer>
<DateRangeComparator.Apply />
<DateRangeComparator.Reset />
</DateRangeComparator.Footer>
<Notice theme='warning' style={{ borderTopLeftRadius: 0, borderTopRightRadius: 0}}>
<Notice.Content>
<Flex mb={1}>
<Notice.Label>
<WarningIcon />
</Notice.Label>
<Text bold>
Notice heading
</Text>
</Flex>
If you change your location, all previously collected data for this article will be lost.
</Notice.Content>
</Notice>
</DateRangeComparator.Popper>
</DateRangeComparator>
<MonthDateRangeComparator>
<MonthDateRangeComparator.Trigger />
<MonthDateRangeComparator.Popper>
<MonthDateRangeComparator.Header>
<MonthDateRangeComparator.ValueDateRange />
<MonthDateRangeComparator.CompareToggle />
<MonthDateRangeComparator.CompareDateRange />
</MonthDateRangeComparator.Header>
<MonthDateRangeComparator.Body>
<MonthDateRangeComparator.RangeCalendar />
<MonthDateRangeComparator.Periods />
</MonthDateRangeComparator.Body>
<MonthDateRangeComparator.Footer>
<MonthDateRangeComparator.Apply />
<MonthDateRangeComparator.Reset />
</MonthDateRangeComparator.Footer>
<Notice theme='warning' style={{ borderTopLeftRadius: 0, borderTopRightRadius: 0}}>
<Notice.Content>
<Flex mb={1}>
<Notice.Label>
<WarningIcon />
</Notice.Label>
<Text bold>
Notice heading
</Text>
</Flex>
If you change your location, all previously collected data for this article will be lost.
</Notice.Content>
</Notice>
</MonthDateRangeComparator.Popper>
</MonthDateRangeComparator>
</Flex>
);
};
import React from 'react';
import { DateRangeComparator, MonthDateRangeComparator } from '@semcore/ui/date-picker';
import { Flex } from '@semcore/ui/flex-box';
import Notice from '@semcore/ui/notice';
import WarningIcon from '@semcore/ui/icon/Warning/m';
import { Text } from '@semcore/ui/typography';
const Demo = () => {
return (
<Flex gap={4} flexWrap>
<DateRangeComparator>
<DateRangeComparator.Trigger />
<DateRangeComparator.Popper>
<DateRangeComparator.Header>
<DateRangeComparator.ValueDateRange />
<DateRangeComparator.CompareToggle />
<DateRangeComparator.CompareDateRange />
</DateRangeComparator.Header>
<DateRangeComparator.Body>
<DateRangeComparator.RangeCalendar />
<DateRangeComparator.Periods />
</DateRangeComparator.Body>
<DateRangeComparator.Footer>
<DateRangeComparator.Apply />
<DateRangeComparator.Reset />
</DateRangeComparator.Footer>
<Notice theme='warning' style={{ borderTopLeftRadius: 0, borderTopRightRadius: 0}}>
<Notice.Content>
<Flex mb={1}>
<Notice.Label>
<WarningIcon />
</Notice.Label>
<Text bold>
Notice heading
</Text>
</Flex>
If you change your location, all previously collected data for this article will be lost.
</Notice.Content>
</Notice>
</DateRangeComparator.Popper>
</DateRangeComparator>
<MonthDateRangeComparator>
<MonthDateRangeComparator.Trigger />
<MonthDateRangeComparator.Popper>
<MonthDateRangeComparator.Header>
<MonthDateRangeComparator.ValueDateRange />
<MonthDateRangeComparator.CompareToggle />
<MonthDateRangeComparator.CompareDateRange />
</MonthDateRangeComparator.Header>
<MonthDateRangeComparator.Body>
<MonthDateRangeComparator.RangeCalendar />
<MonthDateRangeComparator.Periods />
</MonthDateRangeComparator.Body>
<MonthDateRangeComparator.Footer>
<MonthDateRangeComparator.Apply />
<MonthDateRangeComparator.Reset />
</MonthDateRangeComparator.Footer>
<Notice theme='warning' style={{ borderTopLeftRadius: 0, borderTopRightRadius: 0}}>
<Notice.Content>
<Flex mb={1}>
<Notice.Label>
<WarningIcon />
</Notice.Label>
<Text bold>
Notice heading
</Text>
</Flex>
If you change your location, all previously collected data for this article will be lost.
</Notice.Content>
</Notice>
</MonthDateRangeComparator.Popper>
</MonthDateRangeComparator>
</Flex>
);
};