import classNames from "classnames"; import * as React from "react"; import { AbstractPureComponent, Button, Classes as CoreClasses, DISPLAYNAME_PREFIX, HTMLInputProps, IButtonProps, IInputGroupProps, IPopoverProps, IProps, MenuItem, Utils, } from "reui-core"; import { ItemListPredicate, ItemRenderer, Select } from "reui-select"; import * as Classes from "../../common/classes"; import { formatTimezone, TimezoneDisplayFormat } from "./timezoneDisplayFormat"; import { getInitialTimezoneItems, getTimezoneItems, ITimezoneItem } from "./timezoneItems"; export { TimezoneDisplayFormat }; export interface ITimezonePickerProps extends IProps { /** * The currently selected timezone UTC identifier, e.g. "Pacific/Honolulu". * See https://www.iana.org/time-zones for more information. */ value: string | undefined; /** * Callback invoked when the user selects a timezone. */ onChange: (timezone: string) => void; /** * This component does not support children. * Use `value`, `valueDisplayFormat` and `buttonProps` to customize the button child. */ children?: never; /** * The date to use when formatting timezone offsets. * An offset date is necessary to account for DST, but typically the default value of `now` will be sufficient. * @default now */ date?: Date; /** * Whether this component is non-interactive. * @default false */ disabled?: boolean; /** * Whether to show the local timezone at the top of the list of initial timezone suggestions. * @default true */ showLocalTimezone?: boolean; /** * Format to use when displaying the selected (or default) timezone within the target element. * @default TimezoneDisplayFormat.OFFSET */ valueDisplayFormat?: TimezoneDisplayFormat; /** * Text to show when no timezone has been selected (`value === undefined`). * @default "Select timezone..." */ placeholder?: string; /** Props to spread to the target `Button`. */ buttonProps?: Partial; /** * Props to spread to the filter `InputGroup`. * All props are supported except `ref` (use `inputRef` instead). * If you want to control the filter input, you can pass `value` and `onChange` here * to override `Select`'s own behavior. */ inputProps?: IInputGroupProps & HTMLInputProps; /** Props to spread to `Popover`. Note that `content` cannot be changed. */ popoverProps?: Partial; } export interface ITimezonePickerState { query: string; } const TypedSelect = Select.ofType(); export class TimezonePicker extends AbstractPureComponent { public static displayName = `${DISPLAYNAME_PREFIX}.TimezonePicker`; public static defaultProps: Partial = { date: new Date(), disabled: false, inputProps: {}, placeholder: "Select timezone...", popoverProps: {}, showLocalTimezone: true, valueDisplayFormat: TimezoneDisplayFormat.OFFSET, }; private timezoneItems: ITimezoneItem[]; private initialTimezoneItems: ITimezoneItem[]; constructor(props: ITimezonePickerProps, context?: any) { super(props, context); const { date = new Date(), showLocalTimezone, inputProps = {} } = props; this.state = { query: inputProps.value || "" }; this.timezoneItems = getTimezoneItems(date); this.initialTimezoneItems = getInitialTimezoneItems(date, showLocalTimezone); } public render() { const { className, disabled, inputProps, popoverProps } = this.props; const { query } = this.state; const finalInputProps: IInputGroupProps & HTMLInputProps = { placeholder: "Search for timezones...", ...inputProps, }; const finalPopoverProps: Partial & object = { ...popoverProps, popoverClassName: classNames(Classes.TIMEZONE_PICKER_POPOVER, popoverProps.popoverClassName), }; return ( } onItemSelect={this.handleItemSelect} resetOnSelect={true} resetOnClose={true} popoverProps={finalPopoverProps} inputProps={finalInputProps} disabled={disabled} onQueryChange={this.handleQueryChange} > {this.renderButton()} ); } public componentWillReceiveProps(nextProps: ITimezonePickerProps) { const { date: nextDate = new Date(), inputProps: nextInputProps = {} } = nextProps; if (this.props.showLocalTimezone !== nextProps.showLocalTimezone) { this.initialTimezoneItems = getInitialTimezoneItems(nextDate, nextProps.showLocalTimezone); } if (nextInputProps.value !== undefined && this.state.query !== nextInputProps.value) { this.setState({ query: nextInputProps.value }); } } private renderButton() { const { buttonProps = {}, date, disabled, placeholder, value, valueDisplayFormat } = this.props; const buttonContent = value ? ( formatTimezone(value, date, valueDisplayFormat) ) : ( {placeholder} ); return