SIGN IN SIGN UP
JedWatson / react-select UNCLAIMED

The Select Component for React.js

0 0 0 TypeScript
/** @jsx jsx */
import { Component } from 'react';
2021-01-29 20:05:07 -05:00
import { jsx } from '@emotion/react';
import { CSSObject } from '@emotion/serialize';
2021-01-27 10:19:14 -05:00
import moment, { Moment } from 'moment';
2021-01-29 20:27:04 -05:00
import * as chrono from 'chrono-node';
2018-01-30 16:30:27 +11:00
2021-01-27 10:19:14 -05:00
import Select, {
GroupProps,
OptionProps,
components as SelectComponents,
} from 'react-select';
2018-01-30 16:30:27 +11:00
2021-01-27 10:19:14 -05:00
interface DateOption {
date: Moment;
value: Date;
label: string;
display?: string;
}
const createOptionForDate = (d: Moment | Date) => {
const date = moment.isMoment(d) ? d : moment(d);
2018-01-30 16:30:27 +11:00
return {
date,
value: date.toDate(),
label: date.calendar(null, {
sameDay: '[Today] (Do MMM YYYY)',
nextDay: '[Tomorrow] (Do MMM YYYY)',
nextWeek: '[Next] dddd (Do MMM YYYY)',
lastDay: '[Yesterday] (Do MMM YYYY)',
lastWeek: '[Last] dddd (Do MMM YYYY)',
2018-01-30 16:30:27 +11:00
sameElse: 'Do MMMM YYYY',
}),
};
};
2021-01-27 10:19:14 -05:00
interface CalendarGroup {
label: string;
options: readonly DateOption[];
}
const defaultOptions: (DateOption | CalendarGroup)[] = [
'today',
'tomorrow',
'yesterday',
2021-04-13 21:25:00 -04:00
].map((i) => createOptionForDate(chrono.parseDate(i)));
const createCalendarOptions = (date = new Date()) => {
2021-01-27 10:19:14 -05:00
const daysInMonth = Array.apply(null, Array(moment(date).daysInMonth())).map(
(x, i) => {
const d = moment(date).date(i + 1);
return { ...createOptionForDate(d), display: 'calendar' };
}
);
return {
label: moment(date).format('MMMM YYYY'),
options: daysInMonth,
};
};
defaultOptions.push(createCalendarOptions());
2018-01-31 11:26:37 +11:00
const suggestions = [
'sunday',
'saturday',
'friday',
'thursday',
'wednesday',
'tuesday',
'monday',
'december',
'november',
'october',
'september',
'august',
'july',
'june',
'may',
'april',
'march',
'february',
'january',
'yesterday',
'tomorrow',
'today',
2021-01-27 10:19:14 -05:00
].reduce<{ [key: string]: string }>((acc, str) => {
2018-01-31 11:26:37 +11:00
for (let i = 1; i < str.length; i++) {
acc[str.substr(0, i)] = str;
}
return acc;
}, {});
2021-01-27 10:19:14 -05:00
const suggest = (str: string) =>
2018-01-31 11:26:37 +11:00
str
.split(/\b/)
2021-04-13 21:25:00 -04:00
.map((i) => suggestions[i] || i)
2018-01-31 11:26:37 +11:00
.join('');
const days = ['S', 'M', 'T', 'W', 'T', 'F', 'S'];
2021-01-29 20:05:07 -05:00
const daysHeaderStyles: CSSObject = {
marginTop: '5px',
paddingTop: '5px',
paddingLeft: '2%',
borderTop: '1px solid #eee',
};
2021-01-29 20:05:07 -05:00
const daysHeaderItemStyles: CSSObject = {
color: '#999',
cursor: 'default',
fontSize: '75%',
2021-01-29 20:05:07 -05:00
fontWeight: 500,
display: 'inline-block',
width: '12%',
margin: '0 1%',
textAlign: 'center',
};
2021-01-29 20:05:07 -05:00
const daysContainerStyles: CSSObject = {
paddingTop: '5px',
paddingLeft: '2%',
};
2021-01-27 10:19:14 -05:00
const Group = (props: GroupProps<DateOption, false>) => {
2019-04-29 17:38:33 +10:00
const {
Heading,
getStyles,
children,
label,
headingProps,
cx,
theme,
2021-03-25 09:37:30 -04:00
selectProps,
2019-04-29 17:38:33 +10:00
} = props;
return (
2021-01-29 20:05:07 -05:00
<div aria-label={label as string} css={getStyles('group', props)}>
2021-03-25 09:37:30 -04:00
<Heading
selectProps={selectProps}
theme={theme}
getStyles={getStyles}
cx={cx}
{...headingProps}
>
2018-04-26 09:57:26 +10:00
{label}
</Heading>
<div css={daysHeaderStyles}>
{days.map((day, i) => (
<span key={`${i}-${day}`} css={daysHeaderItemStyles}>
{day}
</span>
))}
</div>
<div css={daysContainerStyles}>{children}</div>
</div>
);
};
2021-03-29 17:40:03 -04:00
const getOptionStyles = (defaultStyles: CSSObject): CSSObject => ({
...defaultStyles,
display: 'inline-block',
width: '12%',
margin: '0 1%',
textAlign: 'center',
borderRadius: '4px',
});
2021-01-27 10:19:14 -05:00
const Option = (props: OptionProps<DateOption, false>) => {
const { data, getStyles, innerRef, innerProps } = props;
if (data.display === 'calendar') {
const defaultStyles = getStyles('option', props);
const styles = getOptionStyles(defaultStyles);
if (data.date.date() === 1) {
const indentBy = data.date.day();
if (indentBy) {
styles.marginLeft = `${indentBy * 14 + 1}%`;
}
}
return (
<span {...innerProps} css={styles} ref={innerRef}>
{data.date.format('D')}
</span>
);
} else return <SelectComponents.Option {...props} />;
};
2018-01-30 16:30:27 +11:00
2021-01-27 10:19:14 -05:00
interface DatePickerProps {
readonly value: DateOption | null;
readonly onChange: (value: DateOption | null) => void;
}
interface DatePickerState {
2021-01-29 20:05:07 -05:00
readonly options: readonly (DateOption | CalendarGroup)[];
2021-01-27 10:19:14 -05:00
}
class DatePicker extends Component<DatePickerProps, DatePickerState> {
state: DatePickerState = {
2018-01-30 16:30:27 +11:00
options: defaultOptions,
};
2021-01-27 10:19:14 -05:00
handleInputChange = (value: string) => {
2018-01-30 16:30:27 +11:00
if (!value) {
this.setState({ options: defaultOptions });
return;
}
2018-01-31 11:26:37 +11:00
const date = chrono.parseDate(suggest(value.toLowerCase()));
2018-01-30 16:30:27 +11:00
if (date) {
this.setState({
options: [createOptionForDate(date), createCalendarOptions(date)],
2018-01-30 16:30:27 +11:00
});
} else {
this.setState({
options: [],
});
}
};
render() {
const { value } = this.props;
const { options } = this.state;
return (
2021-01-27 10:19:14 -05:00
<Select<DateOption, false>
2018-01-30 16:30:27 +11:00
{...this.props}
components={{ Group, Option }}
2018-01-30 16:30:27 +11:00
filterOption={null}
isMulti={false}
2021-04-13 21:25:00 -04:00
isOptionSelected={(o, v) => v.some((i) => i.date.isSame(o.date, 'day'))}
maxMenuHeight={380}
2018-01-30 16:30:27 +11:00
onChange={this.props.onChange}
onInputChange={this.handleInputChange}
options={options}
value={value}
/>
);
}
}
2021-01-27 10:19:14 -05:00
interface State {
readonly value: DateOption | null;
}
export default class Experimental extends Component<{}, State> {
state: State = {
2021-01-29 20:05:07 -05:00
value: defaultOptions[0] as DateOption,
2018-01-30 16:30:27 +11:00
};
2021-01-27 10:19:14 -05:00
handleChange = (value: DateOption | null) => {
2018-01-30 16:30:27 +11:00
this.setState({ value });
};
render() {
const { value } = this.state;
2021-01-27 10:19:14 -05:00
const displayValue =
value && 'value' in value ? value.value.toString() : 'null';
2018-01-30 16:30:27 +11:00
return (
<div>
<pre>Value: {displayValue}</pre>
2018-03-02 13:26:44 +11:00
<DatePicker value={value} onChange={this.handleChange} />
2018-01-30 16:30:27 +11:00
</div>
);
}
}