Skip to content

Commit be7ac9a

Browse files
committed
chore: update calendar events
1 parent 295b9a9 commit be7ac9a

File tree

9 files changed

+252
-168
lines changed

9 files changed

+252
-168
lines changed

packages/pluggableWidgets/calendar-web/src/Calendar.editorConfig.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {
33
rowLayout,
44
structurePreviewPalette,
55
StructurePreviewProps,
6-
svgImage,
76
text
87
} from "@mendix/widget-plugin-platform/preview/structure-preview-api";
98
import { Properties, hidePropertyIn, hidePropertiesIn } from "@mendix/pluggable-widgets-tools";
@@ -71,9 +70,12 @@ export function getPreview(_values: CalendarPreviewProps, isDarkMode: boolean):
7170
columnSize: "grow",
7271
padding: 6
7372
})(
74-
svgImage({ width: 16, height: 16, grow: 0 })(
75-
decodeURIComponent((isDarkMode ? IconSVGDark : IconSVG).replace("data:image/svg+xml,", ""))
76-
),
73+
{
74+
type: "Image",
75+
document: decodeURIComponent((isDarkMode ? IconSVGDark : IconSVG).replace("data:image/svg+xml,", "")),
76+
width: 16,
77+
height: 16
78+
},
7779
text({ fontColor: palette.text.primary })("Calendar")
7880
)
7981
);

packages/pluggableWidgets/calendar-web/src/Calendar.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,25 @@ import { CalendarPropsBuilder } from "./helpers/CalendarPropsBuilder";
55
import { DnDCalendar } from "./utils/calendar-utils";
66
import { constructWrapperStyle } from "./utils/style-utils";
77
import "./ui/Calendar.scss";
8+
import { useCalendarEvents } from "./helpers/useCalendarEvents";
89

910
export default function MxCalendar(props: CalendarContainerProps): ReactElement {
10-
const wrapperStyle = useMemo(() => constructWrapperStyle(props), [props]);
11-
const calendarProps = useMemo(() => new CalendarPropsBuilder(props).build(), [props]);
11+
// useMemo with empty dependency array is used
12+
// because style and calendar controller needs to be created only once
13+
// and not on every re-render
14+
// eslint-disable-next-line react-hooks/exhaustive-deps
15+
const wrapperStyle = useMemo(() => constructWrapperStyle(props), []);
16+
// eslint-disable-next-line react-hooks/exhaustive-deps
17+
const calendarController = useMemo(() => new CalendarPropsBuilder(props), []);
18+
const calendarProps = useMemo(() => {
19+
calendarController.updateProps(props);
20+
return calendarController.build();
21+
}, [props, calendarController]);
22+
23+
const calendarEvents = useCalendarEvents(props);
1224
return (
1325
<div className={classNames("widget-calendar", props.class)} style={wrapperStyle}>
14-
<DnDCalendar {...calendarProps} />
26+
<DnDCalendar {...calendarProps} {...calendarEvents} />
1527
</div>
1628
);
1729
}

packages/pluggableWidgets/calendar-web/src/Calendar.xml

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -71,17 +71,15 @@
7171
<enumerationValue key="custom">Custom work-week</enumerationValue>
7272
</enumerationValues>
7373
</property>
74-
<property key="editable" type="enumeration" defaultValue="default">
74+
<property key="editable" type="expression" defaultValue="true">
7575
<caption>Editable</caption>
7676
<description />
77-
<enumerationValues>
78-
<enumerationValue key="default">Default</enumerationValue>
79-
<enumerationValue key="never">Never</enumerationValue>
80-
</enumerationValues>
77+
<returnType type="Boolean" />
8178
</property>
82-
<property key="enableCreate" type="boolean" defaultValue="true">
79+
<property key="enableCreate" type="expression" defaultValue="true">
8380
<caption>Enable create</caption>
8481
<description />
82+
<returnType type="Boolean" />
8583
</property>
8684
<property key="showEventDate" type="boolean" defaultValue="true">
8785
<caption>Show event date range</caption>
@@ -171,13 +169,6 @@
171169
</propertyGroup>
172170
</propertyGroup>
173171
<propertyGroup caption="Events">
174-
<property key="eventDataAttribute" type="attribute" required="false">
175-
<caption>Event data attribute</caption>
176-
<description>The attribute to store received raw data</description>
177-
<attributeTypes>
178-
<attributeType name="String" />
179-
</attributeTypes>
180-
</property>
181172
<property key="onEditEvent" type="action" required="false" dataSource="databaseDataSource">
182173
<caption>On edit</caption>
183174
<description />
Lines changed: 48 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,19 @@
11
import { ObjectItem } from "mendix";
2-
import {
3-
CalendarProps as ReactCalendarProps,
4-
DateLocalizer,
5-
Formats,
6-
NavigateAction,
7-
ViewsProps
8-
} from "react-big-calendar";
9-
import { withDragAndDropProps } from "react-big-calendar/lib/addons/dragAndDrop";
10-
2+
import { DateLocalizer, Formats, ViewsProps } from "react-big-calendar";
113
import { CalendarContainerProps } from "../../typings/CalendarProps";
124
import { CustomToolbar } from "../components/Toolbar";
13-
import { eventPropGetter, getViewRange, localizer } from "../utils/calendar-utils";
5+
import { eventPropGetter, localizer } from "../utils/calendar-utils";
6+
import { CalendarEvent, DragAndDropCalendarProps } from "../utils/typings";
147
import { CustomWeekController } from "./CustomWeekController";
158

