diff --git a/client/VERSION b/client/VERSION index e46a05b19..68167133b 100644 --- a/client/VERSION +++ b/client/VERSION @@ -1 +1 @@ -2.6.4 \ No newline at end of file +2.6.5 \ No newline at end of file diff --git a/client/package.json b/client/package.json index f8a736710..772362570 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "lowcoder-frontend", - "version": "2.6.4", + "version": "2.6.5", "type": "module", "private": true, "workspaces": [ diff --git a/client/packages/lowcoder-comps/package.json b/client/packages/lowcoder-comps/package.json index 4fb56a02f..fb022019e 100644 --- a/client/packages/lowcoder-comps/package.json +++ b/client/packages/lowcoder-comps/package.json @@ -1,6 +1,6 @@ { "name": "lowcoder-comps", - "version": "2.6.5", + "version": "2.6.6", "type": "module", "license": "MIT", "dependencies": { diff --git a/client/packages/lowcoder-comps/src/comps/calendarComp/calendarComp.tsx b/client/packages/lowcoder-comps/src/comps/calendarComp/calendarComp.tsx index 61305500f..43ddfbaf3 100644 --- a/client/packages/lowcoder-comps/src/comps/calendarComp/calendarComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/calendarComp/calendarComp.tsx @@ -15,7 +15,7 @@ import timeGridPlugin from "@fullcalendar/timegrid"; import interactionPlugin, { EventResizeDoneArg } from "@fullcalendar/interaction"; import listPlugin from "@fullcalendar/list"; import allLocales from "@fullcalendar/core/locales-all"; -import { EventContentArg, DateSelectArg, EventDropArg } from "@fullcalendar/core"; +import { EventContentArg, DateSelectArg, EventDropArg, EventInput } from "@fullcalendar/core"; import momentPlugin from "@fullcalendar/moment"; import ErrorBoundary from "./errorBoundary"; @@ -58,6 +58,8 @@ import { depsConfig, stateComp, JSONObject, + isDynamicSegment, + Theme, } from 'lowcoder-sdk'; import { @@ -81,11 +83,14 @@ import { resourcesDefaultData, resourceTimeLineHeaderToolbar, resourceTimeGridHeaderToolbar, + formattedEvents, } from "./calendarConstants"; import { EventOptionControl } from "./eventOptionsControl"; import { EventImpl } from "@fullcalendar/core/internal"; import DatePicker from "antd/es/date-picker"; +type Theme = typeof Theme; + const DATE_TIME_FORMAT = "YYYY-MM-DD HH:mm:ss"; function fixOldData(oldData: any) { @@ -206,6 +211,7 @@ let childrenMap: any = { showVerticalScrollbar: withDefault(BoolControl, false), showResourceEventsInFreeView: withDefault(BoolControl, false), initialData: stateComp({}), + updatedEventsData: stateComp(defaultEvents), updatedEvents: stateComp({}), insertedEvents: stateComp({}), deletedEvents: stateComp({}), @@ -251,15 +257,16 @@ let CalendarBasicComp = (function () { showVerticalScrollbar?:boolean; showResourceEventsInFreeView?: boolean; initialData: Array; + updatedEventsData: Array; inputFormat: string; }, dispatch: any) => { const comp = useContext(EditorContext)?.getUICompByName( useContext(CompNameContext) ); - const theme = useContext(ThemeContext); + const theme: Theme | undefined = useContext(ThemeContext); const ref = createRef(); - const editEvent = useRef(); + const editEvent = useRef(); const initData = useRef(false); const [form] = Form.useForm(); const [left, setLeft] = useState(undefined); @@ -294,63 +301,75 @@ let CalendarBasicComp = (function () { const currentEvents = useMemo(() => { if (props.showResourceEventsInFreeView && Boolean(props.licenseKey)) { - return props.events.filter((event: { resourceId: any; }) => Boolean(event.resourceId)) + return props.updatedEventsData.filter((event: { resourceId?: any; }) => Boolean(event.resourceId)) } return currentView == "resourceTimelineDay" || currentView == "resourceTimeGridDay" - ? props.events.filter((event: { resourceId: any; }) => Boolean(event.resourceId)) - : props.events.filter((event: { resourceId: any; }) => !Boolean(event.resourceId)); + ? props.updatedEventsData.filter((event: { resourceId?: any; }) => Boolean(event.resourceId)) + : props.updatedEventsData.filter((event: { resourceId?: any; }) => !Boolean(event.resourceId)); }, [ currentView, - props.events, + props.updatedEventsData, props.showResourceEventsInFreeView, ]) // we use one central stack of events for all views - const events = useMemo(() => { - return Array.isArray(currentEvents) ? currentEvents.map((item: EventType) => { - return { - title: item.label, - id: item.id, - start: dayjs(item.start, DateParser).format(), - end: dayjs(item.end, DateParser).format(), - allDay: item.allDay, - ...(item.resourceId ? { resourceId: item.resourceId } : {}), - ...(item.groupId ? { groupId: item.groupId } : {}), - backgroundColor: item.backgroundColor, - 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]; + const events: EventInput = useMemo(() => { + return formattedEvents(currentEvents, theme); }, [currentEvents, theme]) + const initialEvents = useMemo(() => { + let eventsList:EventType[] = []; + if (props.showResourceEventsInFreeView && Boolean(props.licenseKey)) { + eventsList = props.events.filter((event: { resourceId?: any; }) => Boolean(event.resourceId)) + } + else { + if (currentView == "resourceTimelineDay" || currentView == "resourceTimeGridDay") { + eventsList = props.events.filter((event: { resourceId?: any; }) => Boolean(event.resourceId)) + } else { + eventsList = props.events.filter((event: { resourceId?: any; }) => !Boolean(event.resourceId)); + } + } + + return eventsList.map(event => ({ + ...event, + start: dayjs(event.start, DateParser).format(), + end: dayjs(event.end, DateParser).format(), + })); + }, [ + JSON.stringify(props.events), + ]) + + useEffect(() => { + initData.current = false; + }, [JSON.stringify(props.events)]); + useEffect(() => { if (initData.current) return; const mapData: Record = {}; - events?.forEach((item: any, index: number) => { + initialEvents?.forEach((item: any, index: number) => { mapData[`${item.id}`] = index; }) - if (!initData.current && events?.length && comp?.children?.comp?.children?.initialData) { + if (!initData.current && initialEvents?.length && comp?.children?.comp?.children?.initialData) { setInitDataMap(mapData); comp?.children?.comp?.children?.initialData?.dispatch?.( - comp?.children?.comp?.children?.initialData?.changeValueAction?.([...events]) + comp?.children?.comp?.children?.initialData?.changeValueAction?.([...initialEvents]) + ); + + const eventsList = props.events.map((event: EventType) => ({ + ...event, + start: dayjs(event.start, DateParser).format(), + end: dayjs(event.end, DateParser).format(), + })); + + comp?.children?.comp?.children?.updatedEventsData?.dispatch?.( + comp?.children?.comp?.children?.updatedEventsData?.changeValueAction?.(eventsList) ); + initData.current = true; } - }, [JSON.stringify(events), comp?.children?.comp?.children?.initialData]); + }, [JSON.stringify(initialEvents), comp?.children?.comp?.children?.initialData]); const resources = useMemo(() => props.resources.value, [props.resources.value]); @@ -413,35 +432,10 @@ let CalendarBasicComp = (function () { const findUpdatedInsertedDeletedEvents = useCallback((data: Array) => { if (!initData.current) return; - let eventsData: Array> = currentView == "resourceTimelineDay" || currentView == "resourceTimeGridDay" + const eventsData: Array = currentView == "resourceTimelineDay" || currentView == "resourceTimeGridDay" ? data.filter((event: { resourceId?: string; }) => Boolean(event.resourceId)) : data.filter((event: { resourceId?: string; }) => !Boolean(event.resourceId)); - eventsData = eventsData.map((item) => ({ - title: item.label, - id: item.id, - start: dayjs(item.start, DateParser).format(), - end: dayjs(item.end, DateParser).format(), - allDay: item.allDay, - ...(item.resourceId ? { resourceId: item.resourceId } : {}), - ...(item.groupId ? { groupId: item.groupId } : {}), - backgroundColor: item.backgroundColor, - 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 - } - })); - const mapData: Record = {}; eventsData?.forEach((item: any, index: number) => { mapData[`${item.id}`] = index; @@ -458,13 +452,8 @@ let CalendarBasicComp = (function () { }, [initDataMap, currentView, props.initialData, initData.current]); 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) + comp?.children?.comp?.children?.updatedEventsData?.dispatch?.( + comp?.children?.comp?.children?.updatedEventsData?.changeValueAction?.(data) ); findUpdatedInsertedDeletedEvents(data); @@ -522,7 +511,7 @@ let CalendarBasicComp = (function () { className="event-remove" onClick={(e) => { e.stopPropagation(); - const events = props.events.filter( + const events = props.updatedEventsData.filter( (item: EventType) => item.id !== eventInfo.event.id ); handleEventDataChange(events); @@ -541,7 +530,7 @@ let CalendarBasicComp = (function () { }, [ theme, props.style, - props.events, + props.updatedEventsData, props.showAllDay, handleEventDataChange, ]); @@ -780,7 +769,7 @@ let CalendarBasicComp = (function () { end, allDay, } = form.getFieldsValue(); - const idExist = props.events.findIndex( + const idExist = props.updatedEventsData.findIndex( (item: EventType) => item.id === id ); if (idExist > -1 && id !== eventId) { @@ -790,7 +779,7 @@ let CalendarBasicComp = (function () { throw new Error(); } if (ifEdit) { - const changeEvents = props.events.map((item: EventType) => { + const changeEvents = props.updatedEventsData.map((item: EventType) => { if (item.id === eventId) { return { ...item, @@ -843,7 +832,7 @@ let CalendarBasicComp = (function () { ...(titleColor !== undefined ? { titleColor } : null), ...(detailColor !== undefined ? { detailColor } : null), }; - handleEventDataChange([...props.events, createInfo]); + handleEventDataChange([...props.updatedEventsData, createInfo]); } form.resetFields(); }); //small change @@ -855,14 +844,14 @@ let CalendarBasicComp = (function () { }, [ form, editEvent, - props.events, + props.updatedEventsData, props?.modalStyle, props?.animationStyle, handleEventDataChange, ]); const handleDbClick = useCallback(() => { - const event = props.events.find( + const event = props.updatedEventsData.find( (item: EventType) => item.id === editEvent.current?.id ) as EventType; if (!props.editable || !editEvent.current) { @@ -880,7 +869,7 @@ let CalendarBasicComp = (function () { } }, [ editEvent, - props.events, + props.updatedEventsData, props.editable, onEventVal, showModal, @@ -911,7 +900,7 @@ let CalendarBasicComp = (function () { const updateEventsOnDragOrResize = useCallback((eventInfo: EventImpl) => { const {extendedProps, title, ...event} = eventInfo.toJSON(); - let eventsList = [...props.events]; + let eventsList = [...props.updatedEventsData]; const eventIdx = eventsList.findIndex( (item: EventType) => item.id === event.id ); @@ -923,7 +912,7 @@ let CalendarBasicComp = (function () { }; handleEventDataChange(eventsList); } - }, [props.events, handleEventDataChange]); + }, [props.updatedEventsData, handleEventDataChange]); const handleDrop = useCallback((eventInfo: EventDropArg) => { updateEventsOnDragOrResize(eventInfo.event); @@ -987,7 +976,7 @@ let CalendarBasicComp = (function () { select={(info) => handleCreate(info)} eventClick={(info) => { const event = events.find( - (item: EventType) => item.id === info.event.id + (item: EventInput) => item.id === info.event.id ); editEvent.current = event; setTimeout(() => { @@ -1018,9 +1007,9 @@ let CalendarBasicComp = (function () { }} eventsSet={(info) => { let needChange = false; - let changeEvents: EventType[] = []; + let changeEvents: EventInput[] = []; info.forEach((item) => { - const event = events.find((i: EventType) => i.id === item.id); + const event = events.find((i: EventInput) => i.id === item.id); const start = dayjs(item.start, DateParser).format(); const end = dayjs(item.end, DateParser).format(); if ( @@ -1076,7 +1065,7 @@ 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; }; showVerticalScrollbar: { propertyView: (arg0: { label: string; }) => any; }; showResourceEventsInFreeView: { propertyView: (arg0: { label: string; }) => any; }; inputFormat: { propertyView: (arg0: {}) => any; }; @@ -1172,25 +1161,25 @@ const TmpCalendarComp = withExposingConfigs(CalendarBasicComp, [ depsConfig({ name: "allEvents", desc: trans("calendar.events"), - depKeys: ["events"], - func: (input: { events: any[]; }) => { - return input.events; + depKeys: ["updatedEventsData"], + func: (input: { updatedEventsData: any[]; }) => { + return input.updatedEventsData; }, }), depsConfig({ name: "events", desc: trans("calendar.events"), - depKeys: ["events"], - func: (input: { events: any[]; }) => { - return input.events.filter(event => !Boolean(event.resourceId)); + depKeys: ["updatedEventsData"], + func: (input: { updatedEventsData: any[]; }) => { + return input.updatedEventsData.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)); + depKeys: ["updatedEventsData"], + func: (input: { updatedEventsData: any[]; }) => { + return input.updatedEventsData.filter(event => Boolean(event.resourceId)); }, }), depsConfig({ diff --git a/client/packages/lowcoder-comps/src/comps/calendarComp/calendarConstants.tsx b/client/packages/lowcoder-comps/src/comps/calendarComp/calendarConstants.tsx index bb1a42d01..306f90a79 100644 --- a/client/packages/lowcoder-comps/src/comps/calendarComp/calendarConstants.tsx +++ b/client/packages/lowcoder-comps/src/comps/calendarComp/calendarConstants.tsx @@ -15,7 +15,10 @@ import { lightenColor, toHex, UnderlineCss, - EventModalStyleType + EventModalStyleType, + DateParser, + isValidColor, + Theme, } from "lowcoder-sdk"; import styled from "styled-components"; import dayjs from "dayjs"; @@ -27,6 +30,10 @@ import { } from "@fullcalendar/core"; import { default as Form } from "antd/es/form"; +type Theme = typeof Theme; +type EventModalStyleType = typeof EventModalStyleType; +type CalendarStyleType = typeof CalendarStyleType; + export const Wrapper = styled.div<{ $editable?: boolean; $style?: CalendarStyleType; @@ -1135,3 +1142,32 @@ export const viewClassNames = (info: ViewContentArg) => { return className; }; +export const formattedEvents = (events: EventType[], theme?: Theme) => { + return events.map((item: EventType) => { + return { + title: item.label, + label: item.label, + id: item.id, + start: dayjs(item.start, DateParser).format(), + end: dayjs(item.end, DateParser).format(), + allDay: item.allDay, + ...(item.resourceId ? { resourceId: item.resourceId } : {}), + ...(item.groupId ? { groupId: item.groupId } : {}), + backgroundColor: item.backgroundColor, + 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 + } + } + }) +} diff --git a/client/packages/lowcoder-design/src/components/keyValueList.tsx b/client/packages/lowcoder-design/src/components/keyValueList.tsx index a847e0e59..264e7f039 100644 --- a/client/packages/lowcoder-design/src/components/keyValueList.tsx +++ b/client/packages/lowcoder-design/src/components/keyValueList.tsx @@ -96,6 +96,7 @@ export const KeyValueList = (props: { onDelete: (item: ReactNode, index: number) => void; isStatic?: boolean; indicatorForAll?: boolean; + allowDeletingAll?: boolean; }) => { return ( <> @@ -105,8 +106,8 @@ export const KeyValueList = (props: { {item} {!props.isStatic && props.list.length > 1 && props.onDelete(item, index)} - $forbidden={props.list.length === 1} + onClick={() => (props.allowDeletingAll || (!props.allowDeletingAll && props.list.length > 1)) && props.onDelete(item, index)} + $forbidden={!props.allowDeletingAll && props.list.length === 1} /> } diff --git a/client/packages/lowcoder/package.json b/client/packages/lowcoder/package.json index f3f02092e..9338fa428 100644 --- a/client/packages/lowcoder/package.json +++ b/client/packages/lowcoder/package.json @@ -1,6 +1,6 @@ { "name": "lowcoder", - "version": "2.6.4", + "version": "2.6.5", "private": true, "type": "module", "main": "src/index.sdk.ts", diff --git a/client/packages/lowcoder/src/appView/bootstrapAt.tsx b/client/packages/lowcoder/src/appView/bootstrapAt.tsx index 1ba424eb3..ed31fd8c7 100644 --- a/client/packages/lowcoder/src/appView/bootstrapAt.tsx +++ b/client/packages/lowcoder/src/appView/bootstrapAt.tsx @@ -2,8 +2,6 @@ import { loadComps } from "comps"; import type { AppViewInstanceOptions } from "./AppViewInstance"; import { createRoot } from "react-dom/client"; -loadComps(); - export async function bootstrapAppAt( appId: string, node: Element | null, @@ -14,6 +12,8 @@ export async function bootstrapAppAt( return; } + loadComps(); + const { AppViewInstance } = await import("./AppViewInstance"); return new AppViewInstance(appId, node, createRoot(node), options); } diff --git a/client/packages/lowcoder/src/comps/comps/customComp/customComp.tsx b/client/packages/lowcoder/src/comps/comps/customComp/customComp.tsx index 053ff02af..bd58829cb 100644 --- a/client/packages/lowcoder/src/comps/comps/customComp/customComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/customComp/customComp.tsx @@ -191,7 +191,8 @@ function InnerCustomComponent(props: IProps) { iframe.addEventListener("load", handleIFrameLoad); // in dev, load from sdk bundle and on prod load from build package - const src = import.meta.env.DEV + const src = (REACT_APP_BUNDLE_TYPE && REACT_APP_BUNDLE_TYPE === 'sdk') + || (import.meta.env && import.meta.env.DEV) ? trans('customComponent.entryUrl') : `${window.location.origin}/custom_component/custom_component.html`; diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/tableCompView.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/tableCompView.tsx index 0325d5d75..792d22379 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/tableCompView.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/tableCompView.tsx @@ -243,7 +243,7 @@ const TableWrapper = styled.div<{ position: -webkit-sticky; // top: ${props.$fixedToolbar ? '47px' : '0'}; top: 0; - z-index: 99; + z-index: 2; ` } > tr { @@ -256,7 +256,14 @@ const TableWrapper = styled.div<{ color: ${(props) => props.$headerStyle.headerText}; // border-inline-end: ${(props) => `${props.$headerStyle.borderWidth} solid ${props.$headerStyle.border}`} !important; - + /* Proper styling for fixed header cells */ + &.ant-table-cell-fix-left, &.ant-table-cell-fix-right { + z-index: 1; + background: ${(props) => props.$headerStyle.headerBackground}; + } + + + > div { margin: ${(props) => props.$headerStyle.margin}; @@ -295,7 +302,27 @@ const TableWrapper = styled.div<{ td { padding: 0px 0px; - // ${(props) => props.$showHRowGridBorder ?'border-bottom: 1px solid #D7D9E0 !important;': `border-bottom: 0px;`} + // ${(props) => props.$showHRowGridBorder ? 'border-bottom: 1px solid #D7D9E0 !important;': `border-bottom: 0px;`} + + /* Proper styling for Fixed columns in the table body */ + &.ant-table-cell-fix-left, &.ant-table-cell-fix-right { + z-index: 1; + background: inherit; + background-color: ${(props) => props.$style.background}; + transition: background-color 0.3s; + } + + } + + /* Fix for selected and hovered rows */ + tr.ant-table-row-selected td.ant-table-cell-fix-left, + tr.ant-table-row-selected td.ant-table-cell-fix-right { + background-color: ${(props) => props.$rowStyle?.selectedRowBackground || '#e6f7ff'} !important; + } + + tr.ant-table-row:hover td.ant-table-cell-fix-left, + tr.ant-table-row:hover td.ant-table-cell-fix-right { + background-color: ${(props) => props.$rowStyle?.hoverRowBackground || '#f5f5f5'} !important; } thead > tr:first-child { @@ -428,7 +455,7 @@ const TableTd = styled.td<{ } &:active { - color: ${(props) => props.$linkStyle?.activeText}}; + color: ${(props) => props.$linkStyle?.activeText}; } } } diff --git a/client/packages/lowcoder/src/comps/controls/actionSelector/actionSelectorControl.tsx b/client/packages/lowcoder/src/comps/controls/actionSelector/actionSelectorControl.tsx index 6166c3714..73d9d145a 100644 --- a/client/packages/lowcoder/src/comps/controls/actionSelector/actionSelectorControl.tsx +++ b/client/packages/lowcoder/src/comps/controls/actionSelector/actionSelectorControl.tsx @@ -251,7 +251,9 @@ function actionSelectorControl(needContext: boolean) { const ignorePromise = Promise.resolve(); const realNeedContext = needContext || getReduceContext().inEventContext; const actionPromise = () => { - return realNeedContext ? action.value.func() : this.children.comp.getView()(); + // return realNeedContext ? action.value.func() : this.children.comp.getView()(); + // commenting because it's using old context for event handlers inside list/grid + return this.children.comp.getView()(); }; handlePromiseAfterResult(action, ignored ? ignorePromise : actionPromise()); return this; diff --git a/client/packages/lowcoder/src/comps/controls/actionSelector/executeQueryAction.tsx b/client/packages/lowcoder/src/comps/controls/actionSelector/executeQueryAction.tsx index a368e73db..2ab7186ed 100644 --- a/client/packages/lowcoder/src/comps/controls/actionSelector/executeQueryAction.tsx +++ b/client/packages/lowcoder/src/comps/controls/actionSelector/executeQueryAction.tsx @@ -9,7 +9,7 @@ import { getPromiseAfterDispatch } from "util/promiseUtils"; import { trans } from "i18n"; import { withDefault } from "comps/generators"; import { keyValueListControl} from "comps/controls/keyValueListControl"; -import { useCallback } from "react"; +import { useCallback, useEffect } from "react"; const ExecuteQueryPropertyView = ({ comp, @@ -19,16 +19,25 @@ const ExecuteQueryPropertyView = ({ placement?: "query" | "table" }) => { const getQueryOptions = useCallback((editorState?: EditorState) => { - const options: { label: string; value: string; variables?: Record }[] = - editorState - ?.queryCompInfoList() - .map((info) => { + if (!editorState) return []; + const options: { + label: string; + value: string; + variables?: Record + }[] = editorState.getQueriesComp() + .getView() + .map((item) => { + const name = item.children.name.getView(); + const qVariables: Record = {}; + item.children.variables.toJsonValue().forEach(v => { + qVariables[v.key!] = ''; + }); return { - label: info.name, - value: info.name, - variables: info.data.variables, + label: name, + value: name, + variables: qVariables, } - }) + }) .filter( // Filter out the current query under query (option) => { @@ -67,7 +76,7 @@ const ExecuteQueryPropertyView = ({ indicatorForAll: true, }); }, [comp.children.queryVariables.getView()]) - + return ( <> @@ -114,19 +123,19 @@ const ExecuteQueryTmpAction = (function () { export class ExecuteQueryAction extends ExecuteQueryTmpAction { override getView() { const queryName = this.children.queryName.getView(); - // const queryParams = keyValueListToSearchStr(Array.isArray(this?.children?.query) ? (this.children.query as unknown as any[]).map((i: any) => i.getView() as KeyValue) : []); - const result = this.children.queryVariables.toJsonValue() - .filter(item => item.key !== "" && item.value !== "") - .map(item => ({[item.key as string]: item.value})) - .reduce((acc, curr) => Object.assign(acc, curr), {}); - - result.$queryName = queryName; if (!queryName) { return () => Promise.resolve(); } - return () => - getPromiseAfterDispatch( + let result = Object.values(this.children.queryVariables.getView()) + .filter((item) => item.children.key.getView() !== "" && item.children.value.getView() !== "") + .map((item) => ({[item.children.key.getView() as string]: {value: item.children.value.getView()}})) + .reduce((acc, curr) => Object.assign(acc, curr), {}); + + result.$queryName = {value: this.children.queryName.getView()}; + + return () => { + return getPromiseAfterDispatch( this.dispatch, routeByNameAction( queryName, @@ -134,6 +143,7 @@ export class ExecuteQueryAction extends ExecuteQueryTmpAction { ), { notHandledError: trans("eventHandler.notHandledError") } ); + } } displayName() { diff --git a/client/packages/lowcoder/src/comps/generators/withSelectedMultiContext.tsx b/client/packages/lowcoder/src/comps/generators/withSelectedMultiContext.tsx index 68b41a59b..8ba85913b 100644 --- a/client/packages/lowcoder/src/comps/generators/withSelectedMultiContext.tsx +++ b/client/packages/lowcoder/src/comps/generators/withSelectedMultiContext.tsx @@ -86,7 +86,12 @@ export function withSelectedMultiContext( action.editDSL || isCustomAction(action, "LazyCompReady") || isCustomAction(action, "moduleReady") - ) && action.path[1] === SELECTED_KEY) { + ) && ( + action.path[1] === SELECTED_KEY + || ( // special check added for modules inside list view + isCustomAction(action, "moduleReady") + && action.path[1] === this.selection) + )) { // broadcast const newAction = { ...action, diff --git a/client/packages/lowcoder/src/comps/queries/queryComp.tsx b/client/packages/lowcoder/src/comps/queries/queryComp.tsx index c8ac3032e..9b8fa0cab 100644 --- a/client/packages/lowcoder/src/comps/queries/queryComp.tsx +++ b/client/packages/lowcoder/src/comps/queries/queryComp.tsx @@ -37,7 +37,6 @@ import { FetchCheckNode, FetchInfo, fromRecord, - fromValue, isCustomAction, MultiBaseComp, multiChangeAction, @@ -369,7 +368,7 @@ QueryCompTmp = class extends QueryCompTmp { } if (action.type === CompActionTypes.EXECUTE_QUERY) { if (getReduceContext().disableUpdateState) return this; - if(!action.args) action.args = this.children.variables.toJsonValue().filter(kv => kv.key).reduce((acc, curr) => Object.assign(acc, {[curr.key as string]:curr.value}), {}); + action.args = action.args || {}; action.args.$queryName = this.children.name.getView(); return this.executeQuery(action); @@ -711,25 +710,18 @@ class QueryListComp extends QueryListTmpComp implements BottomResListComp { } nameAndExposingInfo(): NameAndExposingInfo { - const result: NameAndExposingInfo = {}; + let result: NameAndExposingInfo = {}; Object.values(this.children).forEach((comp) => { result[comp.children.name.getView()] = comp.exposingInfo(); - const variables = comp.children.variables.toJsonValue(); - variables.forEach((variable: Record) => { - result[variable.key] = { - property: fromRecord({ - value: fromValue(variable.value), - }), - propertyValue: { - value: variable.value, - }, - propertyDesc: {}, - methods: {}, - }; - }) + const variables = comp.children.variables.nameAndExposingInfo(); + if (variables) { + result = { + ...result, + ...variables, + } + } }); - return result; } diff --git a/client/packages/lowcoder/src/comps/queries/queryComp/variablesComp.tsx b/client/packages/lowcoder/src/comps/queries/queryComp/variablesComp.tsx index 337a2df61..538277f2b 100644 --- a/client/packages/lowcoder/src/comps/queries/queryComp/variablesComp.tsx +++ b/client/packages/lowcoder/src/comps/queries/queryComp/variablesComp.tsx @@ -1,4 +1,4 @@ -import { simpleMultiComp } from "../../generators"; +import { MultiCompBuilder, simpleMultiComp } from "../../generators"; import { SimpleNameComp } from "@lowcoder-ee/comps/comps/simpleNameComp"; import { StringControl } from "@lowcoder-ee/comps/controls/codeControl"; import { list } from "@lowcoder-ee/comps/generators/list"; @@ -9,7 +9,9 @@ import { KeyValueList } from "components/keyValueList"; import { trans } from "i18n"; import { PopupCard } from "components/popupCard"; import { EditorContext, EditorState } from "@lowcoder-ee/comps/editorState"; -import { migrateOldData } from "@lowcoder-ee/comps/generators/simpleGenerators"; +import { withExposingRaw } from "@lowcoder-ee/comps/generators/withExposing"; +import { NameAndExposingInfo } from "@lowcoder-ee/comps/utils/exposingTypes"; +import { fromRecord } from "lowcoder-core"; interface VariablesParams { // variables: string[]; todo support parse variables @@ -50,26 +52,29 @@ const VariableKey = ({children, dispatch}: any) => { ) } -const VariableItem = class extends simpleMultiComp({ + +const VariableItemBase = new MultiCompBuilder({ key: SimpleNameComp, value: StringControl, -}) { - propertyView(params: VariablesParams): ReactNode { - return ( - <> -
- -
- {this.children.value.propertyView({ placeholder: "value" })} -
-
- - ) - } -} +}, (props) => props) +.setPropertyViewFn((children, dispatch) => (<> +
+ +
+ {children.value.propertyView({ placeholder: "value" })} +
+
+)) +.build() + +const VariableItem = withExposingRaw(VariableItemBase, {}, (comp) => + fromRecord({ + value: comp.children.value.exposingNode(), + }) +); const VariableListPropertyViewWrapper = ({children}: any) => { const editorState = useContext(EditorContext); @@ -77,6 +82,14 @@ const VariableListPropertyViewWrapper = ({children}: any) => { } export const VariablesComp = class extends list(VariableItem) { + nameAndExposingInfo(): NameAndExposingInfo { + const result: NameAndExposingInfo = {}; + Object.values(this.children).forEach((comp) => { + result[comp.children.key.getView()] = comp.exposingInfo(); + }) + return result; + } + genNewName(editorState: EditorState) { const name = editorState.getNameGenerator().genItemName("variable"); return name; @@ -98,7 +111,8 @@ export const VariablesComp = class extends list(VariableItem) { {(editorState: EditorState) => ( child.propertyView(params))} + allowDeletingAll + list={this.getView().map((child) => child.getPropertyView())} onAdd={() => this.add(editorState)} onDelete={(item, index) => this.dispatch(this.deleteAction(index))} /> diff --git a/client/packages/lowcoder/src/pages/editor/bottom/BottomSidebar.tsx b/client/packages/lowcoder/src/pages/editor/bottom/BottomSidebar.tsx index 03ff67c75..70caf29d1 100644 --- a/client/packages/lowcoder/src/pages/editor/bottom/BottomSidebar.tsx +++ b/client/packages/lowcoder/src/pages/editor/bottom/BottomSidebar.tsx @@ -225,7 +225,15 @@ export function BottomSidebar(props: BottomSidebarProps) { refTreeComp.children.items.dispatch(pushAction); }); } - }, [itemsNotInTree, refTreeComp.children.items]); + }, [JSON.stringify(itemsNotInTree)]); + + useEffect(() => { + node?.items.forEach((item, idx) => { + if(!Boolean(item.id)) { + node?.deleteItem(idx); + } + }) + }, [node?.items]) return ( diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/FolderController.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/FolderController.java index 801d0c854..e15cac105 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/FolderController.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/FolderController.java @@ -8,6 +8,7 @@ import org.lowcoder.api.util.GidService; import org.lowcoder.domain.application.model.ApplicationType; import org.lowcoder.domain.folder.model.Folder; +import org.lowcoder.domain.folder.model.FolderElement; import org.lowcoder.domain.folder.service.FolderElementRelationService; import org.lowcoder.domain.folder.service.FolderService; import org.lowcoder.domain.permission.model.ResourceRole; @@ -92,7 +93,7 @@ public Mono> getElements(@RequestParam(value = "id", require @Override public Mono> move(@PathVariable("id") String applicationLikeId, @RequestParam(value = "targetFolderId", required = false) String targetFolderId) { - return folderElementRelationService.getByElementIds(List.of(applicationLikeId)).next().flatMap(folderElement -> + return folderElementRelationService.getByElementIds(List.of(applicationLikeId)).next().defaultIfEmpty(new FolderElement(null, null)).flatMap(folderElement -> gidService.convertFolderIdToObjectId(targetFolderId).flatMap(objectId -> folderApiService.move(applicationLikeId, objectId.orElse(null)) .then(businessEventPublisher.publishApplicationCommonEvent(applicationLikeId, folderElement.folderId(), objectId.orElse(null), APPLICATION_MOVE)) diff --git a/server/api-service/pom.xml b/server/api-service/pom.xml index 510750c14..973c5133d 100644 --- a/server/api-service/pom.xml +++ b/server/api-service/pom.xml @@ -12,7 +12,7 @@ - 2.6.4 + 2.6.5 17 ${java.version} ${java.version} @@ -164,4 +164,3 @@ - diff --git a/server/node-service/package.json b/server/node-service/package.json index 90b0536e3..d7676d9ce 100644 --- a/server/node-service/package.json +++ b/server/node-service/package.json @@ -1,6 +1,6 @@ { "name": "lowcoder-node-server", - "version": "2.6.4", + "version": "2.6.5", "private": true, "engines": { "node": "^14.18.0 || >=16.0.0"