132 lines
4.7 KiB
TypeScript
132 lines
4.7 KiB
TypeScript
import React, { useMemo } from 'react';
|
|
import { defineMessages, useIntl } from 'react-intl';
|
|
|
|
import IconButton from 'soapbox/components/icon_button';
|
|
import BundleContainer from 'soapbox/features/ui/containers/bundle_container';
|
|
import { DatePicker } from 'soapbox/features/ui/util/async-components';
|
|
import { useAppSelector, useFeatures } from 'soapbox/hooks';
|
|
|
|
const messages = defineMessages({
|
|
birthdayPlaceholder: { id: 'edit_profile.fields.birthday_placeholder', defaultMessage: 'Your birthday' },
|
|
previousMonth: { id: 'datepicker.previous_month', defaultMessage: 'Previous month' },
|
|
nextMonth: { id: 'datepicker.next_month', defaultMessage: 'Next month' },
|
|
previousYear: { id: 'datepicker.previous_year', defaultMessage: 'Previous year' },
|
|
nextYear: { id: 'datepicker.next_year', defaultMessage: 'Next year' },
|
|
});
|
|
|
|
interface IBirthdayInput {
|
|
value?: string,
|
|
onChange: (value: string) => void,
|
|
required?: boolean,
|
|
}
|
|
|
|
const BirthdayInput: React.FC<IBirthdayInput> = ({ value, onChange, required }) => {
|
|
const intl = useIntl();
|
|
const features = useFeatures();
|
|
|
|
const supportsBirthdays = features.birthdays;
|
|
const minAge = useAppSelector((state) => state.instance.pleroma.getIn(['metadata', 'birthday_min_age'])) as number;
|
|
|
|
const maxDate = useMemo(() => {
|
|
if (!supportsBirthdays) return null;
|
|
|
|
let maxDate = new Date();
|
|
maxDate = new Date(maxDate.getTime() - minAge * 1000 * 60 * 60 * 24 + maxDate.getTimezoneOffset() * 1000 * 60);
|
|
return maxDate;
|
|
}, [minAge]);
|
|
|
|
const selected = useMemo(() => {
|
|
if (!supportsBirthdays || !value) return null;
|
|
|
|
const date = new Date(value);
|
|
return new Date(date.getTime() + (date.getTimezoneOffset() * 60000));
|
|
}, [value]);
|
|
|
|
if (!supportsBirthdays) return null;
|
|
|
|
const renderCustomHeader = ({
|
|
decreaseMonth,
|
|
increaseMonth,
|
|
prevMonthButtonDisabled,
|
|
nextMonthButtonDisabled,
|
|
decreaseYear,
|
|
increaseYear,
|
|
prevYearButtonDisabled,
|
|
nextYearButtonDisabled,
|
|
date,
|
|
}: {
|
|
decreaseMonth(): void,
|
|
increaseMonth(): void,
|
|
prevMonthButtonDisabled: boolean,
|
|
nextMonthButtonDisabled: boolean,
|
|
decreaseYear(): void,
|
|
increaseYear(): void,
|
|
prevYearButtonDisabled: boolean,
|
|
nextYearButtonDisabled: boolean,
|
|
date: Date,
|
|
}) => {
|
|
return (
|
|
<div className='flex flex-col gap-2'>
|
|
<div className='flex items-center justify-between'>
|
|
<IconButton
|
|
className='datepicker__button'
|
|
src={require('@tabler/icons/icons/chevron-left.svg')}
|
|
onClick={decreaseMonth}
|
|
disabled={prevMonthButtonDisabled}
|
|
aria-label={intl.formatMessage(messages.previousMonth)}
|
|
title={intl.formatMessage(messages.previousMonth)}
|
|
/>
|
|
{intl.formatDate(date, { month: 'long' })}
|
|
<IconButton
|
|
className='datepicker__button'
|
|
src={require('@tabler/icons/icons/chevron-right.svg')}
|
|
onClick={increaseMonth}
|
|
disabled={nextMonthButtonDisabled}
|
|
aria-label={intl.formatMessage(messages.nextMonth)}
|
|
title={intl.formatMessage(messages.nextMonth)}
|
|
/>
|
|
</div>
|
|
<div className='flex items-center justify-between'>
|
|
<IconButton
|
|
className='datepicker__button'
|
|
src={require('@tabler/icons/icons/chevron-left.svg')}
|
|
onClick={decreaseYear}
|
|
disabled={prevYearButtonDisabled}
|
|
aria-label={intl.formatMessage(messages.previousYear)}
|
|
title={intl.formatMessage(messages.previousYear)}
|
|
/>
|
|
{intl.formatDate(date, { year: 'numeric' })}
|
|
<IconButton
|
|
className='datepicker__button'
|
|
src={require('@tabler/icons/icons/chevron-right.svg')}
|
|
onClick={increaseYear}
|
|
disabled={nextYearButtonDisabled}
|
|
aria-label={intl.formatMessage(messages.nextYear)}
|
|
title={intl.formatMessage(messages.nextYear)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const handleChange = (date: Date) => onChange(new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(0, 10));
|
|
|
|
return (
|
|
<div className='mt-1 relative rounded-md shadow-sm'>
|
|
<BundleContainer fetchComponent={DatePicker}>
|
|
{Component => (<Component
|
|
selected={selected}
|
|
wrapperClassName='react-datepicker-wrapper'
|
|
onChange={handleChange}
|
|
placeholderText={intl.formatMessage(messages.birthdayPlaceholder)}
|
|
minDate={new Date('1900-01-01')}
|
|
maxDate={maxDate}
|
|
required={required}
|
|
renderCustomHeader={renderCustomHeader}
|
|
/>)}
|
|
</BundleContainer>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default BirthdayInput;
|