16-
export interface CalendarEvent {
17-
title: string;
18-
start: Date;
19-
end: Date;
20-
allDay: boolean;
21-
color?: string;
22-
item: ObjectItem;
23-
}
24-
25-
type EventDropOrResize = {
26-
event: CalendarEvent;
27-
start: Date;
28-
end: Date;
29-
};
30-
31-
interface DragAndDropCalendarProps<TEvent extends object = Event, TResource extends object = object>
32-
extends ReactCalendarProps<TEvent, TResource>,
33-
withDragAndDropProps<TEvent, TResource> {}
34-
359
export class CalendarPropsBuilder {
36-
private readonly visibleDays: Set<number>;
37-
private readonly defaultView: "month" | "week" | "work_week" | "day" | "agenda";
38-
private readonly customCaption: string;
39-
private readonly isCustomView: boolean;
40-
private readonly events: CalendarEvent[];
41-
private readonly minTime: Date;
42-
private readonly maxTime: Date;
43-
44-
// Keeps the currently focused/selected event. Updated on every selection.
45-
private selectedEvent?: CalendarEvent;
46-
/**
47-
* Stores the event that should be ignored on the next onSelectEvent call.
48-
* This is set when a double-click is detected so we can ignore the
49-
* secondary click that React-Big-Calendar dispatches as part of the same
50-
* interaction. After the click has been ignored, the field is cleared so
51-
* that future selections are handled normally.
52-
*/
53-
private ignoreNextClickFor?: CalendarEvent;
10+
private visibleDays: Set<number>;
11+
private defaultView: "month" | "week" | "work_week" | "day" | "agenda";
12+
private customCaption: string;
13+
private isCustomView: boolean;
14+
private events: CalendarEvent[];
15+
private minTime: Date;
16+
private maxTime: Date;
5417

5518
constructor(private props: CalendarContainerProps) {
5619
this.isCustomView = props.view === "custom";
@@ -62,6 +25,17 @@ export class CalendarPropsBuilder {
6225
this.maxTime = this.buildTime(props.maxHour ?? 24);
6326
}
6427

28+
updateProps(props: CalendarContainerProps): void {
29+
this.props = props;
30+
this.isCustomView = props.view === "custom";
31+
this.defaultView = this.isCustomView ? props.defaultViewCustom : props.defaultViewStandard;
32+
this.customCaption = props.customViewCaption?.value ?? "Custom";
33+
this.visibleDays = this.buildVisibleDays();
34+
this.events = this.buildEvents(props.databaseDataSource?.items ?? []);
35+
this.minTime = this.buildTime(props.minHour ?? 0);
36+
this.maxTime = this.buildTime(props.maxHour ?? 24);
37+
}
38+
6539
build(): DragAndDropCalendarProps<CalendarEvent> {
6640
const CustomWeek = CustomWeekController.getComponent(this.visibleDays);
6741
const formats = this.buildFormats();
@@ -80,21 +54,14 @@ export class CalendarPropsBuilder {
8054
events: this.events,
8155
formats,
8256
localizer,
83-
resizable: this.props.editable !== "never",
84-
selectable: this.props.enableCreate,
57+
resizable: this.props.editable.value ?? true,
58+
selectable: this.props.enableCreate.value ?? true,
8559
views,
8660
allDayAccessor: (event: CalendarEvent) => event.allDay,
8761
endAccessor: (event: CalendarEvent) => event.end,
8862
eventPropGetter,
8963
// @ts-expect-error – navigatable prop not yet in typings but exists in runtime component
9064
navigatable: true,
91-
onEventDrop: this.handleEventDropOrResize,
92-
onEventResize: this.handleEventDropOrResize,
93-
onNavigate: this.handleRangeChange,
94-
onSelectEvent: this.handleSelectEvent,
95-
onDoubleClickEvent: this.handleDoubleClickEvent,
96-
onKeyPressEvent: this.handleKeyPressEvent,
97-
onSelectSlot: this.handleCreateEvent,
9865
startAccessor: (event: CalendarEvent) => event.start,
9966
titleAccessor: (event: CalendarEvent) => event.title,
10067
showAllEvents: this.props.showAllEvents,
@@ -186,87 +153,29 @@ export class CalendarPropsBuilder {
186153
return new Set(visibleDays);
187154
}
188155

189-
private handleEventDropOrResize = ({ event, start, end }: EventDropOrResize): void => {
190-
const action = this.props.onDragDropResize?.get(event.item);
191-
192-
if (action?.canExecute) {
193-
action.execute({
194-
oldStart: event.start,
195-
oldEnd: event.end,
196-
newStart: start,
197-
newEnd: end
198-
});
199-
}
200-
};
201-
202-
private handleRangeChange = (date: Date, view: string, _action: NavigateAction): void => {
203-
const action = this.props.onViewRangeChange;
204-
205-
if (action?.canExecute) {
206-
const { start, end } = getViewRange(view, date);
207-
action.execute({
208-
rangeStart: start,
209-
rangeEnd: end,
210-
currentView: view
211-
});
212-
}
213-
};
214-
215-
/**
216-
* Called when an event is single-clicked (selected).
217-
* First click only stores the selection; a consecutive click on the same event triggers edit.
218-
*/
219-
private handleSelectEvent = (event: CalendarEvent): void => {
220-
if (this.ignoreNextClickFor === event) {
221-
// Skip this click – it belongs to a double-click we've already handled.
222-
this.ignoreNextClickFor = undefined;
223-
return;
224-
}
225-
226-
if (this.selectedEvent === event) {
227-
// Second click on the already-selected event => open edit panel.
228-
this.invokeEdit(event);
229-
} else {
230-
// Update current selection.
231-
this.selectedEvent = event;
232-
}
233-
};
234-
235-
/**
236-
* Fast double click should open edit immediately.
237-
*/
238-
private handleDoubleClickEvent = (event: CalendarEvent): void => {
239-
// Open edit immediately & prevent the subsequent single-click handler from firing edit again.
240-
this.invokeEdit(event);
241-
this.ignoreNextClickFor = event;
242-
};
243-
244-
/**
245-
* When the event has focus, pressing Enter should open edit.
246-
*/
247-
private handleKeyPressEvent = (event: CalendarEvent, e: any): void => {
248-
if (e.key === "Enter") {
249-
this.invokeEdit(event);
250-
}
251-
};
252-
253-
private invokeEdit(event: CalendarEvent): void {
254-
const action = this.props.onEditEvent?.get(event.item);
255-
256-
if (action?.canExecute) {
257-
action.execute();
258-
}
259-
}
260-
261-
private handleCreateEvent = (slotInfo: { start: Date; end: Date; action: string }): void => {
262-
const action = this.props.onCreateEvent;
263-
264-
if (action?.canExecute && this.props.enableCreate) {
265-
action?.execute({
266-
startDate: slotInfo.start,
267-
endDate: slotInfo.end,
268-
allDay: slotInfo.action === "select"
269-
});
270-
}
271-
};
156+
// private handleEventDropOrResize = ({ event, start, end }: EventDropOrResize): void => {
157+
// const action = this.props.onDragDropResize?.get(event.item);
158+
159+
// if (action?.canExecute) {
160+
// action.execute({
161+
// oldStart: event.start,
162+
// oldEnd: event.end,
163+
// newStart: start,
164+
// newEnd: end
165+
// });
166+
// }
167+
// };
168+
169+
// private handleRangeChange = (date: Date, view: string, _action: NavigateAction): void => {
170+
// const action = this.props.onViewRangeChange;
171+
172+
// if (action?.canExecute) {
173+
// const { start, end } = getViewRange(view, date);
174+
// action.execute({
175+
// rangeStart: start,
176+
// rangeEnd: end,
177+
// currentView: view
178+
// });
179+
// }
180+
// };
272181
}

0 commit comments

Comments
 (0)