From 62c5decc15b3cbf0fdc1a69c17e1c8a1ec6cbbeb Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Mon, 2 Dec 2024 18:44:56 +0500 Subject: [PATCH 1/3] expose inserted,updated and deleted events + added startTime,endTime and allDay in edit modal + event dragging fixes --- .../src/comps/calendarComp/calendarComp.tsx | 184 +++++++++++++++--- .../comps/calendarComp/calendarConstants.tsx | 19 ++ .../src/i18n/comps/locales/en.ts | 3 + 3 files changed, 178 insertions(+), 28 deletions(-) diff --git a/client/packages/lowcoder-comps/src/comps/calendarComp/calendarComp.tsx b/client/packages/lowcoder-comps/src/comps/calendarComp/calendarComp.tsx index 5021ee568..477729146 100644 --- a/client/packages/lowcoder-comps/src/comps/calendarComp/calendarComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/calendarComp/calendarComp.tsx @@ -1,6 +1,7 @@ import { default as Form } from "antd/es/form"; import { default as Input } from "antd/es/input"; import { default as ColorPicker } from "antd/es/color-picker"; +import { default as Switch } from "antd/es/switch"; import { trans, getCalendarLocale } from "../../i18n/comps"; import { createRef, useContext, useRef, useState, useEffect, useCallback, useMemo, Suspense } from "react"; import dayjs from "dayjs"; @@ -19,6 +20,7 @@ import momentPlugin from "@fullcalendar/moment"; import ErrorBoundary from "./errorBoundary"; import { default as Tabs } from "antd/es/tabs"; +import { differenceBy, differenceWith, isEqual, filter, includes } from "lodash"; import { isValidColor, @@ -54,6 +56,8 @@ import { migrateOldData, controlItem, depsConfig, + stateComp, + JSONObject, } from 'lowcoder-sdk'; import { @@ -196,6 +200,10 @@ let childrenMap: any = { currentPremiumView: dropdownControl(DefaultWithPremiumViewOptions, "resourceTimelineDay"), animationStyle: styleControl(AnimationStyle, 'animationStyle'), showVerticalScrollbar: withDefault(BoolControl, false), + initialData: stateComp({}), + updatedEvents: stateComp({}), + insertedEvents: stateComp({}), + deletedEvents: stateComp({}), }; // this should ensure backwards compatibility with older versions of the SDK @@ -233,8 +241,9 @@ let CalendarBasicComp = (function () { currentFreeView?: string; currentPremiumView?: string; animationStyle?:any; - modalStyle?:any - showVerticalScrollbar?:boolean + modalStyle?:any; + showVerticalScrollbar?:boolean; + initialData: Array; }, dispatch: any) => { const comp = useContext(EditorContext)?.getUICompByName( useContext(CompNameContext) @@ -243,11 +252,13 @@ let CalendarBasicComp = (function () { const theme = useContext(ThemeContext); const ref = createRef(); const editEvent = useRef(); + const initData = useRef(false); const [form] = Form.useForm(); const [left, setLeft] = useState(undefined); const [licensed, setLicensed] = useState(props.licenseKey !== ""); const [currentSlotLabelFormat, setCurrentSlotLabelFormat] = useState(slotLabelFormat); - + const [initDataMap, setInitDataMap] = useState>({}); + useEffect(() => { setLicensed(props.licenseKey !== ""); }, [props.licenseKey]); @@ -290,27 +301,53 @@ let CalendarBasicComp = (function () { start: dayjs(item.start, DateParser).format(), end: dayjs(item.end, DateParser).format(), allDay: item.allDay, - resourceId: item.resourceId ? item.resourceId : null, - groupId: item.groupId ? item.groupId : null, + ...(item.resourceId ? { resourceId: item.resourceId } : {}), + ...(item.groupId ? { groupId: item.groupId } : {}), backgroundColor: item.backgroundColor, - extendedProps: { - color: isValidColor(item.color || "") ? item.color : theme?.theme?.primary, - ...(item.groupId ? { groupId: item.groupId } : {}), // Ensure color is in extendedProps - detail: item.detail, - titleColor:item.titleColor, - detailColor:item.detailColor, - titleFontWeight:item.titleFontWeight, - titleFontStyle:item.titleFontStyle, - detailFontWeight:item.detailFontWeight, - detailFontStyle:item.detailFontStyle, - animation:item?.animation, - animationDelay:item?.animationDelay, - animationDuration:item?.animationDuration, - animationIterationCount:item?.animationIterationCount - }} + extendedProps: { // Ensure color is in extendedProps + color: isValidColor(item.color || "") ? item.color : theme?.theme?.primary, + detail: item.detail, + titleColor:item.titleColor, + detailColor:item.detailColor, + titleFontWeight:item.titleFontWeight, + titleFontStyle:item.titleFontStyle, + detailFontWeight:item.detailFontWeight, + detailFontStyle:item.detailFontStyle, + animation:item?.animation, + animationDelay:item?.animationDelay, + animationDuration:item?.animationDuration, + animationIterationCount:item?.animationIterationCount + } + } }) : [currentEvents]; }, [currentEvents, theme]) + useEffect(() => { + const mapData: Record = {}; + events?.forEach((item: any, index: number) => { + mapData[`${item.id}`] = index; + }) + + if (initData.current) { + const difference = differenceWith(events, props.initialData, isEqual); + const inserted = differenceBy(difference, Object.keys(initDataMap)?.map(id => ({ id })), 'id') + const updated = filter(difference, obj => includes(Object.keys(initDataMap), String(obj.id))); + const deleted = differenceBy(props.initialData, Object.keys(mapData)?.map(id => ({ id })), 'id') + + comp.children?.comp.children?.updatedEvents.dispatchChangeValueAction(updated); + comp.children?.comp.children?.insertedEvents.dispatchChangeValueAction(inserted); + comp.children?.comp.children?.deletedEvents.dispatchChangeValueAction(deleted); + } + + if (!initData.current && events?.length && comp?.children?.comp?.children?.initialData) { + setInitDataMap(mapData); + comp?.children?.comp?.children?.initialData?.dispatch?.( + comp?.children?.comp?.children?.initialData?.changeValueAction?.([...events]) + ); + initData.current = true; + } + }, [JSON.stringify(events), comp?.children?.comp?.children?.initialData]); + const resources = useMemo(() => props.resources.value, [props.resources.value]); // list all plugins for Fullcalendar @@ -370,12 +407,12 @@ let CalendarBasicComp = (function () { }, [slotLabelFormat, slotLabelFormatWeek, slotLabelFormatMonth]); const handleEventDataChange = useCallback((data: Array>) => { - comp.children?.comp.children.events.children.manual.children.manual.dispatch( - comp.children?.comp.children.events.children.manual.children.manual.setChildrensAction( + comp?.children?.comp.children.events.children.manual.children.manual.dispatch( + comp?.children?.comp.children.events.children.manual.children.manual.setChildrensAction( data ) ); - comp.children?.comp.children.events.children.mapData.children.data.dispatchChangeValueAction( + comp?.children?.comp.children.events.children.mapData.children.data.dispatchChangeValueAction( JSON.stringify(data) ); props.onEvent("change"); @@ -506,6 +543,24 @@ let CalendarBasicComp = (function () { > + + + + + + + + + @@ -768,12 +823,21 @@ let CalendarBasicComp = (function () { showModal(event, false); }, [editEvent, showModal]); - const handleDrop = useCallback(() => { + const handleDrop = useCallback((eventInfo: EventType) => { + let eventsList = [...props.events]; + const eventIdx = eventsList.findIndex( + (item: EventType) => item.id === eventInfo.id + ); + if (eventIdx > -1) { + eventsList[eventIdx] = eventInfo; + handleEventDataChange(eventsList); + } + if (typeof props.onDropEvent === 'function') { props.onDropEvent("dropEvent"); } }, [props.onDropEvent]); - + return ( { + eventDrop={(info) => { + const {extendedProps, ...event} = info.event.toJSON(); if (info.view) { - handleDrop(); + handleDrop({ + ...event, + ...extendedProps, + }); } }} /> @@ -1007,6 +1075,30 @@ const TmpCalendarComp = withExposingConfigs(CalendarBasicComp, [ return input.events.filter(event => Boolean(event.resourceId)); }, }), + depsConfig({ + name: "toUpdatedEvents", + desc: trans("calendar.updatedEvents"), + depKeys: ["updatedEvents"], + func: (input: { updatedEvents: any[]; }) => { + return input.updatedEvents; + }, + }), + depsConfig({ + name: "toInsertedEvents", + desc: trans("calendar.insertedEvents"), + depKeys: ["insertedEvents"], + func: (input: { insertedEvents: any[]; }) => { + return input.insertedEvents; + }, + }), + depsConfig({ + name: "toDeletedEvents", + desc: trans("calendar.deletedEvents"), + depKeys: ["deletedEvents"], + func: (input: { deletedEvents: any[]; }) => { + return input.deletedEvents; + }, + }), ]); let CalendarComp = withMethodExposing(TmpCalendarComp, [ @@ -1124,7 +1216,43 @@ let CalendarComp = withMethodExposing(TmpCalendarComp, [ const viewKey = comp.children.licenseKey.getView() === "" ? 'defaultFreeView' : 'defaultPremiumView'; comp.children["viewKey"].dispatchChangeValueAction("multiMonthYear"); } - } + }, + { + method: { + name: "clearUpdatedEvents", + detail: "Clear updated events list", + params: [], + }, + execute: (comp) => { + comp?.children?.updatedEvents.dispatch( + comp?.children?.updatedEvents.changeValueAction([]) + ); + } + }, + { + method: { + name: "clearInsertedEvents", + detail: "Clear inserted events list", + params: [], + }, + execute: (comp) => { + comp?.children?.insertedEvents.dispatch( + comp?.children?.insertedEvents.changeValueAction([]) + ); + } + }, + { + method: { + name: "clearDeletedEvents", + detail: "Clear deleted events list", + params: [], + }, + execute: (comp) => { + comp?.children?.deletedEvents.dispatch( + comp?.children?.deletedEvents.changeValueAction([]) + ); + } + }, ]); diff --git a/client/packages/lowcoder-comps/src/comps/calendarComp/calendarConstants.tsx b/client/packages/lowcoder-comps/src/comps/calendarComp/calendarConstants.tsx index e8602a8fe..a88ab756a 100644 --- a/client/packages/lowcoder-comps/src/comps/calendarComp/calendarConstants.tsx +++ b/client/packages/lowcoder-comps/src/comps/calendarComp/calendarConstants.tsx @@ -988,6 +988,25 @@ export const defaultEvents = [ end: dayjs().hour(21).minute(30).second(0).format(DATE_TIME_FORMAT), color: "#079968", }, + { + id: "6", + label: "Coding", + start: dayjs().hour(15).minute(0).second(0).format(DATE_TIME_FORMAT), + end: dayjs().hour(17).minute(30).second(0).format(DATE_TIME_FORMAT), + color: "#079968", + backgroundColor:"#ffffff", + detail: 'Discuss project milestones and deliverables.', + titleColor:"#000000", + detailColor:"#000000", + titleFontWeight:"normal", + titleFontStyle:"italic", + detailFontWeight:"normal", + detailFontStyle:"italic", + animation:"none", + animationDelay:"0s", + animationDuration:"0s", + animationIterationCount:"0", + }, ]; export const resourcesEventsDefaultData = [ { diff --git a/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts b/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts index f6261b84f..f8d5f77f7 100644 --- a/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts +++ b/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts @@ -271,6 +271,9 @@ export const en = { resourcesDefault: "Rooms", resourcesName: "Resource Name", resourcesEvents : "Resources Events Data", + deletedEvents : "List of deleted events", + updatedEvents : "List of updated events", + insertedEvents : "List of inserted events", editable: "Editable", license: "Licence Key", licenseTooltip: "Get your licence key from https://fullcalendar.io/purchase to enable premium views like Resource Timeline and Resource Grid.", From 3d1e09e9123f8de9ef55d90b376a37fb19faac6b Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Mon, 2 Dec 2024 23:23:20 +0500 Subject: [PATCH 2/3] update data on events drag/drop --- .../src/comps/calendarComp/calendarComp.tsx | 40 +++++++++++-------- .../comps/calendarComp/calendarConstants.tsx | 19 --------- 2 files changed, 24 insertions(+), 35 deletions(-) diff --git a/client/packages/lowcoder-comps/src/comps/calendarComp/calendarComp.tsx b/client/packages/lowcoder-comps/src/comps/calendarComp/calendarComp.tsx index 477729146..06eead610 100644 --- a/client/packages/lowcoder-comps/src/comps/calendarComp/calendarComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/calendarComp/calendarComp.tsx @@ -12,10 +12,10 @@ import adaptivePlugin from "@fullcalendar/adaptive"; import dayGridPlugin from "@fullcalendar/daygrid"; import multiMonthPlugin from '@fullcalendar/multimonth'; import timeGridPlugin from "@fullcalendar/timegrid"; -import interactionPlugin from "@fullcalendar/interaction"; +import interactionPlugin, { EventResizeDoneArg } from "@fullcalendar/interaction"; import listPlugin from "@fullcalendar/list"; import allLocales from "@fullcalendar/core/locales-all"; -import { EventContentArg, DateSelectArg } from "@fullcalendar/core"; +import { EventContentArg, DateSelectArg, EventDropArg } from "@fullcalendar/core"; import momentPlugin from "@fullcalendar/moment"; import ErrorBoundary from "./errorBoundary"; @@ -83,6 +83,7 @@ import { resourceTimeGridHeaderToolbar, } from "./calendarConstants"; import { EventOptionControl } from "./eventOptionsControl"; +import { EventImpl } from "@fullcalendar/core/internal"; function fixOldData(oldData: any) { if(!Boolean(oldData)) return; @@ -823,20 +824,34 @@ let CalendarBasicComp = (function () { showModal(event, false); }, [editEvent, showModal]); - const handleDrop = useCallback((eventInfo: EventType) => { + const updateEventsOnDragOrResize = useCallback((eventInfo: EventImpl) => { + const {extendedProps, title, ...event} = eventInfo.toJSON(); + let eventsList = [...props.events]; const eventIdx = eventsList.findIndex( - (item: EventType) => item.id === eventInfo.id + (item: EventType) => item.id === event.id ); if (eventIdx > -1) { - eventsList[eventIdx] = eventInfo; + eventsList[eventIdx] = { + label: title, + ...event, + ...extendedProps, + }; handleEventDataChange(eventsList); } + }, [props.events, handleEventDataChange]); + + const handleDrop = useCallback((eventInfo: EventDropArg) => { + updateEventsOnDragOrResize(eventInfo.event); if (typeof props.onDropEvent === 'function') { props.onDropEvent("dropEvent"); } - }, [props.onDropEvent]); + }, [props.onDropEvent, updateEventsOnDragOrResize]); + + const handleResize = useCallback((eventInfo: EventResizeDoneArg) => { + updateEventsOnDragOrResize(eventInfo.event); + }, [props.onDropEvent, updateEventsOnDragOrResize]); return ( { - const {extendedProps, ...event} = info.event.toJSON(); - if (info.view) { - handleDrop({ - ...event, - ...extendedProps, - }); - } - }} + eventDrop={handleDrop} + eventResize={handleResize} /> diff --git a/client/packages/lowcoder-comps/src/comps/calendarComp/calendarConstants.tsx b/client/packages/lowcoder-comps/src/comps/calendarComp/calendarConstants.tsx index a88ab756a..e8602a8fe 100644 --- a/client/packages/lowcoder-comps/src/comps/calendarComp/calendarConstants.tsx +++ b/client/packages/lowcoder-comps/src/comps/calendarComp/calendarConstants.tsx @@ -988,25 +988,6 @@ export const defaultEvents = [ end: dayjs().hour(21).minute(30).second(0).format(DATE_TIME_FORMAT), color: "#079968", }, - { - id: "6", - label: "Coding", - start: dayjs().hour(15).minute(0).second(0).format(DATE_TIME_FORMAT), - end: dayjs().hour(17).minute(30).second(0).format(DATE_TIME_FORMAT), - color: "#079968", - backgroundColor:"#ffffff", - detail: 'Discuss project milestones and deliverables.', - titleColor:"#000000", - detailColor:"#000000", - titleFontWeight:"normal", - titleFontStyle:"italic", - detailFontWeight:"normal", - detailFontStyle:"italic", - animation:"none", - animationDelay:"0s", - animationDuration:"0s", - animationIterationCount:"0", - }, ]; export const resourcesEventsDefaultData = [ { From 3a00b2197c14851ea72024fea1f06e05d3cccdb2 Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Wed, 4 Dec 2024 12:43:24 +0500 Subject: [PATCH 3/3] fix drag/drop event triggers --- .../lowcoder-comps/src/comps/calendarComp/calendarComp.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/client/packages/lowcoder-comps/src/comps/calendarComp/calendarComp.tsx b/client/packages/lowcoder-comps/src/comps/calendarComp/calendarComp.tsx index 06eead610..da34b5610 100644 --- a/client/packages/lowcoder-comps/src/comps/calendarComp/calendarComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/calendarComp/calendarComp.tsx @@ -845,7 +845,7 @@ let CalendarBasicComp = (function () { updateEventsOnDragOrResize(eventInfo.event); if (typeof props.onDropEvent === 'function') { - props.onDropEvent("dropEvent"); + props.onDropEvent("drop"); } }, [props.onDropEvent, updateEventsOnDragOrResize]); @@ -959,6 +959,11 @@ let CalendarBasicComp = (function () { props.onEvent("change"); } }} + eventDragStart={() => { + if (typeof props.onDropEvent === 'function') { + props.onDropEvent("drag"); + } + }} eventDrop={handleDrop} eventResize={handleResize} />