1 import { c, msgid } from 'ttag';
3 import { IntegerInput, Option, SelectTwo } from '@proton/components';
4 import { FREQUENCY, FREQUENCY_INTERVALS_MAX } from '@proton/shared/lib/calendar/constants';
5 import type { WeekStartsOn } from '@proton/shared/lib/date-fns-utc/interface';
6 import type { DateTimeModel, EventModelErrors, FrequencyModel } from '@proton/shared/lib/interfaces/calendar';
7 import clsx from '@proton/utils/clsx';
9 import SelectMonthlyType from '../inputs/SelectMonthlyType';
10 import RepeatOnRow from './RepeatOnRow';
13 frequencyModel: FrequencyModel;
15 weekStartsOn: WeekStartsOn;
16 onChange: (value: FrequencyModel) => void;
17 errors: EventModelErrors;
19 displayStacked?: boolean;
22 const getMaxFrequencyInterval = (frequency: FREQUENCY) => {
23 return FREQUENCY_INTERVALS_MAX[frequency];
26 const RepeatEveryRow = ({
33 displayStacked = false,
35 const isMonthly = frequencyModel.frequency === FREQUENCY.MONTHLY;
36 const isWeekly = frequencyModel.frequency === FREQUENCY.WEEKLY;
37 const safeIntervalPlural = frequencyModel.interval || 1; // Can get undefined through the input
38 const intervalOptions = [
39 { text: c('Option').ngettext(msgid`Day`, `Days`, safeIntervalPlural), value: FREQUENCY.DAILY },
40 { text: c('Option').ngettext(msgid`Week`, `Weeks`, safeIntervalPlural), value: FREQUENCY.WEEKLY },
41 { text: c('Option').ngettext(msgid`Month`, `Months`, safeIntervalPlural), value: FREQUENCY.MONTHLY },
42 { text: c('Option').ngettext(msgid`Year`, `Years`, safeIntervalPlural), value: FREQUENCY.YEARLY },
45 const handleChangeInterval = (interval: number | undefined) => {
46 if (interval !== undefined && (interval > getMaxFrequencyInterval(frequencyModel.frequency) || interval < 1)) {
49 onChange({ ...frequencyModel, interval });
51 const handleChangeFrequency = (frequency: FREQUENCY) => {
52 const newMaxInterval = getMaxFrequencyInterval(frequency);
53 const interval = Math.min(frequencyModel.interval || 0, newMaxInterval);
54 onChange({ ...frequencyModel, frequency, interval });
58 <div className={clsx('flex flex-column', displayStacked ? '*:min-size-auto gap-4' : 'md:flex-row')}>
59 <div className="md:flex-1">
61 className={clsx(displayStacked && 'text-semibold')}
62 htmlFor="event-custom-frequency-number"
63 id="label-event-custom-frequency"
64 >{c('Label').t`Repeat every`}</label>
65 <div className="flex flex-column md:flex-row my-2">
66 <div className="flex flex-nowrap md:flex-1">
67 <span className="w-custom" style={{ '--w-custom': '6em' }}>
69 id="event-custom-frequency-number"
70 data-testid="event-modal/custom-frequency/interval:input"
72 value={frequencyModel.interval}
73 onChange={handleChangeInterval}
75 if (!frequencyModel.interval) {
76 handleChangeInterval(1);
79 aria-invalid={isSubmitted && !!errors.interval}
80 isSubmitted={isSubmitted}
81 title={c('Title').t`Choose how often this event repeats`}
82 aria-describedby="label-event-custom-frequency event-custom-frequency-select"
85 <span className="flex-1 ml-2">
87 id="event-custom-frequency-select"
88 data-testid="event-modal/custom-frequency/interval:frequency"
89 value={frequencyModel.frequency}
90 onChange={({ value }) => handleChangeFrequency(value as FREQUENCY)}
91 title={c('Title').t`Select event frequency interval`}
92 aria-describedby="label-event-custom-frequency event-custom-frequency-number event-custom-frequency-select"
94 {intervalOptions.map(({ text, value }) => (
95 <Option key={value} value={value} title={text} />
101 <div className="md:flex-1 ml-0 mt-2 md:ml-2 md:mt-0">
103 id="event-custom-monthly-select"
104 value={frequencyModel.monthly.type}
106 onChange={(type) => onChange({ ...frequencyModel, monthly: { type } })}
107 title={c('Title').t`Select a day in the month`}
115 frequencyModel={frequencyModel}
117 weekStartsOn={weekStartsOn}
119 displayStacked={displayStacked}
126 export default RepeatEveryRow;