Skip to content

Commit

Permalink
Merge pull request #3941 from webkom/function-components
Browse files Browse the repository at this point in the history
  • Loading branch information
eikhr committed Aug 29, 2023
2 parents c7169e9 + fd78baf commit 4e95168
Show file tree
Hide file tree
Showing 9 changed files with 190 additions and 278 deletions.
5 changes: 3 additions & 2 deletions app/components/Form/DatePicker.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@
display: flex;
width: 100%;
flex-wrap: wrap;
margin: 0;
margin: 0 0 10px;
}

.dropdown {
width: auto;
max-width: 320px;
padding: 0 20px;
padding: 0 20px 10px;
flex: 1;
}

.inputField {
Expand Down
212 changes: 85 additions & 127 deletions app/components/Form/DatePicker.tsx
Original file line number Diff line number Diff line change
@@ -1,154 +1,112 @@
import cx from 'classnames';
import moment from 'moment-timezone';
import { Component } from 'react';
import { useState } from 'react';
import Dropdown from 'app/components/Dropdown';
import Icon from 'app/components/Icon';
import Flex from 'app/components/Layout/Flex';
import createMonthlyCalendar from 'app/utils/createMonthlyCalendar';
import parseDateValue from 'app/utils/parseDateValue';
import styles from './DatePicker.css';
import { createField } from './Field';
import TextInput from './TextInput';
import TimePicker from './TimePicker';
import type { Moment } from 'moment';

type Props = {
onChange: (arg0: string) => void;
onChange: (selectedDate: string) => void;
onBlur: (selectedDate?: string) => void;
onFocus: () => void;
className?: string;
value: string | null | undefined;
value?: string;
showTimePicker?: boolean;
dateFormat?: string;
name?: string;
};
type State = {
pickerOpen: boolean;
date: moment$Moment;
value: moment$Moment;
};

class DatePicker extends Component<Props, State> {
static defaultProps = {
value: '',
showTimePicker: true,
dateFormat: 'lll',
};
static Field: any;
state = {
pickerOpen: false,
date: moment(),
value: parseDateValue(this.props.value),
};
onNext = () => {
this.setState((prevState) => ({
date: prevState.date.clone().add(1, 'month'),
}));
};
onPrev = () => {
this.setState((prevState) => ({
date: prevState.date.clone().subtract(1, 'month'),
}));
};
onChange = (day: moment$Moment) => {
this.setState((prevState) => {
const value = day
.clone()
.hour(prevState.value.hour())
.minute(prevState.value.minute());
return {
value,
pickerOpen: false,
};
}, this._notifyParent);
};
onChangeTime = (time: moment$Moment) => {
this.setState((prevState) => {
const value = prevState.value
.clone()
.hour(time.hour())
.minute(time.minute());
return {
value,
};
}, this._notifyParent);
const DatePicker = ({
onChange,
onFocus,
onBlur,
className,
value,
showTimePicker = true,
dateFormat = 'lll',
name,
}: Props) => {
const [pickerOpen, setPickerOpen] = useState(false);
const [date, setDate] = useState(moment());
const parsedValue = parseDateValue(value);

const onNext = () => setDate(date.clone().add(1, 'month'));
const onPrev = () => setDate(date.clone().subtract(1, 'month'));

const togglePicker = (open = !pickerOpen) => {
setPickerOpen(open);
open ? onFocus() : onBlur(value);
};
_notifyParent = () => this.props.onChange(this.state.value.toISOString());
toggleDropdown = () => {
this.setState((prevState) => ({
pickerOpen: !prevState.pickerOpen,
}));

const changeDay = (day: Moment) => {
const value = day
.clone()
.hour(parsedValue.hour())
.minute(parsedValue.minute());
onChange(value.toISOString());
onBlur(value.toISOString());
setPickerOpen(false);
};

componentDidUpdate(prevProps: Props) {
if (prevProps.value !== this.props.value) {
this.setState({
value: parseDateValue(this.props.value),
});
}
}
return (
<Dropdown
show={pickerOpen}
toggle={() => togglePicker()}
triggerComponent={
<TextInput
className={cx(styles.inputField, className)}
value={parsedValue.format(dateFormat)}
name={name}
readOnly
/>
}
componentClass="div"
contentClassName={styles.dropdown}
>
<div onClick={(e) => e.stopPropagation()}>
<Flex
justifyContent="space-between"
alignItems="center"
className={styles.header}
>
<button onClick={onPrev} className={styles.arrowIcon}>
<Icon name="arrow-back-outline" />
</button>
<span>{date.format('MMMM YYYY')}</span>
<button onClick={onNext} className={styles.arrowIcon}>
<Icon name="arrow-forward-outline" />
</button>
</Flex>

render() {
const { showTimePicker, className, name } = this.props;
const { date } = this.state;
return (
<Dropdown
show={this.state.pickerOpen}
toggle={this.toggleDropdown}
triggerComponent={
<TextInput
className={cx(styles.inputField, className)}
value={this.state.value.format(this.props.dateFormat)}
name={name}
readOnly
/>
}
componentClass="div"
contentClassName={styles.dropdown}
style={{
flex: 1,
}}
>
<div className={styles.datePicker} onClick={(e) => e.stopPropagation()}>
<Flex
justifyContent="space-between"
alignItems="center"
className={styles.header}
>
<button onClick={this.onPrev} className={styles.arrowIcon}>
<i className="fa fa-long-arrow-left" />
<div className={styles.calendar}>
{createMonthlyCalendar(date).map((dateProps, i) => (
<button
key={i}
className={cx(
styles.calendarItem,
dateProps.prevOrNextMonth && styles.prevOrNextMonth,
dateProps.day.isSame(parsedValue, 'day') && styles.selectedDate
)}
onClick={() => changeDay(dateProps.day)}
disabled={dateProps.prevOrNextMonth}
>
{dateProps.day.date()}
</button>
<span>{date.format('MMMM YYYY')}</span>
<button onClick={this.onNext} className={styles.arrowIcon}>
<i className="fa fa-long-arrow-right" />
</button>
</Flex>

<div className={styles.calendar}>
{createMonthlyCalendar(date).map((dateProps, i) => (
<button
key={i}
className={cx(
styles.calendarItem,
dateProps.prevOrNextMonth && styles.prevOrNextMonth,
dateProps.day.isSame(this.state.value, 'day') &&
styles.selectedDate
)}
onClick={() => this.onChange(dateProps.day)}
disabled={dateProps.prevOrNextMonth}
>
{dateProps.day.date()}
</button>
))}
</div>

{showTimePicker && (
<TimePicker
value={this.state.value.toISOString()}
onChange={this.onChangeTime}
/>
)}
))}
</div>
</Dropdown>
);
}
}

{showTimePicker && <TimePicker value={value} onChange={onChange} />}
</div>
</Dropdown>
);
};

DatePicker.Field = createField(DatePicker);
export default DatePicker;
2 changes: 1 addition & 1 deletion app/components/Form/Field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ export function createField(Component: ComponentType<any>, options?: Options) {
{...input}
{...props}
onChange={(value) => {
input.onChange(value);
input.onChange?.(value);
onChange?.(value);
}}
className={cx(
Expand Down

0 comments on commit 4e95168

Please sign in to comment.