@ -1,40 +1,16 @@
import * as React from 'react' ;
import { TextField , Select , MenuItem , Form Group, Form ControlLabel, Checkbox , InputLabel , FormControl } from '@material-ui/core' ;
import { TextField , Select , MenuItem , Form ControlLabel, InputLabel , FormControl } from '@material-ui/core' ;
import DateTimePicker from '../widgets/DateTimePicker' ;
import { isNumber } from 'util ';
import * as ICAL from 'ical.js ';
interface PropsType {
onChange : ( rrule : RRuleOptions ) = > void ;
rrule : RRuleOptions ;
}
export interface RRuleOptions {
freq : Frequency ;
interval : number ;
until? : Date ;
count? : number ;
byweekday? : Weekday [ ] ;
bymonthday? : number ;
byyearday? : number ;
byweekno? : number ;
bymonth? : Months ;
bysetpos? : number ;
wkst? : Weekday ;
bysecond? : number [ ] ;
byminute? : number [ ] ;
byday? : number [ ] ;
export type RRuleOptions = ICAL . RecurData ;
}
enum Frequency {
YEARLY ,
MONTHLY ,
WEEKLY ,
DAILY ,
}
enum Ends {
Never ,
Date ,
After ,
}
enum Months {
Jan = 1 ,
Feb ,
@ -49,45 +25,64 @@ enum Months {
Nov ,
Dec ,
}
enum MonthRepeat {
Bysetpos ,
Bymonthday ,
}
enum Weekday {
enum WeekDay {
Su = 1 ,
Mo ,
Tu ,
We ,
Th ,
Fr ,
Sa ,
Su
}
const menuItemsMonths = Object . keys ( Months ) . filter ( ( key ) = > Number ( key ) ) . map ( ( key ) = > {
const disableComplex = true ;
const weekdays : WeekDay [ ] = Array . from ( Array ( 7 ) ) . map ( ( _ , i ) = > i + 1 ) ;
const months : Months [ ] = Array . from ( Array ( 12 ) ) . map ( ( _ , i ) = > i + 1 ) ;
const menuItemsEnds = [ Ends . Never , Ends . Date , Ends . After ] . map ( ( key ) = > {
return (
< MenuItem key = { key } value = { key } > { Months [ key ] } < / MenuItem >
< MenuItem key = { key } value = { key } > { End s[ key ] } < / MenuItem >
) ;
} ) ;
const menuItemsEnds = [ Ends . Never , Ends . Date , Ends . After ] . map ( ( key ) = > {
const menuItems Frequency = [ 'YEARLY' , 'MONTHLY' , 'WEEKLY' , 'DAILY' ] . map ( ( value ) = > {
return (
< MenuItem key = { key} value = { key } > { Ends [ key ] } < / MenuItem >
< MenuItem key = { value} value = { value } > { value . toLowerCase ( ) } < / MenuItem >
) ;
} ) ;
const weekdays = [ Weekday . Mo , Weekday . Tu , Weekday . We , Weekday . Th , Weekday . Fr , Weekday . Sa , Weekday . Su ] ;
const menuItemsFrequency = [ Frequency . YEARLY , Frequency . MONTHLY , Frequency . WEEKLY , Frequency . DAILY ] . map ( ( value ) = > {
const menuItemMonths = months . map ( ( month ) = > {
return (
< MenuItem key = { value } value = { value } > { Frequency [ value ] } < / MenuItem >
< MenuItem key = { month } value = { month } > { Months [ month ] } < / MenuItem >
) ;
} ) ;
const menuItemsWeekDays = weekdays . map ( ( day ) = > {
return (
< MenuItem key = { day } value = { WeekDay [ day ] . toUpperCase ( ) } > { WeekDay [ day ] } < / MenuItem >
) ;
} ) ;
export default function RRuleEteSync ( props : PropsType ) {
const options = props . rrule ;
const styles = {
multiSelect : { minWidth : 120 , maxWidth : '100%' } ,
width : { width : 120 } ,
} ;
interface PropsType {
onChange : ( rrule : RRuleOptions ) = > void ;
rrule : RRuleOptions ;
}
export default function RRule ( props : PropsType ) {
const options = props . rrule ;
function updateRule ( newOptions : Partial < RRuleOptions > ) : void {
const updatedOptions = { . . . options , . . . newOptions } ;
props . onChange ( updatedOptions ) ;
}
function getEnds ( ) : Ends {
if ( options . until && ! options . count ) {
return Ends . Date ;
@ -97,63 +92,24 @@ export default function RRuleEteSync(props: PropsType) {
return Ends . Never ;
}
}
function handleCheckboxWeekday ( event : React.FormEvent < { value : unknown } > ) : void {
const checkbox = event . target as HTMLInputElement ;
const weekday = Number ( checkbox . value ) ;
let byweekdayArray = options . byweekday as Weekday [ ] ;
let byweekday ;
if ( ! checkbox . checked && byweekdayArray ) {
byweekday = byweekdayArray . filter ( ( day ) = > day !== weekday ) ;
} else if ( byweekdayArray ) {
byweekdayArray = byweekdayArray . filter ( ( day ) = > day !== weekday ) ;
byweekday = [ . . . byweekdayArray , weekday ] ;
} else {
byweekday = [ weekday ] ;
}
updateRule ( { byweekday : byweekday } ) ;
}
function isWeekdayChecked ( day : number ) : boolean {
const weekdayArray = options . byweekday ;
if ( weekdayArray ) {
return isNumber ( weekdayArray . find ( ( value ) = > Weekday [ value ] === Weekday [ day ] ) ) ;
} else {
return false ;
}
}
const checkboxWeekDays = weekdays . map ( ( _ , index ) = > {
return (
< FormControlLabel
control = {
< Checkbox
key = { index }
value = { index }
checked = { isWeekdayChecked ( index ) }
onChange = { handleCheckboxWeekday }
/ > }
key = { index }
label = { Weekday [ index ] } / >
) ;
} ) ;
return (
< >
< div style = { { display : 'flex' } } >
< FormControlLabel
style = { { marginRight : 0 } }
value = { options . freq }
label = "Repeat every : "
label = "Repeat every"
labelPlacement = "start"
control = { < TextField
style = { { marginLeft : '0.5em' , width : '4em' } }
type = "number"
inputProps = { { min : 1 , max : 1000 } }
value = { options . interval }
value = { options . interval ? ? 1 }
onChange = { ( event : React.FormEvent < { value : unknown } > ) = > {
event . preventDefault ( ) ;
const inputNode = event . currentTarget as HTMLInputElement ;
if ( inputNode . value === '' ) {
updateRule ( { interval : undefined } ) ;
updateRule ( { interval : 1 } ) ;
} else if ( inputNode . valueAsNumber ) {
updateRule ( { interval : inputNode.valueAsNumber } ) ;
}
@ -162,70 +118,118 @@ export default function RRuleEteSync(props: PropsType) {
/ >
< Select
value = { options . freq }
style = { { alignSelf: 'flex-end' , marginLeft : 20 } }
style = { { marginLeft: '0.5em' } }
onChange = { ( event : React.FormEvent < { value : unknown } > ) = > {
const freq = Number ( ( event . target as HTMLSelectElement ) . value ) ;
const updatedOptions = {
freq : freq ,
bysetpos : undefined ,
bymonthday : freq === Frequency . MONTHLY || Frequency . YEARLY === freq ? 1 : undefined ,
byweekday : undefined ,
bymonth : freq === Frequency . YEARLY ? Months.Jan : undefined ,
} ;
updateRule ( updatedOptions ) ;
const freq = ( event . target as HTMLSelectElement ) . value as ICAL . FrequencyValues ;
updateRule ( { freq : freq } ) ;
} }
>
{ menuItemsFrequency }
< / Select >
< / div >
< div style = { { display : 'flex' } } >
{ ( options . freq === Frequency . MONTHLY ) &&
< Select
value = { options . bysetpos ? MonthRepeat.Bysetpos : MonthRepeat.Bymonthday }
onChange = { ( event : React.FormEvent < { value : unknown } > ) = > {
const value = Number ( ( event . target as HTMLInputElement ) . value ) ;
if ( value === MonthRepeat . Bymonthday ) {
updateRule ( { bymonthday : 1 , bysetpos : undefined , bymonth : Months.Jan } ) ;
} else if ( value === MonthRepeat . Bysetpos ) {
updateRule ( { bysetpos : 1 , bymonthday : undefined , bymonth : undefined } ) ;
}
} }
>
< MenuItem value = { MonthRepeat . Bymonthday } > On < / MenuItem >
< MenuItem value = { MonthRepeat . Bysetpos } > On the < / MenuItem >
< / Select >
}
{ options . bysetpos &&
< Select
value = { options . bysetpos }
onChange = { ( event : React.FormEvent < { value : unknown } > ) = > {
updateRule ( { bysetpos : Number ( ( event . target as HTMLInputElement ) . value ) } ) ;
} } >
< MenuItem value = { 1 } > First < / MenuItem >
< MenuItem value = { 2 } > Second < / MenuItem >
< MenuItem value = { 3 } > Third < / MenuItem >
< MenuItem value = { 4 } > Fourth < / MenuItem >
< MenuItem value = { - 1 } > Last < / MenuItem >
< / Select >
}
{ ( options . freq === Frequency . YEARLY && options . bymonth ) &&
< Select
value = { options . bymonth }
onChange = { ( event : React.FormEvent < { value : unknown } > ) = > {
updateRule ( { bymonth : Number ( ( event . target as HTMLInputElement ) . value ) } ) ;
} } >
{ menuItemsMonths }
< / Select >
{ ! disableComplex && (
< div style = { { display : 'flex' } } >
{ ( options . freq === 'MONTHLY' ) &&
< Select value = { options . bysetpos ? MonthRepeat.Bysetpos : MonthRepeat.Bymonthday }
onChange = { ( event : React.FormEvent < { value : unknown } > ) = > {
const value = Number ( ( event . target as HTMLInputElement ) . value ) ;
if ( value === MonthRepeat . Bymonthday ) {
updateRule ( { bymonthday : [ 1 ] , bysetpos : undefined , bymonth : [ Months . Jan ] } ) ;
} else if ( value === MonthRepeat . Bysetpos ) {
updateRule ( { bysetpos : [ 1 ] , bymonthday : undefined , bymonth : undefined } ) ;
}
} } >
< MenuItem value = { MonthRepeat . Bymonthday } > On < / MenuItem >
< MenuItem value = { MonthRepeat . Bysetpos } > On the < / MenuItem >
< / Select >
}
{ options . bysetpos &&
< Select value = { options . bysetpos [ 0 ] }
onChange = { ( event : React.FormEvent < { value : unknown } > ) = > {
updateRule ( { bysetpos : [ Number ( ( event . target as HTMLInputElement ) . value ) ] } ) ;
} } >
< MenuItem value = { 1 } > First < / MenuItem >
< MenuItem value = { 2 } > Second < / MenuItem >
< MenuItem value = { 3 } > Third < / MenuItem >
< MenuItem value = { 4 } > Fourth < / MenuItem >
< MenuItem value = { - 1 } > Last < / MenuItem >
< / Select >
}
< / div >
) }
< FormControl >
< InputLabel > Ends < / InputLabel >
< Select
value = { getEnds ( ) }
style = { styles . width }
onChange = { ( event : React.FormEvent < { value : unknown } > ) = > {
const value = Number ( ( event . target as HTMLSelectElement ) . value ) ;
let updateOptions ;
if ( value === Ends . Date ) {
updateOptions = { count : undefined , until : ICAL.Time.now ( ) } ;
} else if ( value === Ends . After ) {
updateOptions = { until : undefined , count : 1 } ;
} else {
updateOptions = { count : undefined , until : undefined } ;
}
updateRule ( updateOptions ) ;
} } >
{ menuItemsEnds }
< / Select >
< / FormControl >
{ options . until &&
< DateTimePicker
dateOnly
value = { options . until ? . toJSDate ( ) || undefined }
placeholder = "Ends"
onChange = { ( date? : Date ) = > {
const value = date ? date : null ;
updateRule ( { until : ICAL.Time.fromJSDate ( value , true ) } ) ;
} }
/ >
}
{ options . count &&
< TextField
type = "number"
value = { options . count }
label = "Count"
style = { { width : 60 } }
inputProps = { { min : 1 , step : 1 } }
onChange = { ( event : React.FormEvent < { value : unknown } > ) = > {
event . preventDefault ( ) ;
const inputNode = event . currentTarget as HTMLInputElement ;
if ( inputNode . value === '' ) {
updateRule ( { count : 1 } ) ;
} else if ( inputNode . valueAsNumber ) {
updateRule ( { count : inputNode.valueAsNumber } ) ;
}
} }
/ >
}
< div >
{ ( options . freq && options . freq !== 'DAILY' ) &&
< div >
< FormControl >
< InputLabel > Weekdays < / InputLabel >
< Select
value = { options . byday ? options . byday : [ ] }
multiple
style = { styles . multiSelect }
onChange = { ( event : React.ChangeEvent < { value : unknown } > ) = > {
const value = event . target . value as string [ ] ;
updateRule ( { byday : value } ) ;
} } >
{ menuItemsWeekDays }
< / Select >
< / FormControl >
< / div >
}
{ options . bymonthday &&
{ options . freq === 'MONTHLY' &&
< TextField
type = "number"
value = { options . bymonthday }
value = { options . bymonthday ? options . bymonthday [ 0 ] : undefined }
label = "Month day"
style = { { width : 100 } }
style = { styles . width }
inputProps = { { min : 1 , step : 1 , max : 31 } }
onChange = { ( event : React.FormEvent < { value : unknown } > ) = > {
event . preventDefault ( ) ;
@ -234,61 +238,27 @@ export default function RRuleEteSync(props: PropsType) {
if ( value === '' ) {
updateRule ( { bymonthday : undefined } ) ;
} else if ( numberValue < 32 && numberValue > 0 ) {
updateRule ( { bymonthday : numberValue } ) ;
updateRule ( { bymonthday : [ numberValue ] } ) ;
}
} }
/ >
}
< / div >
< div >
{ options . freq !== Frequency . DAILY &&
< FormGroup row > { checkboxWeekDays } < / FormGroup >
}
< FormControl >
< InputLabel > Ends < / InputLabel >
< Select
value = { getEnds ( ) }
onChange = { ( event : React.FormEvent < { value : unknown } > ) = > {
const value = Number ( ( event . target as HTMLSelectElement ) . value ) ;
let updateOptions ;
if ( value === Ends . Date ) {
updateOptions = { count : undefined , until : new Date ( ) } ;
} else if ( value === Ends . After ) {
updateOptions = { until : undefined , count : 1 } ;
} else {
updateOptions = { count : undefined , until : undefined } ;
}
updateRule ( updateOptions ) ;
} } >
{ menuItemsEnds }
< / Select >
< / FormControl >
{ options . until &&
< DateTimePicker
dateOnly
value = { options . until || undefined }
placeholder = "Ends"
onChange = { ( date? : Date ) = > updateRule ( { until : date } ) }
/ >
}
{ options . count &&
< TextField
type = "number"
value = { options . count }
label = "Count"
style = { { width : 60 } }
inputProps = { { min : 1 , step : 1 } }
onChange = { ( event : React.FormEvent < { value : unknown } > ) = > {
event . preventDefault ( ) ;
const inputNode = event . currentTarget as HTMLInputElement ;
if ( inputNode . value === '' ) {
updateRule ( { count : 1 } ) ;
} else if ( inputNode . valueAsNumber ) {
updateRule ( { count : inputNode.valueAsNumber } ) ;
}
} }
/ >
{ options . freq === 'YEARLY' &&
< div >
< FormControl >
< InputLabel > Months < / InputLabel >
< Select
style = { styles . multiSelect }
value = { options . bymonth ? options . bymonth : [ ] }
multiple
onChange = { ( event : React.ChangeEvent < { value : unknown } > ) = > {
const value = event . target . value as string [ ] ;
updateRule ( { bymonth : value.map ( ( month ) = > Number ( month ) ) } ) ;
} } >
{ menuItemMonths }
< / Select >
< / FormControl >
< / div >
}
< / div >
< / >