1
1
import * as dateFns from "date-fns" ;
2
- import { Calendar , CalendarProps , dateFnsLocalizer , ViewsProps } from "react-big-calendar" ;
2
+ import { ObjectItem } from "mendix" ;
3
+ import { Calendar , CalendarProps , dateFnsLocalizer , NavigateAction , ViewsProps } from "react-big-calendar" ;
3
4
import withDragAndDrop , { withDragAndDropProps } from "react-big-calendar/lib/addons/dragAndDrop" ;
4
5
import { CalendarContainerProps } from "../../typings/CalendarProps" ;
5
6
import { CustomToolbar } from "../components/Toolbar" ;
7
+ import { createElement , ReactElement } from "react" ;
8
+ // @ts -expect-error - TimeGrid is not part of public typings
9
+ import TimeGrid from "react-big-calendar/lib/TimeGrid" ;
6
10
7
11
import "react-big-calendar/lib/addons/dragAndDrop/styles.css" ;
8
12
import "react-big-calendar/lib/css/react-big-calendar.css" ;
9
13
10
- // Define the event shape
11
14
export interface CalEvent {
12
15
title : string ;
13
16
start : Date ;
14
17
end : Date ;
15
18
allDay : boolean ;
16
19
color ?: string ;
20
+ item : ObjectItem ;
17
21
}
18
22
19
23
// Configure date-fns localizer
@@ -63,6 +67,82 @@ interface DragAndDropCalendarProps<TEvent extends object = Event, TResource exte
63
67
withDragAndDropProps < TEvent , TResource > { }
64
68
65
69
export function extractCalendarProps ( props : CalendarContainerProps ) : DragAndDropCalendarProps < CalEvent , object > {
70
+ const visibleSet = new Set < number > ( ) ;
71
+ // Caption for custom work week button / title
72
+
73
+ const customCaption : string = props . customViewCaption ?? "Custom" ;
74
+ const dayProps = [
75
+ { prop : props . showSunday , day : 0 } ,
76
+ { prop : props . showMonday , day : 1 } ,
77
+ { prop : props . showTuesday , day : 2 } ,
78
+ { prop : props . showWednesday , day : 3 } ,
79
+ { prop : props . showThursday , day : 4 } ,
80
+ { prop : props . showFriday , day : 5 } ,
81
+ { prop : props . showSaturday , day : 6 }
82
+ ] ;
83
+
84
+ dayProps . forEach ( ( { prop, day } ) => {
85
+ if ( prop ) visibleSet . add ( day ) ;
86
+ } ) ;
87
+
88
+ function customRange ( date : Date ) : Date [ ] {
89
+ const startOfWeekDate = dateFns . startOfWeek ( date , { weekStartsOn : 0 } ) ;
90
+ const range : Date [ ] = [ ] ;
91
+ for ( let i = 0 ; i < 7 ; i ++ ) {
92
+ const current = dateFns . addDays ( startOfWeekDate , i ) ;
93
+ if ( visibleSet . has ( current . getDay ( ) ) ) {
94
+ range . push ( current ) ;
95
+ }
96
+ }
97
+ return range ;
98
+ }
99
+
100
+ // Custom work-week view component based on TimeGrid
101
+ const CustomWeek = ( viewProps : CalendarProps ) : ReactElement => {
102
+ const { date } = viewProps ;
103
+ const range = customRange ( date as Date ) ;
104
+
105
+ return createElement ( TimeGrid as any , { ...viewProps , range, eventOffset : 15 } ) ;
106
+ } ;
107
+
108
+ CustomWeek . range = customRange ;
109
+ CustomWeek . navigate = ( date : Date , action : NavigateAction ) : Date => {
110
+ switch ( action ) {
111
+ case "PREV" :
112
+ return dateFns . addWeeks ( date , - 1 ) ;
113
+ case "NEXT" :
114
+ return dateFns . addWeeks ( date , 1 ) ;
115
+ default :
116
+ return date ;
117
+ }
118
+ } ;
119
+
120
+ CustomWeek . title = ( date : Date , options : any ) : string => {
121
+ const loc = options ?. localizer ?? {
122
+ // Fallback localizer (EN)
123
+ format : ( d : Date , _fmt : string ) => d . toLocaleDateString ( undefined , { month : "short" , day : "2-digit" } )
124
+ } ;
125
+
126
+ const range = customRange ( date ) ;
127
+
128
+ // Determine if the dates are contiguous (difference of 1 day between successive dates)
129
+ const isContiguous = range . every (
130
+ ( curr , idx , arr ) => idx === 0 || dateFns . differenceInCalendarDays ( curr , arr [ idx - 1 ] ) === 1
131
+ ) ;
132
+
133
+ if ( isContiguous ) {
134
+ // Keep default first–last representation (e.g. "Mar 11 – Mar 15")
135
+ const first = range [ 0 ] ;
136
+ const last = range [ range . length - 1 ] ;
137
+ return `${ loc . format ( first , "MMM dd" ) } – ${ loc . format ( last , "MMM dd" ) } ` ;
138
+ }
139
+
140
+ // Non-contiguous selection → list individual weekday names (Mon, Wed, Fri)
141
+ const weekdayList = range . map ( d => loc . format ( d , "EEE" ) ) . join ( ", " ) ;
142
+
143
+ return weekdayList ;
144
+ } ;
145
+
66
146
const items = props . databaseDataSource ?. items ?? [ ] ;
67
147
const events : CalEvent [ ] = items . map ( item => {
68
148
const title =
@@ -75,11 +155,19 @@ export function extractCalendarProps(props: CalendarContainerProps): DragAndDrop
75
155
const end = props . endAttribute ?. get ( item ) . value ?? start ;
76
156
const allDay = props . allDayAttribute ?. get ( item ) . value ?? false ;
77
157
const color = props . eventColor ?. get ( item ) . value ;
78
- return { title, start, end, allDay, color } ;
158
+ return { title, start, end, allDay, color, item } ;
79
159
} ) ;
80
160
161
+ // Update button label inside localizer messages
162
+ ( localizer as any ) . messages = {
163
+ ...localizer . messages ,
164
+ work_week : customCaption
165
+ } ;
166
+
81
167
const viewsOption : ViewsProps < CalEvent , object > =
82
- props . view === "standard" ? [ "day" , "week" , "month" ] : [ "month" , "week" , "work_week" , "day" , "agenda" ] ;
168
+ props . view === "standard"
169
+ ? { day : true , week : true , month : true }
170
+ : { day : true , week : true , month : true , work_week : CustomWeek , agenda : true } ;
83
171
84
172
// Compute minimum and maximum times for the day based on configured hours
85
173
const minTime = new Date ( ) ;
@@ -88,8 +176,8 @@ export function extractCalendarProps(props: CalendarContainerProps): DragAndDrop
88
176
maxTime . setHours ( props . maxHour ?? 24 , 0 , 0 , 0 ) ;
89
177
90
178
const handleSelectEvent = ( event : CalEvent ) : void => {
91
- if ( props . onClickEvent ?. canExecute ) {
92
- props . onClickEvent . execute ( {
179
+ if ( props . onClickEvent ?. get ( event . item ) . canExecute ) {
180
+ props . onClickEvent . get ( event . item ) . execute ( {
93
181
startDate : event . start ,
94
182
endDate : event . end ,
95
183
allDay : event . allDay ,
@@ -109,8 +197,8 @@ export function extractCalendarProps(props: CalendarContainerProps): DragAndDrop
109
197
} ;
110
198
111
199
const handleEventDropOrResize = ( { event, start, end } : { event : CalEvent ; start : Date ; end : Date } ) : void => {
112
- if ( props . onChange ?. canExecute ) {
113
- props . onChange . execute ( {
200
+ if ( props . onChange ?. get ( event . item ) . canExecute ) {
201
+ props . onChange . get ( event . item ) . execute ( {
114
202
oldStart : event . start ,
115
203
oldEnd : event . end ,
116
204
newStart : start ,
@@ -119,7 +207,7 @@ export function extractCalendarProps(props: CalendarContainerProps): DragAndDrop
119
207
}
120
208
} ;
121
209
122
- const handleRangeChange = ( date : Date , view : string ) : void => {
210
+ const handleRangeChange = ( date : Date , view : string , _action : NavigateAction ) : void => {
123
211
if ( props . onRangeChange ?. canExecute ) {
124
212
const { start, end } = getViewRange ( view , date ) ;
125
213
props . onRangeChange . execute ( {
@@ -135,6 +223,9 @@ export function extractCalendarProps(props: CalendarContainerProps): DragAndDrop
135
223
toolbar : CustomToolbar
136
224
} ,
137
225
defaultView : props . defaultView ,
226
+ messages : {
227
+ work_week : customCaption
228
+ } ,
138
229
events,
139
230
localizer,
140
231
resizable : props . editable !== "never" ,
0 commit comments