From 0c1a9f24cf3f97d35def148ca8298d119427a9b4 Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Wed, 25 Sep 2024 12:51:43 +0500 Subject: [PATCH] combine events/resourceEvents + added manual/map data support for events --- .../src/comps/calendarComp/calendarComp.tsx | 248 ++++++++++++++---- .../comps/calendarComp/calendarConstants.tsx | 38 ++- .../calendarComp/eventOptionsControl.tsx | 169 ++++++++++++ .../src/i18n/comps/locales/en.ts | 4 + 4 files changed, 400 insertions(+), 59 deletions(-) create mode 100644 client/packages/lowcoder-comps/src/comps/calendarComp/eventOptionsControl.tsx diff --git a/client/packages/lowcoder-comps/src/comps/calendarComp/calendarComp.tsx b/client/packages/lowcoder-comps/src/comps/calendarComp/calendarComp.tsx index 9a24ef242..dfff1a453 100644 --- a/client/packages/lowcoder-comps/src/comps/calendarComp/calendarComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/calendarComp/calendarComp.tsx @@ -1,7 +1,8 @@ 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 { trans, getCalendarLocale } from "../../i18n/comps"; -import { createRef, useContext, useRef, useState, useEffect } from "react"; +import { createRef, useContext, useRef, useState, useEffect, useCallback } from "react"; import dayjs from "dayjs"; import FullCalendar from "@fullcalendar/react"; import resourceTimelinePlugin from "@fullcalendar/resource-timeline"; @@ -49,7 +50,10 @@ import { EditorContext, CompNameContext, AnimationStyle, - EventModalStyle + EventModalStyle, + migrateOldData, + controlItem, + depsConfig, } from 'lowcoder-sdk'; import { @@ -60,7 +64,7 @@ import { Event, Remove, EventType, - defaultData, + defaultEvents, ViewType, buttonText, headerToolbar, @@ -71,18 +75,68 @@ import { viewClassNames, FormWrapper, resourcesDefaultData, - resourcesEventsDefaultData, resourceTimeLineHeaderToolbar, resourceTimeGridHeaderToolbar, } from "./calendarConstants"; +import { EventOptionControl } from "./eventOptionsControl"; + +function fixOldData(oldData: any) { + if(!Boolean(oldData)) return; + let {events, resourcesEvents, ...data } = oldData; + let allEvents: any[] = []; + + if (events && typeof events === 'string') { + let eventsList = JSON.parse(events); + if (eventsList && eventsList.length) { + eventsList = eventsList?.map(event => { + const {title, ...eventData} = event; + return { + ...eventData, + label: title, // replace title field with label + } + }); + allEvents = allEvents.concat(eventsList); + } + } + if (resourcesEvents && typeof resourcesEvents === 'string') { + let resourceEventsList = JSON.parse(resourcesEvents); + if (resourceEventsList && resourceEventsList.length) { + resourceEventsList = resourceEventsList?.map(event => { + const {title, ...eventData} = event; + return { + ...eventData, + label: title, // replace title field with label + } + }); + allEvents = allEvents.concat(resourceEventsList); + } + } + + if (allEvents.length) { + return { + ...data, + events: { + manual: { + manual: allEvents, + }, + mapData: { + data: JSON.stringify(allEvents, null, 2), + }, + optionType: "manual", + }, + }; + } + return { + ...data, + events, + }; +} let childrenMap: any = { - events: jsonValueExposingStateControl("events", defaultData), - resourcesEvents: jsonValueExposingStateControl("resourcesEvents", resourcesEventsDefaultData), + events: EventOptionControl, resources: jsonValueExposingStateControl("resources", resourcesDefaultData), resourceName: withDefault(StringControl, trans("calendar.resourcesDefault")), onEvent: CalendarEventHandlerControl ? CalendarEventHandlerControl : ChangeEventHandlerControl, - // onDropEvent: safeDragEventHandlerControl, editable: withDefault(BoolControl, true), showEventTime: withDefault(BoolControl, true), showWeekends: withDefault(BoolControl, true), @@ -97,6 +151,7 @@ let childrenMap: any = { currentPremiumView: dropdownControl(DefaultWithPremiumViewOptions, "resourceTimelineDay"), animationStyle: styleControl(AnimationStyle, 'animationStyle'), }; + // this should ensure backwards compatibility with older versions of the SDK if (DragEventHandlerControl) { childrenMap = { @@ -114,7 +169,6 @@ if (EventModalStyle) { let CalendarBasicComp = (function () { return new UICompBuilder(childrenMap, (props: { events: any; - resourcesEvents: any; resources: any; resourceName : string onEvent?: any; @@ -134,14 +188,11 @@ let CalendarBasicComp = (function () { currentPremiumView?: string; animationStyle?:any; modalStyle?:any - - }, dispatch: any) => { - + }) => { const comp = useContext(EditorContext)?.getUICompByName( useContext(CompNameContext) ); const onEventVal = comp?.toJsonValue()?.comp?.onEvent; - const theme = useContext(ThemeContext); const ref = createRef(); @@ -155,17 +206,20 @@ let CalendarBasicComp = (function () { }, [props.licenseKey]); let currentView = licensed ? props.currentPremiumView : props.currentFreeView; - let currentEvents = currentView == "resourceTimelineDay" || currentView == "resourceTimeGridDay" ? props.resourcesEvents : props.events; + let currentEvents = currentView == "resourceTimelineDay" || currentView == "resourceTimeGridDay" + ? props.events.filter((event: { resourceId: any; }) => Boolean(event.resourceId)) + : props.events.filter((event: { resourceId: any; }) => !Boolean(event.resourceId)); // we use one central stack of events for all views - let events = Array.isArray(currentEvents.value) ? currentEvents.value.map((item: EventType) => { + let events = Array.isArray(currentEvents) ? currentEvents.map((item: EventType) => { return { - title: item.title, + title: item.label, id: item.id, 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, backgroundColor: item.backgroundColor, extendedProps: { color: isValidColor(item.color || "") ? item.color : theme?.theme?.primary, @@ -182,7 +236,7 @@ let CalendarBasicComp = (function () { animationDuration:item?.animationDuration, animationIterationCount:item?.animationIterationCount }} - }) : [currentEvents.value]; + }) : [currentEvents]; const resources = props.resources.value; @@ -263,6 +317,18 @@ let CalendarBasicComp = (function () { modalStyle, } = props; + 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( + data + ) + ); + comp.children?.comp.children.events.children.mapData.children.data.dispatchChangeValueAction( + JSON.stringify(data) + ); + props.onEvent("change"); + }, [comp, props.onEvent]); + function renderEventContent(eventInfo: EventContentArg) { const isList = eventInfo.view.type === "listWeek"; let sizeClass = ""; @@ -304,11 +370,10 @@ let CalendarBasicComp = (function () { className="event-remove" onClick={(e) => { e.stopPropagation(); - props.onEvent("change"); - const event = events.filter( + const events = props.events.filter( (item: EventType) => item.id !== eventInfo.event.id ); - props.events.onChange(event); + handleEventDataChange(events); }} onMouseDown={(e) => { e.stopPropagation(); @@ -321,14 +386,23 @@ let CalendarBasicComp = (function () { ); } const handleDbClick = () => { - const event = props.events.value.find( + const event = props.events.find( (item: EventType) => item.id === editEvent.current?.id ) as EventType; if (!editable || !editEvent.current) { return; } if (event) { - const { title, groupId, color, id , backgroundColor,detail,titleColor,detailColor, + const { + id , + label, + detail, + groupId, + resourceId, + color, + backgroundColor, + titleColor, + detailColor, titleFontWeight, titleFontStyle, detailFontWeight, @@ -337,12 +411,11 @@ let CalendarBasicComp = (function () { animationDelay, animationDuration, animationIterationCount, - - } = event; const eventInfo = { - title, + label, groupId, + resourceId, color, id, backgroundColor, @@ -374,7 +447,7 @@ let CalendarBasicComp = (function () { allDay: info.allDay, start: info.startStr, end: info.endStr, - }; + } as EventType; const view = info.view.type as ViewType; const duration = dayjs(info.end).diff(dayjs(info.start), "minutes"); const singleClick = @@ -405,9 +478,9 @@ let CalendarBasicComp = (function () { customStyles: { backgroundColor:props?.modalStyle?.background, animationStyle:props?.animationStyle, - }, - width: "450px", - content: ( + }, + width: "450px", + content: ( @@ -426,7 +499,7 @@ let CalendarBasicComp = (function () { + + + @@ -453,25 +532,53 @@ let CalendarBasicComp = (function () { label={trans("calendar.eventTitleColor")} name="titleColor" > - + node.parentNode} + defaultValue={form.getFieldValue('titleColor')} + showText + allowClear + format="hex" + onChange={(_, hex) => form.setFieldValue('titleColor', hex)} + /> - + node.parentNode} + defaultValue={form.getFieldValue('detailColor')} + showText + allowClear + format="hex" + onChange={(_, hex) => form.setFieldValue('detailColor', hex)} + /> - + node.parentNode} + defaultValue={form.getFieldValue('color')} + showText + allowClear + format="hex" + onChange={(_, hex) => form.setFieldValue('color', hex)} + /> - + node.parentNode} + defaultValue={form.getFieldValue('backgroundColor')} + showText + allowClear + format="hex" + onChange={(_, hex) => form.setFieldValue('backgroundColor', hex)} + /> @@ -536,7 +643,17 @@ let CalendarBasicComp = (function () { onConfirm: () => { form.submit(); return form.validateFields().then(() => { - const { id, groupId, color, title = "", backgroundColor,detail,titleColor,detailColor , titleFontWeight, + const { + id, + label = "", + detail, + groupId, + resourceId, + color, + backgroundColor, + titleColor, + detailColor, + titleFontWeight, titleFontStyle, detailFontWeight, detailFontStyle, @@ -544,7 +661,7 @@ let CalendarBasicComp = (function () { animationDelay, animationDuration, animationIterationCount } = form.getFieldsValue(); - const idExist = props.events.value.findIndex( + const idExist = props.events.findIndex( (item: EventType) => item.id === id ); if (idExist > -1 && id !== eventId) { @@ -554,14 +671,15 @@ let CalendarBasicComp = (function () { throw new Error(); } if (ifEdit) { - const changeEvents = props.events.value.map((item: EventType) => { + const changeEvents = props.events.map((item: EventType) => { if (item.id === eventId) { return { ...item, - title, + label, detail, id, ...(groupId !== undefined ? { groupId } : null), + ...(resourceId !== undefined ? { resourceId } : null), ...(color !== undefined ? { color } : null), ...(backgroundColor !== undefined ? { backgroundColor } : null), ...(titleColor !== undefined ? { titleColor } : null), @@ -579,14 +697,14 @@ let CalendarBasicComp = (function () { return item; } }); - props.events.onChange(changeEvents); + handleEventDataChange(changeEvents); } else { const createInfo = { allDay: event.allDay, start: event.start, end: event.end, id, - title, + label, detail, titleFontWeight, titleFontStyle, @@ -597,14 +715,14 @@ let CalendarBasicComp = (function () { animationDuration, animationIterationCount, ...(groupId !== undefined ? { groupId } : null), + ...(resourceId !== undefined ? { resourceId } : null), ...(color !== undefined ? { color } : null), ...(backgroundColor !== undefined ? { backgroundColor } : null), ...(titleColor !== undefined ? { titleColor } : null), ...(detailColor !== undefined ? { detailColor } : null), }; - props.events.onChange([...props.events.value, createInfo]); + handleEventDataChange([...props.events, createInfo]); } - props.onEvent("change"); form.resetFields(); }); //small change }, @@ -620,6 +738,7 @@ let CalendarBasicComp = (function () { props.onDropEvent("dropEvent"); } }; + return ( { if (info.view) { - handleDrop + handleDrop(); } }} /> @@ -737,7 +856,6 @@ let CalendarBasicComp = (function () { }) .setPropertyViewFn((children: { events: { propertyView: (arg0: {}) => any; }; - resourcesEvents: { propertyView: (arg0: {}) => any; }; resources: { propertyView: (arg0: {}) => any; }; resourceName: { propertyView: (arg0: {}) => any; }; onEvent: { propertyView: ({title}?: {title?: string}) => any; }; @@ -755,22 +873,26 @@ let CalendarBasicComp = (function () { style: { getPropertyView: () => any; }; animationStyle: { getPropertyView: () => any; }; modalStyle: { getPropertyView: () => any; }; - licenseKey: { getView: () => any; propertyView: (arg0: { label: string; }) => any; }; + licenseKey: { getView: () => any; propertyView: (arg0: { label: string; tooltip: string; }) => any; }; }) => { - const license = children.licenseKey.getView(); return ( <>
{children.defaultDate.propertyView({ label: trans("calendar.defaultDate"), tooltip: trans("calendar.defaultDateTooltip"), })} - {children.events.propertyView({label: trans("calendar.events")})} - {license == "" ? null : children.resourcesEvents.propertyView({label: trans("calendar.resourcesEvents")})} + {controlItem({filterText: 'events'}, ( +

{trans("calendar.events")}

+ ))} + {children.events.propertyView({ + title: "Events", + newOptionLabel: "Event", + })}
{ license != "" &&
- {children.resources.propertyView({label: trans("calendar.resources")})} {children.resourceName.propertyView({label: trans("calendar.resourcesName")})} + {children.resources.propertyView({label: trans("calendar.resources")})}
}
@@ -818,11 +940,35 @@ CalendarBasicComp = class extends CalendarBasicComp { } }; +CalendarBasicComp = migrateOldData(CalendarBasicComp, fixOldData) + const TmpCalendarComp = withExposingConfigs(CalendarBasicComp, [ - new NameConfig("events", trans("calendar.events")), - new NameConfig("resourcesEvents", trans("calendar.resourcesEvents")), - new NameConfig("resources", trans("calendar.resources")), NameConfigHidden, + new NameConfig("resources", trans("calendar.resources")), + depsConfig({ + name: "allEvents", + desc: trans("calendar.events"), + depKeys: ["events"], + func: (input: { events: any[]; }) => { + return input.events; + }, + }), + depsConfig({ + name: "events", + desc: trans("calendar.events"), + depKeys: ["events"], + func: (input: { events: any[]; }) => { + return input.events.filter(event => !Boolean(event.resourceId)); + }, + }), + depsConfig({ + name: "resourcesEvents", + desc: trans("calendar.resourcesEvents"), + depKeys: ["events"], + func: (input: { events: any[]; }) => { + return input.events.filter(event => Boolean(event.resourceId)); + }, + }), ]); let CalendarComp = withMethodExposing(TmpCalendarComp, [ diff --git a/client/packages/lowcoder-comps/src/comps/calendarComp/calendarConstants.tsx b/client/packages/lowcoder-comps/src/comps/calendarComp/calendarConstants.tsx index 0b84dba58..a1b3f4203 100644 --- a/client/packages/lowcoder-comps/src/comps/calendarComp/calendarConstants.tsx +++ b/client/packages/lowcoder-comps/src/comps/calendarComp/calendarConstants.tsx @@ -765,8 +765,6 @@ export const Event = styled.div<{ } `; - - export const FormWrapper = styled(Form)<{ $modalStyle?: EventModalStyleType }>` @@ -934,10 +932,10 @@ export const FirstDayOptions = [ }, ]; -export const defaultData = [ +export const defaultEvents = [ { id: "1", - title: "Coding", + label: "Coding", start: dayjs().hour(10).minute(0).second(0).format(DATE_TIME_FORMAT), end: dayjs().hour(12).minute(30).second(0).format(DATE_TIME_FORMAT), color: "#079968", @@ -956,18 +954,42 @@ export const defaultData = [ }, { id: "2", - title: "Rest", + label: "Rest", start: dayjs().hour(24).format(DATE_FORMAT), end: dayjs().hour(48).format(DATE_FORMAT), color: "#079968", allDay: true, }, + { + id: "3", + resourceId: "d1", + label: "event 1", + start: dayjs().hour(10).minute(0).second(0).format(DATE_TIME_FORMAT), + end: dayjs().hour(17).minute(30).second(0).format(DATE_TIME_FORMAT), + color: "#079968", + }, + { + id: "4", + resourceId: "b", + label: "event 5", + start: dayjs().hour(8).minute(0).second(0).format(DATE_TIME_FORMAT), + end: dayjs().hour(16).minute(30).second(0).format(DATE_TIME_FORMAT), + color: "#079968", + }, + { + id: "5", + resourceId: "a", + label: "event 3", + start: dayjs().hour(12).minute(0).second(0).format(DATE_TIME_FORMAT), + end: dayjs().hour(21).minute(30).second(0).format(DATE_TIME_FORMAT), + color: "#079968", + }, ]; export const resourcesEventsDefaultData = [ { id: "1", resourceId: "d1", - title: "event 1", + label: "event 1", start: dayjs().hour(10).minute(0).second(0).format(DATE_TIME_FORMAT), end: dayjs().hour(17).minute(30).second(0).format(DATE_TIME_FORMAT), color: "#079968", @@ -975,7 +997,7 @@ export const resourcesEventsDefaultData = [ { id: "2", resourceId: "b", - title: "event 5", + label: "event 5", start: dayjs().hour(8).minute(0).second(0).format(DATE_TIME_FORMAT), end: dayjs().hour(16).minute(30).second(0).format(DATE_TIME_FORMAT), color: "#079968", @@ -983,7 +1005,7 @@ export const resourcesEventsDefaultData = [ { id: "3", resourceId: "a", - title: "event 3", + label: "event 3", start: dayjs().hour(12).minute(0).second(0).format(DATE_TIME_FORMAT), end: dayjs().hour(21).minute(30).second(0).format(DATE_TIME_FORMAT), color: "#079968", diff --git a/client/packages/lowcoder-comps/src/comps/calendarComp/eventOptionsControl.tsx b/client/packages/lowcoder-comps/src/comps/calendarComp/eventOptionsControl.tsx new file mode 100644 index 000000000..5b74888c9 --- /dev/null +++ b/client/packages/lowcoder-comps/src/comps/calendarComp/eventOptionsControl.tsx @@ -0,0 +1,169 @@ +import { + MultiCompBuilder, + StringControl, + BoolControl, + ColorControl, + optionsControl, + NewChildren, + RecordConstructorToComp, + controlItem +} from 'lowcoder-sdk'; +import { default as Divider } from "antd/es/divider"; +import { trans } from "../../i18n/comps"; +import styled from "styled-components"; +import { defaultEvents } from './calendarConstants'; + +const PropertyViewWrapper = styled.div` + max-height: 80vh; + height: 100%; + overflow-y: auto; + display: flex; + flex-direction: column; + gap: 8px; +`; + +type NewChildren = typeof NewChildren; +type RecordConstructorToComp = typeof RecordConstructorToComp; + +const eventChildrenMap = { + id: StringControl, + label: StringControl, + detail: StringControl, + groupId: StringControl, + resourceId: StringControl, + start: StringControl, // TODO: Create Date,Time & DateTime Controls + end: StringControl, + allDay: BoolControl, + color: ColorControl, + backgroundColor: ColorControl, + titleColor: ColorControl, + detailColor: ColorControl, + titleFontWeight: StringControl, + titleFontStyle: StringControl, + detailFontWeight: StringControl, + detailFontStyle: StringControl, + animation: StringControl, + animationDelay: StringControl, + animationDuration: StringControl, + animationIterationCount: StringControl, +}; + +type EventChildrenType = NewChildren>; + +const EventGeneral = ({ childrenMap }: { childrenMap: EventChildrenType }) => ( + <> + {controlItem({filterText: 'general'}, ( + <>{trans("calendar.general")} + ))} + {childrenMap.id.propertyView({ + label: trans("calendar.eventId"), + tooltip: trans("calendar.eventIdTooltip"), + })} + {childrenMap.label.propertyView({ + label: trans("calendar.eventName"), + })} + {childrenMap.detail.propertyView({ + label: trans("calendar.eventdetail"), + })} + {childrenMap.groupId.propertyView({ + label: trans("calendar.eventGroupId"), + tooltip: trans("calendar.groupIdTooltip"), + })} + {childrenMap.resourceId.propertyView({ + label: trans("calendar.eventResourceId"), + })} + {childrenMap.start.propertyView({ + label: trans("calendar.eventStartTime"), + })} + {childrenMap.end.propertyView({ + label: trans("calendar.eventEndTime"), + })} + {childrenMap.allDay.propertyView({ + label: trans("calendar.eventAllDay"), + })} + +); + +const EventColorStyles = ({ childrenMap }: { childrenMap: EventChildrenType }) => ( + <> + {controlItem({filterText: 'colorStyles'}, ( + <>{trans("calendar.colorStyles")} + ))} + {childrenMap.titleColor.propertyView({ + label: trans("calendar.eventTitleColor"), + })} + {childrenMap.detailColor.propertyView({ + label: trans("calendar.eventdetailColor"), + })} + {childrenMap.color.propertyView({ + label: trans("calendar.eventColor"), + })} + {childrenMap.backgroundColor.propertyView({ + label: trans("calendar.eventBackgroundColor"), + })} + +); + +const EventFontStyles = ({ childrenMap }: { childrenMap: EventChildrenType }) => ( + <> + {controlItem({filterText: 'fontStyles'}, ( + <>{trans("calendar.fontStyles")} + ))} + {childrenMap.titleFontWeight.propertyView({ + label: trans("calendar.eventTitleFontWeight"), + })} + {childrenMap.titleFontStyle.propertyView({ + label: trans("calendar.eventTitleFontStyle"), + })} + {childrenMap.detailFontWeight.propertyView({ + label: trans("calendar.eventdetailFontWeight"), + })} + {childrenMap.detailFontStyle.propertyView({ + label: trans("calendar.eventdetailFontStyle"), + })} + +); + +const EventAnimations = ({ childrenMap }: { childrenMap: EventChildrenType }) => ( + <> + {controlItem({filterText: 'animations'}, ( + <>{trans("calendar.animations")} + ))} + {childrenMap.animation.propertyView({ + label: trans("calendar.animationType"), + })} + {childrenMap.animationDelay.propertyView({ + label: trans("calendar.animationDelay"), + })} + {childrenMap.animationDuration.propertyView({ + label: trans("calendar.animationDuration"), + })} + {childrenMap.animationIterationCount.propertyView({ + label: trans("calendar.animationIterationCount"), + })} + +); + +let EventOption = new MultiCompBuilder(eventChildrenMap, + (props: any) => props +) +.setPropertyViewFn((children: EventChildrenType) => ( + + + + + + + + + +)) +.build(); + +export const EventOptionControl = optionsControl( + EventOption, + { + initOptions: defaultEvents, + uniqField: "id", + } +); 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 c421bb60e..b1819204a 100644 --- a/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts +++ b/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts @@ -329,6 +329,10 @@ export const en = { eventIdRequire: "Enter Event ID", eventIdTooltip: "Unique ID for each event", eventIdExist: "ID Exists", + eventStartTime: "Start Time", + eventEndTime: "End Time", + eventAllDay: "All Day", + eventResourceId: "Resource ID", dragDropEventHandlers: "Drag/Drop Event Handlers", general: "General", colorStyles:"Color Styles",