-
-
Save artyorsh/f9846c93e42a5ed45364ea747d0c4101 to your computer and use it in GitHub Desktop.
| { | |
| "components": { | |
| "Timepicker": { | |
| "meta": { | |
| "scope": "all", | |
| "parameters": { | |
| "minHeight": { | |
| "type": "number" | |
| }, | |
| "paddingHorizontal": { | |
| "type": "number" | |
| }, | |
| "paddingVertical": { | |
| "type": "number" | |
| }, | |
| "borderRadius": { | |
| "type": "number" | |
| }, | |
| "borderColor": { | |
| "type": "string" | |
| }, | |
| "borderWidth": { | |
| "type": "number" | |
| }, | |
| "backgroundColor": { | |
| "type": "string" | |
| }, | |
| "textMarginHorizontal": { | |
| "type": "number" | |
| }, | |
| "textFontSize": { | |
| "type": "number" | |
| }, | |
| "textFontWeight": { | |
| "type": "string" | |
| }, | |
| "textFontFamily": { | |
| "type": "string" | |
| }, | |
| "textColor": { | |
| "type": "string" | |
| }, | |
| "placeholderColor": { | |
| "type": "string" | |
| }, | |
| "iconWidth": { | |
| "type": "number" | |
| }, | |
| "iconHeight": { | |
| "type": "number" | |
| }, | |
| "iconMarginHorizontal": { | |
| "type": "number" | |
| }, | |
| "iconTintColor": { | |
| "type": "string" | |
| }, | |
| "labelColor": { | |
| "type": "string" | |
| }, | |
| "labelFontFamily": { | |
| "type": "string" | |
| }, | |
| "labelFontSize": { | |
| "type": "number" | |
| }, | |
| "labelFontWeight": { | |
| "type": "string" | |
| }, | |
| "labelMarginBottom": { | |
| "type": "number" | |
| }, | |
| "captionMarginTop": { | |
| "type": "number" | |
| }, | |
| "captionColor": { | |
| "type": "string" | |
| }, | |
| "captionFontFamily": { | |
| "type": "string" | |
| }, | |
| "captionFontSize": { | |
| "type": "number" | |
| }, | |
| "captionFontWeight": { | |
| "type": "string" | |
| }, | |
| "captionIconWidth": { | |
| "type": "number" | |
| }, | |
| "captionIconHeight": { | |
| "type": "number" | |
| }, | |
| "captionIconMarginRight": { | |
| "type": "number" | |
| }, | |
| "captionIconTintColor": { | |
| "type": "string" | |
| } | |
| }, | |
| "appearances": { | |
| "default": { | |
| "default": true | |
| } | |
| }, | |
| "variantGroups": { | |
| "status": { | |
| "basic": { | |
| "default": true | |
| }, | |
| "primary": { | |
| "default": false | |
| }, | |
| "success": { | |
| "default": false | |
| }, | |
| "info": { | |
| "default": false | |
| }, | |
| "warning": { | |
| "default": false | |
| }, | |
| "danger": { | |
| "default": false | |
| }, | |
| "control": { | |
| "default": false | |
| } | |
| }, | |
| "size": { | |
| "small": { | |
| "default": false | |
| }, | |
| "medium": { | |
| "default": true | |
| }, | |
| "large": { | |
| "default": false | |
| } | |
| } | |
| }, | |
| "states": { | |
| "disabled": { | |
| "default": false, | |
| "priority": 0, | |
| "scope": "all" | |
| }, | |
| "active": { | |
| "default": false, | |
| "priority": 1, | |
| "scope": "all" | |
| } | |
| } | |
| }, | |
| "appearances": { | |
| "default": { | |
| "mapping": { | |
| "paddingHorizontal": 8, | |
| "textMarginHorizontal": 8, | |
| "textFontFamily": "text-font-family", | |
| "iconWidth": 24, | |
| "iconHeight": 24, | |
| "iconMarginHorizontal": 8, | |
| "labelMarginBottom": 4, | |
| "labelFontSize": "text-label-font-size", | |
| "labelFontWeight": "text-label-font-weight", | |
| "labelFontFamily": "text-label-font-family", | |
| "captionMarginTop": 4, | |
| "captionFontSize": "text-caption-1-font-size", | |
| "captionFontWeight": "text-caption-1-font-weight", | |
| "captionFontFamily": "text-caption-1-font-family", | |
| "captionIconWidth": 10, | |
| "captionIconHeight": 10, | |
| "captionIconMarginRight": 8 | |
| }, | |
| "variantGroups": { | |
| "status": { | |
| "basic": { | |
| "borderColor": "border-basic-color-4", | |
| "backgroundColor": "background-basic-color-2", | |
| "textColor": "text-basic-color", | |
| "labelColor": "text-hint-color", | |
| "captionColor": "text-hint-color", | |
| "placeholderColor": "text-hint-color", | |
| "iconTintColor": "text-hint-color", | |
| "captionIconTintColor": "text-hint-color", | |
| "state": { | |
| "active": { | |
| "borderColor": "color-primary-default", | |
| "backgroundColor": "background-basic-color-1", | |
| "textColor": "text-basic-color" | |
| }, | |
| "disabled": { | |
| "borderColor": "border-basic-color-4", | |
| "backgroundColor": "background-basic-color-2", | |
| "textColor": "text-disabled-color", | |
| "iconTintColor": "text-disabled-color" | |
| } | |
| } | |
| }, | |
| "primary": { | |
| "borderColor": "color-primary-default", | |
| "backgroundColor": "background-basic-color-2", | |
| "textColor": "text-basic-color", | |
| "labelColor": "text-hint-color", | |
| "captionColor": "text-primary-color", | |
| "placeholderColor": "text-hint-color", | |
| "iconTintColor": "text-primary-color", | |
| "captionIconTintColor": "text-primary-color", | |
| "state": { | |
| "active": { | |
| "borderColor": "color-primary-active", | |
| "backgroundColor": "background-basic-color-1", | |
| "textColor": "text-basic-color" | |
| }, | |
| "disabled": { | |
| "borderColor": "border-basic-color-4", | |
| "backgroundColor": "background-basic-color-2", | |
| "textColor": "text-disabled-color", | |
| "iconTintColor": "text-disabled-color" | |
| } | |
| } | |
| }, | |
| "success": { | |
| "borderColor": "color-success-default", | |
| "backgroundColor": "background-basic-color-2", | |
| "textColor": "text-basic-color", | |
| "labelColor": "text-hint-color", | |
| "captionColor": "text-success-color", | |
| "placeholderColor": "text-hint-color", | |
| "iconTintColor": "text-success-color", | |
| "captionIconTintColor": "text-success-color", | |
| "state": { | |
| "active": { | |
| "borderColor": "color-success-active", | |
| "backgroundColor": "background-basic-color-1", | |
| "textColor": "text-basic-color" | |
| }, | |
| "disabled": { | |
| "borderColor": "border-basic-color-4", | |
| "backgroundColor": "background-basic-color-2", | |
| "textColor": "text-disabled-color", | |
| "iconTintColor": "text-disabled-color" | |
| } | |
| } | |
| }, | |
| "info": { | |
| "borderColor": "color-info-default", | |
| "backgroundColor": "background-basic-color-2", | |
| "textColor": "text-basic-color", | |
| "labelColor": "text-hint-color", | |
| "captionColor": "text-info-color", | |
| "placeholderColor": "text-hint-color", | |
| "iconTintColor": "text-info-color", | |
| "captionIconTintColor": "text-info-color", | |
| "state": { | |
| "active": { | |
| "borderColor": "color-info-active", | |
| "backgroundColor": "background-basic-color-1", | |
| "textColor": "text-basic-color" | |
| }, | |
| "disabled": { | |
| "borderColor": "border-basic-color-4", | |
| "backgroundColor": "background-basic-color-2", | |
| "textColor": "text-disabled-color", | |
| "iconTintColor": "text-disabled-color" | |
| } | |
| } | |
| }, | |
| "warning": { | |
| "borderColor": "color-warning-default", | |
| "backgroundColor": "background-basic-color-2", | |
| "textColor": "text-basic-color", | |
| "labelColor": "text-hint-color", | |
| "captionColor": "text-warning-color", | |
| "placeholderColor": "text-hint-color", | |
| "iconTintColor": "text-warning-color", | |
| "captionIconTintColor": "text-warning-color", | |
| "state": { | |
| "active": { | |
| "borderColor": "color-warning-active", | |
| "backgroundColor": "background-basic-color-1", | |
| "textColor": "text-basic-color" | |
| }, | |
| "disabled": { | |
| "borderColor": "border-basic-color-4", | |
| "backgroundColor": "background-basic-color-2", | |
| "textColor": "text-disabled-color", | |
| "iconTintColor": "text-disabled-color" | |
| } | |
| } | |
| }, | |
| "danger": { | |
| "borderColor": "color-danger-default", | |
| "backgroundColor": "background-basic-color-2", | |
| "textColor": "text-basic-color", | |
| "labelColor": "text-hint-color", | |
| "captionColor": "text-danger-color", | |
| "placeholderColor": "text-hint-color", | |
| "iconTintColor": "text-danger-color", | |
| "captionIconTintColor": "text-danger-color", | |
| "state": { | |
| "active": { | |
| "borderColor": "color-danger-active", | |
| "backgroundColor": "background-basic-color-1", | |
| "textColor": "text-basic-color" | |
| }, | |
| "disabled": { | |
| "borderColor": "border-basic-color-4", | |
| "backgroundColor": "background-basic-color-2", | |
| "textColor": "text-disabled-color", | |
| "iconTintColor": "text-disabled-color" | |
| } | |
| } | |
| }, | |
| "control": { | |
| "borderColor": "color-basic-control-transparent-500", | |
| "backgroundColor": "color-basic-control-transparent-300", | |
| "textColor": "text-control-color", | |
| "labelColor": "text-control-color", | |
| "captionColor": "text-control-color", | |
| "placeholderColor": "text-control-color", | |
| "iconTintColor": "text-control-color", | |
| "captionIconTintColor": "text-control-color", | |
| "state": { | |
| "active": { | |
| "borderColor": "color-control-transparent-active-border", | |
| "backgroundColor": "background-basic-color-1", | |
| "textColor": "text-basic-color" | |
| }, | |
| "disabled": { | |
| "borderColor": "color-control-transparent-disabled-border", | |
| "backgroundColor": "color-control-transparent-disabled", | |
| "textColor": "text-control-color", | |
| "iconTintColor": "text-control-color" | |
| } | |
| } | |
| } | |
| }, | |
| "size": { | |
| "small": { | |
| "minHeight": "size-small", | |
| "borderRadius": "border-radius", | |
| "borderWidth": "border-width", | |
| "paddingVertical": 3, | |
| "textFontSize": "text-subtitle-2-font-size", | |
| "textFontWeight": "normal" | |
| }, | |
| "medium": { | |
| "minHeight": "size-medium", | |
| "borderRadius": "border-radius", | |
| "borderWidth": "border-width", | |
| "paddingVertical": 7, | |
| "textFontSize": "text-subtitle-1-font-size", | |
| "textFontWeight": "normal" | |
| }, | |
| "large": { | |
| "minHeight": "size-large", | |
| "borderRadius": "border-radius", | |
| "borderWidth": "border-width", | |
| "paddingVertical": 11, | |
| "textFontSize": "text-subtitle-1-font-size", | |
| "textFontWeight": "normal" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } |
| import React from 'react'; | |
| import { | |
| GestureResponderEvent, | |
| ImageProps, | |
| StyleProp, | |
| StyleSheet, | |
| TouchableOpacityProps, | |
| View, | |
| ViewProps, | |
| ViewStyle, | |
| Platform, | |
| } from 'react-native'; | |
| import DateTimePicker, { TimePickerOptions } from '@react-native-community/datetimepicker'; | |
| import { | |
| EvaInputSize, | |
| EvaStatus, | |
| FalsyFC, | |
| FalsyText, | |
| RenderProp, | |
| TouchableWithoutFeedback, | |
| } from '@ui-kitten/components/devsupport'; | |
| import { PopoverPlacement } from '@ui-kitten/components/ui/popover/type'; | |
| import { | |
| Interaction, | |
| StyledComponentProps, | |
| StyleType, | |
| TextProps, | |
| styled, | |
| Card, | |
| Popover, | |
| PopoverPlacements, | |
| DateService, | |
| NativeDateService, | |
| } from '@ui-kitten/components'; | |
| type TimePickerOmitChangeProps = Omit<TimePickerOptions, 'onChange'>; | |
| type TimePickerOmitProps = Omit<TimePickerOmitChangeProps, 'value'>; | |
| export interface TimepickerProps<D = Date> extends StyledComponentProps, TouchableOpacityProps, TimePickerOmitProps { | |
| controlStyle?: StyleProp<ViewStyle>; | |
| date?: D; | |
| label?: RenderProp<TextProps> | React.ReactText; | |
| caption?: RenderProp<TextProps> | React.ReactText; | |
| captionIcon?: RenderProp<Partial<ImageProps>>; | |
| accessoryLeft?: RenderProp<Partial<ImageProps>>; | |
| accessoryRight?: RenderProp<Partial<ImageProps>>; | |
| status?: EvaStatus; | |
| size?: EvaInputSize; | |
| placeholder?: RenderProp<TextProps> | React.ReactText; | |
| placement?: PopoverPlacement | string; | |
| backdropStyle?: StyleProp<ViewStyle>; | |
| onSelect?: (date: D) => void; | |
| onFocus?: () => void; | |
| onBlur?: () => void; | |
| dateService?: DateService<D>; | |
| } | |
| interface State { | |
| visible: boolean; | |
| } | |
| @styled('Timepicker') | |
| export class Timepicker<P> extends React.Component<TimepickerProps & P, State> { | |
| static defaultProps: Partial<TimepickerProps> = { | |
| placement: PopoverPlacements.BOTTOM_START, | |
| dateService: new NativeDateService(), | |
| }; | |
| public state: State = { | |
| visible: false, | |
| }; | |
| private get title(): RenderProp<TextProps> | React.ReactText | undefined { | |
| const formattedDate = this.props.date && this.props.dateService?.format(this.props.date as Date, 'HH:mm'); | |
| return formattedDate || this.props.placeholder; | |
| } | |
| private getComponentStyle = (style: StyleType) => { | |
| const { | |
| textMarginHorizontal, | |
| textFontFamily, | |
| textFontSize, | |
| textFontWeight, | |
| textColor, | |
| placeholderColor, | |
| iconWidth, | |
| iconHeight, | |
| iconMarginHorizontal, | |
| iconTintColor, | |
| labelColor, | |
| labelFontSize, | |
| labelMarginBottom, | |
| labelFontWeight, | |
| labelFontFamily, | |
| captionMarginTop, | |
| captionColor, | |
| captionFontSize, | |
| captionFontWeight, | |
| captionFontFamily, | |
| captionIconWidth, | |
| captionIconHeight, | |
| captionIconMarginRight, | |
| captionIconTintColor, | |
| popoverWidth, | |
| ...controlParameters | |
| } = style; | |
| return { | |
| control: controlParameters, | |
| captionContainer: { | |
| marginTop: captionMarginTop, | |
| }, | |
| text: { | |
| marginHorizontal: textMarginHorizontal, | |
| fontFamily: textFontFamily, | |
| fontSize: textFontSize, | |
| fontWeight: textFontWeight, | |
| color: textColor, | |
| }, | |
| placeholder: { | |
| marginHorizontal: textMarginHorizontal, | |
| color: placeholderColor, | |
| }, | |
| icon: { | |
| width: iconWidth, | |
| height: iconHeight, | |
| marginHorizontal: iconMarginHorizontal, | |
| tintColor: iconTintColor, | |
| }, | |
| label: { | |
| color: labelColor, | |
| fontSize: labelFontSize, | |
| fontFamily: labelFontFamily, | |
| marginBottom: labelMarginBottom, | |
| fontWeight: labelFontWeight, | |
| }, | |
| captionIcon: { | |
| width: captionIconWidth, | |
| height: captionIconHeight, | |
| tintColor: captionIconTintColor, | |
| marginRight: captionIconMarginRight, | |
| }, | |
| captionLabel: { | |
| fontSize: captionFontSize, | |
| fontWeight: captionFontWeight, | |
| fontFamily: captionFontFamily, | |
| color: captionColor, | |
| }, | |
| popover: { | |
| width: popoverWidth, | |
| marginBottom: captionMarginTop, | |
| }, | |
| }; | |
| }; | |
| private onPress = (event: GestureResponderEvent): void => { | |
| this.setPickerVisible(); | |
| this.props.onPress && this.props.onPress(event); | |
| }; | |
| private onPressIn = (event: GestureResponderEvent): void => { | |
| // @ts-ignore | |
| this.props.eva.dispatch([Interaction.ACTIVE]); | |
| this.props.onPressIn && this.props.onPressIn(event); | |
| }; | |
| private onPressOut = (event: GestureResponderEvent): void => { | |
| // @ts-ignore | |
| this.props.eva.dispatch([]); | |
| this.props.onPressOut && this.props.onPressOut(event); | |
| }; | |
| private onValueChange = (event: React.SyntheticEvent, value?: Date): void => { | |
| Platform.OS !== 'ios' && this.setPickerInvisible(); | |
| value && this.props.onSelect && this.props.onSelect(value); | |
| }; | |
| private onPickerVisible = (): void => { | |
| // @ts-ignore | |
| this.props.eva.dispatch([Interaction.ACTIVE]); | |
| this.props.onFocus && this.props.onFocus(); | |
| }; | |
| private onPickerInvisible = (): void => { | |
| // @ts-ignore | |
| this.props.eva.dispatch([]); | |
| this.props.onBlur && this.props.onBlur(); | |
| }; | |
| private setPickerVisible = (): void => { | |
| this.setState({ visible: true }, this.onPickerVisible); | |
| }; | |
| private setPickerInvisible = (): void => { | |
| this.setState({ visible: false }, this.onPickerInvisible); | |
| }; | |
| private renderPickerDefault = (): React.ReactElement => { | |
| return ( | |
| <DateTimePicker | |
| mode='time' | |
| display={this.props.display} | |
| is24Hour={this.props.is24Hour} | |
| value={this.props.date as Date || new Date()} | |
| onChange={this.onValueChange} | |
| /> | |
| ); | |
| }; | |
| private renderPickerIOS = (): React.ReactElement => { | |
| return ( | |
| <Card disabled={true}> | |
| {this.renderPickerDefault()} | |
| </Card> | |
| ); | |
| }; | |
| private renderInputDefault = (props: Partial<TimepickerProps>, evaStyle): React.ReactElement => { | |
| return ( | |
| <TouchableWithoutFeedback | |
| {...props} | |
| style={[evaStyle.control, styles.control, props.controlStyle]} | |
| onPress={this.onPress} | |
| onPressIn={this.onPressIn} | |
| onPressOut={this.onPressOut}> | |
| <FalsyFC | |
| style={evaStyle.icon} | |
| component={props.accessoryLeft} | |
| /> | |
| <FalsyText | |
| style={evaStyle.text} | |
| numberOfLines={1} | |
| ellipsizeMode='tail' | |
| component={this.title} | |
| /> | |
| <FalsyFC | |
| style={evaStyle.icon} | |
| component={this.props.accessoryRight} | |
| /> | |
| </TouchableWithoutFeedback> | |
| ); | |
| }; | |
| private renderInputIOS = (props: Partial<TimepickerProps>, evaStyle): React.ReactElement => { | |
| return ( | |
| <Popover | |
| style={[evaStyle.popover, styles.popover]} | |
| backdropStyle={props.backdropStyle} | |
| fullWidth={true} | |
| placement={props.placement} | |
| visible={this.state.visible} | |
| anchor={() => this.renderInputDefault(props, evaStyle)} | |
| onBackdropPress={this.setPickerInvisible}> | |
| {this.renderPickerIOS()} | |
| </Popover> | |
| ); | |
| }; | |
| private renderInput = (props: Partial<TimepickerProps>, evaStyle): React.ReactElement => { | |
| return Platform.select({ | |
| ios: this.renderInputIOS(props, evaStyle), | |
| default: this.renderInputDefault(props, evaStyle), | |
| }); | |
| }; | |
| public render(): React.ReactElement<ViewProps> { | |
| const { eva, style, label, caption, captionIcon, ...inputProps } = this.props; | |
| const evaStyle = this.getComponentStyle(this.props.eva?.style as StyleType); | |
| return ( | |
| <React.Fragment> | |
| <View style={style}> | |
| <FalsyText | |
| style={[evaStyle.label, styles.label]} | |
| component={label} | |
| /> | |
| {this.renderInput(inputProps, evaStyle)} | |
| <View style={[evaStyle.captionContainer, styles.captionContainer]}> | |
| <FalsyFC | |
| style={evaStyle.captionIcon} | |
| component={captionIcon} | |
| /> | |
| <FalsyText | |
| style={[evaStyle.captionLabel, styles.captionLabel]} | |
| component={caption} | |
| /> | |
| </View> | |
| </View> | |
| {Platform.OS !== 'ios' && this.state.visible && this.renderPickerDefault()} | |
| </React.Fragment> | |
| ); | |
| } | |
| } | |
| const styles = StyleSheet.create({ | |
| popover: { | |
| borderWidth: 0, | |
| }, | |
| control: { | |
| flexDirection: 'row', | |
| alignItems: 'center', | |
| justifyContent: 'space-between', | |
| }, | |
| label: { | |
| textAlign: 'left', | |
| }, | |
| captionContainer: { | |
| flexDirection: 'row', | |
| alignItems: 'center', | |
| }, | |
| captionLabel: { | |
| textAlign: 'left', | |
| }, | |
| closeButton: { | |
| alignSelf: 'flex-end', | |
| }, | |
| }); |
Thanks for the amazing library, really loving how easy it is to use... how can I theme the time picker pop up window though? it pops up with a blue theme, but my theme color is red and it really clashes with my UI... I used this exact code to create the timepicker component... Thanks for your help... @artyorsh
Thank you!
@psalm987 did you find any solution?
@artyorsh thanks for your work... but the time picker pop-up color is not changing based on the mapping or eva design. Is it possible to change the color of the picker dialog if it allows us?
Tried using this and got an error
ERROR TypeError: undefined is not an object (evaluating 'this.meta.appearances')
Not sure what is causing it, any way to resolve this?
EDIT: after modifying the metro.config.js following the docs here, it works!
@artyorsh thanks for amazing library and work on this component.
Since there is still no time-picker component I just wanted to warn anyone using this code - time display in input is not honoring is24Hour property.
In order to correct it, just replace title() function (lines 78-84) in time-picker.component.tsx with:
private get title(): RenderProp<TextProps> | string | undefined {
const tformat = this.props.is24Hour ? 'HH:mm' : 'hh:mm a';
const formattedDate =
this.props.date &&
this.props.dateService?.format(this.props.date as Date, tformat);
return formattedDate || this.props.placeholder;
}

Great! Thank you!